blob: b85fbf3d0951dde93ff9806bb89973a6b8bb176c [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Eric Laurentc34dcc12012-09-10 13:51:52 -070019import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080020import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080021import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040024import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080026import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040028import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040029import android.graphics.PixelFormat;
30import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040031import android.media.AudioAttributes;
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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.Handler;
42import android.os.Message;
43import android.os.Vibrator;
John Spurlockae641c92014-06-30 18:11:40 -040044import android.provider.Settings.Global;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040046import android.util.SparseArray;
John Spurlock3346a802014-05-20 16:25:37 -040047import android.view.Gravity;
48import android.view.LayoutInflater;
49import android.view.MotionEvent;
50import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040051import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040052import android.view.ViewGroup;
53import android.view.Window;
54import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070055import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080057import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080058import android.widget.SeekBar.OnSeekBarChangeListener;
59
John Spurlock86005342014-05-23 11:58:00 -040060import com.android.internal.R;
61import com.android.systemui.statusbar.policy.ZenModeController;
62
John Spurlockad494bc2014-07-19 15:56:19 -040063import java.io.FileDescriptor;
64import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080065
66/**
John Spurlock3346a802014-05-20 16:25:37 -040067 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070068 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 * @hide
70 */
John Spurlock3346a802014-05-20 16:25:37 -040071public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040072 private static final String TAG = "VolumePanel";
73 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
John Spurlock3346a802014-05-20 16:25:37 -040075 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
77 /**
78 * The delay before vibrating. This small period exists so if the user is
79 * moving to silent mode, it will not emit a short vibrate (it normally
80 * would since vibrate is between normal mode and silent mode using hardware
81 * keys).
82 */
83 public static final int VIBRATE_DELAY = 300;
84
85 private static final int VIBRATE_DURATION = 300;
86 private static final int BEEP_DURATION = 150;
87 private static final int MAX_VOLUME = 100;
88 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080089 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040090 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040091 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock3bd4fee2014-05-29 20:51:09 -040092 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
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;
John Spurlockae641c92014-06-30 18:11:40 -0400108 private static final int MSG_USER_ACTIVITY = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400110 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800111 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700112 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400113
John Spurlock7b414672014-07-18 13:02:39 -0400114 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
115 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
116 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
117 .build();
118
John Spurlock86005342014-05-23 11:58:00 -0400119 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400120 protected final Context mContext;
121 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400122 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700123 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700124 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400125 private boolean mZenModeCapable;
John Spurlock8845da72014-07-07 21:29:48 -0400126 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400127 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400128 private float mDisabledAlpha;
129 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
130 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800131
Christopher Tatec4b78d22012-05-22 13:57:58 -0700132 // True if we want to play tones on the system stream when the master stream is specified.
133 private final boolean mPlayMasterStreamTones;
134
John Spurlock86005342014-05-23 11:58:00 -0400135
136 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400138 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400139 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800140
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700141 /** The visible portion of the volume overlay */
142 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400143 /** Contains the slider and its touchable icons */
144 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400145 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400146 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400147
John Spurlockae641c92014-06-30 18:11:40 -0400148 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800149
150 /** Currently active stream that shows up at the top of the list of sliders */
151 private int mActiveStreamType = -1;
152 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400153 private SparseArray<StreamControl> mStreamControls;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800154
Amith Yamasani71def772011-10-12 12:25:24 -0700155 private enum StreamResources {
156 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
157 R.string.volume_icon_description_bluetooth,
158 R.drawable.ic_audio_bt,
159 R.drawable.ic_audio_bt,
160 false),
161 RingerStream(AudioManager.STREAM_RING,
162 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400163 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400164 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700165 false),
166 VoiceStream(AudioManager.STREAM_VOICE_CALL,
167 R.string.volume_icon_description_incall,
168 R.drawable.ic_audio_phone,
169 R.drawable.ic_audio_phone,
170 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700171 AlarmStream(AudioManager.STREAM_ALARM,
172 R.string.volume_alarm,
173 R.drawable.ic_audio_alarm,
174 R.drawable.ic_audio_alarm_mute,
175 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700176 MediaStream(AudioManager.STREAM_MUSIC,
177 R.string.volume_icon_description_media,
178 R.drawable.ic_audio_vol,
179 R.drawable.ic_audio_vol_mute,
180 true),
181 NotificationStream(AudioManager.STREAM_NOTIFICATION,
182 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400183 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400184 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400185 true),
186 // for now, use media resources for master volume
187 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700188 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400189 R.drawable.ic_audio_vol,
190 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700191 false),
192 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
193 R.string.volume_icon_description_media, //FIXME should have its own description
194 R.drawable.ic_media_route_on_holo_dark,
195 R.drawable.ic_media_route_disabled_holo_dark,
196 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700197
198 int streamType;
199 int descRes;
200 int iconRes;
201 int iconMuteRes;
202 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
203 boolean show;
204
205 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
206 this.streamType = streamType;
207 this.descRes = descRes;
208 this.iconRes = iconRes;
209 this.iconMuteRes = iconMuteRes;
210 this.show = show;
211 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700212 }
Amith Yamasani71def772011-10-12 12:25:24 -0700213
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800214 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700215 private static final StreamResources[] STREAMS = {
216 StreamResources.BluetoothSCOStream,
217 StreamResources.RingerStream,
218 StreamResources.VoiceStream,
219 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700220 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400221 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700222 StreamResources.MasterStream,
223 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800224 };
225
226 /** Object that contains data for each slider */
227 private class StreamControl {
228 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700229 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800230 ViewGroup group;
231 ImageView icon;
232 SeekBar seekbarView;
233 int iconRes;
234 int iconMuteRes;
235 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236
237 // Synchronize when accessing this
238 private ToneGenerator mToneGenerators[];
239 private Vibrator mVibrator;
240
Eric Laurentc34dcc12012-09-10 13:51:52 -0700241 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700242 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700243
244 private static class WarningDialogReceiver extends BroadcastReceiver
245 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800246 private final Context mContext;
247 private final Dialog mDialog;
248 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700249
Eric Laurentfde16d52012-12-03 14:42:39 -0800250 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700251 mContext = context;
252 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800253 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700254 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
255 context.registerReceiver(this, filter);
256 }
257
258 @Override
259 public void onReceive(Context context, Intent intent) {
260 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800261 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700262 }
263
Alan Viverette494fb7b2014-04-10 18:12:56 -0700264 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700265 public void onDismiss(DialogInterface unused) {
266 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800267 cleanUp();
268 }
269
270 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700271 synchronized (sConfirmSafeVolumeLock) {
272 sConfirmSafeVolumeDialog = null;
273 }
John Spurlock1dad2722014-07-11 11:07:53 -0400274 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800275 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700276 }
277 }
278
John Spurlockeb2727b2014-07-19 23:11:36 -0400279 public VolumePanel(Context context, ZenModeController zenController) {
280 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400282 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400285 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700286 final Resources res = context.getResources();
287 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400288 if (useMasterVolume) {
289 for (int i = 0; i < STREAMS.length; i++) {
290 StreamResources streamRes = STREAMS[i];
291 streamRes.show = (streamRes.streamType == STREAM_MASTER);
292 }
293 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400294 if (LOGD) Log.d(mTag, "new VolumePanel");
295
John Spurlockad494bc2014-07-19 15:56:19 -0400296 mDisabledAlpha = 0.5f;
297 if (mContext.getTheme() != null) {
298 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
299 new int[] { android.R.attr.disabledAlpha });
300 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
301 arr.recycle();
302 }
303
John Spurlockeb2727b2014-07-19 23:11:36 -0400304 mDialog = new Dialog(context) {
305 @Override
306 public boolean onTouchEvent(MotionEvent event) {
307 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
308 sConfirmSafeVolumeDialog == null) {
309 forceTimeout(0);
310 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700311 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400312 return false;
313 }
314 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700315
John Spurlockeb2727b2014-07-19 23:11:36 -0400316 // Change some window properties
317 final Window window = mDialog.getWindow();
318 final LayoutParams lp = window.getAttributes();
319 lp.token = null;
320 // Offset from the top
321 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
322 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
323 lp.format = PixelFormat.TRANSLUCENT;
324 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
325 lp.gravity = Gravity.TOP;
326 window.setAttributes(lp);
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
331 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
332 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
333 mDialog.setCanceledOnTouchOutside(true);
334 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
335 mDialog.setOnDismissListener(new OnDismissListener() {
336 @Override
337 public void onDismiss(DialogInterface dialog) {
338 mActiveStreamType = -1;
339 mAudioManager.forceVolumeControlStream(mActiveStreamType);
340 setZenPanelVisible(false);
341 }
342 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700343
John Spurlockeb2727b2014-07-19 23:11:36 -0400344 mDialog.create();
345 // temporary workaround, until we support window-level shadows
346 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700347
John Spurlockeb2727b2014-07-19 23:11:36 -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 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
358 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400359 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
360 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700363 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700364 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700365
John Spurlock86005342014-05-23 11:58:00 -0400366 mZenModeCapable = !useMasterVolume && mZenController != null;
John Spurlockae641c92014-06-30 18:11:40 -0400367 updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
John Spurlock86005342014-05-23 11:58:00 -0400368 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700369
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700370 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
371 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700372 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
373
John Spurlock8c79d2e2014-07-24 15:15:25 -0400374 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800375 }
376
John Spurlockad494bc2014-07-19 15:56:19 -0400377 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
378 pw.println("VolumePanel state:");
379 pw.print(" mTag="); pw.println(mTag);
380 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
381 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
382 pw.print(" mZenModeCapable="); pw.println(mZenModeCapable);
383 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
384 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
385 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
386 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
387 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
388 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
389 pw.print(" isShowing()="); pw.println(isShowing());
390 pw.print(" mCallback="); pw.println(mCallback);
391 pw.print(" sConfirmSafeVolumeDialog=");
392 pw.println(sConfirmSafeVolumeDialog != null ? "<not null>" : null);
393 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
394 pw.print(" mStreamControls=");
395 if (mStreamControls == null) {
396 pw.println("null");
397 } else {
398 final int N = mStreamControls.size();
399 pw.print("<size "); pw.print(N); pw.println('>');
400 for (int i = 0; i < N; i++) {
401 final StreamControl sc = mStreamControls.valueAt(i);
402 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
403 if (sc.seekbarView != null) {
404 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
405 pw.print(" of "); pw.print(sc.seekbarView.getMax());
406 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
407 }
408 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
409 pw.println();
410 }
411 }
412 }
413
John Spurlockeb2727b2014-07-19 23:11:36 -0400414 private void initZenModePanel() {
415 mZenPanel.init(mZenController);
416 mZenPanel.setCallback(new ZenModePanel.Callback() {
417 @Override
418 public void onMoreSettings() {
419 if (mCallback != null) {
420 mCallback.onZenSettings();
421 }
422 }
423
424 @Override
425 public void onInteraction() {
426 resetTimeout();
427 }
428
429 @Override
430 public void onExpanded(boolean expanded) {
431 if (mZenPanelExpanded == expanded) return;
432 mZenPanelExpanded = expanded;
433 updateTimeoutDelay();
434 resetTimeout();
435 }
436 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400437 }
438
John Spurlock86005342014-05-23 11:58:00 -0400439 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800440 mPanel.setLayoutDirection(layoutDirection);
441 updateStates();
442 }
443
John Spurlock8c79d2e2014-07-24 15:15:25 -0400444 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800445 final IntentFilter filter = new IntentFilter();
446 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400447 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800448 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700449 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800450 public void onReceive(Context context, Intent intent) {
451 final String action = intent.getAction();
452
453 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
454 removeMessages(MSG_RINGER_MODE_CHANGED);
455 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
456 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400457
458 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
459 postDismiss(0);
460 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800461 }
462 }, filter);
463 }
464
465 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400466 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700467 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700468 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700469 // TODO do we need to support a distinct mute property for remote?
470 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400471 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700472 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400473 }
474 }
475
476 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400477 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700478 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700479 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700480 if (mStreamControls != null) {
481 StreamControl sc = mStreamControls.get(streamType);
482 if (sc != null && sc.controller != null) {
483 VolumeInfo vi = sc.controller.getVolumeInfo();
484 return vi.getMaxVolume();
485 }
486 }
487 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400488 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700489 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400490 }
491 }
492
493 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400494 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700495 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700496 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700497 if (mStreamControls != null) {
498 StreamControl sc = mStreamControls.get(streamType);
499 if (sc != null && sc.controller != null) {
500 VolumeInfo vi = sc.controller.getVolumeInfo();
501 return vi.getCurrentVolume();
502 }
503 }
504 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400505 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700506 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400507 }
508 }
509
RoboErik19c95182014-06-23 15:38:48 -0700510 private void setStreamVolume(StreamControl sc, int index, int flags) {
511 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
512 if (sc.controller != null) {
513 sc.controller.setVolumeTo(index, flags);
514 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700515 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700516 }
517 } else if (getStreamVolume(sc.streamType) != index) {
518 if (sc.streamType == STREAM_MASTER) {
519 mAudioManager.setMasterVolume(index, flags);
520 } else {
521 mAudioManager.setStreamVolume(sc.streamType, index, flags);
522 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400523 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800524 }
525
526 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700527 final Resources res = mContext.getResources();
528 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
529 Context.LAYOUT_INFLATER_SERVICE);
530
John Spurlockad494bc2014-07-19 15:56:19 -0400531 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700532
Amith Yamasani71def772011-10-12 12:25:24 -0700533 for (int i = 0; i < STREAMS.length; i++) {
534 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700535
536 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700537
538 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700539 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400540 sc.group = (ViewGroup) inflater.inflate(
541 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800542 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400543 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800544 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700545 sc.icon.setContentDescription(res.getString(streamRes.descRes));
546 sc.iconRes = streamRes.iconRes;
547 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800548 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400549 sc.icon.setClickable(isNotificationOrRing(streamType));
550 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400551 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400552 sc.icon.setOnClickListener(new OnClickListener() {
553 @Override
554 public void onClick(View v) {
555 resetTimeout();
556 toggle(sc);
557 }
558 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400559 }
John Spurlock86005342014-05-23 11:58:00 -0400560 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700561 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700562 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400563 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700564 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800565 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700566 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800567 }
568 }
569
John Spurlock7f1df5e2014-05-31 19:11:40 -0400570 private void toggle(StreamControl sc) {
571 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
572 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
573 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
574 } else {
575 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
576 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
577 }
578 }
579
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800580 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400581 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800582
Alan Viverette494fb7b2014-04-10 18:12:56 -0700583 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800584 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400585 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800586 mActiveStreamType = -1;
587 } else {
John Spurlock86005342014-05-23 11:58:00 -0400588 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800589 mActiveStreamType = activeStreamType;
590 active.group.setVisibility(View.VISIBLE);
591 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400592 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400593 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800594 }
595 }
596
John Spurlockad494bc2014-07-19 15:56:19 -0400597 private void updateSliderProgress(StreamControl sc, int progress) {
598 final boolean isRinger = isNotificationOrRing(sc.streamType);
599 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
600 progress = mLastRingerProgress;
601 }
602 if (progress < 0) {
603 progress = getStreamVolume(sc.streamType);
604 }
605 sc.seekbarView.setProgress(progress);
606 if (isRinger) {
607 mLastRingerProgress = progress;
608 }
609 }
610
611 private void updateSliderIcon(StreamControl sc, boolean muted) {
612 if (isNotificationOrRing(sc.streamType)) {
613 int ringerMode = mAudioManager.getRingerMode();
614 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
615 ringerMode = mLastRingerMode;
616 } else {
617 mLastRingerMode = ringerMode;
618 }
619 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
620 }
621 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
622 }
623
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800624 /** Update the mute and progress state of a slider */
625 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400626 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800627 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800628 // Force reloading the image resource
629 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400630 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400631 updateSliderEnabled(sc, muted, false);
632 }
633
John Spurlock5f640e42014-05-31 20:15:59 -0400634 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
635 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400636 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700637 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
638 // never disable touch interactions for remote playback, the muting is not tied to
639 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700640 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400641 } else if (fixedVolume ||
642 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800643 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700644 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400645 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400646 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400647 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400648 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400649 sc.icon.setClickable(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700650 } else {
651 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400652 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400653 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700654 }
John Spurlockae641c92014-06-30 18:11:40 -0400655 // show the silent hint when the disabled slider is touched in silent mode
656 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400657 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400658 sc.group.setOnTouchListener(null);
659 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400660 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400661 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400662 @Override
663 public boolean onTouch(View v, MotionEvent event) {
664 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400665 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400666 return false;
667 }
John Spurlockae641c92014-06-30 18:11:40 -0400668 };
669 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400670 }
671 }
672 }
673
John Spurlockae641c92014-06-30 18:11:40 -0400674 private void showSilentHint() {
675 if (mZenPanel != null) {
676 mZenPanel.showSilentHint();
677 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800678 }
679
John Spurlock7f1df5e2014-05-31 19:11:40 -0400680 private static boolean isNotificationOrRing(int streamType) {
681 return streamType == AudioManager.STREAM_RING
682 || streamType == AudioManager.STREAM_NOTIFICATION;
683 }
684
John Spurlockae641c92014-06-30 18:11:40 -0400685 public void setCallback(Callback callback) {
686 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400687 }
688
John Spurlock8845da72014-07-07 21:29:48 -0400689 private void updateTimeoutDelay() {
690 mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400691 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
692 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
693 : TIMEOUT_DELAY;
694 }
695
696 private boolean isZenPanelVisible() {
697 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400698 }
699
John Spurlockae641c92014-06-30 18:11:40 -0400700 private void setZenPanelVisible(boolean visible) {
701 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400702 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400703 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400704 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400705 resetTimeout();
706 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400707 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800708 }
John Spurlockea9938c2014-07-11 18:51:32 -0400709 if (changing) {
710 updateTimeoutDelay();
711 resetTimeout();
712 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800713 }
714
Eric Laurentfde16d52012-12-03 14:42:39 -0800715 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400716 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800717 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400718 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800719 updateSlider(sc);
720 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 }
722
John Spurlockae641c92014-06-30 18:11:40 -0400723 private void updateZenMode(int zen) {
724 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
725 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400726 }
727
John Spurlockae641c92014-06-30 18:11:40 -0400728 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400729 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400730 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400731 }
732
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 public void postVolumeChanged(int streamType, int flags) {
734 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700735 synchronized (this) {
736 if (mStreamControls == null) {
737 createSliders();
738 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800739 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800740 removeMessages(MSG_FREE_RESOURCES);
741 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
742 }
743
RoboErik19c95182014-06-23 15:38:48 -0700744 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700745 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
746 synchronized (this) {
747 if (mStreamControls == null) {
748 createSliders();
749 }
750 }
751 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700752 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700753 }
754
755 public void postRemoteSliderVisibility(boolean visible) {
756 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
757 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
758 }
759
760 /**
761 * Called by AudioService when it has received new remote playback information that
762 * would affect the VolumePanel display (mainly volumes). The difference with
763 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
764 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
765 * displayed.
766 * This special code path is due to the fact that remote volume updates arrive to AudioService
767 * asynchronously. So after AudioService has sent the volume update (which should be treated
768 * as a request to update the volume), the application will likely set a new volume. If the UI
769 * is still up, we need to refresh the display to show this new value.
770 */
771 public void postHasNewRemotePlaybackInfo() {
772 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
773 // don't create or prevent resources to be freed, if they disappear, this update came too
774 // late and shouldn't warrant the panel to be displayed longer
775 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
776 }
777
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400778 public void postMasterVolumeChanged(int flags) {
779 postVolumeChanged(STREAM_MASTER, flags);
780 }
781
Mike Lockwoodce952c82011-11-14 10:47:42 -0800782 public void postMuteChanged(int streamType, int flags) {
783 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700784 synchronized (this) {
785 if (mStreamControls == null) {
786 createSliders();
787 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800788 }
789 removeMessages(MSG_FREE_RESOURCES);
790 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
791 }
792
793 public void postMasterMuteChanged(int flags) {
794 postMuteChanged(STREAM_MASTER, flags);
795 }
796
Eric Laurentfde16d52012-12-03 14:42:39 -0800797 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700798 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800799 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700800 }
801
John Spurlock1dad2722014-07-11 11:07:53 -0400802 public void postDismiss(long delay) {
803 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400804 }
805
806 public void postLayoutDirection(int layoutDirection) {
807 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400808 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400809 }
810
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 /**
812 * Override this if you have other work to do when the volume changes (for
813 * example, vibrating, playing a sound, etc.). Make sure to call through to
814 * the superclass implementation.
815 */
816 protected void onVolumeChanged(int streamType, int flags) {
817
John Spurlock86005342014-05-23 11:58:00 -0400818 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800819
820 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700821 synchronized (this) {
822 if (mActiveStreamType != streamType) {
823 reorderSliders(streamType);
824 }
RoboErik19c95182014-06-23 15:38:48 -0700825 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800826 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800827 }
828
Marco Nelissen69f593c2009-07-28 09:55:04 -0700829 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 removeMessages(MSG_PLAY_SOUND);
831 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
832 }
833
834 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
835 removeMessages(MSG_PLAY_SOUND);
836 removeMessages(MSG_VIBRATE);
837 onStopSounds();
838 }
839
840 removeMessages(MSG_FREE_RESOURCES);
841 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800842 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800843 }
844
Mike Lockwoodce952c82011-11-14 10:47:42 -0800845 protected void onMuteChanged(int streamType, int flags) {
846
John Spurlock86005342014-05-23 11:58:00 -0400847 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800848
849 StreamControl sc = mStreamControls.get(streamType);
850 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400851 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800852 }
853
854 onVolumeChanged(streamType, flags);
855 }
856
RoboErik19c95182014-06-23 15:38:48 -0700857 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700858 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800859
Marco Nelissen69f593c2009-07-28 09:55:04 -0700860 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800861
862 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400863 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800864 + ", flags: " + flags + "), index: " + index);
865 }
866
867 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800868
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400869 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700870 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871
872 switch (streamType) {
873
874 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800875// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700876 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
877 mContext, RingtoneManager.TYPE_RINGTONE);
878 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700879 mRingIsSilent = true;
880 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800881 break;
882 }
883
884 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800885 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800886 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
887 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
888 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
889 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800890 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800892 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 }
894 break;
895 }
896
897 case AudioManager.STREAM_VOICE_CALL: {
898 /*
899 * For in-call voice call volume, there is no inaudible volume.
900 * Rescale the UI control so the progress bar doesn't go all
901 * the way to zero and don't show the mute icon.
902 */
903 index++;
904 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800905 break;
906 }
907
908 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 break;
910 }
911
912 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700913 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
914 mContext, RingtoneManager.TYPE_NOTIFICATION);
915 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700916 mRingIsSilent = true;
917 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800918 break;
919 }
920
921 case AudioManager.STREAM_BLUETOOTH_SCO: {
922 /*
923 * For in-call voice call volume, there is no inaudible volume.
924 * Rescale the UI control so the progress bar doesn't go all
925 * the way to zero and don't show the mute icon.
926 */
927 index++;
928 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 break;
930 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700931
932 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700933 if (controller == null && sc != null) {
934 // If we weren't passed one try using the last one set.
935 controller = sc.controller;
936 }
937 if (controller == null) {
938 // We still don't have one, ignore the command.
939 Log.w(mTag, "sent remote volume change without a controller!");
940 } else {
941 VolumeInfo vi = controller.getVolumeInfo();
942 index = vi.getCurrentVolume();
943 max = vi.getMaxVolume();
944 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
945 // if the remote volume is fixed add the flag for the UI
946 flags |= AudioManager.FLAG_FIXED_VOLUME;
947 }
948 }
John Spurlock86005342014-05-23 11:58:00 -0400949 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700950 break;
951 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800952 }
953
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800954 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700955 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
956 if (sc.controller != null) {
957 sc.controller.removeCallback(mMediaControllerCb);
958 }
959 sc.controller = controller;
960 if (controller != null) {
961 sc.controller.addCallback(mMediaControllerCb);
962 }
963 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700964 if (sc.seekbarView.getMax() != max) {
965 sc.seekbarView.setMax(max);
966 }
John Spurlockad494bc2014-07-19 15:56:19 -0400967 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400968 updateSliderEnabled(sc, isMuted(streamType),
969 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 }
971
John Spurlock86005342014-05-23 11:58:00 -0400972 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700973 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
974 // when the stream is for remote playback, use -1 to reset the stream type evaluation
975 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700976
John Spurlockeb2727b2014-07-19 23:11:36 -0400977 mDialog.show();
978 if (mCallback != null) {
979 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -0400980 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 }
982
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700984 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
985 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400986 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700987 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
989 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400990
991 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -0400992 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
993 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -0400994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996
John Spurlock86005342014-05-23 11:58:00 -0400997 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400998 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -0400999 }
1000
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 protected void onPlaySound(int streamType, int flags) {
1002
1003 if (hasMessages(MSG_STOP_SOUNDS)) {
1004 removeMessages(MSG_STOP_SOUNDS);
1005 // Force stop right now
1006 onStopSounds();
1007 }
1008
1009 synchronized (this) {
1010 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001011 if (toneGen != null) {
1012 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1013 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1014 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001015 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 }
1017
1018 protected void onStopSounds() {
1019
1020 synchronized (this) {
1021 int numStreamTypes = AudioSystem.getNumStreamTypes();
1022 for (int i = numStreamTypes - 1; i >= 0; i--) {
1023 ToneGenerator toneGen = mToneGenerators[i];
1024 if (toneGen != null) {
1025 toneGen.stopTone();
1026 }
1027 }
1028 }
1029 }
1030
1031 protected void onVibrate() {
1032
1033 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001034 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 return;
1036 }
1037
John Spurlock7b414672014-07-18 13:02:39 -04001038 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 }
1040
RoboErik19c95182014-06-23 15:38:48 -07001041 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1042 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1043 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001044
John Spurlock86005342014-05-23 11:58:00 -04001045 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001046 synchronized (this) {
1047 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1048 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1049 }
RoboErik19c95182014-06-23 15:38:48 -07001050 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001051 }
1052 } else {
John Spurlock86005342014-05-23 11:58:00 -04001053 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001054 }
1055
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001056 removeMessages(MSG_FREE_RESOURCES);
1057 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001058 resetTimeout();
1059 }
1060
1061 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001062 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1063 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001064 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1065 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001066 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001067 }
1068 }
1069
RoboErik19c95182014-06-23 15:38:48 -07001070 /**
1071 * Clear the current remote stream controller.
1072 */
1073 private void clearRemoteStreamController() {
1074 if (mStreamControls != null) {
1075 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1076 if (sc != null) {
1077 if (sc.controller != null) {
1078 sc.controller.removeCallback(mMediaControllerCb);
1079 sc.controller = null;
1080 }
1081 }
1082 }
1083 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001084
1085 /**
1086 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1087 * Hide or show a slider
1088 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1089 * or AudioService.STREAM_REMOTE_MUSIC
1090 * @param visible
1091 */
1092 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001093 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001094 boolean isVisible = (visible == 1);
1095 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1096 StreamResources streamRes = STREAMS[i];
1097 if (streamRes.streamType == streamType) {
1098 streamRes.show = isVisible;
1099 if (!isVisible && (mActiveStreamType == streamType)) {
1100 mActiveStreamType = -1;
1101 }
1102 break;
1103 }
1104 }
1105 }
1106
Eric Laurentfde16d52012-12-03 14:42:39 -08001107 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001108 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001109 synchronized (sConfirmSafeVolumeLock) {
1110 if (sConfirmSafeVolumeDialog != null) {
1111 return;
1112 }
1113 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1114 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1115 .setPositiveButton(com.android.internal.R.string.yes,
1116 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001117 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001118 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001119 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001120 }
1121 })
1122 .setNegativeButton(com.android.internal.R.string.no, null)
1123 .setIconAttribute(android.R.attr.alertDialogIcon)
1124 .create();
1125 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1126 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001127
Eric Laurentfde16d52012-12-03 14:42:39 -08001128 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1129 sConfirmSafeVolumeDialog.getWindow().setType(
1130 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1131 sConfirmSafeVolumeDialog.show();
1132 }
1133 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001134 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001135 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001136 }
1137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 /**
1139 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1140 */
1141 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001142 if (streamType == STREAM_MASTER) {
1143 // For devices that use the master volume setting only but still want to
1144 // play a volume-changed tone, direct the master volume pseudostream to
1145 // the system stream's tone generator.
1146 if (mPlayMasterStreamTones) {
1147 streamType = AudioManager.STREAM_SYSTEM;
1148 } else {
1149 return null;
1150 }
1151 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 synchronized (this) {
1153 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001154 try {
1155 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1156 } catch (RuntimeException e) {
1157 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001158 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001159 + "RuntimeException: " + e);
1160 }
1161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001163 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 }
1165 }
1166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001167
1168 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001169 * Switch between icons because Bluetooth music is same as music volume, but with
1170 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001172 private void setMusicIcon(int resId, int resMuteId) {
1173 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1174 if (sc != null) {
1175 sc.iconRes = resId;
1176 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001177 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001178 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001179 }
1180
1181 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001182 synchronized (this) {
1183 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1184 if (mToneGenerators[i] != null) {
1185 mToneGenerators[i].release();
1186 }
1187 mToneGenerators[i] = null;
1188 }
1189 }
1190 }
1191
1192 @Override
1193 public void handleMessage(Message msg) {
1194 switch (msg.what) {
1195
1196 case MSG_VOLUME_CHANGED: {
1197 onVolumeChanged(msg.arg1, msg.arg2);
1198 break;
1199 }
1200
Mike Lockwoodce952c82011-11-14 10:47:42 -08001201 case MSG_MUTE_CHANGED: {
1202 onMuteChanged(msg.arg1, msg.arg2);
1203 break;
1204 }
1205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 case MSG_FREE_RESOURCES: {
1207 onFreeResources();
1208 break;
1209 }
1210
1211 case MSG_STOP_SOUNDS: {
1212 onStopSounds();
1213 break;
1214 }
1215
1216 case MSG_PLAY_SOUND: {
1217 onPlaySound(msg.arg1, msg.arg2);
1218 break;
1219 }
1220
1221 case MSG_VIBRATE: {
1222 onVibrate();
1223 break;
1224 }
1225
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001226 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001227 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001228 mDialog.dismiss();
1229 clearRemoteStreamController();
1230 mActiveStreamType = -1;
1231 if (mCallback != null) {
1232 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001233 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001234 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001235 synchronized (sConfirmSafeVolumeLock) {
1236 if (sConfirmSafeVolumeDialog != null) {
1237 sConfirmSafeVolumeDialog.dismiss();
1238 }
1239 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001240 break;
1241 }
1242 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001243 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001244 updateStates();
1245 }
1246 break;
1247 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001248
1249 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001250 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001251 break;
1252 }
1253
1254 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1255 onRemoteVolumeUpdateIfShown();
1256 break;
1257
1258 case MSG_SLIDER_VISIBILITY_CHANGED:
1259 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1260 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001261
1262 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001263 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001264 break;
John Spurlock86005342014-05-23 11:58:00 -04001265
1266 case MSG_LAYOUT_DIRECTION:
1267 setLayoutDirection(msg.arg1);
1268 break;
1269
1270 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001271 updateZenMode(msg.arg1);
1272 break;
1273
1274 case MSG_USER_ACTIVITY:
1275 if (mCallback != null) {
1276 mCallback.onInteraction();
1277 }
John Spurlock86005342014-05-23 11:58:00 -04001278 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 }
1280 }
1281
John Spurlockae641c92014-06-30 18:11:40 -04001282 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001283 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1284 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001285 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001286 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001287 removeMessages(MSG_USER_ACTIVITY);
1288 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001289 }
1290
John Spurlock1dad2722014-07-11 11:07:53 -04001291 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001292 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001293 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001294 }
1295
1296 public ZenModeController getZenController() {
1297 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001298 }
1299
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001300 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1301 @Override
1302 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1303 final Object tag = seekBar.getTag();
1304 if (fromUser && tag instanceof StreamControl) {
1305 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001306 setStreamVolume(sc, progress,
1307 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001308 }
1309 resetTimeout();
1310 }
1311
1312 @Override
1313 public void onStartTrackingTouch(SeekBar seekBar) {
1314 }
1315
1316 @Override
1317 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001318 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001319 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001320
John Spurlock86005342014-05-23 11:58:00 -04001321 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001322 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001323 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001324 }
1325 };
RoboErik19c95182014-06-23 15:38:48 -07001326
1327 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1328 public void onVolumeInfoChanged(VolumeInfo info) {
1329 onRemoteVolumeUpdateIfShown();
1330 }
1331 };
John Spurlockae641c92014-06-30 18:11:40 -04001332
1333 public interface Callback {
1334 void onZenSettings();
1335 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001336 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001337 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001338}