blob: 9ba9745ea757f6e60ac707b8a2229cb7a3481741 [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
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800374 listenToRingerMode();
375 }
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
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800444 private void listenToRingerMode() {
445 final IntentFilter filter = new IntentFilter();
446 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
447 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700448 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800449 public void onReceive(Context context, Intent intent) {
450 final String action = intent.getAction();
451
452 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
453 removeMessages(MSG_RINGER_MODE_CHANGED);
454 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
455 }
456 }
457 }, filter);
458 }
459
460 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400461 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700462 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700463 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700464 // TODO do we need to support a distinct mute property for remote?
465 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400466 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700467 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400468 }
469 }
470
471 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400472 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700473 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700474 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700475 if (mStreamControls != null) {
476 StreamControl sc = mStreamControls.get(streamType);
477 if (sc != null && sc.controller != null) {
478 VolumeInfo vi = sc.controller.getVolumeInfo();
479 return vi.getMaxVolume();
480 }
481 }
482 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400483 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700484 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400485 }
486 }
487
488 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400489 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700490 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700491 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700492 if (mStreamControls != null) {
493 StreamControl sc = mStreamControls.get(streamType);
494 if (sc != null && sc.controller != null) {
495 VolumeInfo vi = sc.controller.getVolumeInfo();
496 return vi.getCurrentVolume();
497 }
498 }
499 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400500 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700501 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400502 }
503 }
504
RoboErik19c95182014-06-23 15:38:48 -0700505 private void setStreamVolume(StreamControl sc, int index, int flags) {
506 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
507 if (sc.controller != null) {
508 sc.controller.setVolumeTo(index, flags);
509 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700510 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700511 }
512 } else if (getStreamVolume(sc.streamType) != index) {
513 if (sc.streamType == STREAM_MASTER) {
514 mAudioManager.setMasterVolume(index, flags);
515 } else {
516 mAudioManager.setStreamVolume(sc.streamType, index, flags);
517 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400518 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800519 }
520
521 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700522 final Resources res = mContext.getResources();
523 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
524 Context.LAYOUT_INFLATER_SERVICE);
525
John Spurlockad494bc2014-07-19 15:56:19 -0400526 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700527
Amith Yamasani71def772011-10-12 12:25:24 -0700528 for (int i = 0; i < STREAMS.length; i++) {
529 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700530
531 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700532
533 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700534 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400535 sc.group = (ViewGroup) inflater.inflate(
536 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800537 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400538 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800539 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700540 sc.icon.setContentDescription(res.getString(streamRes.descRes));
541 sc.iconRes = streamRes.iconRes;
542 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800543 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400544 sc.icon.setClickable(isNotificationOrRing(streamType));
545 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400546 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400547 sc.icon.setOnClickListener(new OnClickListener() {
548 @Override
549 public void onClick(View v) {
550 resetTimeout();
551 toggle(sc);
552 }
553 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400554 }
John Spurlock86005342014-05-23 11:58:00 -0400555 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700556 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700557 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400558 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700559 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800560 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700561 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800562 }
563 }
564
John Spurlock7f1df5e2014-05-31 19:11:40 -0400565 private void toggle(StreamControl sc) {
566 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
567 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
568 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
569 } else {
570 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
571 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
572 }
573 }
574
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800575 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400576 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800577
Alan Viverette494fb7b2014-04-10 18:12:56 -0700578 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800579 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400580 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800581 mActiveStreamType = -1;
582 } else {
John Spurlock86005342014-05-23 11:58:00 -0400583 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800584 mActiveStreamType = activeStreamType;
585 active.group.setVisibility(View.VISIBLE);
586 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400587 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400588 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800589 }
590 }
591
John Spurlockad494bc2014-07-19 15:56:19 -0400592 private void updateSliderProgress(StreamControl sc, int progress) {
593 final boolean isRinger = isNotificationOrRing(sc.streamType);
594 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
595 progress = mLastRingerProgress;
596 }
597 if (progress < 0) {
598 progress = getStreamVolume(sc.streamType);
599 }
600 sc.seekbarView.setProgress(progress);
601 if (isRinger) {
602 mLastRingerProgress = progress;
603 }
604 }
605
606 private void updateSliderIcon(StreamControl sc, boolean muted) {
607 if (isNotificationOrRing(sc.streamType)) {
608 int ringerMode = mAudioManager.getRingerMode();
609 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
610 ringerMode = mLastRingerMode;
611 } else {
612 mLastRingerMode = ringerMode;
613 }
614 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
615 }
616 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
617 }
618
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800619 /** Update the mute and progress state of a slider */
620 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400621 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800622 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800623 // Force reloading the image resource
624 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400625 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400626 updateSliderEnabled(sc, muted, false);
627 }
628
John Spurlock5f640e42014-05-31 20:15:59 -0400629 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
630 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400631 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700632 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
633 // never disable touch interactions for remote playback, the muting is not tied to
634 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700635 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400636 } else if (fixedVolume ||
637 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800638 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700639 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400640 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400641 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400642 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400643 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400644 sc.icon.setClickable(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700645 } else {
646 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400647 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400648 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700649 }
John Spurlockae641c92014-06-30 18:11:40 -0400650 // show the silent hint when the disabled slider is touched in silent mode
651 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400652 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400653 sc.group.setOnTouchListener(null);
654 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400655 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400656 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400657 @Override
658 public boolean onTouch(View v, MotionEvent event) {
659 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400660 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400661 return false;
662 }
John Spurlockae641c92014-06-30 18:11:40 -0400663 };
664 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400665 }
666 }
667 }
668
John Spurlockae641c92014-06-30 18:11:40 -0400669 private void showSilentHint() {
670 if (mZenPanel != null) {
671 mZenPanel.showSilentHint();
672 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800673 }
674
John Spurlock7f1df5e2014-05-31 19:11:40 -0400675 private static boolean isNotificationOrRing(int streamType) {
676 return streamType == AudioManager.STREAM_RING
677 || streamType == AudioManager.STREAM_NOTIFICATION;
678 }
679
John Spurlockae641c92014-06-30 18:11:40 -0400680 public void setCallback(Callback callback) {
681 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400682 }
683
John Spurlock8845da72014-07-07 21:29:48 -0400684 private void updateTimeoutDelay() {
685 mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400686 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
687 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
688 : TIMEOUT_DELAY;
689 }
690
691 private boolean isZenPanelVisible() {
692 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400693 }
694
John Spurlockae641c92014-06-30 18:11:40 -0400695 private void setZenPanelVisible(boolean visible) {
696 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400697 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400698 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400699 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400700 resetTimeout();
701 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400702 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800703 }
John Spurlockea9938c2014-07-11 18:51:32 -0400704 if (changing) {
705 updateTimeoutDelay();
706 resetTimeout();
707 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800708 }
709
Eric Laurentfde16d52012-12-03 14:42:39 -0800710 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400711 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800712 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400713 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800714 updateSlider(sc);
715 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800716 }
717
John Spurlockae641c92014-06-30 18:11:40 -0400718 private void updateZenMode(int zen) {
719 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
720 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400721 }
722
John Spurlockae641c92014-06-30 18:11:40 -0400723 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400724 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400725 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400726 }
727
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800728 public void postVolumeChanged(int streamType, int flags) {
729 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700730 synchronized (this) {
731 if (mStreamControls == null) {
732 createSliders();
733 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 removeMessages(MSG_FREE_RESOURCES);
736 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
737 }
738
RoboErik19c95182014-06-23 15:38:48 -0700739 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700740 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
741 synchronized (this) {
742 if (mStreamControls == null) {
743 createSliders();
744 }
745 }
746 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700747 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700748 }
749
750 public void postRemoteSliderVisibility(boolean visible) {
751 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
752 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
753 }
754
755 /**
756 * Called by AudioService when it has received new remote playback information that
757 * would affect the VolumePanel display (mainly volumes). The difference with
758 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
759 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
760 * displayed.
761 * This special code path is due to the fact that remote volume updates arrive to AudioService
762 * asynchronously. So after AudioService has sent the volume update (which should be treated
763 * as a request to update the volume), the application will likely set a new volume. If the UI
764 * is still up, we need to refresh the display to show this new value.
765 */
766 public void postHasNewRemotePlaybackInfo() {
767 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
768 // don't create or prevent resources to be freed, if they disappear, this update came too
769 // late and shouldn't warrant the panel to be displayed longer
770 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
771 }
772
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400773 public void postMasterVolumeChanged(int flags) {
774 postVolumeChanged(STREAM_MASTER, flags);
775 }
776
Mike Lockwoodce952c82011-11-14 10:47:42 -0800777 public void postMuteChanged(int streamType, int flags) {
778 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700779 synchronized (this) {
780 if (mStreamControls == null) {
781 createSliders();
782 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800783 }
784 removeMessages(MSG_FREE_RESOURCES);
785 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
786 }
787
788 public void postMasterMuteChanged(int flags) {
789 postMuteChanged(STREAM_MASTER, flags);
790 }
791
Eric Laurentfde16d52012-12-03 14:42:39 -0800792 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700793 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800794 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700795 }
796
John Spurlock1dad2722014-07-11 11:07:53 -0400797 public void postDismiss(long delay) {
798 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400799 }
800
801 public void postLayoutDirection(int layoutDirection) {
802 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400803 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400804 }
805
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800806 /**
807 * Override this if you have other work to do when the volume changes (for
808 * example, vibrating, playing a sound, etc.). Make sure to call through to
809 * the superclass implementation.
810 */
811 protected void onVolumeChanged(int streamType, int flags) {
812
John Spurlock86005342014-05-23 11:58:00 -0400813 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814
815 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700816 synchronized (this) {
817 if (mActiveStreamType != streamType) {
818 reorderSliders(streamType);
819 }
RoboErik19c95182014-06-23 15:38:48 -0700820 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800821 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 }
823
Marco Nelissen69f593c2009-07-28 09:55:04 -0700824 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 removeMessages(MSG_PLAY_SOUND);
826 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
827 }
828
829 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
830 removeMessages(MSG_PLAY_SOUND);
831 removeMessages(MSG_VIBRATE);
832 onStopSounds();
833 }
834
835 removeMessages(MSG_FREE_RESOURCES);
836 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800837 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 }
839
Mike Lockwoodce952c82011-11-14 10:47:42 -0800840 protected void onMuteChanged(int streamType, int flags) {
841
John Spurlock86005342014-05-23 11:58:00 -0400842 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800843
844 StreamControl sc = mStreamControls.get(streamType);
845 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400846 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800847 }
848
849 onVolumeChanged(streamType, flags);
850 }
851
RoboErik19c95182014-06-23 15:38:48 -0700852 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700853 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800854
Marco Nelissen69f593c2009-07-28 09:55:04 -0700855 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856
857 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400858 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 + ", flags: " + flags + "), index: " + index);
860 }
861
862 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800863
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400864 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700865 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866
867 switch (streamType) {
868
869 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800870// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700871 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
872 mContext, RingtoneManager.TYPE_RINGTONE);
873 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700874 mRingIsSilent = true;
875 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 break;
877 }
878
879 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800880 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800881 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
882 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
883 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
884 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800885 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800887 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800888 }
889 break;
890 }
891
892 case AudioManager.STREAM_VOICE_CALL: {
893 /*
894 * For in-call voice call volume, there is no inaudible volume.
895 * Rescale the UI control so the progress bar doesn't go all
896 * the way to zero and don't show the mute icon.
897 */
898 index++;
899 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800900 break;
901 }
902
903 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800904 break;
905 }
906
907 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700908 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
909 mContext, RingtoneManager.TYPE_NOTIFICATION);
910 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700911 mRingIsSilent = true;
912 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 break;
914 }
915
916 case AudioManager.STREAM_BLUETOOTH_SCO: {
917 /*
918 * For in-call voice call volume, there is no inaudible volume.
919 * Rescale the UI control so the progress bar doesn't go all
920 * the way to zero and don't show the mute icon.
921 */
922 index++;
923 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 break;
925 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700926
927 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700928 if (controller == null && sc != null) {
929 // If we weren't passed one try using the last one set.
930 controller = sc.controller;
931 }
932 if (controller == null) {
933 // We still don't have one, ignore the command.
934 Log.w(mTag, "sent remote volume change without a controller!");
935 } else {
936 VolumeInfo vi = controller.getVolumeInfo();
937 index = vi.getCurrentVolume();
938 max = vi.getMaxVolume();
939 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
940 // if the remote volume is fixed add the flag for the UI
941 flags |= AudioManager.FLAG_FIXED_VOLUME;
942 }
943 }
John Spurlock86005342014-05-23 11:58:00 -0400944 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700945 break;
946 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800949 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700950 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
951 if (sc.controller != null) {
952 sc.controller.removeCallback(mMediaControllerCb);
953 }
954 sc.controller = controller;
955 if (controller != null) {
956 sc.controller.addCallback(mMediaControllerCb);
957 }
958 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700959 if (sc.seekbarView.getMax() != max) {
960 sc.seekbarView.setMax(max);
961 }
John Spurlockad494bc2014-07-19 15:56:19 -0400962 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400963 updateSliderEnabled(sc, isMuted(streamType),
964 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 }
966
John Spurlock86005342014-05-23 11:58:00 -0400967 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700968 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
969 // when the stream is for remote playback, use -1 to reset the stream type evaluation
970 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700971
John Spurlockeb2727b2014-07-19 23:11:36 -0400972 mDialog.show();
973 if (mCallback != null) {
974 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -0400975 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976 }
977
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800978 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700979 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
980 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400981 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700982 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800983 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
984 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400985
986 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -0400987 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
988 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -0400989 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 }
991
John Spurlock86005342014-05-23 11:58:00 -0400992 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400993 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -0400994 }
995
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 protected void onPlaySound(int streamType, int flags) {
997
998 if (hasMessages(MSG_STOP_SOUNDS)) {
999 removeMessages(MSG_STOP_SOUNDS);
1000 // Force stop right now
1001 onStopSounds();
1002 }
1003
1004 synchronized (this) {
1005 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001006 if (toneGen != null) {
1007 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1008 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1009 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 }
1012
1013 protected void onStopSounds() {
1014
1015 synchronized (this) {
1016 int numStreamTypes = AudioSystem.getNumStreamTypes();
1017 for (int i = numStreamTypes - 1; i >= 0; i--) {
1018 ToneGenerator toneGen = mToneGenerators[i];
1019 if (toneGen != null) {
1020 toneGen.stopTone();
1021 }
1022 }
1023 }
1024 }
1025
1026 protected void onVibrate() {
1027
1028 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001029 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 return;
1031 }
1032
John Spurlock7b414672014-07-18 13:02:39 -04001033 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 }
1035
RoboErik19c95182014-06-23 15:38:48 -07001036 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1037 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1038 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001039
John Spurlock86005342014-05-23 11:58:00 -04001040 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001041 synchronized (this) {
1042 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1043 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1044 }
RoboErik19c95182014-06-23 15:38:48 -07001045 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001046 }
1047 } else {
John Spurlock86005342014-05-23 11:58:00 -04001048 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001049 }
1050
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001051 removeMessages(MSG_FREE_RESOURCES);
1052 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001053 resetTimeout();
1054 }
1055
1056 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001057 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1058 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001059 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1060 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001061 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001062 }
1063 }
1064
RoboErik19c95182014-06-23 15:38:48 -07001065 /**
1066 * Clear the current remote stream controller.
1067 */
1068 private void clearRemoteStreamController() {
1069 if (mStreamControls != null) {
1070 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1071 if (sc != null) {
1072 if (sc.controller != null) {
1073 sc.controller.removeCallback(mMediaControllerCb);
1074 sc.controller = null;
1075 }
1076 }
1077 }
1078 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001079
1080 /**
1081 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1082 * Hide or show a slider
1083 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1084 * or AudioService.STREAM_REMOTE_MUSIC
1085 * @param visible
1086 */
1087 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001088 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001089 boolean isVisible = (visible == 1);
1090 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1091 StreamResources streamRes = STREAMS[i];
1092 if (streamRes.streamType == streamType) {
1093 streamRes.show = isVisible;
1094 if (!isVisible && (mActiveStreamType == streamType)) {
1095 mActiveStreamType = -1;
1096 }
1097 break;
1098 }
1099 }
1100 }
1101
Eric Laurentfde16d52012-12-03 14:42:39 -08001102 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001103 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001104 synchronized (sConfirmSafeVolumeLock) {
1105 if (sConfirmSafeVolumeDialog != null) {
1106 return;
1107 }
1108 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1109 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1110 .setPositiveButton(com.android.internal.R.string.yes,
1111 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001112 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001113 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001114 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001115 }
1116 })
1117 .setNegativeButton(com.android.internal.R.string.no, null)
1118 .setIconAttribute(android.R.attr.alertDialogIcon)
1119 .create();
1120 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1121 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001122
Eric Laurentfde16d52012-12-03 14:42:39 -08001123 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1124 sConfirmSafeVolumeDialog.getWindow().setType(
1125 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1126 sConfirmSafeVolumeDialog.show();
1127 }
1128 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001129 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001130 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001131 }
1132
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001133 /**
1134 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1135 */
1136 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001137 if (streamType == STREAM_MASTER) {
1138 // For devices that use the master volume setting only but still want to
1139 // play a volume-changed tone, direct the master volume pseudostream to
1140 // the system stream's tone generator.
1141 if (mPlayMasterStreamTones) {
1142 streamType = AudioManager.STREAM_SYSTEM;
1143 } else {
1144 return null;
1145 }
1146 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 synchronized (this) {
1148 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001149 try {
1150 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1151 } catch (RuntimeException e) {
1152 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001153 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001154 + "RuntimeException: " + e);
1155 }
1156 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001158 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 }
1160 }
1161
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162
1163 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001164 * Switch between icons because Bluetooth music is same as music volume, but with
1165 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001167 private void setMusicIcon(int resId, int resMuteId) {
1168 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1169 if (sc != null) {
1170 sc.iconRes = resId;
1171 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001172 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001174 }
1175
1176 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 synchronized (this) {
1178 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1179 if (mToneGenerators[i] != null) {
1180 mToneGenerators[i].release();
1181 }
1182 mToneGenerators[i] = null;
1183 }
1184 }
1185 }
1186
1187 @Override
1188 public void handleMessage(Message msg) {
1189 switch (msg.what) {
1190
1191 case MSG_VOLUME_CHANGED: {
1192 onVolumeChanged(msg.arg1, msg.arg2);
1193 break;
1194 }
1195
Mike Lockwoodce952c82011-11-14 10:47:42 -08001196 case MSG_MUTE_CHANGED: {
1197 onMuteChanged(msg.arg1, msg.arg2);
1198 break;
1199 }
1200
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 case MSG_FREE_RESOURCES: {
1202 onFreeResources();
1203 break;
1204 }
1205
1206 case MSG_STOP_SOUNDS: {
1207 onStopSounds();
1208 break;
1209 }
1210
1211 case MSG_PLAY_SOUND: {
1212 onPlaySound(msg.arg1, msg.arg2);
1213 break;
1214 }
1215
1216 case MSG_VIBRATE: {
1217 onVibrate();
1218 break;
1219 }
1220
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001221 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001222 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001223 mDialog.dismiss();
1224 clearRemoteStreamController();
1225 mActiveStreamType = -1;
1226 if (mCallback != null) {
1227 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001228 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001229 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001230 synchronized (sConfirmSafeVolumeLock) {
1231 if (sConfirmSafeVolumeDialog != null) {
1232 sConfirmSafeVolumeDialog.dismiss();
1233 }
1234 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001235 break;
1236 }
1237 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001238 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001239 updateStates();
1240 }
1241 break;
1242 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001243
1244 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001245 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001246 break;
1247 }
1248
1249 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1250 onRemoteVolumeUpdateIfShown();
1251 break;
1252
1253 case MSG_SLIDER_VISIBILITY_CHANGED:
1254 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1255 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001256
1257 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001258 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001259 break;
John Spurlock86005342014-05-23 11:58:00 -04001260
1261 case MSG_LAYOUT_DIRECTION:
1262 setLayoutDirection(msg.arg1);
1263 break;
1264
1265 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001266 updateZenMode(msg.arg1);
1267 break;
1268
1269 case MSG_USER_ACTIVITY:
1270 if (mCallback != null) {
1271 mCallback.onInteraction();
1272 }
John Spurlock86005342014-05-23 11:58:00 -04001273 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 }
1275 }
1276
John Spurlockae641c92014-06-30 18:11:40 -04001277 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001278 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1279 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001280 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001281 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001282 removeMessages(MSG_USER_ACTIVITY);
1283 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001284 }
1285
John Spurlock1dad2722014-07-11 11:07:53 -04001286 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001287 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001288 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001289 }
1290
1291 public ZenModeController getZenController() {
1292 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001293 }
1294
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001295 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1296 @Override
1297 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1298 final Object tag = seekBar.getTag();
1299 if (fromUser && tag instanceof StreamControl) {
1300 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001301 setStreamVolume(sc, progress,
1302 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001303 }
1304 resetTimeout();
1305 }
1306
1307 @Override
1308 public void onStartTrackingTouch(SeekBar seekBar) {
1309 }
1310
1311 @Override
1312 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001313 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001314 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001315
John Spurlock86005342014-05-23 11:58:00 -04001316 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001317 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001318 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001319 }
1320 };
RoboErik19c95182014-06-23 15:38:48 -07001321
1322 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1323 public void onVolumeInfoChanged(VolumeInfo info) {
1324 onRemoteVolumeUpdateIfShown();
1325 }
1326 };
John Spurlockae641c92014-06-30 18:11:40 -04001327
1328 public interface Callback {
1329 void onZenSettings();
1330 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001331 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001332 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333}