blob: 53daaae1b3bfb86dd79e2f9533f31a9f667fe299 [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(
John Spurlock7f8f22a2014-07-02 18:54:17 -0400361 com.android.systemui.R.layout.volume_panel, parent, false);
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 Spurlock7f8f22a2014-07-02 18:54:17 -0400387 public View getContentView() {
388 return mView;
389 }
390
John Spurlock86005342014-05-23 11:58:00 -0400391 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800392 mPanel.setLayoutDirection(layoutDirection);
393 updateStates();
394 }
395
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800396 private void listenToRingerMode() {
397 final IntentFilter filter = new IntentFilter();
398 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
399 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700400 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800401 public void onReceive(Context context, Intent intent) {
402 final String action = intent.getAction();
403
404 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
405 removeMessages(MSG_RINGER_MODE_CHANGED);
406 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
407 }
408 }
409 }, filter);
410 }
411
412 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400413 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700414 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700415 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700416 // TODO do we need to support a distinct mute property for remote?
417 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400418 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700419 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400420 }
421 }
422
423 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400424 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700425 return mAudioManager.getMasterMaxVolume();
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.getMaxVolume();
432 }
433 }
434 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400435 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700436 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400437 }
438 }
439
440 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400441 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700442 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700443 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700444 if (mStreamControls != null) {
445 StreamControl sc = mStreamControls.get(streamType);
446 if (sc != null && sc.controller != null) {
447 VolumeInfo vi = sc.controller.getVolumeInfo();
448 return vi.getCurrentVolume();
449 }
450 }
451 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400452 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700453 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400454 }
455 }
456
RoboErik19c95182014-06-23 15:38:48 -0700457 private void setStreamVolume(StreamControl sc, int index, int flags) {
458 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
459 if (sc.controller != null) {
460 sc.controller.setVolumeTo(index, flags);
461 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700462 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700463 }
464 } else if (getStreamVolume(sc.streamType) != index) {
465 if (sc.streamType == STREAM_MASTER) {
466 mAudioManager.setMasterVolume(index, flags);
467 } else {
468 mAudioManager.setStreamVolume(sc.streamType, index, flags);
469 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400470 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800471 }
472
473 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700474 final Resources res = mContext.getResources();
475 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
476 Context.LAYOUT_INFLATER_SERVICE);
477
Amith Yamasani71def772011-10-12 12:25:24 -0700478 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700479
Amith Yamasani71def772011-10-12 12:25:24 -0700480 for (int i = 0; i < STREAMS.length; i++) {
481 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700482
483 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700484
485 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700486 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400487 sc.group = (ViewGroup) inflater.inflate(
488 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800489 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400490 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800491 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700492 sc.icon.setContentDescription(res.getString(streamRes.descRes));
493 sc.iconRes = streamRes.iconRes;
494 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800495 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400496 sc.icon.setClickable(isNotificationOrRing(streamType));
497 if (sc.icon.isClickable()) {
498 sc.icon.setOnClickListener(new OnClickListener() {
499 @Override
500 public void onClick(View v) {
501 resetTimeout();
502 toggle(sc);
503 }
504 });
505 sc.icon.setOnLongClickListener(new OnLongClickListener() {
506 @Override
507 public boolean onLongClick(View v) {
508 resetTimeout();
509 longToggle(sc);
510 return true;
511 }
512 });
513 }
John Spurlock5f640e42014-05-31 20:15:59 -0400514 sc.seekbarContainer =
515 sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
John Spurlock86005342014-05-23 11:58:00 -0400516 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700517 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700518 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400519 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700520 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800521 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700522 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800523 }
524 }
525
John Spurlock7f1df5e2014-05-31 19:11:40 -0400526 private void toggle(StreamControl sc) {
527 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
528 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
529 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
530 } else {
531 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
532 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
533 }
534 }
535
536 private void longToggle(StreamControl sc) {
537 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
538 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
539 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
540 } else {
541 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
542 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
543 }
544 }
545
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800546 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400547 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800548
Alan Viverette494fb7b2014-04-10 18:12:56 -0700549 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800550 if (active == null) {
551 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
552 mActiveStreamType = -1;
553 } else {
John Spurlock86005342014-05-23 11:58:00 -0400554 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800555 mActiveStreamType = activeStreamType;
556 active.group.setVisibility(View.VISIBLE);
557 updateSlider(active);
John Spurlock86005342014-05-23 11:58:00 -0400558 updateZenMode(mZenController == null ? false : mZenController.isZen());
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800559 }
560 }
561
562 /** Update the mute and progress state of a slider */
563 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700564 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800565 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800566 // Force reloading the image resource
567 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800568 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400569 if (isNotificationOrRing(sc.streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700570 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
John Spurlock86005342014-05-23 11:58:00 -0400571 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700572 }
John Spurlock7f1df5e2014-05-31 19:11:40 -0400573 updateSliderEnabled(sc, muted, false);
574 }
575
John Spurlock5f640e42014-05-31 20:15:59 -0400576 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
577 final boolean wasEnabled = sc.seekbarView.isEnabled();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700578 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
579 // never disable touch interactions for remote playback, the muting is not tied to
580 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700581 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400582 } else if (fixedVolume ||
583 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800584 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700585 sc.seekbarView.setEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400586 } else if (isNotificationOrRing(sc.streamType)
587 && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
588 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700589 } else {
590 sc.seekbarView.setEnabled(true);
591 }
John Spurlock5f640e42014-05-31 20:15:59 -0400592 // pulse the ringer icon when the disabled slider is touched in silent mode
593 if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
594 if (sc.seekbarView.isEnabled()) {
595 sc.seekbarContainer.setOnTouchListener(null);
596 } else {
597 sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
598 @Override
599 public boolean onTouch(View v, MotionEvent event) {
600 resetTimeout();
601 pulseIcon(sc.icon);
602 return false;
603 }
604 });
605 }
606 }
607 }
608
609 private void pulseIcon(final ImageView icon) {
610 if (icon.getScaleX() != 1) return; // already running
611 icon.animate().cancel();
612 icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
613 .setInterpolator(mFastOutSlowInInterpolator)
614 .setListener(new AnimatorListenerAdapter() {
615 @Override
616 public void onAnimationEnd(Animator animation) {
617 icon.animate().scaleX(1).scaleY(1).setListener(null);
618 }
619 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800620 }
621
John Spurlock7f1df5e2014-05-31 19:11:40 -0400622 private static boolean isNotificationOrRing(int streamType) {
623 return streamType == AudioManager.STREAM_RING
624 || streamType == AudioManager.STREAM_NOTIFICATION;
625 }
626
John Spurlock86005342014-05-23 11:58:00 -0400627 public void setZenModePanelCallback(ZenModePanel.Callback callback) {
628 mZenPanelCallback = callback;
629 }
630
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800631 private void expand() {
John Spurlock86005342014-05-23 11:58:00 -0400632 if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
633 if (mZenPanel == null) {
634 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
John Spurlock7f8f22a2014-07-02 18:54:17 -0400635 final boolean isDialog = mDialog != null;
636 mZenPanel.init(mZenController, isDialog ? 'D' : 'E', isDialog);
John Spurlock86005342014-05-23 11:58:00 -0400637 mZenPanel.setCallback(new ZenModePanel.Callback() {
638 @Override
639 public void onMoreSettings() {
640 if (mZenPanelCallback != null) {
641 mZenPanelCallback.onMoreSettings();
642 }
643 }
644
645 @Override
646 public void onInteraction() {
John Spurlock3bd4fee2014-05-29 20:51:09 -0400647 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400648 if (mZenPanelCallback != null) {
649 mZenPanelCallback.onInteraction();
650 }
651 }
652 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800653 }
John Spurlock86005342014-05-23 11:58:00 -0400654 mZenPanel.setVisibility(View.VISIBLE);
655 mZenPanelDivider.setVisibility(View.VISIBLE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400656 mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
657 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800658 }
659
660 private void collapse() {
John Spurlock86005342014-05-23 11:58:00 -0400661 if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
662 if (mZenPanel != null) {
663 mZenPanel.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800664 }
John Spurlock86005342014-05-23 11:58:00 -0400665 mZenPanelDivider.setVisibility(View.GONE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400666 mTimeoutDelay = TIMEOUT_DELAY;
667 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800668 }
669
Eric Laurentfde16d52012-12-03 14:42:39 -0800670 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400671 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800672 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400673 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800674 updateSlider(sc);
675 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 }
677
John Spurlock86005342014-05-23 11:58:00 -0400678 private void updateZenMode(boolean zen) {
679 if (mZenModeCapable) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400680 final boolean show = isNotificationOrRing(mActiveStreamType);
John Spurlock86005342014-05-23 11:58:00 -0400681 mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
682 mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
683 mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
684 : com.android.systemui.R.drawable.ic_vol_zen_off);
John Spurlock2769ac22014-06-05 20:46:01 -0400685 if (show && !zen) {
686 collapse();
John Spurlock856edeb2014-06-01 20:36:47 -0400687 }
John Spurlock86005342014-05-23 11:58:00 -0400688 } else {
689 mExpandButton.setVisibility(View.GONE);
690 mExpandDivider.setVisibility(View.GONE);
691 }
692 }
693
694 public void postZenModeChanged(boolean zen) {
695 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlock84da84c2014-05-31 22:21:52 -0400696 obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400697 }
698
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800699 public void postVolumeChanged(int streamType, int flags) {
700 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700701 synchronized (this) {
702 if (mStreamControls == null) {
703 createSliders();
704 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800705 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800706 removeMessages(MSG_FREE_RESOURCES);
707 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
708 }
709
RoboErik19c95182014-06-23 15:38:48 -0700710 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700711 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
712 synchronized (this) {
713 if (mStreamControls == null) {
714 createSliders();
715 }
716 }
717 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700718 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700719 }
720
721 public void postRemoteSliderVisibility(boolean visible) {
722 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
723 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
724 }
725
726 /**
727 * Called by AudioService when it has received new remote playback information that
728 * would affect the VolumePanel display (mainly volumes). The difference with
729 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
730 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
731 * displayed.
732 * This special code path is due to the fact that remote volume updates arrive to AudioService
733 * asynchronously. So after AudioService has sent the volume update (which should be treated
734 * as a request to update the volume), the application will likely set a new volume. If the UI
735 * is still up, we need to refresh the display to show this new value.
736 */
737 public void postHasNewRemotePlaybackInfo() {
738 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
739 // don't create or prevent resources to be freed, if they disappear, this update came too
740 // late and shouldn't warrant the panel to be displayed longer
741 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
742 }
743
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400744 public void postMasterVolumeChanged(int flags) {
745 postVolumeChanged(STREAM_MASTER, flags);
746 }
747
Mike Lockwoodce952c82011-11-14 10:47:42 -0800748 public void postMuteChanged(int streamType, int flags) {
749 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700750 synchronized (this) {
751 if (mStreamControls == null) {
752 createSliders();
753 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800754 }
755 removeMessages(MSG_FREE_RESOURCES);
756 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
757 }
758
759 public void postMasterMuteChanged(int flags) {
760 postMuteChanged(STREAM_MASTER, flags);
761 }
762
Eric Laurentfde16d52012-12-03 14:42:39 -0800763 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700764 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800765 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700766 }
767
John Spurlock3346a802014-05-20 16:25:37 -0400768 public void postDismiss() {
John Spurlock86005342014-05-23 11:58:00 -0400769 forceTimeout();
770 }
771
772 public void postLayoutDirection(int layoutDirection) {
773 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400774 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400775 }
776
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 /**
778 * Override this if you have other work to do when the volume changes (for
779 * example, vibrating, playing a sound, etc.). Make sure to call through to
780 * the superclass implementation.
781 */
782 protected void onVolumeChanged(int streamType, int flags) {
783
John Spurlock86005342014-05-23 11:58:00 -0400784 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785
786 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700787 synchronized (this) {
788 if (mActiveStreamType != streamType) {
789 reorderSliders(streamType);
790 }
RoboErik19c95182014-06-23 15:38:48 -0700791 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800792 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793 }
794
Marco Nelissen69f593c2009-07-28 09:55:04 -0700795 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 removeMessages(MSG_PLAY_SOUND);
797 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
798 }
799
800 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
801 removeMessages(MSG_PLAY_SOUND);
802 removeMessages(MSG_VIBRATE);
803 onStopSounds();
804 }
805
806 removeMessages(MSG_FREE_RESOURCES);
807 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800808 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 }
810
Mike Lockwoodce952c82011-11-14 10:47:42 -0800811 protected void onMuteChanged(int streamType, int flags) {
812
John Spurlock86005342014-05-23 11:58:00 -0400813 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800814
815 StreamControl sc = mStreamControls.get(streamType);
816 if (sc != null) {
817 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
818 }
819
820 onVolumeChanged(streamType, flags);
821 }
822
RoboErik19c95182014-06-23 15:38:48 -0700823 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700824 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800825
Marco Nelissen69f593c2009-07-28 09:55:04 -0700826 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827
828 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400829 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 + ", flags: " + flags + "), index: " + index);
831 }
832
833 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800834
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400835 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700836 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837
838 switch (streamType) {
839
840 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800841// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700842 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
843 mContext, RingtoneManager.TYPE_RINGTONE);
844 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700845 mRingIsSilent = true;
846 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 break;
848 }
849
850 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800851 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800852 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
853 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
854 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
855 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800856 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800857 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800858 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
860 break;
861 }
862
863 case AudioManager.STREAM_VOICE_CALL: {
864 /*
865 * For in-call voice call volume, there is no inaudible volume.
866 * Rescale the UI control so the progress bar doesn't go all
867 * the way to zero and don't show the mute icon.
868 */
869 index++;
870 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871 break;
872 }
873
874 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875 break;
876 }
877
878 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700879 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
880 mContext, RingtoneManager.TYPE_NOTIFICATION);
881 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700882 mRingIsSilent = true;
883 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800884 break;
885 }
886
887 case AudioManager.STREAM_BLUETOOTH_SCO: {
888 /*
889 * For in-call voice call volume, there is no inaudible volume.
890 * Rescale the UI control so the progress bar doesn't go all
891 * the way to zero and don't show the mute icon.
892 */
893 index++;
894 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 break;
896 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700897
898 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700899 if (controller == null && sc != null) {
900 // If we weren't passed one try using the last one set.
901 controller = sc.controller;
902 }
903 if (controller == null) {
904 // We still don't have one, ignore the command.
905 Log.w(mTag, "sent remote volume change without a controller!");
906 } else {
907 VolumeInfo vi = controller.getVolumeInfo();
908 index = vi.getCurrentVolume();
909 max = vi.getMaxVolume();
910 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
911 // if the remote volume is fixed add the flag for the UI
912 flags |= AudioManager.FLAG_FIXED_VOLUME;
913 }
914 }
John Spurlock86005342014-05-23 11:58:00 -0400915 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700916 break;
917 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 }
919
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800920 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700921 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
922 if (sc.controller != null) {
923 sc.controller.removeCallback(mMediaControllerCb);
924 }
925 sc.controller = controller;
926 if (controller != null) {
927 sc.controller.addCallback(mMediaControllerCb);
928 }
929 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700930 if (sc.seekbarView.getMax() != max) {
931 sc.seekbarView.setMax(max);
932 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700933
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800934 sc.seekbarView.setProgress(index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400935 updateSliderEnabled(sc, isMuted(streamType),
936 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 }
938
John Spurlock86005342014-05-23 11:58:00 -0400939 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700940 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
941 // when the stream is for remote playback, use -1 to reset the stream type evaluation
942 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700943
John Spurlock86005342014-05-23 11:58:00 -0400944 if (mDialog != null) {
945 mDialog.show();
946 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700950 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
951 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400952 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700953 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800954 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
955 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400956
957 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
958 if (sc != null && (flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
959 pulseIcon(sc.icon);
960 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 }
962
John Spurlock86005342014-05-23 11:58:00 -0400963 private boolean isShowing() {
964 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
965 }
966
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 protected void onPlaySound(int streamType, int flags) {
968
969 if (hasMessages(MSG_STOP_SOUNDS)) {
970 removeMessages(MSG_STOP_SOUNDS);
971 // Force stop right now
972 onStopSounds();
973 }
974
975 synchronized (this) {
976 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800977 if (toneGen != null) {
978 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
979 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 }
983
984 protected void onStopSounds() {
985
986 synchronized (this) {
987 int numStreamTypes = AudioSystem.getNumStreamTypes();
988 for (int i = numStreamTypes - 1; i >= 0; i--) {
989 ToneGenerator toneGen = mToneGenerators[i];
990 if (toneGen != null) {
991 toneGen.stopTone();
992 }
993 }
994 }
995 }
996
997 protected void onVibrate() {
998
999 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001000 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 return;
1002 }
1003
John Spurlockf9e1a0b2014-03-19 22:09:06 -04001004 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 }
1006
RoboErik19c95182014-06-23 15:38:48 -07001007 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1008 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1009 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001010
John Spurlock86005342014-05-23 11:58:00 -04001011 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001012 synchronized (this) {
1013 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1014 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1015 }
RoboErik19c95182014-06-23 15:38:48 -07001016 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001017 }
1018 } else {
John Spurlock86005342014-05-23 11:58:00 -04001019 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001020 }
1021
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001022 removeMessages(MSG_FREE_RESOURCES);
1023 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001024 resetTimeout();
1025 }
1026
1027 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001028 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1029 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001030 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1031 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001032 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001033 }
1034 }
1035
RoboErik19c95182014-06-23 15:38:48 -07001036 /**
1037 * Clear the current remote stream controller.
1038 */
1039 private void clearRemoteStreamController() {
1040 if (mStreamControls != null) {
1041 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1042 if (sc != null) {
1043 if (sc.controller != null) {
1044 sc.controller.removeCallback(mMediaControllerCb);
1045 sc.controller = null;
1046 }
1047 }
1048 }
1049 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001050
1051 /**
1052 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1053 * Hide or show a slider
1054 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1055 * or AudioService.STREAM_REMOTE_MUSIC
1056 * @param visible
1057 */
1058 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001059 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001060 boolean isVisible = (visible == 1);
1061 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1062 StreamResources streamRes = STREAMS[i];
1063 if (streamRes.streamType == streamType) {
1064 streamRes.show = isVisible;
1065 if (!isVisible && (mActiveStreamType == streamType)) {
1066 mActiveStreamType = -1;
1067 }
1068 break;
1069 }
1070 }
1071 }
1072
Eric Laurentfde16d52012-12-03 14:42:39 -08001073 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001074 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001075 synchronized (sConfirmSafeVolumeLock) {
1076 if (sConfirmSafeVolumeDialog != null) {
1077 return;
1078 }
1079 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1080 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1081 .setPositiveButton(com.android.internal.R.string.yes,
1082 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001083 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001084 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001085 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001086 }
1087 })
1088 .setNegativeButton(com.android.internal.R.string.no, null)
1089 .setIconAttribute(android.R.attr.alertDialogIcon)
1090 .create();
1091 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1092 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001093
Eric Laurentfde16d52012-12-03 14:42:39 -08001094 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1095 sConfirmSafeVolumeDialog.getWindow().setType(
1096 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1097 sConfirmSafeVolumeDialog.show();
1098 }
1099 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001100 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001101 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001102 }
1103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 /**
1105 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1106 */
1107 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001108 if (streamType == STREAM_MASTER) {
1109 // For devices that use the master volume setting only but still want to
1110 // play a volume-changed tone, direct the master volume pseudostream to
1111 // the system stream's tone generator.
1112 if (mPlayMasterStreamTones) {
1113 streamType = AudioManager.STREAM_SYSTEM;
1114 } else {
1115 return null;
1116 }
1117 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 synchronized (this) {
1119 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001120 try {
1121 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1122 } catch (RuntimeException e) {
1123 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001124 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001125 + "RuntimeException: " + e);
1126 }
1127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001129 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001130 }
1131 }
1132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133
1134 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001135 * Switch between icons because Bluetooth music is same as music volume, but with
1136 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001138 private void setMusicIcon(int resId, int resMuteId) {
1139 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1140 if (sc != null) {
1141 sc.iconRes = resId;
1142 sc.iconMuteRes = resMuteId;
1143 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 }
1146
1147 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 synchronized (this) {
1149 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1150 if (mToneGenerators[i] != null) {
1151 mToneGenerators[i].release();
1152 }
1153 mToneGenerators[i] = null;
1154 }
1155 }
1156 }
1157
1158 @Override
1159 public void handleMessage(Message msg) {
1160 switch (msg.what) {
1161
1162 case MSG_VOLUME_CHANGED: {
1163 onVolumeChanged(msg.arg1, msg.arg2);
1164 break;
1165 }
1166
Mike Lockwoodce952c82011-11-14 10:47:42 -08001167 case MSG_MUTE_CHANGED: {
1168 onMuteChanged(msg.arg1, msg.arg2);
1169 break;
1170 }
1171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 case MSG_FREE_RESOURCES: {
1173 onFreeResources();
1174 break;
1175 }
1176
1177 case MSG_STOP_SOUNDS: {
1178 onStopSounds();
1179 break;
1180 }
1181
1182 case MSG_PLAY_SOUND: {
1183 onPlaySound(msg.arg1, msg.arg2);
1184 break;
1185 }
1186
1187 case MSG_VIBRATE: {
1188 onVibrate();
1189 break;
1190 }
1191
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001192 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001193 if (isShowing()) {
1194 if (mDialog != null) {
1195 mDialog.dismiss();
RoboErik19c95182014-06-23 15:38:48 -07001196 clearRemoteStreamController();
John Spurlockf71205c2014-05-29 10:17:51 -04001197 mActiveStreamType = -1;
John Spurlock86005342014-05-23 11:58:00 -04001198 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001199 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001200 synchronized (sConfirmSafeVolumeLock) {
1201 if (sConfirmSafeVolumeDialog != null) {
1202 sConfirmSafeVolumeDialog.dismiss();
1203 }
1204 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001205 break;
1206 }
1207 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001208 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001209 updateStates();
1210 }
1211 break;
1212 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001213
1214 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001215 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001216 break;
1217 }
1218
1219 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1220 onRemoteVolumeUpdateIfShown();
1221 break;
1222
1223 case MSG_SLIDER_VISIBILITY_CHANGED:
1224 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1225 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001226
1227 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001228 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001229 break;
John Spurlock86005342014-05-23 11:58:00 -04001230
1231 case MSG_LAYOUT_DIRECTION:
1232 setLayoutDirection(msg.arg1);
1233 break;
1234
1235 case MSG_ZEN_MODE_CHANGED:
1236 updateZenMode(msg.arg1 != 0);
1237 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001238 }
1239 }
1240
John Spurlock86005342014-05-23 11:58:00 -04001241 public void resetTimeout() {
1242 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001243 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001244 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001245 }
1246
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001247 private void forceTimeout() {
1248 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001249 sendEmptyMessage(MSG_TIMEOUT);
1250 }
1251
1252 public ZenModeController getZenController() {
1253 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001254 }
1255
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001256 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1257 @Override
1258 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1259 final Object tag = seekBar.getTag();
1260 if (fromUser && tag instanceof StreamControl) {
1261 StreamControl sc = (StreamControl) tag;
RoboErik19c95182014-06-23 15:38:48 -07001262 setStreamVolume(sc, progress, 0);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001263 }
1264 resetTimeout();
1265 }
1266
1267 @Override
1268 public void onStartTrackingTouch(SeekBar seekBar) {
1269 }
1270
1271 @Override
1272 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001273 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001274 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001275
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001276 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1277 @Override
1278 public void onClick(View v) {
John Spurlock86005342014-05-23 11:58:00 -04001279 if (v == mExpandButton && mZenController != null) {
1280 final boolean newZen = !mZenController.isZen();
John Spurlock3bd4fee2014-05-29 20:51:09 -04001281 AsyncTask.execute(new Runnable() {
1282 @Override
1283 public void run() {
1284 mZenController.setZen(newZen);
1285 }
1286 });
John Spurlock86005342014-05-23 11:58:00 -04001287 if (newZen) {
1288 expand();
1289 } else {
1290 collapse();
1291 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001292 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001293 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001294 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001295 };
John Spurlock86005342014-05-23 11:58:00 -04001296
1297 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
1298 public void onZenChanged(boolean zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001299 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001300 }
1301 };
RoboErik19c95182014-06-23 15:38:48 -07001302
1303 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1304 public void onVolumeInfoChanged(VolumeInfo info) {
1305 onRemoteVolumeUpdateIfShown();
1306 }
1307 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308}