blob: 360dee59d2d7baa401885e1987245f5148980f6a [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;
John Spurlockb4782522014-08-22 14:54:46 -040022import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080024import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040025import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080027import android.content.IntentFilter;
John Spurlockb4782522014-08-22 14:54:46 -040028import android.content.pm.PackageManager;
John Spurlockb4782522014-08-22 14:54:46 -040029import android.content.pm.ServiceInfo;
John Spurlock7e6809a2014-08-06 16:03:14 -040030import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040032import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040033import android.graphics.PixelFormat;
34import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040035import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.media.AudioManager;
37import android.media.AudioService;
38import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070039import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070041import android.media.VolumeProvider;
42import android.media.session.MediaController;
RoboErikd2b8c942014-08-19 11:23:40 -070043import android.media.session.MediaController.PlaybackInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070044import android.net.Uri;
John Spurlocka0457c22014-09-26 13:22:08 -040045import android.os.Debug;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.os.Handler;
47import android.os.Message;
48import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040050import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040051import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040052import android.view.LayoutInflater;
53import android.view.MotionEvent;
54import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040055import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040056import android.view.ViewGroup;
57import android.view.Window;
58import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070059import android.view.WindowManager.LayoutParams;
Selim Cinek62ea3402014-09-08 12:11:51 +020060import android.view.accessibility.AccessibilityEvent;
Selim Cinek9f6ceb12014-08-29 16:28:39 +020061import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080062import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080063import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080064import android.widget.SeekBar.OnSeekBarChangeListener;
John Spurlockb4782522014-08-22 14:54:46 -040065import android.widget.TextView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080066
John Spurlock86005342014-05-23 11:58:00 -040067import com.android.internal.R;
John Spurlock35134602014-07-24 18:10:48 -040068import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040069import com.android.systemui.statusbar.policy.ZenModeController;
70
John Spurlockad494bc2014-07-19 15:56:19 -040071import java.io.FileDescriptor;
72import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080073
74/**
John Spurlock3346a802014-05-20 16:25:37 -040075 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070076 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 * @hide
78 */
John Spurlock3346a802014-05-20 16:25:37 -040079public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040080 private static final String TAG = "VolumePanel";
81 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
John Spurlock3346a802014-05-20 16:25:37 -040083 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084
85 /**
86 * The delay before vibrating. This small period exists so if the user is
87 * moving to silent mode, it will not emit a short vibrate (it normally
88 * would since vibrate is between normal mode and silent mode using hardware
89 * keys).
90 */
91 public static final int VIBRATE_DELAY = 300;
92
93 private static final int VIBRATE_DURATION = 300;
94 private static final int BEEP_DURATION = 150;
95 private static final int MAX_VOLUME = 100;
96 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080097 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040098 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040099 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -0400100 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400101 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102
103 private static final int MSG_VOLUME_CHANGED = 0;
104 private static final int MSG_FREE_RESOURCES = 1;
105 private static final int MSG_PLAY_SOUND = 2;
106 private static final int MSG_STOP_SOUNDS = 3;
107 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800108 private static final int MSG_TIMEOUT = 5;
109 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800110 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700111 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
112 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
113 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700114 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400115 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400116 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400117 private static final int MSG_USER_ACTIVITY = 14;
John Spurlockb4782522014-08-22 14:54:46 -0400118 private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400120 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800121 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700122 // Pseudo stream type for remote volume
123 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400124
John Spurlock7b414672014-07-18 13:02:39 -0400125 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
126 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
127 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
128 .build();
129
John Spurlock4bebb1d2014-10-02 19:17:19 -0400130 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
131 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
132
John Spurlock86005342014-05-23 11:58:00 -0400133 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400134 protected final Context mContext;
135 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400136 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700137 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700138 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400139 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400140 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400141 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400142 private float mDisabledAlpha;
143 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
144 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145
Christopher Tatec4b78d22012-05-22 13:57:58 -0700146 // True if we want to play tones on the system stream when the master stream is specified.
147 private final boolean mPlayMasterStreamTones;
148
John Spurlock86005342014-05-23 11:58:00 -0400149
150 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400152 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400153 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800154
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700155 /** The visible portion of the volume overlay */
156 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400157 /** Contains the slider and its touchable icons */
158 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400159 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400160 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400161
John Spurlockae641c92014-06-30 18:11:40 -0400162 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800163
164 /** Currently active stream that shows up at the top of the list of sliders */
165 private int mActiveStreamType = -1;
166 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400167 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200168 private final AccessibilityManager mAccessibilityManager;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800169
Amith Yamasani71def772011-10-12 12:25:24 -0700170 private enum StreamResources {
171 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
172 R.string.volume_icon_description_bluetooth,
173 R.drawable.ic_audio_bt,
174 R.drawable.ic_audio_bt,
175 false),
176 RingerStream(AudioManager.STREAM_RING,
177 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400178 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400179 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700180 false),
181 VoiceStream(AudioManager.STREAM_VOICE_CALL,
182 R.string.volume_icon_description_incall,
183 R.drawable.ic_audio_phone,
184 R.drawable.ic_audio_phone,
185 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700186 AlarmStream(AudioManager.STREAM_ALARM,
187 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400188 com.android.systemui.R.drawable.ic_audio_alarm,
189 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700190 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700191 MediaStream(AudioManager.STREAM_MUSIC,
192 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400193 IC_AUDIO_VOL,
194 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700195 true),
196 NotificationStream(AudioManager.STREAM_NOTIFICATION,
197 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400198 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400199 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400200 true),
201 // for now, use media resources for master volume
202 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700203 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlock4bebb1d2014-10-02 19:17:19 -0400204 IC_AUDIO_VOL,
205 IC_AUDIO_VOL_MUTE,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700206 false),
RoboErik2811dd32014-08-12 09:48:13 -0700207 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700208 R.string.volume_icon_description_media, //FIXME should have its own description
209 R.drawable.ic_media_route_on_holo_dark,
210 R.drawable.ic_media_route_disabled_holo_dark,
211 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700212
213 int streamType;
214 int descRes;
215 int iconRes;
216 int iconMuteRes;
217 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
218 boolean show;
219
220 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
221 this.streamType = streamType;
222 this.descRes = descRes;
223 this.iconRes = iconRes;
224 this.iconMuteRes = iconMuteRes;
225 this.show = show;
226 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700227 }
Amith Yamasani71def772011-10-12 12:25:24 -0700228
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800229 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700230 private static final StreamResources[] STREAMS = {
231 StreamResources.BluetoothSCOStream,
232 StreamResources.RingerStream,
233 StreamResources.VoiceStream,
234 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700235 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400236 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700237 StreamResources.MasterStream,
238 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800239 };
240
241 /** Object that contains data for each slider */
242 private class StreamControl {
243 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700244 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800245 ViewGroup group;
246 ImageView icon;
247 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400248 TextView suppressorView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800249 int iconRes;
250 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400251 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800253
254 // Synchronize when accessing this
255 private ToneGenerator mToneGenerators[];
256 private Vibrator mVibrator;
257
John Spurlock35134602014-07-24 18:10:48 -0400258 private static AlertDialog sSafetyWarning;
259 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700260
John Spurlock35134602014-07-24 18:10:48 -0400261 private static class SafetyWarning extends SystemUIDialog
262 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800263 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800264 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400265 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700266
John Spurlock2d28d6e2014-08-01 13:10:14 -0400267 private boolean mNewVolumeUp;
268
John Spurlock35134602014-07-24 18:10:48 -0400269 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
270 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700271 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800272 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400273 mAudioManager = audioManager;
274
275 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
276 setButton(DialogInterface.BUTTON_POSITIVE,
277 mContext.getString(com.android.internal.R.string.yes), this);
278 setButton(DialogInterface.BUTTON_NEGATIVE,
279 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
280 setOnDismissListener(this);
281
Eric Laurentc34dcc12012-09-10 13:51:52 -0700282 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400283 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700284 }
285
286 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400287 public boolean onKeyDown(int keyCode, KeyEvent event) {
288 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
289 mNewVolumeUp = true;
290 }
291 return super.onKeyDown(keyCode, event);
292 }
293
294 @Override
295 public boolean onKeyUp(int keyCode, KeyEvent event) {
296 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
297 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
298 mAudioManager.disableSafeMediaVolume();
299 dismiss();
300 }
301 return super.onKeyUp(keyCode, event);
302 }
303
304 @Override
John Spurlock35134602014-07-24 18:10:48 -0400305 public void onClick(DialogInterface dialog, int which) {
306 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700307 }
308
Alan Viverette494fb7b2014-04-10 18:12:56 -0700309 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700310 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400311 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800312 cleanUp();
313 }
314
315 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400316 synchronized (sSafetyWarningLock) {
317 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700318 }
John Spurlock1dad2722014-07-11 11:07:53 -0400319 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800320 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700321 }
John Spurlock35134602014-07-24 18:10:48 -0400322
323 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
324 @Override
325 public void onReceive(Context context, Intent intent) {
326 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
327 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
328 cancel();
329 cleanUp();
330 }
331 }
332 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700333 }
334
John Spurlockeb2727b2014-07-19 23:11:36 -0400335 public VolumePanel(Context context, ZenModeController zenController) {
336 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400338 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200340 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
341 Context.ACCESSIBILITY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400343 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700344 final Resources res = context.getResources();
345 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400346 if (useMasterVolume) {
347 for (int i = 0; i < STREAMS.length; i++) {
348 StreamResources streamRes = STREAMS[i];
349 streamRes.show = (streamRes.streamType == STREAM_MASTER);
350 }
351 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400352 if (LOGD) Log.d(mTag, "new VolumePanel");
353
John Spurlockad494bc2014-07-19 15:56:19 -0400354 mDisabledAlpha = 0.5f;
355 if (mContext.getTheme() != null) {
356 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
357 new int[] { android.R.attr.disabledAlpha });
358 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
359 arr.recycle();
360 }
361
John Spurlockeb2727b2014-07-19 23:11:36 -0400362 mDialog = new Dialog(context) {
363 @Override
364 public boolean onTouchEvent(MotionEvent event) {
365 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400366 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400367 forceTimeout(0);
368 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700369 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400370 return false;
371 }
372 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700373
John Spurlockeb2727b2014-07-19 23:11:36 -0400374 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400375 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400376 mDialog.setCanceledOnTouchOutside(true);
377 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
378 mDialog.setOnDismissListener(new OnDismissListener() {
379 @Override
380 public void onDismiss(DialogInterface dialog) {
381 mActiveStreamType = -1;
382 mAudioManager.forceVolumeControlStream(mActiveStreamType);
383 setZenPanelVisible(false);
384 }
385 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700386
John Spurlockeb2727b2014-07-19 23:11:36 -0400387 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700388
John Spurlock7e6809a2014-08-06 16:03:14 -0400389 final LayoutParams lp = window.getAttributes();
390 lp.token = null;
391 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
392 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
393 lp.format = PixelFormat.TRANSLUCENT;
394 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
395 lp.setTitle(TAG);
396 window.setAttributes(lp);
397
398 updateWidth();
399
400 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
401 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
402 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
403 | LayoutParams.FLAG_NOT_TOUCH_MODAL
404 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
405 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400406 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400407 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400408 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400409 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400410 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400411 }
412 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700413
John Spurlock86005342014-05-23 11:58:00 -0400414 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
415 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400416 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
417 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800418
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700420 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700421 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700422
John Spurlock45601d62014-08-07 17:40:50 -0400423 if (mZenController != null && !useMasterVolume) {
424 mZenModeAvailable = mZenController.isZenAvailable();
425 mZenController.addCallback(mZenCallback);
426 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700427
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700428 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
429 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700430 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
431
John Spurlock8c79d2e2014-07-24 15:15:25 -0400432 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800433 }
434
John Spurlock7e6809a2014-08-06 16:03:14 -0400435 public void onConfigurationChanged(Configuration newConfig) {
436 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400437 if (mZenPanel != null) {
438 mZenPanel.updateLocale();
439 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400440 }
441
442 private void updateWidth() {
443 final Resources res = mContext.getResources();
444 final LayoutParams lp = mDialog.getWindow().getAttributes();
445 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
446 lp.gravity =
447 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
448 mDialog.getWindow().setAttributes(lp);
449 }
450
John Spurlockad494bc2014-07-19 15:56:19 -0400451 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
452 pw.println("VolumePanel state:");
453 pw.print(" mTag="); pw.println(mTag);
454 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
455 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock45601d62014-08-07 17:40:50 -0400456 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400457 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
458 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
459 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
460 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
461 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
462 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
463 pw.print(" isShowing()="); pw.println(isShowing());
464 pw.print(" mCallback="); pw.println(mCallback);
465 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400466 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400467 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
468 pw.print(" mStreamControls=");
469 if (mStreamControls == null) {
470 pw.println("null");
471 } else {
472 final int N = mStreamControls.size();
473 pw.print("<size "); pw.print(N); pw.println('>');
474 for (int i = 0; i < N; i++) {
475 final StreamControl sc = mStreamControls.valueAt(i);
476 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
477 if (sc.seekbarView != null) {
478 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
479 pw.print(" of "); pw.print(sc.seekbarView.getMax());
480 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
481 }
482 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
483 pw.println();
484 }
485 }
486 }
487
John Spurlockeb2727b2014-07-19 23:11:36 -0400488 private void initZenModePanel() {
489 mZenPanel.init(mZenController);
490 mZenPanel.setCallback(new ZenModePanel.Callback() {
491 @Override
492 public void onMoreSettings() {
493 if (mCallback != null) {
494 mCallback.onZenSettings();
495 }
496 }
497
498 @Override
499 public void onInteraction() {
500 resetTimeout();
501 }
502
503 @Override
504 public void onExpanded(boolean expanded) {
505 if (mZenPanelExpanded == expanded) return;
506 mZenPanelExpanded = expanded;
507 updateTimeoutDelay();
508 resetTimeout();
509 }
510 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400511 }
512
John Spurlock86005342014-05-23 11:58:00 -0400513 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800514 mPanel.setLayoutDirection(layoutDirection);
515 updateStates();
516 }
517
John Spurlock8c79d2e2014-07-24 15:15:25 -0400518 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800519 final IntentFilter filter = new IntentFilter();
520 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400521 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800522 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700523 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800524 public void onReceive(Context context, Intent intent) {
525 final String action = intent.getAction();
526
527 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
528 removeMessages(MSG_RINGER_MODE_CHANGED);
529 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
530 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400531
532 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
533 postDismiss(0);
534 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800535 }
536 }, filter);
537 }
538
539 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400540 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700541 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700542 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700543 // TODO do we need to support a distinct mute property for remote?
544 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400545 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700546 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400547 }
548 }
549
550 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400551 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700552 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700553 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700554 if (mStreamControls != null) {
555 StreamControl sc = mStreamControls.get(streamType);
556 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700557 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700558 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700559 }
560 }
561 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400562 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700563 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400564 }
565 }
566
567 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400568 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700569 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700570 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700571 if (mStreamControls != null) {
572 StreamControl sc = mStreamControls.get(streamType);
573 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700574 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700575 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700576 }
577 }
578 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400579 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700580 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400581 }
582 }
583
RoboErik19c95182014-06-23 15:38:48 -0700584 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700585 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700586 if (sc.controller != null) {
587 sc.controller.setVolumeTo(index, flags);
588 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700589 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700590 }
591 } else if (getStreamVolume(sc.streamType) != index) {
592 if (sc.streamType == STREAM_MASTER) {
593 mAudioManager.setMasterVolume(index, flags);
594 } else {
595 mAudioManager.setStreamVolume(sc.streamType, index, flags);
596 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400597 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800598 }
599
600 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700601 final Resources res = mContext.getResources();
602 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
603 Context.LAYOUT_INFLATER_SERVICE);
604
John Spurlockad494bc2014-07-19 15:56:19 -0400605 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700606
Amith Yamasani71def772011-10-12 12:25:24 -0700607 for (int i = 0; i < STREAMS.length; i++) {
608 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700609
610 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700611
612 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700613 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400614 sc.group = (ViewGroup) inflater.inflate(
615 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800616 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400617 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800618 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700619 sc.icon.setContentDescription(res.getString(streamRes.descRes));
620 sc.iconRes = streamRes.iconRes;
621 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800622 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400623 sc.icon.setClickable(isNotificationOrRing(streamType));
624 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400625 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400626 sc.icon.setOnClickListener(new OnClickListener() {
627 @Override
628 public void onClick(View v) {
629 resetTimeout();
630 toggle(sc);
631 }
632 });
John Spurlockb4782522014-08-22 14:54:46 -0400633 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400634 }
John Spurlock86005342014-05-23 11:58:00 -0400635 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400636 sc.suppressorView =
637 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
638 sc.suppressorView.setVisibility(View.GONE);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700639 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700640 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400641 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700642 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800643 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700644 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800645 }
646 }
647
John Spurlock7f1df5e2014-05-31 19:11:40 -0400648 private void toggle(StreamControl sc) {
649 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
650 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
651 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
652 } else {
653 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
654 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
655 }
656 }
657
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800658 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400659 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800660
Alan Viverette494fb7b2014-04-10 18:12:56 -0700661 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800662 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400663 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800664 mActiveStreamType = -1;
665 } else {
John Spurlock86005342014-05-23 11:58:00 -0400666 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800667 mActiveStreamType = activeStreamType;
668 active.group.setVisibility(View.VISIBLE);
669 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400670 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400671 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800672 }
673 }
674
John Spurlockad494bc2014-07-19 15:56:19 -0400675 private void updateSliderProgress(StreamControl sc, int progress) {
676 final boolean isRinger = isNotificationOrRing(sc.streamType);
677 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
678 progress = mLastRingerProgress;
679 }
680 if (progress < 0) {
681 progress = getStreamVolume(sc.streamType);
682 }
683 sc.seekbarView.setProgress(progress);
684 if (isRinger) {
685 mLastRingerProgress = progress;
686 }
687 }
688
689 private void updateSliderIcon(StreamControl sc, boolean muted) {
690 if (isNotificationOrRing(sc.streamType)) {
691 int ringerMode = mAudioManager.getRingerMode();
692 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
693 ringerMode = mLastRingerMode;
694 } else {
695 mLastRingerMode = ringerMode;
696 }
697 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
698 }
699 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
700 }
701
John Spurlockb4782522014-08-22 14:54:46 -0400702 private void updateSliderSupressor(StreamControl sc) {
703 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
704 ? mZenController.getEffectsSuppressor() : null;
705 if (suppressor == null) {
706 sc.seekbarView.setVisibility(View.VISIBLE);
707 sc.suppressorView.setVisibility(View.GONE);
708 } else {
709 sc.seekbarView.setVisibility(View.GONE);
710 sc.suppressorView.setVisibility(View.VISIBLE);
711 sc.suppressorView.setText(mContext.getString(com.android.systemui.R.string.muted_by,
712 getSuppressorCaption(suppressor)));
713 sc.icon.setImageResource(sc.iconSuppressedRes);
714 }
715 }
716
717 private String getSuppressorCaption(ComponentName suppressor) {
718 final PackageManager pm = mContext.getPackageManager();
719 try {
720 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
721 if (info != null) {
722 final CharSequence seq = info.loadLabel(pm);
723 if (seq != null) {
724 final String str = seq.toString().trim();
725 if (str.length() > 0) {
726 return str;
727 }
728 }
729 }
730 } catch (Throwable e) {
731 Log.w(TAG, "Error loading suppressor caption", e);
732 }
733 return suppressor.getPackageName();
734 }
735
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800736 /** Update the mute and progress state of a slider */
737 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400738 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800739 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800740 // Force reloading the image resource
741 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400742 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400743 updateSliderEnabled(sc, muted, false);
John Spurlockb4782522014-08-22 14:54:46 -0400744 updateSliderSupressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400745 }
746
John Spurlock5f640e42014-05-31 20:15:59 -0400747 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
748 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400749 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700750 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700751 // never disable touch interactions for remote playback, the muting is not tied to
752 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700753 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400754 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400755 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400756 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400757 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400758 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400759 } else if (fixedVolume ||
760 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
761 (sSafetyWarning != null)) {
762 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700763 } else {
764 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400765 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400766 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700767 }
John Spurlockae641c92014-06-30 18:11:40 -0400768 // show the silent hint when the disabled slider is touched in silent mode
769 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400770 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400771 sc.group.setOnTouchListener(null);
772 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400773 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400774 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400775 @Override
776 public boolean onTouch(View v, MotionEvent event) {
777 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400778 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400779 return false;
780 }
John Spurlockae641c92014-06-30 18:11:40 -0400781 };
782 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400783 }
784 }
785 }
786
John Spurlockae641c92014-06-30 18:11:40 -0400787 private void showSilentHint() {
788 if (mZenPanel != null) {
789 mZenPanel.showSilentHint();
790 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800791 }
792
John Spurlock7f1df5e2014-05-31 19:11:40 -0400793 private static boolean isNotificationOrRing(int streamType) {
794 return streamType == AudioManager.STREAM_RING
795 || streamType == AudioManager.STREAM_NOTIFICATION;
796 }
797
John Spurlockae641c92014-06-30 18:11:40 -0400798 public void setCallback(Callback callback) {
799 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400800 }
801
John Spurlock8845da72014-07-07 21:29:48 -0400802 private void updateTimeoutDelay() {
Selim Cinek1cf89062014-08-29 19:21:44 +0200803 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400804 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400805 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
806 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
807 : TIMEOUT_DELAY;
808 }
809
810 private boolean isZenPanelVisible() {
811 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400812 }
813
John Spurlockae641c92014-06-30 18:11:40 -0400814 private void setZenPanelVisible(boolean visible) {
815 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400816 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400817 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400818 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400819 resetTimeout();
820 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400821 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800822 }
John Spurlockea9938c2014-07-11 18:51:32 -0400823 if (changing) {
824 updateTimeoutDelay();
825 resetTimeout();
826 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800827 }
828
Eric Laurentfde16d52012-12-03 14:42:39 -0800829 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400830 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800831 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400832 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800833 updateSlider(sc);
834 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800835 }
836
John Spurlock45601d62014-08-07 17:40:50 -0400837 private void updateZenPanelVisible() {
838 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400839 }
840
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841 public void postVolumeChanged(int streamType, int flags) {
842 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700843 synchronized (this) {
844 if (mStreamControls == null) {
845 createSliders();
846 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800847 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 removeMessages(MSG_FREE_RESOURCES);
849 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
850 }
851
RoboErik19c95182014-06-23 15:38:48 -0700852 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700853 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
854 synchronized (this) {
855 if (mStreamControls == null) {
856 createSliders();
857 }
858 }
859 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700860 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700861 }
862
863 public void postRemoteSliderVisibility(boolean visible) {
864 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700865 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700866 }
867
868 /**
869 * Called by AudioService when it has received new remote playback information that
870 * would affect the VolumePanel display (mainly volumes). The difference with
871 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
872 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
873 * displayed.
874 * This special code path is due to the fact that remote volume updates arrive to AudioService
875 * asynchronously. So after AudioService has sent the volume update (which should be treated
876 * as a request to update the volume), the application will likely set a new volume. If the UI
877 * is still up, we need to refresh the display to show this new value.
878 */
879 public void postHasNewRemotePlaybackInfo() {
880 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
881 // don't create or prevent resources to be freed, if they disappear, this update came too
882 // late and shouldn't warrant the panel to be displayed longer
883 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
884 }
885
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400886 public void postMasterVolumeChanged(int flags) {
887 postVolumeChanged(STREAM_MASTER, flags);
888 }
889
Mike Lockwoodce952c82011-11-14 10:47:42 -0800890 public void postMuteChanged(int streamType, int flags) {
891 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700892 synchronized (this) {
893 if (mStreamControls == null) {
894 createSliders();
895 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800896 }
897 removeMessages(MSG_FREE_RESOURCES);
898 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
899 }
900
901 public void postMasterMuteChanged(int flags) {
902 postMuteChanged(STREAM_MASTER, flags);
903 }
904
Eric Laurentfde16d52012-12-03 14:42:39 -0800905 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700906 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800907 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700908 }
909
John Spurlock1dad2722014-07-11 11:07:53 -0400910 public void postDismiss(long delay) {
911 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400912 }
913
914 public void postLayoutDirection(int layoutDirection) {
915 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400916 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400917 }
918
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800919 /**
920 * Override this if you have other work to do when the volume changes (for
921 * example, vibrating, playing a sound, etc.). Make sure to call through to
922 * the superclass implementation.
923 */
924 protected void onVolumeChanged(int streamType, int flags) {
925
John Spurlock86005342014-05-23 11:58:00 -0400926 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927
928 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700929 synchronized (this) {
930 if (mActiveStreamType != streamType) {
931 reorderSliders(streamType);
932 }
RoboErik19c95182014-06-23 15:38:48 -0700933 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
936
Marco Nelissen69f593c2009-07-28 09:55:04 -0700937 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 removeMessages(MSG_PLAY_SOUND);
939 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
940 }
941
942 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
943 removeMessages(MSG_PLAY_SOUND);
944 removeMessages(MSG_VIBRATE);
945 onStopSounds();
946 }
947
948 removeMessages(MSG_FREE_RESOURCES);
949 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800950 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 }
952
Mike Lockwoodce952c82011-11-14 10:47:42 -0800953 protected void onMuteChanged(int streamType, int flags) {
954
John Spurlock86005342014-05-23 11:58:00 -0400955 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800956
957 StreamControl sc = mStreamControls.get(streamType);
958 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400959 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800960 }
961
962 onVolumeChanged(streamType, flags);
963 }
964
RoboErik19c95182014-06-23 15:38:48 -0700965 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700966 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800967
Marco Nelissen69f593c2009-07-28 09:55:04 -0700968 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969
970 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400971 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 + ", flags: " + flags + "), index: " + index);
973 }
974
975 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800976
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400977 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700978 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979
980 switch (streamType) {
981
982 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800983// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700984 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
985 mContext, RingtoneManager.TYPE_RINGTONE);
986 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700987 mRingIsSilent = true;
988 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800989 break;
990 }
991
992 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800993 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800994 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
995 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
996 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
997 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800998 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800999 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001000 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 }
1002 break;
1003 }
1004
1005 case AudioManager.STREAM_VOICE_CALL: {
1006 /*
1007 * For in-call voice call volume, there is no inaudible volume.
1008 * Rescale the UI control so the progress bar doesn't go all
1009 * the way to zero and don't show the mute icon.
1010 */
1011 index++;
1012 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 break;
1014 }
1015
1016 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 break;
1018 }
1019
1020 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001021 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1022 mContext, RingtoneManager.TYPE_NOTIFICATION);
1023 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001024 mRingIsSilent = true;
1025 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 break;
1027 }
1028
1029 case AudioManager.STREAM_BLUETOOTH_SCO: {
1030 /*
1031 * For in-call voice call volume, there is no inaudible volume.
1032 * Rescale the UI control so the progress bar doesn't go all
1033 * the way to zero and don't show the mute icon.
1034 */
1035 index++;
1036 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 break;
1038 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001039
RoboErik2811dd32014-08-12 09:48:13 -07001040 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001041 if (controller == null && sc != null) {
1042 // If we weren't passed one try using the last one set.
1043 controller = sc.controller;
1044 }
1045 if (controller == null) {
1046 // We still don't have one, ignore the command.
1047 Log.w(mTag, "sent remote volume change without a controller!");
1048 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001049 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001050 index = vi.getCurrentVolume();
1051 max = vi.getMaxVolume();
1052 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1053 // if the remote volume is fixed add the flag for the UI
1054 flags |= AudioManager.FLAG_FIXED_VOLUME;
1055 }
1056 }
John Spurlock86005342014-05-23 11:58:00 -04001057 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001058 break;
1059 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 }
1061
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001062 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001063 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001064 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001065 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001066 }
1067 sc.controller = controller;
1068 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001069 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001070 }
1071 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001072 if (sc.seekbarView.getMax() != max) {
1073 sc.seekbarView.setMax(max);
1074 }
John Spurlockad494bc2014-07-19 15:56:19 -04001075 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001076 updateSliderEnabled(sc, isMuted(streamType),
1077 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 }
1079
John Spurlock86005342014-05-23 11:58:00 -04001080 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001081 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001082 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1083 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001084 mDialog.show();
1085 if (mCallback != null) {
1086 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001087 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001088 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 }
1090
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001092 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001093 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001094 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001095 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1097 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001098
1099 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001100 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1101 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001102 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 }
1104
Selim Cinek62ea3402014-09-08 12:11:51 +02001105 private void announceDialogShown() {
1106 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1107 }
1108
John Spurlock86005342014-05-23 11:58:00 -04001109 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001110 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001111 }
1112
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 protected void onPlaySound(int streamType, int flags) {
1114
1115 if (hasMessages(MSG_STOP_SOUNDS)) {
1116 removeMessages(MSG_STOP_SOUNDS);
1117 // Force stop right now
1118 onStopSounds();
1119 }
1120
1121 synchronized (this) {
1122 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001123 if (toneGen != null) {
1124 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1125 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1126 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 }
1129
1130 protected void onStopSounds() {
1131
1132 synchronized (this) {
1133 int numStreamTypes = AudioSystem.getNumStreamTypes();
1134 for (int i = numStreamTypes - 1; i >= 0; i--) {
1135 ToneGenerator toneGen = mToneGenerators[i];
1136 if (toneGen != null) {
1137 toneGen.stopTone();
1138 }
1139 }
1140 }
1141 }
1142
1143 protected void onVibrate() {
1144
1145 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001146 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001147 return;
1148 }
1149
John Spurlock7b414672014-07-18 13:02:39 -04001150 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001151 }
1152
RoboErik19c95182014-06-23 15:38:48 -07001153 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1154 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1155 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001156
John Spurlock86005342014-05-23 11:58:00 -04001157 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001158 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001159 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1160 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001161 }
RoboErik2811dd32014-08-12 09:48:13 -07001162 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001163 }
1164 } else {
John Spurlock86005342014-05-23 11:58:00 -04001165 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001166 }
1167
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001168 removeMessages(MSG_FREE_RESOURCES);
1169 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001170 resetTimeout();
1171 }
1172
1173 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001174 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1175 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001176 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001177 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001178 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001179 }
1180 }
1181
RoboErik19c95182014-06-23 15:38:48 -07001182 /**
1183 * Clear the current remote stream controller.
1184 */
1185 private void clearRemoteStreamController() {
1186 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001187 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001188 if (sc != null) {
1189 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001190 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001191 sc.controller = null;
1192 }
1193 }
1194 }
1195 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001196
1197 /**
RoboErik2811dd32014-08-12 09:48:13 -07001198 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1199 *
1200 * @param streamType can be a valid stream type value, or
1201 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001202 * @param visible
1203 */
1204 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001205 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001206 boolean isVisible = (visible == 1);
1207 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1208 StreamResources streamRes = STREAMS[i];
1209 if (streamRes.streamType == streamType) {
1210 streamRes.show = isVisible;
1211 if (!isVisible && (mActiveStreamType == streamType)) {
1212 mActiveStreamType = -1;
1213 }
1214 break;
1215 }
1216 }
1217 }
1218
Eric Laurentfde16d52012-12-03 14:42:39 -08001219 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001220 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1221 || isShowing()) {
1222 synchronized (sSafetyWarningLock) {
1223 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001224 return;
1225 }
John Spurlock35134602014-07-24 18:10:48 -04001226 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1227 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001228 }
1229 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001230 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001231 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1232 removeMessages(MSG_TIMEOUT);
1233 } else {
1234 updateTimeoutDelay();
1235 resetTimeout();
1236 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001237 }
1238
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001239 /**
1240 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1241 */
1242 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001243 if (streamType == STREAM_MASTER) {
1244 // For devices that use the master volume setting only but still want to
1245 // play a volume-changed tone, direct the master volume pseudostream to
1246 // the system stream's tone generator.
1247 if (mPlayMasterStreamTones) {
1248 streamType = AudioManager.STREAM_SYSTEM;
1249 } else {
1250 return null;
1251 }
1252 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001253 synchronized (this) {
1254 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001255 try {
1256 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1257 } catch (RuntimeException e) {
1258 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001259 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001260 + "RuntimeException: " + e);
1261 }
1262 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001264 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265 }
1266 }
1267
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268
1269 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001270 * Switch between icons because Bluetooth music is same as music volume, but with
1271 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001272 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001273 private void setMusicIcon(int resId, int resMuteId) {
1274 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1275 if (sc != null) {
1276 sc.iconRes = resId;
1277 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001278 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 }
1281
1282 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001283 synchronized (this) {
1284 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1285 if (mToneGenerators[i] != null) {
1286 mToneGenerators[i].release();
1287 }
1288 mToneGenerators[i] = null;
1289 }
1290 }
1291 }
1292
1293 @Override
1294 public void handleMessage(Message msg) {
1295 switch (msg.what) {
1296
1297 case MSG_VOLUME_CHANGED: {
1298 onVolumeChanged(msg.arg1, msg.arg2);
1299 break;
1300 }
1301
Mike Lockwoodce952c82011-11-14 10:47:42 -08001302 case MSG_MUTE_CHANGED: {
1303 onMuteChanged(msg.arg1, msg.arg2);
1304 break;
1305 }
1306
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001307 case MSG_FREE_RESOURCES: {
1308 onFreeResources();
1309 break;
1310 }
1311
1312 case MSG_STOP_SOUNDS: {
1313 onStopSounds();
1314 break;
1315 }
1316
1317 case MSG_PLAY_SOUND: {
1318 onPlaySound(msg.arg1, msg.arg2);
1319 break;
1320 }
1321
1322 case MSG_VIBRATE: {
1323 onVibrate();
1324 break;
1325 }
1326
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001327 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001328 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001329 mDialog.dismiss();
1330 clearRemoteStreamController();
1331 mActiveStreamType = -1;
1332 if (mCallback != null) {
1333 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001334 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001335 }
John Spurlock35134602014-07-24 18:10:48 -04001336 synchronized (sSafetyWarningLock) {
1337 if (sSafetyWarning != null) {
1338 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1339 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001340 }
1341 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001342 break;
1343 }
John Spurlockb4782522014-08-22 14:54:46 -04001344
1345 case MSG_RINGER_MODE_CHANGED:
1346 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001347 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001348 updateStates();
1349 }
1350 break;
1351 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001352
1353 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001354 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001355 break;
1356 }
1357
1358 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1359 onRemoteVolumeUpdateIfShown();
1360 break;
1361
1362 case MSG_SLIDER_VISIBILITY_CHANGED:
1363 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1364 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001365
1366 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001367 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001368 break;
John Spurlock86005342014-05-23 11:58:00 -04001369
1370 case MSG_LAYOUT_DIRECTION:
1371 setLayoutDirection(msg.arg1);
1372 break;
1373
John Spurlock45601d62014-08-07 17:40:50 -04001374 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1375 mZenModeAvailable = msg.arg1 != 0;
1376 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001377 break;
1378
1379 case MSG_USER_ACTIVITY:
1380 if (mCallback != null) {
1381 mCallback.onInteraction();
1382 }
John Spurlock86005342014-05-23 11:58:00 -04001383 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 }
1385 }
1386
John Spurlockae641c92014-06-30 18:11:40 -04001387 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001388 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001389 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001390 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1391 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001392 removeMessages(MSG_TIMEOUT);
1393 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1394 removeMessages(MSG_USER_ACTIVITY);
1395 sendEmptyMessage(MSG_USER_ACTIVITY);
1396 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001397 }
1398
John Spurlock1dad2722014-07-11 11:07:53 -04001399 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001400 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001401 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001402 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001403 }
1404
1405 public ZenModeController getZenController() {
1406 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001407 }
1408
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001409 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1410 @Override
1411 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1412 final Object tag = seekBar.getTag();
1413 if (fromUser && tag instanceof StreamControl) {
1414 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001415 setStreamVolume(sc, progress,
1416 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001417 }
1418 resetTimeout();
1419 }
1420
1421 @Override
1422 public void onStartTrackingTouch(SeekBar seekBar) {
1423 }
1424
1425 @Override
1426 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001427 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001428 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001429
John Spurlock86005342014-05-23 11:58:00 -04001430 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001431 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001432 public void onZenAvailableChanged(boolean available) {
1433 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001434 }
John Spurlockb4782522014-08-22 14:54:46 -04001435 @Override
1436 public void onEffectsSupressorChanged() {
1437 obtainMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED,
1438 mZenController.getEffectsSuppressor()).sendToTarget();
1439 }
John Spurlock86005342014-05-23 11:58:00 -04001440 };
RoboErik19c95182014-06-23 15:38:48 -07001441
1442 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001443 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001444 onRemoteVolumeUpdateIfShown();
1445 }
1446 };
John Spurlockae641c92014-06-30 18:11:40 -04001447
1448 public interface Callback {
1449 void onZenSettings();
1450 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001451 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001452 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001453}