blob: 99cba4d8f83ed8987a462b30d334a5bc2c493aad [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
John Spurlock5f640e42014-05-31 20:15:59 -040019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Eric Laurentc34dcc12012-09-10 13:51:52 -070021import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080022import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080025import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040026import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080028import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.res.Resources;
John Spurlock2078caf2014-05-29 22:20:14 -040030import android.graphics.PixelFormat;
31import android.graphics.drawable.ColorDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.media.AudioManager;
33import android.media.AudioService;
34import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070035import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070037import android.media.VolumeProvider;
38import android.media.session.MediaController;
39import android.media.session.MediaController.VolumeInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070040import android.net.Uri;
John Spurlock3bd4fee2014-05-29 20:51:09 -040041import android.os.AsyncTask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.Handler;
43import android.os.Message;
44import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040046import android.view.Gravity;
47import android.view.LayoutInflater;
48import android.view.MotionEvent;
49import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040050import android.view.View.OnClickListener;
51import android.view.View.OnLongClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040052import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040053import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040054import android.view.Window;
55import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070056import android.view.WindowManager.LayoutParams;
John Spurlock5f640e42014-05-31 20:15:59 -040057import android.view.animation.AnimationUtils;
58import android.view.animation.Interpolator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080059import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080060import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080061import android.widget.SeekBar.OnSeekBarChangeListener;
62
John Spurlock86005342014-05-23 11:58:00 -040063import com.android.internal.R;
64import com.android.systemui.statusbar.policy.ZenModeController;
65
Amith Yamasani2bbdd772011-02-02 18:54:13 -080066import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68/**
John Spurlock3346a802014-05-20 16:25:37 -040069 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070070 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 * @hide
72 */
John Spurlock3346a802014-05-20 16:25:37 -040073public class VolumePanel extends Handler {
Marco Nelissen69f593c2009-07-28 09:55:04 -070074 private static boolean LOGD = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
John Spurlock3346a802014-05-20 16:25:37 -040076 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
78 /**
79 * The delay before vibrating. This small period exists so if the user is
80 * moving to silent mode, it will not emit a short vibrate (it normally
81 * would since vibrate is between normal mode and silent mode using hardware
82 * keys).
83 */
84 public static final int VIBRATE_DELAY = 300;
85
86 private static final int VIBRATE_DURATION = 300;
87 private static final int BEEP_DURATION = 150;
88 private static final int MAX_VOLUME = 100;
89 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080090 private static final int TIMEOUT_DELAY = 3000;
John Spurlock3bd4fee2014-05-29 20:51:09 -040091 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
John Spurlock5f640e42014-05-31 20:15:59 -040092 private static final float ICON_PULSE_SCALE = 1.3f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093
94 private static final int MSG_VOLUME_CHANGED = 0;
95 private static final int MSG_FREE_RESOURCES = 1;
96 private static final int MSG_PLAY_SOUND = 2;
97 private static final int MSG_STOP_SOUNDS = 3;
98 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080099 private static final int MSG_TIMEOUT = 5;
100 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800101 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700102 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
103 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
104 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700105 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400106 private static final int MSG_LAYOUT_DIRECTION = 12;
107 private static final int MSG_ZEN_MODE_CHANGED = 13;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400109 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800110 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700111 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400112
John Spurlock86005342014-05-23 11:58:00 -0400113 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400114 protected final Context mContext;
115 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400116 private final ZenModeController mZenController;
John Spurlock5f640e42014-05-31 20:15:59 -0400117 private final Interpolator mFastOutSlowInInterpolator;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700118 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700119 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400120 private boolean mZenModeCapable;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400121 private int mTimeoutDelay = TIMEOUT_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800122
Christopher Tatec4b78d22012-05-22 13:57:58 -0700123 // True if we want to play tones on the system stream when the master stream is specified.
124 private final boolean mPlayMasterStreamTones;
125
John Spurlock86005342014-05-23 11:58:00 -0400126
127 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400129 /** Dialog hosting the panel, if not embedded */
130 private final Dialog mDialog;
131 /** Parent view hosting the panel, if embedded */
132 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800133
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700134 /** The visible portion of the volume overlay */
135 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400136 /** Contains the slider and its touchable icons */
137 private final ViewGroup mSliderPanel;
138 /** The button that expands the dialog to show the zen panel */
139 private final ImageView mExpandButton;
140 /** Dummy divider icon that needs to vanish with the expand button */
141 private final View mExpandDivider;
142 /** The zen mode configuration panel view stub */
143 private final ViewStub mZenPanelStub;
144 /** The zen mode configuration panel view, once inflated */
145 private ZenModePanel mZenPanel;
146 /** Dummy divider icon that needs to vanish with the zen panel */
147 private final View mZenPanelDivider;
148
149 private ZenModePanel.Callback mZenPanelCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800150
151 /** Currently active stream that shows up at the top of the list of sliders */
152 private int mActiveStreamType = -1;
153 /** All the slider controls mapped by stream type */
154 private HashMap<Integer,StreamControl> mStreamControls;
155
Amith Yamasani71def772011-10-12 12:25:24 -0700156 private enum StreamResources {
157 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
158 R.string.volume_icon_description_bluetooth,
159 R.drawable.ic_audio_bt,
160 R.drawable.ic_audio_bt,
161 false),
162 RingerStream(AudioManager.STREAM_RING,
163 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400164 com.android.systemui.R.drawable.ic_ringer_audible,
165 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700166 false),
167 VoiceStream(AudioManager.STREAM_VOICE_CALL,
168 R.string.volume_icon_description_incall,
169 R.drawable.ic_audio_phone,
170 R.drawable.ic_audio_phone,
171 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700172 AlarmStream(AudioManager.STREAM_ALARM,
173 R.string.volume_alarm,
174 R.drawable.ic_audio_alarm,
175 R.drawable.ic_audio_alarm_mute,
176 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700177 MediaStream(AudioManager.STREAM_MUSIC,
178 R.string.volume_icon_description_media,
179 R.drawable.ic_audio_vol,
180 R.drawable.ic_audio_vol_mute,
181 true),
182 NotificationStream(AudioManager.STREAM_NOTIFICATION,
183 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400184 com.android.systemui.R.drawable.ic_ringer_audible,
185 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400186 true),
187 // for now, use media resources for master volume
188 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700189 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400190 R.drawable.ic_audio_vol,
191 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700192 false),
193 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
194 R.string.volume_icon_description_media, //FIXME should have its own description
195 R.drawable.ic_media_route_on_holo_dark,
196 R.drawable.ic_media_route_disabled_holo_dark,
197 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700198
199 int streamType;
200 int descRes;
201 int iconRes;
202 int iconMuteRes;
203 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
204 boolean show;
205
206 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
207 this.streamType = streamType;
208 this.descRes = descRes;
209 this.iconRes = iconRes;
210 this.iconMuteRes = iconMuteRes;
211 this.show = show;
212 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700213 }
Amith Yamasani71def772011-10-12 12:25:24 -0700214
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800215 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700216 private static final StreamResources[] STREAMS = {
217 StreamResources.BluetoothSCOStream,
218 StreamResources.RingerStream,
219 StreamResources.VoiceStream,
220 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700221 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400222 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700223 StreamResources.MasterStream,
224 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800225 };
226
227 /** Object that contains data for each slider */
228 private class StreamControl {
229 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700230 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800231 ViewGroup group;
232 ImageView icon;
233 SeekBar seekbarView;
John Spurlock5f640e42014-05-31 20:15:59 -0400234 View seekbarContainer;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800235 int iconRes;
236 int iconMuteRes;
237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238
239 // Synchronize when accessing this
240 private ToneGenerator mToneGenerators[];
241 private Vibrator mVibrator;
242
Eric Laurentc34dcc12012-09-10 13:51:52 -0700243 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700244 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700245
246 private static class WarningDialogReceiver extends BroadcastReceiver
247 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800248 private final Context mContext;
249 private final Dialog mDialog;
250 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700251
Eric Laurentfde16d52012-12-03 14:42:39 -0800252 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700253 mContext = context;
254 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800255 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700256 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
257 context.registerReceiver(this, filter);
258 }
259
260 @Override
261 public void onReceive(Context context, Intent intent) {
262 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800263 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700264 }
265
Alan Viverette494fb7b2014-04-10 18:12:56 -0700266 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700267 public void onDismiss(DialogInterface unused) {
268 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800269 cleanUp();
270 }
271
272 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700273 synchronized (sConfirmSafeVolumeLock) {
274 sConfirmSafeVolumeDialog = null;
275 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800276 mVolumePanel.forceTimeout();
277 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700278 }
279 }
280
281
John Spurlock86005342014-05-23 11:58:00 -0400282 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
283 mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400285 mParent = parent;
286 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
John Spurlock5f640e42014-05-31 20:15:59 -0400288 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
289 android.R.interpolator.fast_out_slow_in);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400291 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700292 final Resources res = context.getResources();
293 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400294 if (useMasterVolume) {
295 for (int i = 0; i < STREAMS.length; i++) {
296 StreamResources streamRes = STREAMS[i];
297 streamRes.show = (streamRes.streamType == STREAM_MASTER);
298 }
299 }
John Spurlock86005342014-05-23 11:58:00 -0400300 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
John Spurlock86005342014-05-23 11:58:00 -0400301 if (parent == null) {
302 // dialog mode
303 mDialog = new Dialog(context) {
304 @Override
305 public boolean onTouchEvent(MotionEvent event) {
306 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
307 sConfirmSafeVolumeDialog == null) {
308 forceTimeout();
309 return true;
310 }
311 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700312 }
John Spurlock86005342014-05-23 11:58:00 -0400313 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700314
John Spurlock86005342014-05-23 11:58:00 -0400315 // Change some window properties
316 final Window window = mDialog.getWindow();
317 final LayoutParams lp = window.getAttributes();
318 lp.token = null;
319 // Offset from the top
320 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
321 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
322 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
John Spurlock2078caf2014-05-29 22:20:14 -0400323 lp.format = PixelFormat.TRANSLUCENT;
John Spurlock86005342014-05-23 11:58:00 -0400324 lp.windowAnimations = R.style.Animation_VolumePanel;
John Spurlock86005342014-05-23 11:58:00 -0400325 window.setAttributes(lp);
326 window.setGravity(Gravity.TOP);
327 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
328 window.requestFeature(Window.FEATURE_NO_TITLE);
329 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
330 | LayoutParams.FLAG_NOT_TOUCH_MODAL
John Spurlock2078caf2014-05-29 22:20:14 -0400331 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
332 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlock86005342014-05-23 11:58:00 -0400333 mDialog.setCanceledOnTouchOutside(true);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400334 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
John Spurlock86005342014-05-23 11:58:00 -0400335 mDialog.setOnDismissListener(new OnDismissListener() {
336 @Override
337 public void onDismiss(DialogInterface dialog) {
338 mActiveStreamType = -1;
339 mAudioManager.forceVolumeControlStream(mActiveStreamType);
John Spurlock2769ac22014-06-05 20:46:01 -0400340 collapse();
John Spurlock86005342014-05-23 11:58:00 -0400341 }
342 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700343
John Spurlock86005342014-05-23 11:58:00 -0400344 mDialog.create();
John Spurlock7f1df5e2014-05-31 19:11:40 -0400345 // temporary workaround, until we support window-level shadows
John Spurlock2078caf2014-05-29 22:20:14 -0400346 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700347
John Spurlock86005342014-05-23 11:58:00 -0400348 mView = window.findViewById(R.id.content);
349 mView.setOnTouchListener(new View.OnTouchListener() {
350 @Override
351 public boolean onTouch(View v, MotionEvent event) {
352 resetTimeout();
353 return false;
354 }
355 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700356
John Spurlock86005342014-05-23 11:58:00 -0400357 } else {
358 // embedded mode
359 mDialog = null;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400360 mView = LayoutInflater.from(mContext).inflate(
361 com.android.systemui.R.layout.volume_panel, parent, true);
John Spurlock86005342014-05-23 11:58:00 -0400362 }
363 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
364 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
365 mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
366 mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
367 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
368 mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700371 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700372 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700373
John Spurlock86005342014-05-23 11:58:00 -0400374 mZenModeCapable = !useMasterVolume && mZenController != null;
375 mZenPanelDivider.setVisibility(View.GONE);
376 mExpandButton.setOnClickListener(mClickListener);
377 updateZenMode(mZenController == null ? false : mZenController.isZen());
378 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700379
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700380 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
381 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700382 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
383
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800384 listenToRingerMode();
385 }
386
John Spurlock86005342014-05-23 11:58:00 -0400387 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800388 mPanel.setLayoutDirection(layoutDirection);
389 updateStates();
390 }
391
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800392 private void listenToRingerMode() {
393 final IntentFilter filter = new IntentFilter();
394 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
395 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700396 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800397 public void onReceive(Context context, Intent intent) {
398 final String action = intent.getAction();
399
400 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
401 removeMessages(MSG_RINGER_MODE_CHANGED);
402 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
403 }
404 }
405 }, filter);
406 }
407
408 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400409 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700410 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700411 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700412 // TODO do we need to support a distinct mute property for remote?
413 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400414 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700415 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400416 }
417 }
418
419 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400420 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700421 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700422 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700423 if (mStreamControls != null) {
424 StreamControl sc = mStreamControls.get(streamType);
425 if (sc != null && sc.controller != null) {
426 VolumeInfo vi = sc.controller.getVolumeInfo();
427 return vi.getMaxVolume();
428 }
429 }
430 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400431 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700432 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400433 }
434 }
435
436 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400437 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700438 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700439 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700440 if (mStreamControls != null) {
441 StreamControl sc = mStreamControls.get(streamType);
442 if (sc != null && sc.controller != null) {
443 VolumeInfo vi = sc.controller.getVolumeInfo();
444 return vi.getCurrentVolume();
445 }
446 }
447 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400448 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700449 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400450 }
451 }
452
RoboErik19c95182014-06-23 15:38:48 -0700453 private void setStreamVolume(StreamControl sc, int index, int flags) {
454 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
455 if (sc.controller != null) {
456 sc.controller.setVolumeTo(index, flags);
457 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700458 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700459 }
460 } else if (getStreamVolume(sc.streamType) != index) {
461 if (sc.streamType == STREAM_MASTER) {
462 mAudioManager.setMasterVolume(index, flags);
463 } else {
464 mAudioManager.setStreamVolume(sc.streamType, index, flags);
465 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400466 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800467 }
468
469 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700470 final Resources res = mContext.getResources();
471 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
472 Context.LAYOUT_INFLATER_SERVICE);
473
Amith Yamasani71def772011-10-12 12:25:24 -0700474 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700475
Amith Yamasani71def772011-10-12 12:25:24 -0700476 for (int i = 0; i < STREAMS.length; i++) {
477 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700478
479 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700480
481 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700482 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400483 sc.group = (ViewGroup) inflater.inflate(
484 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800485 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400486 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800487 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700488 sc.icon.setContentDescription(res.getString(streamRes.descRes));
489 sc.iconRes = streamRes.iconRes;
490 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800491 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400492 sc.icon.setClickable(isNotificationOrRing(streamType));
493 if (sc.icon.isClickable()) {
494 sc.icon.setOnClickListener(new OnClickListener() {
495 @Override
496 public void onClick(View v) {
497 resetTimeout();
498 toggle(sc);
499 }
500 });
501 sc.icon.setOnLongClickListener(new OnLongClickListener() {
502 @Override
503 public boolean onLongClick(View v) {
504 resetTimeout();
505 longToggle(sc);
506 return true;
507 }
508 });
509 }
John Spurlock5f640e42014-05-31 20:15:59 -0400510 sc.seekbarContainer =
511 sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
John Spurlock86005342014-05-23 11:58:00 -0400512 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700513 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700514 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400515 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700516 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800517 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700518 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800519 }
520 }
521
John Spurlock7f1df5e2014-05-31 19:11:40 -0400522 private void toggle(StreamControl sc) {
523 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
524 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
525 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
526 } else {
527 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
528 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
529 }
530 }
531
532 private void longToggle(StreamControl sc) {
533 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
534 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
535 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
536 } else {
537 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
538 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
539 }
540 }
541
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800542 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400543 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800544
Alan Viverette494fb7b2014-04-10 18:12:56 -0700545 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800546 if (active == null) {
547 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
548 mActiveStreamType = -1;
549 } else {
John Spurlock86005342014-05-23 11:58:00 -0400550 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800551 mActiveStreamType = activeStreamType;
552 active.group.setVisibility(View.VISIBLE);
553 updateSlider(active);
John Spurlock86005342014-05-23 11:58:00 -0400554 updateZenMode(mZenController == null ? false : mZenController.isZen());
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800555 }
556 }
557
558 /** Update the mute and progress state of a slider */
559 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700560 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800561 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800562 // Force reloading the image resource
563 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800564 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400565 if (isNotificationOrRing(sc.streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700566 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
John Spurlock86005342014-05-23 11:58:00 -0400567 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700568 }
John Spurlock7f1df5e2014-05-31 19:11:40 -0400569 updateSliderEnabled(sc, muted, false);
570 }
571
John Spurlock5f640e42014-05-31 20:15:59 -0400572 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
573 final boolean wasEnabled = sc.seekbarView.isEnabled();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700574 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
575 // never disable touch interactions for remote playback, the muting is not tied to
576 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700577 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400578 } else if (fixedVolume ||
579 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800580 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700581 sc.seekbarView.setEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400582 } else if (isNotificationOrRing(sc.streamType)
583 && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
584 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700585 } else {
586 sc.seekbarView.setEnabled(true);
587 }
John Spurlock5f640e42014-05-31 20:15:59 -0400588 // pulse the ringer icon when the disabled slider is touched in silent mode
589 if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
590 if (sc.seekbarView.isEnabled()) {
591 sc.seekbarContainer.setOnTouchListener(null);
592 } else {
593 sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
594 @Override
595 public boolean onTouch(View v, MotionEvent event) {
596 resetTimeout();
597 pulseIcon(sc.icon);
598 return false;
599 }
600 });
601 }
602 }
603 }
604
605 private void pulseIcon(final ImageView icon) {
606 if (icon.getScaleX() != 1) return; // already running
607 icon.animate().cancel();
608 icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
609 .setInterpolator(mFastOutSlowInInterpolator)
610 .setListener(new AnimatorListenerAdapter() {
611 @Override
612 public void onAnimationEnd(Animator animation) {
613 icon.animate().scaleX(1).scaleY(1).setListener(null);
614 }
615 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800616 }
617
John Spurlock7f1df5e2014-05-31 19:11:40 -0400618 private static boolean isNotificationOrRing(int streamType) {
619 return streamType == AudioManager.STREAM_RING
620 || streamType == AudioManager.STREAM_NOTIFICATION;
621 }
622
John Spurlock86005342014-05-23 11:58:00 -0400623 public void setZenModePanelCallback(ZenModePanel.Callback callback) {
624 mZenPanelCallback = callback;
625 }
626
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800627 private void expand() {
John Spurlock86005342014-05-23 11:58:00 -0400628 if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
629 if (mZenPanel == null) {
630 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
John Spurlock856edeb2014-06-01 20:36:47 -0400631 mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
John Spurlock86005342014-05-23 11:58:00 -0400632 mZenPanel.setCallback(new ZenModePanel.Callback() {
633 @Override
634 public void onMoreSettings() {
635 if (mZenPanelCallback != null) {
636 mZenPanelCallback.onMoreSettings();
637 }
638 }
639
640 @Override
641 public void onInteraction() {
John Spurlock3bd4fee2014-05-29 20:51:09 -0400642 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400643 if (mZenPanelCallback != null) {
644 mZenPanelCallback.onInteraction();
645 }
646 }
647 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800648 }
John Spurlock86005342014-05-23 11:58:00 -0400649 mZenPanel.setVisibility(View.VISIBLE);
650 mZenPanelDivider.setVisibility(View.VISIBLE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400651 mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
652 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800653 }
654
655 private void collapse() {
John Spurlock86005342014-05-23 11:58:00 -0400656 if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
657 if (mZenPanel != null) {
658 mZenPanel.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800659 }
John Spurlock86005342014-05-23 11:58:00 -0400660 mZenPanelDivider.setVisibility(View.GONE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400661 mTimeoutDelay = TIMEOUT_DELAY;
662 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800663 }
664
Eric Laurentfde16d52012-12-03 14:42:39 -0800665 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400666 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800667 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400668 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800669 updateSlider(sc);
670 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 }
672
John Spurlock86005342014-05-23 11:58:00 -0400673 private void updateZenMode(boolean zen) {
674 if (mZenModeCapable) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400675 final boolean show = isNotificationOrRing(mActiveStreamType);
John Spurlock86005342014-05-23 11:58:00 -0400676 mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
677 mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
678 mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
679 : com.android.systemui.R.drawable.ic_vol_zen_off);
John Spurlock2769ac22014-06-05 20:46:01 -0400680 if (show && !zen) {
681 collapse();
John Spurlock856edeb2014-06-01 20:36:47 -0400682 }
John Spurlock86005342014-05-23 11:58:00 -0400683 } else {
684 mExpandButton.setVisibility(View.GONE);
685 mExpandDivider.setVisibility(View.GONE);
686 }
687 }
688
689 public void postZenModeChanged(boolean zen) {
690 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlock84da84c2014-05-31 22:21:52 -0400691 obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400692 }
693
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800694 public void postVolumeChanged(int streamType, int flags) {
695 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700696 synchronized (this) {
697 if (mStreamControls == null) {
698 createSliders();
699 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800700 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800701 removeMessages(MSG_FREE_RESOURCES);
702 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
703 }
704
RoboErik19c95182014-06-23 15:38:48 -0700705 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700706 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
707 synchronized (this) {
708 if (mStreamControls == null) {
709 createSliders();
710 }
711 }
712 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700713 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700714 }
715
716 public void postRemoteSliderVisibility(boolean visible) {
717 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
718 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
719 }
720
721 /**
722 * Called by AudioService when it has received new remote playback information that
723 * would affect the VolumePanel display (mainly volumes). The difference with
724 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
725 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
726 * displayed.
727 * This special code path is due to the fact that remote volume updates arrive to AudioService
728 * asynchronously. So after AudioService has sent the volume update (which should be treated
729 * as a request to update the volume), the application will likely set a new volume. If the UI
730 * is still up, we need to refresh the display to show this new value.
731 */
732 public void postHasNewRemotePlaybackInfo() {
733 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
734 // don't create or prevent resources to be freed, if they disappear, this update came too
735 // late and shouldn't warrant the panel to be displayed longer
736 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
737 }
738
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400739 public void postMasterVolumeChanged(int flags) {
740 postVolumeChanged(STREAM_MASTER, flags);
741 }
742
Mike Lockwoodce952c82011-11-14 10:47:42 -0800743 public void postMuteChanged(int streamType, int flags) {
744 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700745 synchronized (this) {
746 if (mStreamControls == null) {
747 createSliders();
748 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800749 }
750 removeMessages(MSG_FREE_RESOURCES);
751 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
752 }
753
754 public void postMasterMuteChanged(int flags) {
755 postMuteChanged(STREAM_MASTER, flags);
756 }
757
Eric Laurentfde16d52012-12-03 14:42:39 -0800758 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700759 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800760 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700761 }
762
John Spurlock3346a802014-05-20 16:25:37 -0400763 public void postDismiss() {
John Spurlock86005342014-05-23 11:58:00 -0400764 forceTimeout();
765 }
766
767 public void postLayoutDirection(int layoutDirection) {
768 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400769 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400770 }
771
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800772 /**
773 * Override this if you have other work to do when the volume changes (for
774 * example, vibrating, playing a sound, etc.). Make sure to call through to
775 * the superclass implementation.
776 */
777 protected void onVolumeChanged(int streamType, int flags) {
778
John Spurlock86005342014-05-23 11:58:00 -0400779 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780
781 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700782 synchronized (this) {
783 if (mActiveStreamType != streamType) {
784 reorderSliders(streamType);
785 }
RoboErik19c95182014-06-23 15:38:48 -0700786 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800787 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800788 }
789
Marco Nelissen69f593c2009-07-28 09:55:04 -0700790 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800791 removeMessages(MSG_PLAY_SOUND);
792 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
793 }
794
795 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
796 removeMessages(MSG_PLAY_SOUND);
797 removeMessages(MSG_VIBRATE);
798 onStopSounds();
799 }
800
801 removeMessages(MSG_FREE_RESOURCES);
802 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800803 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 }
805
Mike Lockwoodce952c82011-11-14 10:47:42 -0800806 protected void onMuteChanged(int streamType, int flags) {
807
John Spurlock86005342014-05-23 11:58:00 -0400808 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800809
810 StreamControl sc = mStreamControls.get(streamType);
811 if (sc != null) {
812 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
813 }
814
815 onVolumeChanged(streamType, flags);
816 }
817
RoboErik19c95182014-06-23 15:38:48 -0700818 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700819 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800820
Marco Nelissen69f593c2009-07-28 09:55:04 -0700821 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822
823 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400824 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 + ", flags: " + flags + "), index: " + index);
826 }
827
828 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800829
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400830 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700831 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832
833 switch (streamType) {
834
835 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800836// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700837 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
838 mContext, RingtoneManager.TYPE_RINGTONE);
839 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700840 mRingIsSilent = true;
841 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 break;
843 }
844
845 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800846 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800847 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
848 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
849 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
850 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800851 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800853 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800854 }
855 break;
856 }
857
858 case AudioManager.STREAM_VOICE_CALL: {
859 /*
860 * For in-call voice call volume, there is no inaudible volume.
861 * Rescale the UI control so the progress bar doesn't go all
862 * the way to zero and don't show the mute icon.
863 */
864 index++;
865 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 break;
867 }
868
869 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800870 break;
871 }
872
873 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700874 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
875 mContext, RingtoneManager.TYPE_NOTIFICATION);
876 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700877 mRingIsSilent = true;
878 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 break;
880 }
881
882 case AudioManager.STREAM_BLUETOOTH_SCO: {
883 /*
884 * For in-call voice call volume, there is no inaudible volume.
885 * Rescale the UI control so the progress bar doesn't go all
886 * the way to zero and don't show the mute icon.
887 */
888 index++;
889 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800890 break;
891 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700892
893 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700894 if (controller == null && sc != null) {
895 // If we weren't passed one try using the last one set.
896 controller = sc.controller;
897 }
898 if (controller == null) {
899 // We still don't have one, ignore the command.
900 Log.w(mTag, "sent remote volume change without a controller!");
901 } else {
902 VolumeInfo vi = controller.getVolumeInfo();
903 index = vi.getCurrentVolume();
904 max = vi.getMaxVolume();
905 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
906 // if the remote volume is fixed add the flag for the UI
907 flags |= AudioManager.FLAG_FIXED_VOLUME;
908 }
909 }
John Spurlock86005342014-05-23 11:58:00 -0400910 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700911 break;
912 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 }
914
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800915 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700916 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
917 if (sc.controller != null) {
918 sc.controller.removeCallback(mMediaControllerCb);
919 }
920 sc.controller = controller;
921 if (controller != null) {
922 sc.controller.addCallback(mMediaControllerCb);
923 }
924 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700925 if (sc.seekbarView.getMax() != max) {
926 sc.seekbarView.setMax(max);
927 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700928
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800929 sc.seekbarView.setProgress(index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400930 updateSliderEnabled(sc, isMuted(streamType),
931 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933
John Spurlock86005342014-05-23 11:58:00 -0400934 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700935 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
936 // when the stream is for remote playback, use -1 to reset the stream type evaluation
937 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700938
John Spurlock86005342014-05-23 11:58:00 -0400939 if (mDialog != null) {
940 mDialog.show();
941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 }
943
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700945 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
946 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400947 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700948 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
950 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400951
952 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
953 if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
954 pulseIcon(sc.icon);
955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 }
957
John Spurlock86005342014-05-23 11:58:00 -0400958 private boolean isShowing() {
959 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
960 }
961
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 protected void onPlaySound(int streamType, int flags) {
963
964 if (hasMessages(MSG_STOP_SOUNDS)) {
965 removeMessages(MSG_STOP_SOUNDS);
966 // Force stop right now
967 onStopSounds();
968 }
969
970 synchronized (this) {
971 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800972 if (toneGen != null) {
973 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
974 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 }
978
979 protected void onStopSounds() {
980
981 synchronized (this) {
982 int numStreamTypes = AudioSystem.getNumStreamTypes();
983 for (int i = numStreamTypes - 1; i >= 0; i--) {
984 ToneGenerator toneGen = mToneGenerators[i];
985 if (toneGen != null) {
986 toneGen.stopTone();
987 }
988 }
989 }
990 }
991
992 protected void onVibrate() {
993
994 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700995 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 return;
997 }
998
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400999 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 }
1001
RoboErik19c95182014-06-23 15:38:48 -07001002 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1003 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1004 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001005
John Spurlock86005342014-05-23 11:58:00 -04001006 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001007 synchronized (this) {
1008 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1009 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1010 }
RoboErik19c95182014-06-23 15:38:48 -07001011 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001012 }
1013 } else {
John Spurlock86005342014-05-23 11:58:00 -04001014 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001015 }
1016
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001017 removeMessages(MSG_FREE_RESOURCES);
1018 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001019 resetTimeout();
1020 }
1021
1022 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001023 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1024 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001025 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1026 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001027 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001028 }
1029 }
1030
RoboErik19c95182014-06-23 15:38:48 -07001031 /**
1032 * Clear the current remote stream controller.
1033 */
1034 private void clearRemoteStreamController() {
1035 if (mStreamControls != null) {
1036 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1037 if (sc != null) {
1038 if (sc.controller != null) {
1039 sc.controller.removeCallback(mMediaControllerCb);
1040 sc.controller = null;
1041 }
1042 }
1043 }
1044 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001045
1046 /**
1047 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1048 * Hide or show a slider
1049 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1050 * or AudioService.STREAM_REMOTE_MUSIC
1051 * @param visible
1052 */
1053 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001054 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001055 boolean isVisible = (visible == 1);
1056 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1057 StreamResources streamRes = STREAMS[i];
1058 if (streamRes.streamType == streamType) {
1059 streamRes.show = isVisible;
1060 if (!isVisible && (mActiveStreamType == streamType)) {
1061 mActiveStreamType = -1;
1062 }
1063 break;
1064 }
1065 }
1066 }
1067
Eric Laurentfde16d52012-12-03 14:42:39 -08001068 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001069 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001070 synchronized (sConfirmSafeVolumeLock) {
1071 if (sConfirmSafeVolumeDialog != null) {
1072 return;
1073 }
1074 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1075 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1076 .setPositiveButton(com.android.internal.R.string.yes,
1077 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001078 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001079 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001080 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001081 }
1082 })
1083 .setNegativeButton(com.android.internal.R.string.no, null)
1084 .setIconAttribute(android.R.attr.alertDialogIcon)
1085 .create();
1086 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1087 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001088
Eric Laurentfde16d52012-12-03 14:42:39 -08001089 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1090 sConfirmSafeVolumeDialog.getWindow().setType(
1091 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1092 sConfirmSafeVolumeDialog.show();
1093 }
1094 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001095 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001096 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001097 }
1098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 /**
1100 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1101 */
1102 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001103 if (streamType == STREAM_MASTER) {
1104 // For devices that use the master volume setting only but still want to
1105 // play a volume-changed tone, direct the master volume pseudostream to
1106 // the system stream's tone generator.
1107 if (mPlayMasterStreamTones) {
1108 streamType = AudioManager.STREAM_SYSTEM;
1109 } else {
1110 return null;
1111 }
1112 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 synchronized (this) {
1114 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001115 try {
1116 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1117 } catch (RuntimeException e) {
1118 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001119 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001120 + "RuntimeException: " + e);
1121 }
1122 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001123 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001124 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 }
1126 }
1127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128
1129 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001130 * Switch between icons because Bluetooth music is same as music volume, but with
1131 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001133 private void setMusicIcon(int resId, int resMuteId) {
1134 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1135 if (sc != null) {
1136 sc.iconRes = resId;
1137 sc.iconMuteRes = resMuteId;
1138 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 }
1141
1142 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 synchronized (this) {
1144 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1145 if (mToneGenerators[i] != null) {
1146 mToneGenerators[i].release();
1147 }
1148 mToneGenerators[i] = null;
1149 }
1150 }
1151 }
1152
1153 @Override
1154 public void handleMessage(Message msg) {
1155 switch (msg.what) {
1156
1157 case MSG_VOLUME_CHANGED: {
1158 onVolumeChanged(msg.arg1, msg.arg2);
1159 break;
1160 }
1161
Mike Lockwoodce952c82011-11-14 10:47:42 -08001162 case MSG_MUTE_CHANGED: {
1163 onMuteChanged(msg.arg1, msg.arg2);
1164 break;
1165 }
1166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167 case MSG_FREE_RESOURCES: {
1168 onFreeResources();
1169 break;
1170 }
1171
1172 case MSG_STOP_SOUNDS: {
1173 onStopSounds();
1174 break;
1175 }
1176
1177 case MSG_PLAY_SOUND: {
1178 onPlaySound(msg.arg1, msg.arg2);
1179 break;
1180 }
1181
1182 case MSG_VIBRATE: {
1183 onVibrate();
1184 break;
1185 }
1186
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001187 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001188 if (isShowing()) {
1189 if (mDialog != null) {
1190 mDialog.dismiss();
RoboErik19c95182014-06-23 15:38:48 -07001191 clearRemoteStreamController();
John Spurlockf71205c2014-05-29 10:17:51 -04001192 mActiveStreamType = -1;
John Spurlock86005342014-05-23 11:58:00 -04001193 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001194 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001195 synchronized (sConfirmSafeVolumeLock) {
1196 if (sConfirmSafeVolumeDialog != null) {
1197 sConfirmSafeVolumeDialog.dismiss();
1198 }
1199 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001200 break;
1201 }
1202 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001203 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001204 updateStates();
1205 }
1206 break;
1207 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001208
1209 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001210 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001211 break;
1212 }
1213
1214 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1215 onRemoteVolumeUpdateIfShown();
1216 break;
1217
1218 case MSG_SLIDER_VISIBILITY_CHANGED:
1219 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1220 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001221
1222 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001223 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001224 break;
John Spurlock86005342014-05-23 11:58:00 -04001225
1226 case MSG_LAYOUT_DIRECTION:
1227 setLayoutDirection(msg.arg1);
1228 break;
1229
1230 case MSG_ZEN_MODE_CHANGED:
1231 updateZenMode(msg.arg1 != 0);
1232 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001233 }
1234 }
1235
John Spurlock86005342014-05-23 11:58:00 -04001236 public void resetTimeout() {
1237 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001238 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001239 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001240 }
1241
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001242 private void forceTimeout() {
1243 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001244 sendEmptyMessage(MSG_TIMEOUT);
1245 }
1246
1247 public ZenModeController getZenController() {
1248 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001249 }
1250
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001251 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1252 @Override
1253 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1254 final Object tag = seekBar.getTag();
1255 if (fromUser && tag instanceof StreamControl) {
1256 StreamControl sc = (StreamControl) tag;
RoboErik19c95182014-06-23 15:38:48 -07001257 setStreamVolume(sc, progress, 0);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001258 }
1259 resetTimeout();
1260 }
1261
1262 @Override
1263 public void onStartTrackingTouch(SeekBar seekBar) {
1264 }
1265
1266 @Override
1267 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001268 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001269 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001270
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001271 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1272 @Override
1273 public void onClick(View v) {
John Spurlock86005342014-05-23 11:58:00 -04001274 if (v == mExpandButton && mZenController != null) {
1275 final boolean newZen = !mZenController.isZen();
John Spurlock3bd4fee2014-05-29 20:51:09 -04001276 AsyncTask.execute(new Runnable() {
1277 @Override
1278 public void run() {
1279 mZenController.setZen(newZen);
1280 }
1281 });
John Spurlock86005342014-05-23 11:58:00 -04001282 if (newZen) {
1283 expand();
1284 } else {
1285 collapse();
1286 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001287 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001288 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001289 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001290 };
John Spurlock86005342014-05-23 11:58:00 -04001291
1292 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
1293 public void onZenChanged(boolean zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001294 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001295 }
1296 };
RoboErik19c95182014-06-23 15:38:48 -07001297
1298 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1299 public void onVolumeInfoChanged(VolumeInfo info) {
1300 onRemoteVolumeUpdateIfShown();
1301 }
1302 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001303}