blob: 0c5d2a5c8ab697630162aa3f0a4a2625e9d28281 [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;
John Spurlock2078caf2014-05-29 22:20:14 -040028import android.graphics.PixelFormat;
29import android.graphics.drawable.ColorDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080030import android.media.AudioManager;
31import android.media.AudioService;
32import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070033import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070035import android.media.VolumeProvider;
36import android.media.session.MediaController;
37import android.media.session.MediaController.VolumeInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070038import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.Handler;
40import android.os.Message;
41import android.os.Vibrator;
John Spurlockae641c92014-06-30 18:11:40 -040042import android.provider.Settings.Global;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040044import android.view.Gravity;
45import android.view.LayoutInflater;
46import android.view.MotionEvent;
47import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040048import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040049import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040050import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040051import android.view.Window;
52import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070053import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080055import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080056import android.widget.SeekBar.OnSeekBarChangeListener;
57
John Spurlock86005342014-05-23 11:58:00 -040058import com.android.internal.R;
59import com.android.systemui.statusbar.policy.ZenModeController;
60
Amith Yamasani2bbdd772011-02-02 18:54:13 -080061import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062
63/**
John Spurlock3346a802014-05-20 16:25:37 -040064 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070065 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066 * @hide
67 */
John Spurlock3346a802014-05-20 16:25:37 -040068public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040069 private static final String TAG = "VolumePanel";
70 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
John Spurlock3346a802014-05-20 16:25:37 -040072 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74 /**
75 * The delay before vibrating. This small period exists so if the user is
76 * moving to silent mode, it will not emit a short vibrate (it normally
77 * would since vibrate is between normal mode and silent mode using hardware
78 * keys).
79 */
80 public static final int VIBRATE_DELAY = 300;
81
82 private static final int VIBRATE_DURATION = 300;
83 private static final int BEEP_DURATION = 150;
84 private static final int MAX_VOLUME = 100;
85 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080086 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040087 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040088 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock3bd4fee2014-05-29 20:51:09 -040089 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
91 private static final int MSG_VOLUME_CHANGED = 0;
92 private static final int MSG_FREE_RESOURCES = 1;
93 private static final int MSG_PLAY_SOUND = 2;
94 private static final int MSG_STOP_SOUNDS = 3;
95 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080096 private static final int MSG_TIMEOUT = 5;
97 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080098 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070099 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
100 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
101 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700102 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400103 private static final int MSG_LAYOUT_DIRECTION = 12;
104 private static final int MSG_ZEN_MODE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400105 private static final int MSG_USER_ACTIVITY = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400107 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800108 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700109 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400110
John Spurlock86005342014-05-23 11:58:00 -0400111 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400112 protected final Context mContext;
113 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400114 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700115 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700116 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400117 private boolean mZenModeCapable;
John Spurlock8845da72014-07-07 21:29:48 -0400118 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400119 private int mTimeoutDelay = TIMEOUT_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120
Christopher Tatec4b78d22012-05-22 13:57:58 -0700121 // True if we want to play tones on the system stream when the master stream is specified.
122 private final boolean mPlayMasterStreamTones;
123
John Spurlock86005342014-05-23 11:58:00 -0400124
125 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400127 /** Dialog hosting the panel, if not embedded */
128 private final Dialog mDialog;
129 /** Parent view hosting the panel, if embedded */
130 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800131
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700132 /** The visible portion of the volume overlay */
133 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400134 /** Contains the slider and its touchable icons */
135 private final ViewGroup mSliderPanel;
John Spurlock86005342014-05-23 11:58:00 -0400136 /** The zen mode configuration panel view stub */
137 private final ViewStub mZenPanelStub;
138 /** The zen mode configuration panel view, once inflated */
139 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400140
John Spurlockae641c92014-06-30 18:11:40 -0400141 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800142
143 /** Currently active stream that shows up at the top of the list of sliders */
144 private int mActiveStreamType = -1;
145 /** All the slider controls mapped by stream type */
146 private HashMap<Integer,StreamControl> mStreamControls;
147
Amith Yamasani71def772011-10-12 12:25:24 -0700148 private enum StreamResources {
149 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
150 R.string.volume_icon_description_bluetooth,
151 R.drawable.ic_audio_bt,
152 R.drawable.ic_audio_bt,
153 false),
154 RingerStream(AudioManager.STREAM_RING,
155 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400156 com.android.systemui.R.drawable.ic_ringer_audible,
157 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700158 false),
159 VoiceStream(AudioManager.STREAM_VOICE_CALL,
160 R.string.volume_icon_description_incall,
161 R.drawable.ic_audio_phone,
162 R.drawable.ic_audio_phone,
163 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700164 AlarmStream(AudioManager.STREAM_ALARM,
165 R.string.volume_alarm,
166 R.drawable.ic_audio_alarm,
167 R.drawable.ic_audio_alarm_mute,
168 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700169 MediaStream(AudioManager.STREAM_MUSIC,
170 R.string.volume_icon_description_media,
171 R.drawable.ic_audio_vol,
172 R.drawable.ic_audio_vol_mute,
173 true),
174 NotificationStream(AudioManager.STREAM_NOTIFICATION,
175 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400176 com.android.systemui.R.drawable.ic_ringer_audible,
177 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400178 true),
179 // for now, use media resources for master volume
180 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700181 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400182 R.drawable.ic_audio_vol,
183 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700184 false),
185 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
186 R.string.volume_icon_description_media, //FIXME should have its own description
187 R.drawable.ic_media_route_on_holo_dark,
188 R.drawable.ic_media_route_disabled_holo_dark,
189 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700190
191 int streamType;
192 int descRes;
193 int iconRes;
194 int iconMuteRes;
195 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
196 boolean show;
197
198 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
199 this.streamType = streamType;
200 this.descRes = descRes;
201 this.iconRes = iconRes;
202 this.iconMuteRes = iconMuteRes;
203 this.show = show;
204 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700205 }
Amith Yamasani71def772011-10-12 12:25:24 -0700206
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800207 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700208 private static final StreamResources[] STREAMS = {
209 StreamResources.BluetoothSCOStream,
210 StreamResources.RingerStream,
211 StreamResources.VoiceStream,
212 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700213 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400214 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700215 StreamResources.MasterStream,
216 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800217 };
218
219 /** Object that contains data for each slider */
220 private class StreamControl {
221 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700222 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800223 ViewGroup group;
224 ImageView icon;
225 SeekBar seekbarView;
226 int iconRes;
227 int iconMuteRes;
228 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800229
230 // Synchronize when accessing this
231 private ToneGenerator mToneGenerators[];
232 private Vibrator mVibrator;
233
Eric Laurentc34dcc12012-09-10 13:51:52 -0700234 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700235 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700236
237 private static class WarningDialogReceiver extends BroadcastReceiver
238 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800239 private final Context mContext;
240 private final Dialog mDialog;
241 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700242
Eric Laurentfde16d52012-12-03 14:42:39 -0800243 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700244 mContext = context;
245 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800246 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700247 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
248 context.registerReceiver(this, filter);
249 }
250
251 @Override
252 public void onReceive(Context context, Intent intent) {
253 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800254 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700255 }
256
Alan Viverette494fb7b2014-04-10 18:12:56 -0700257 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700258 public void onDismiss(DialogInterface unused) {
259 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800260 cleanUp();
261 }
262
263 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700264 synchronized (sConfirmSafeVolumeLock) {
265 sConfirmSafeVolumeDialog = null;
266 }
John Spurlock1dad2722014-07-11 11:07:53 -0400267 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800268 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700269 }
270 }
271
272
John Spurlock86005342014-05-23 11:58:00 -0400273 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
John Spurlockae641c92014-06-30 18:11:40 -0400274 mTag = String.format("%s.%s.%08x", TAG, parent == null ? "Dialog" : "Embed", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400276 mParent = parent;
277 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800278 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800279
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400280 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700281 final Resources res = context.getResources();
282 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400283 if (useMasterVolume) {
284 for (int i = 0; i < STREAMS.length; i++) {
285 StreamResources streamRes = STREAMS[i];
286 streamRes.show = (streamRes.streamType == STREAM_MASTER);
287 }
288 }
John Spurlock86005342014-05-23 11:58:00 -0400289 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
John Spurlock86005342014-05-23 11:58:00 -0400290 if (parent == null) {
291 // dialog mode
292 mDialog = new Dialog(context) {
293 @Override
294 public boolean onTouchEvent(MotionEvent event) {
295 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
296 sConfirmSafeVolumeDialog == null) {
John Spurlock1dad2722014-07-11 11:07:53 -0400297 forceTimeout(0);
John Spurlock86005342014-05-23 11:58:00 -0400298 return true;
299 }
300 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700301 }
John Spurlock86005342014-05-23 11:58:00 -0400302 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700303
John Spurlock86005342014-05-23 11:58:00 -0400304 // Change some window properties
305 final Window window = mDialog.getWindow();
306 final LayoutParams lp = window.getAttributes();
307 lp.token = null;
308 // Offset from the top
309 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
John Spurlockae641c92014-06-30 18:11:40 -0400310 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
John Spurlock2078caf2014-05-29 22:20:14 -0400311 lp.format = PixelFormat.TRANSLUCENT;
John Spurlockae641c92014-06-30 18:11:40 -0400312 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
313 lp.gravity = Gravity.TOP;
John Spurlock86005342014-05-23 11:58:00 -0400314 window.setAttributes(lp);
John Spurlock86005342014-05-23 11:58:00 -0400315 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
316 window.requestFeature(Window.FEATURE_NO_TITLE);
317 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
318 | LayoutParams.FLAG_NOT_TOUCH_MODAL
John Spurlock2078caf2014-05-29 22:20:14 -0400319 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
320 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlock86005342014-05-23 11:58:00 -0400321 mDialog.setCanceledOnTouchOutside(true);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400322 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
John Spurlock86005342014-05-23 11:58:00 -0400323 mDialog.setOnDismissListener(new OnDismissListener() {
324 @Override
325 public void onDismiss(DialogInterface dialog) {
326 mActiveStreamType = -1;
327 mAudioManager.forceVolumeControlStream(mActiveStreamType);
John Spurlockae641c92014-06-30 18:11:40 -0400328 setZenPanelVisible(false);
John Spurlock86005342014-05-23 11:58:00 -0400329 }
330 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700331
John Spurlock86005342014-05-23 11:58:00 -0400332 mDialog.create();
John Spurlock7f1df5e2014-05-31 19:11:40 -0400333 // temporary workaround, until we support window-level shadows
John Spurlock2078caf2014-05-29 22:20:14 -0400334 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700335
John Spurlock86005342014-05-23 11:58:00 -0400336 mView = window.findViewById(R.id.content);
337 mView.setOnTouchListener(new View.OnTouchListener() {
338 @Override
339 public boolean onTouch(View v, MotionEvent event) {
340 resetTimeout();
341 return false;
342 }
343 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700344
John Spurlock86005342014-05-23 11:58:00 -0400345 } else {
346 // embedded mode
347 mDialog = null;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400348 mView = LayoutInflater.from(mContext).inflate(
John Spurlock7f8f22a2014-07-02 18:54:17 -0400349 com.android.systemui.R.layout.volume_panel, parent, false);
John Spurlock86005342014-05-23 11:58:00 -0400350 }
351 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
352 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlock86005342014-05-23 11:58:00 -0400353 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800354
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700356 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700357 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700358
John Spurlock86005342014-05-23 11:58:00 -0400359 mZenModeCapable = !useMasterVolume && mZenController != null;
John Spurlockae641c92014-06-30 18:11:40 -0400360 updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
John Spurlock86005342014-05-23 11:58:00 -0400361 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700362
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700363 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
364 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700365 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
366
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800367 listenToRingerMode();
368 }
369
John Spurlock7f8f22a2014-07-02 18:54:17 -0400370 public View getContentView() {
371 return mView;
372 }
373
John Spurlock86005342014-05-23 11:58:00 -0400374 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800375 mPanel.setLayoutDirection(layoutDirection);
376 updateStates();
377 }
378
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800379 private void listenToRingerMode() {
380 final IntentFilter filter = new IntentFilter();
381 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
382 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700383 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800384 public void onReceive(Context context, Intent intent) {
385 final String action = intent.getAction();
386
387 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
388 removeMessages(MSG_RINGER_MODE_CHANGED);
389 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
390 }
391 }
392 }, filter);
393 }
394
395 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400396 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700397 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700398 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700399 // TODO do we need to support a distinct mute property for remote?
400 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400401 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700402 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400403 }
404 }
405
406 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400407 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700408 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700409 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700410 if (mStreamControls != null) {
411 StreamControl sc = mStreamControls.get(streamType);
412 if (sc != null && sc.controller != null) {
413 VolumeInfo vi = sc.controller.getVolumeInfo();
414 return vi.getMaxVolume();
415 }
416 }
417 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400418 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700419 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400420 }
421 }
422
423 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400424 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700425 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700426 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700427 if (mStreamControls != null) {
428 StreamControl sc = mStreamControls.get(streamType);
429 if (sc != null && sc.controller != null) {
430 VolumeInfo vi = sc.controller.getVolumeInfo();
431 return vi.getCurrentVolume();
432 }
433 }
434 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400435 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700436 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400437 }
438 }
439
RoboErik19c95182014-06-23 15:38:48 -0700440 private void setStreamVolume(StreamControl sc, int index, int flags) {
441 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
442 if (sc.controller != null) {
443 sc.controller.setVolumeTo(index, flags);
444 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700445 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700446 }
447 } else if (getStreamVolume(sc.streamType) != index) {
448 if (sc.streamType == STREAM_MASTER) {
449 mAudioManager.setMasterVolume(index, flags);
450 } else {
451 mAudioManager.setStreamVolume(sc.streamType, index, flags);
452 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400453 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800454 }
455
456 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700457 final Resources res = mContext.getResources();
458 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
459 Context.LAYOUT_INFLATER_SERVICE);
460
Amith Yamasani71def772011-10-12 12:25:24 -0700461 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700462
Amith Yamasani71def772011-10-12 12:25:24 -0700463 for (int i = 0; i < STREAMS.length; i++) {
464 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700465
466 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700467
468 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700469 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400470 sc.group = (ViewGroup) inflater.inflate(
471 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800472 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400473 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800474 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700475 sc.icon.setContentDescription(res.getString(streamRes.descRes));
476 sc.iconRes = streamRes.iconRes;
477 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800478 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400479 sc.icon.setClickable(isNotificationOrRing(streamType));
480 if (sc.icon.isClickable()) {
481 sc.icon.setOnClickListener(new OnClickListener() {
482 @Override
483 public void onClick(View v) {
484 resetTimeout();
485 toggle(sc);
486 }
487 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400488 }
John Spurlock86005342014-05-23 11:58:00 -0400489 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700490 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700491 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400492 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700493 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800494 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700495 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800496 }
497 }
498
John Spurlock7f1df5e2014-05-31 19:11:40 -0400499 private void toggle(StreamControl sc) {
500 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
501 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
502 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
503 } else {
504 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
505 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
506 }
507 }
508
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800509 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400510 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800511
Alan Viverette494fb7b2014-04-10 18:12:56 -0700512 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800513 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400514 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800515 mActiveStreamType = -1;
516 } else {
John Spurlock86005342014-05-23 11:58:00 -0400517 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800518 mActiveStreamType = activeStreamType;
519 active.group.setVisibility(View.VISIBLE);
520 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400521 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400522 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800523 }
524 }
525
526 /** Update the mute and progress state of a slider */
527 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700528 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800529 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800530 // Force reloading the image resource
531 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800532 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400533 if (isNotificationOrRing(sc.streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700534 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
John Spurlock86005342014-05-23 11:58:00 -0400535 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700536 }
John Spurlock7f1df5e2014-05-31 19:11:40 -0400537 updateSliderEnabled(sc, muted, false);
538 }
539
John Spurlock5f640e42014-05-31 20:15:59 -0400540 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
541 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400542 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700543 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
544 // never disable touch interactions for remote playback, the muting is not tied to
545 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700546 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400547 } else if (fixedVolume ||
548 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800549 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700550 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400551 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400552 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400553 sc.icon.setEnabled(false);
554 sc.icon.setClickable(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700555 } else {
556 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400557 sc.icon.setEnabled(true);
Eric Laurent8c787522012-05-14 14:09:43 -0700558 }
John Spurlockae641c92014-06-30 18:11:40 -0400559 // show the silent hint when the disabled slider is touched in silent mode
560 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400561 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400562 sc.group.setOnTouchListener(null);
563 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400564 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400565 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400566 @Override
567 public boolean onTouch(View v, MotionEvent event) {
568 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400569 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400570 return false;
571 }
John Spurlockae641c92014-06-30 18:11:40 -0400572 };
573 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400574 }
575 }
576 }
577
John Spurlockae641c92014-06-30 18:11:40 -0400578 private void showSilentHint() {
579 if (mZenPanel != null) {
580 mZenPanel.showSilentHint();
581 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800582 }
583
John Spurlock7f1df5e2014-05-31 19:11:40 -0400584 private static boolean isNotificationOrRing(int streamType) {
585 return streamType == AudioManager.STREAM_RING
586 || streamType == AudioManager.STREAM_NOTIFICATION;
587 }
588
John Spurlockae641c92014-06-30 18:11:40 -0400589 public void setCallback(Callback callback) {
590 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400591 }
592
John Spurlock8845da72014-07-07 21:29:48 -0400593 private void updateTimeoutDelay() {
594 mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400595 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
596 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
597 : TIMEOUT_DELAY;
598 }
599
600 private boolean isZenPanelVisible() {
601 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400602 }
603
John Spurlockae641c92014-06-30 18:11:40 -0400604 private void setZenPanelVisible(boolean visible) {
605 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400606 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400607 if (visible) {
608 if (mZenPanel == null) {
609 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
610 mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
611 mZenPanel.setCallback(new ZenModePanel.Callback() {
612 @Override
613 public void onMoreSettings() {
614 if (mCallback != null) {
615 mCallback.onZenSettings();
616 }
John Spurlock86005342014-05-23 11:58:00 -0400617 }
John Spurlock86005342014-05-23 11:58:00 -0400618
John Spurlockae641c92014-06-30 18:11:40 -0400619 @Override
620 public void onInteraction() {
621 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400622 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800623
John Spurlockae641c92014-06-30 18:11:40 -0400624 @Override
625 public void onExpanded(boolean expanded) {
John Spurlock8845da72014-07-07 21:29:48 -0400626 if (mZenPanelExpanded == expanded) return;
627 mZenPanelExpanded = expanded;
628 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400629 resetTimeout();
630 }
631 });
632 }
633 mZenPanel.setVisibility(View.VISIBLE);
634 resetTimeout();
635 } else {
636 if (mZenPanel != null) {
637 mZenPanel.setVisibility(View.GONE);
638 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800639 }
John Spurlockea9938c2014-07-11 18:51:32 -0400640 if (changing) {
641 updateTimeoutDelay();
642 resetTimeout();
643 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800644 }
645
Eric Laurentfde16d52012-12-03 14:42:39 -0800646 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400647 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800648 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400649 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800650 updateSlider(sc);
651 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 }
653
John Spurlockae641c92014-06-30 18:11:40 -0400654 private void updateZenMode(int zen) {
655 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
656 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400657 }
658
John Spurlockae641c92014-06-30 18:11:40 -0400659 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400660 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400661 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400662 }
663
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 public void postVolumeChanged(int streamType, int flags) {
665 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700666 synchronized (this) {
667 if (mStreamControls == null) {
668 createSliders();
669 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800670 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 removeMessages(MSG_FREE_RESOURCES);
672 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
673 }
674
RoboErik19c95182014-06-23 15:38:48 -0700675 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700676 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
677 synchronized (this) {
678 if (mStreamControls == null) {
679 createSliders();
680 }
681 }
682 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700683 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700684 }
685
686 public void postRemoteSliderVisibility(boolean visible) {
687 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
688 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
689 }
690
691 /**
692 * Called by AudioService when it has received new remote playback information that
693 * would affect the VolumePanel display (mainly volumes). The difference with
694 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
695 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
696 * displayed.
697 * This special code path is due to the fact that remote volume updates arrive to AudioService
698 * asynchronously. So after AudioService has sent the volume update (which should be treated
699 * as a request to update the volume), the application will likely set a new volume. If the UI
700 * is still up, we need to refresh the display to show this new value.
701 */
702 public void postHasNewRemotePlaybackInfo() {
703 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
704 // don't create or prevent resources to be freed, if they disappear, this update came too
705 // late and shouldn't warrant the panel to be displayed longer
706 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
707 }
708
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400709 public void postMasterVolumeChanged(int flags) {
710 postVolumeChanged(STREAM_MASTER, flags);
711 }
712
Mike Lockwoodce952c82011-11-14 10:47:42 -0800713 public void postMuteChanged(int streamType, int flags) {
714 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700715 synchronized (this) {
716 if (mStreamControls == null) {
717 createSliders();
718 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800719 }
720 removeMessages(MSG_FREE_RESOURCES);
721 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
722 }
723
724 public void postMasterMuteChanged(int flags) {
725 postMuteChanged(STREAM_MASTER, flags);
726 }
727
Eric Laurentfde16d52012-12-03 14:42:39 -0800728 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700729 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800730 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700731 }
732
John Spurlock1dad2722014-07-11 11:07:53 -0400733 public void postDismiss(long delay) {
734 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400735 }
736
737 public void postLayoutDirection(int layoutDirection) {
738 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400739 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400740 }
741
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800742 /**
743 * Override this if you have other work to do when the volume changes (for
744 * example, vibrating, playing a sound, etc.). Make sure to call through to
745 * the superclass implementation.
746 */
747 protected void onVolumeChanged(int streamType, int flags) {
748
John Spurlock86005342014-05-23 11:58:00 -0400749 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800750
751 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700752 synchronized (this) {
753 if (mActiveStreamType != streamType) {
754 reorderSliders(streamType);
755 }
RoboErik19c95182014-06-23 15:38:48 -0700756 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800757 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800758 }
759
Marco Nelissen69f593c2009-07-28 09:55:04 -0700760 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800761 removeMessages(MSG_PLAY_SOUND);
762 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
763 }
764
765 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
766 removeMessages(MSG_PLAY_SOUND);
767 removeMessages(MSG_VIBRATE);
768 onStopSounds();
769 }
770
771 removeMessages(MSG_FREE_RESOURCES);
772 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800773 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800774 }
775
Mike Lockwoodce952c82011-11-14 10:47:42 -0800776 protected void onMuteChanged(int streamType, int flags) {
777
John Spurlock86005342014-05-23 11:58:00 -0400778 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800779
780 StreamControl sc = mStreamControls.get(streamType);
781 if (sc != null) {
782 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
783 }
784
785 onVolumeChanged(streamType, flags);
786 }
787
RoboErik19c95182014-06-23 15:38:48 -0700788 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700789 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800790
Marco Nelissen69f593c2009-07-28 09:55:04 -0700791 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792
793 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400794 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800795 + ", flags: " + flags + "), index: " + index);
796 }
797
798 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800799
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400800 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700801 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802
803 switch (streamType) {
804
805 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800806// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700807 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
808 mContext, RingtoneManager.TYPE_RINGTONE);
809 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700810 mRingIsSilent = true;
811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 break;
813 }
814
815 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800816 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800817 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
818 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
819 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
820 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800821 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800823 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
825 break;
826 }
827
828 case AudioManager.STREAM_VOICE_CALL: {
829 /*
830 * For in-call voice call volume, there is no inaudible volume.
831 * Rescale the UI control so the progress bar doesn't go all
832 * the way to zero and don't show the mute icon.
833 */
834 index++;
835 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 break;
837 }
838
839 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 break;
841 }
842
843 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700844 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
845 mContext, RingtoneManager.TYPE_NOTIFICATION);
846 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700847 mRingIsSilent = true;
848 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 break;
850 }
851
852 case AudioManager.STREAM_BLUETOOTH_SCO: {
853 /*
854 * For in-call voice call volume, there is no inaudible volume.
855 * Rescale the UI control so the progress bar doesn't go all
856 * the way to zero and don't show the mute icon.
857 */
858 index++;
859 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 break;
861 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700862
863 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700864 if (controller == null && sc != null) {
865 // If we weren't passed one try using the last one set.
866 controller = sc.controller;
867 }
868 if (controller == null) {
869 // We still don't have one, ignore the command.
870 Log.w(mTag, "sent remote volume change without a controller!");
871 } else {
872 VolumeInfo vi = controller.getVolumeInfo();
873 index = vi.getCurrentVolume();
874 max = vi.getMaxVolume();
875 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
876 // if the remote volume is fixed add the flag for the UI
877 flags |= AudioManager.FLAG_FIXED_VOLUME;
878 }
879 }
John Spurlock86005342014-05-23 11:58:00 -0400880 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700881 break;
882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800885 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700886 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
887 if (sc.controller != null) {
888 sc.controller.removeCallback(mMediaControllerCb);
889 }
890 sc.controller = controller;
891 if (controller != null) {
892 sc.controller.addCallback(mMediaControllerCb);
893 }
894 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700895 if (sc.seekbarView.getMax() != max) {
896 sc.seekbarView.setMax(max);
897 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700898
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800899 sc.seekbarView.setProgress(index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400900 updateSliderEnabled(sc, isMuted(streamType),
901 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800902 }
903
John Spurlock86005342014-05-23 11:58:00 -0400904 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700905 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
906 // when the stream is for remote playback, use -1 to reset the stream type evaluation
907 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700908
John Spurlock86005342014-05-23 11:58:00 -0400909 if (mDialog != null) {
910 mDialog.show();
John Spurlock33f4e042014-07-11 13:10:58 -0400911 if (mCallback != null) {
912 mCallback.onVisible(true);
913 }
John Spurlock86005342014-05-23 11:58:00 -0400914 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 }
916
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700918 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
919 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400920 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700921 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
923 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400924
925 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -0400926 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
927 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -0400928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930
John Spurlock86005342014-05-23 11:58:00 -0400931 private boolean isShowing() {
932 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
933 }
934
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 protected void onPlaySound(int streamType, int flags) {
936
937 if (hasMessages(MSG_STOP_SOUNDS)) {
938 removeMessages(MSG_STOP_SOUNDS);
939 // Force stop right now
940 onStopSounds();
941 }
942
943 synchronized (this) {
944 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800945 if (toneGen != null) {
946 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
947 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
948 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800950 }
951
952 protected void onStopSounds() {
953
954 synchronized (this) {
955 int numStreamTypes = AudioSystem.getNumStreamTypes();
956 for (int i = numStreamTypes - 1; i >= 0; i--) {
957 ToneGenerator toneGen = mToneGenerators[i];
958 if (toneGen != null) {
959 toneGen.stopTone();
960 }
961 }
962 }
963 }
964
965 protected void onVibrate() {
966
967 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700968 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 return;
970 }
971
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400972 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 }
974
RoboErik19c95182014-06-23 15:38:48 -0700975 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
976 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
977 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700978
John Spurlock86005342014-05-23 11:58:00 -0400979 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700980 synchronized (this) {
981 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
982 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
983 }
RoboErik19c95182014-06-23 15:38:48 -0700984 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700985 }
986 } else {
John Spurlock86005342014-05-23 11:58:00 -0400987 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700988 }
989
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700990 removeMessages(MSG_FREE_RESOURCES);
991 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700992 resetTimeout();
993 }
994
995 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -0400996 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
997 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700998 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
999 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001000 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001001 }
1002 }
1003
RoboErik19c95182014-06-23 15:38:48 -07001004 /**
1005 * Clear the current remote stream controller.
1006 */
1007 private void clearRemoteStreamController() {
1008 if (mStreamControls != null) {
1009 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1010 if (sc != null) {
1011 if (sc.controller != null) {
1012 sc.controller.removeCallback(mMediaControllerCb);
1013 sc.controller = null;
1014 }
1015 }
1016 }
1017 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001018
1019 /**
1020 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1021 * Hide or show a slider
1022 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1023 * or AudioService.STREAM_REMOTE_MUSIC
1024 * @param visible
1025 */
1026 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001027 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001028 boolean isVisible = (visible == 1);
1029 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1030 StreamResources streamRes = STREAMS[i];
1031 if (streamRes.streamType == streamType) {
1032 streamRes.show = isVisible;
1033 if (!isVisible && (mActiveStreamType == streamType)) {
1034 mActiveStreamType = -1;
1035 }
1036 break;
1037 }
1038 }
1039 }
1040
Eric Laurentfde16d52012-12-03 14:42:39 -08001041 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001042 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001043 synchronized (sConfirmSafeVolumeLock) {
1044 if (sConfirmSafeVolumeDialog != null) {
1045 return;
1046 }
1047 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1048 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1049 .setPositiveButton(com.android.internal.R.string.yes,
1050 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001051 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001052 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001053 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001054 }
1055 })
1056 .setNegativeButton(com.android.internal.R.string.no, null)
1057 .setIconAttribute(android.R.attr.alertDialogIcon)
1058 .create();
1059 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1060 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001061
Eric Laurentfde16d52012-12-03 14:42:39 -08001062 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1063 sConfirmSafeVolumeDialog.getWindow().setType(
1064 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1065 sConfirmSafeVolumeDialog.show();
1066 }
1067 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001068 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001069 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001070 }
1071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 /**
1073 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1074 */
1075 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001076 if (streamType == STREAM_MASTER) {
1077 // For devices that use the master volume setting only but still want to
1078 // play a volume-changed tone, direct the master volume pseudostream to
1079 // the system stream's tone generator.
1080 if (mPlayMasterStreamTones) {
1081 streamType = AudioManager.STREAM_SYSTEM;
1082 } else {
1083 return null;
1084 }
1085 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 synchronized (this) {
1087 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001088 try {
1089 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1090 } catch (RuntimeException e) {
1091 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001092 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001093 + "RuntimeException: " + e);
1094 }
1095 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001097 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 }
1099 }
1100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101
1102 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001103 * Switch between icons because Bluetooth music is same as music volume, but with
1104 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001106 private void setMusicIcon(int resId, int resMuteId) {
1107 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1108 if (sc != null) {
1109 sc.iconRes = resId;
1110 sc.iconMuteRes = resMuteId;
1111 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001112 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 }
1114
1115 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 synchronized (this) {
1117 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1118 if (mToneGenerators[i] != null) {
1119 mToneGenerators[i].release();
1120 }
1121 mToneGenerators[i] = null;
1122 }
1123 }
1124 }
1125
1126 @Override
1127 public void handleMessage(Message msg) {
1128 switch (msg.what) {
1129
1130 case MSG_VOLUME_CHANGED: {
1131 onVolumeChanged(msg.arg1, msg.arg2);
1132 break;
1133 }
1134
Mike Lockwoodce952c82011-11-14 10:47:42 -08001135 case MSG_MUTE_CHANGED: {
1136 onMuteChanged(msg.arg1, msg.arg2);
1137 break;
1138 }
1139
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 case MSG_FREE_RESOURCES: {
1141 onFreeResources();
1142 break;
1143 }
1144
1145 case MSG_STOP_SOUNDS: {
1146 onStopSounds();
1147 break;
1148 }
1149
1150 case MSG_PLAY_SOUND: {
1151 onPlaySound(msg.arg1, msg.arg2);
1152 break;
1153 }
1154
1155 case MSG_VIBRATE: {
1156 onVibrate();
1157 break;
1158 }
1159
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001160 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001161 if (isShowing()) {
1162 if (mDialog != null) {
1163 mDialog.dismiss();
RoboErik19c95182014-06-23 15:38:48 -07001164 clearRemoteStreamController();
John Spurlockf71205c2014-05-29 10:17:51 -04001165 mActiveStreamType = -1;
John Spurlock33f4e042014-07-11 13:10:58 -04001166 if (mCallback != null) {
1167 mCallback.onVisible(false);
1168 }
John Spurlock86005342014-05-23 11:58:00 -04001169 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001170 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001171 synchronized (sConfirmSafeVolumeLock) {
1172 if (sConfirmSafeVolumeDialog != null) {
1173 sConfirmSafeVolumeDialog.dismiss();
1174 }
1175 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001176 break;
1177 }
1178 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001179 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001180 updateStates();
1181 }
1182 break;
1183 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001184
1185 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001186 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001187 break;
1188 }
1189
1190 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1191 onRemoteVolumeUpdateIfShown();
1192 break;
1193
1194 case MSG_SLIDER_VISIBILITY_CHANGED:
1195 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1196 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001197
1198 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001199 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001200 break;
John Spurlock86005342014-05-23 11:58:00 -04001201
1202 case MSG_LAYOUT_DIRECTION:
1203 setLayoutDirection(msg.arg1);
1204 break;
1205
1206 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001207 updateZenMode(msg.arg1);
1208 break;
1209
1210 case MSG_USER_ACTIVITY:
1211 if (mCallback != null) {
1212 mCallback.onInteraction();
1213 }
John Spurlock86005342014-05-23 11:58:00 -04001214 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001215 }
1216 }
1217
John Spurlockae641c92014-06-30 18:11:40 -04001218 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001219 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1220 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001221 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001222 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001223 removeMessages(MSG_USER_ACTIVITY);
1224 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001225 }
1226
John Spurlock1dad2722014-07-11 11:07:53 -04001227 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001228 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001229 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001230 }
1231
1232 public ZenModeController getZenController() {
1233 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001234 }
1235
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001236 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1237 @Override
1238 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1239 final Object tag = seekBar.getTag();
1240 if (fromUser && tag instanceof StreamControl) {
1241 StreamControl sc = (StreamControl) tag;
RoboErik19c95182014-06-23 15:38:48 -07001242 setStreamVolume(sc, progress, 0);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001243 }
1244 resetTimeout();
1245 }
1246
1247 @Override
1248 public void onStartTrackingTouch(SeekBar seekBar) {
1249 }
1250
1251 @Override
1252 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001253 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001254 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001255
John Spurlock86005342014-05-23 11:58:00 -04001256 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001257 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001258 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001259 }
1260 };
RoboErik19c95182014-06-23 15:38:48 -07001261
1262 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1263 public void onVolumeInfoChanged(VolumeInfo info) {
1264 onRemoteVolumeUpdateIfShown();
1265 }
1266 };
John Spurlockae641c92014-06-30 18:11:40 -04001267
1268 public interface Callback {
1269 void onZenSettings();
1270 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001271 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001272 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001273}