blob: 40bdea2b60d1963cf9a7e1e13190df992684432b [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 Spurlock86005342014-05-23 11:58:00 -0400130 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400131 protected final Context mContext;
132 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400133 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700134 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700135 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400136 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400137 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400138 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400139 private float mDisabledAlpha;
140 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
141 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800142
Christopher Tatec4b78d22012-05-22 13:57:58 -0700143 // True if we want to play tones on the system stream when the master stream is specified.
144 private final boolean mPlayMasterStreamTones;
145
John Spurlock86005342014-05-23 11:58:00 -0400146
147 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800148 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400149 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400150 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800151
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700152 /** The visible portion of the volume overlay */
153 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400154 /** Contains the slider and its touchable icons */
155 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400156 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400157 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400158
John Spurlockae641c92014-06-30 18:11:40 -0400159 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800160
161 /** Currently active stream that shows up at the top of the list of sliders */
162 private int mActiveStreamType = -1;
163 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400164 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200165 private final AccessibilityManager mAccessibilityManager;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800166
Amith Yamasani71def772011-10-12 12:25:24 -0700167 private enum StreamResources {
168 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
169 R.string.volume_icon_description_bluetooth,
170 R.drawable.ic_audio_bt,
171 R.drawable.ic_audio_bt,
172 false),
173 RingerStream(AudioManager.STREAM_RING,
174 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400175 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400176 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700177 false),
178 VoiceStream(AudioManager.STREAM_VOICE_CALL,
179 R.string.volume_icon_description_incall,
180 R.drawable.ic_audio_phone,
181 R.drawable.ic_audio_phone,
182 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700183 AlarmStream(AudioManager.STREAM_ALARM,
184 R.string.volume_alarm,
185 R.drawable.ic_audio_alarm,
186 R.drawable.ic_audio_alarm_mute,
187 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700188 MediaStream(AudioManager.STREAM_MUSIC,
189 R.string.volume_icon_description_media,
190 R.drawable.ic_audio_vol,
191 R.drawable.ic_audio_vol_mute,
192 true),
193 NotificationStream(AudioManager.STREAM_NOTIFICATION,
194 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400195 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400196 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400197 true),
198 // for now, use media resources for master volume
199 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700200 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400201 R.drawable.ic_audio_vol,
202 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700203 false),
RoboErik2811dd32014-08-12 09:48:13 -0700204 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700205 R.string.volume_icon_description_media, //FIXME should have its own description
206 R.drawable.ic_media_route_on_holo_dark,
207 R.drawable.ic_media_route_disabled_holo_dark,
208 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700209
210 int streamType;
211 int descRes;
212 int iconRes;
213 int iconMuteRes;
214 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
215 boolean show;
216
217 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
218 this.streamType = streamType;
219 this.descRes = descRes;
220 this.iconRes = iconRes;
221 this.iconMuteRes = iconMuteRes;
222 this.show = show;
223 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700224 }
Amith Yamasani71def772011-10-12 12:25:24 -0700225
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800226 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700227 private static final StreamResources[] STREAMS = {
228 StreamResources.BluetoothSCOStream,
229 StreamResources.RingerStream,
230 StreamResources.VoiceStream,
231 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700232 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400233 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700234 StreamResources.MasterStream,
235 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800236 };
237
238 /** Object that contains data for each slider */
239 private class StreamControl {
240 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700241 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800242 ViewGroup group;
243 ImageView icon;
244 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400245 TextView suppressorView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800246 int iconRes;
247 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400248 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800250
251 // Synchronize when accessing this
252 private ToneGenerator mToneGenerators[];
253 private Vibrator mVibrator;
254
John Spurlock35134602014-07-24 18:10:48 -0400255 private static AlertDialog sSafetyWarning;
256 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700257
John Spurlock35134602014-07-24 18:10:48 -0400258 private static class SafetyWarning extends SystemUIDialog
259 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800260 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800261 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400262 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700263
John Spurlock2d28d6e2014-08-01 13:10:14 -0400264 private boolean mNewVolumeUp;
265
John Spurlock35134602014-07-24 18:10:48 -0400266 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
267 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700268 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800269 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400270 mAudioManager = audioManager;
271
272 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
273 setButton(DialogInterface.BUTTON_POSITIVE,
274 mContext.getString(com.android.internal.R.string.yes), this);
275 setButton(DialogInterface.BUTTON_NEGATIVE,
276 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
277 setOnDismissListener(this);
278
Eric Laurentc34dcc12012-09-10 13:51:52 -0700279 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400280 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700281 }
282
283 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400284 public boolean onKeyDown(int keyCode, KeyEvent event) {
285 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
286 mNewVolumeUp = true;
287 }
288 return super.onKeyDown(keyCode, event);
289 }
290
291 @Override
292 public boolean onKeyUp(int keyCode, KeyEvent event) {
293 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
294 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
295 mAudioManager.disableSafeMediaVolume();
296 dismiss();
297 }
298 return super.onKeyUp(keyCode, event);
299 }
300
301 @Override
John Spurlock35134602014-07-24 18:10:48 -0400302 public void onClick(DialogInterface dialog, int which) {
303 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700304 }
305
Alan Viverette494fb7b2014-04-10 18:12:56 -0700306 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700307 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400308 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800309 cleanUp();
310 }
311
312 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400313 synchronized (sSafetyWarningLock) {
314 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700315 }
John Spurlock1dad2722014-07-11 11:07:53 -0400316 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800317 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700318 }
John Spurlock35134602014-07-24 18:10:48 -0400319
320 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
321 @Override
322 public void onReceive(Context context, Intent intent) {
323 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
324 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
325 cancel();
326 cleanUp();
327 }
328 }
329 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700330 }
331
John Spurlockeb2727b2014-07-19 23:11:36 -0400332 public VolumePanel(Context context, ZenModeController zenController) {
333 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400335 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200337 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
338 Context.ACCESSIBILITY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800339
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400340 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700341 final Resources res = context.getResources();
342 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400343 if (useMasterVolume) {
344 for (int i = 0; i < STREAMS.length; i++) {
345 StreamResources streamRes = STREAMS[i];
346 streamRes.show = (streamRes.streamType == STREAM_MASTER);
347 }
348 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400349 if (LOGD) Log.d(mTag, "new VolumePanel");
350
John Spurlockad494bc2014-07-19 15:56:19 -0400351 mDisabledAlpha = 0.5f;
352 if (mContext.getTheme() != null) {
353 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
354 new int[] { android.R.attr.disabledAlpha });
355 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
356 arr.recycle();
357 }
358
John Spurlockeb2727b2014-07-19 23:11:36 -0400359 mDialog = new Dialog(context) {
360 @Override
361 public boolean onTouchEvent(MotionEvent event) {
362 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400363 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400364 forceTimeout(0);
365 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700366 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400367 return false;
368 }
369 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700370
John Spurlockeb2727b2014-07-19 23:11:36 -0400371 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400372 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400373 mDialog.setCanceledOnTouchOutside(true);
374 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
375 mDialog.setOnDismissListener(new OnDismissListener() {
376 @Override
377 public void onDismiss(DialogInterface dialog) {
378 mActiveStreamType = -1;
379 mAudioManager.forceVolumeControlStream(mActiveStreamType);
380 setZenPanelVisible(false);
381 }
382 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700383
John Spurlockeb2727b2014-07-19 23:11:36 -0400384 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700385
John Spurlock7e6809a2014-08-06 16:03:14 -0400386 final LayoutParams lp = window.getAttributes();
387 lp.token = null;
388 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
389 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
390 lp.format = PixelFormat.TRANSLUCENT;
391 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
392 lp.setTitle(TAG);
393 window.setAttributes(lp);
394
395 updateWidth();
396
397 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
398 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
399 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
400 | LayoutParams.FLAG_NOT_TOUCH_MODAL
401 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
402 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400403 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400404 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400405 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400406 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400407 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400408 }
409 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700410
John Spurlock86005342014-05-23 11:58:00 -0400411 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
412 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400413 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
414 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800415
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800416 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700417 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700418 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700419
John Spurlock45601d62014-08-07 17:40:50 -0400420 if (mZenController != null && !useMasterVolume) {
421 mZenModeAvailable = mZenController.isZenAvailable();
422 mZenController.addCallback(mZenCallback);
423 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700424
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700425 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
426 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700427 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
428
John Spurlock8c79d2e2014-07-24 15:15:25 -0400429 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800430 }
431
John Spurlock7e6809a2014-08-06 16:03:14 -0400432 public void onConfigurationChanged(Configuration newConfig) {
433 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400434 if (mZenPanel != null) {
435 mZenPanel.updateLocale();
436 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400437 }
438
439 private void updateWidth() {
440 final Resources res = mContext.getResources();
441 final LayoutParams lp = mDialog.getWindow().getAttributes();
442 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
443 lp.gravity =
444 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
445 mDialog.getWindow().setAttributes(lp);
446 }
447
John Spurlockad494bc2014-07-19 15:56:19 -0400448 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
449 pw.println("VolumePanel state:");
450 pw.print(" mTag="); pw.println(mTag);
451 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
452 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock45601d62014-08-07 17:40:50 -0400453 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400454 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
455 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
456 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
457 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
458 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
459 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
460 pw.print(" isShowing()="); pw.println(isShowing());
461 pw.print(" mCallback="); pw.println(mCallback);
462 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400463 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400464 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
465 pw.print(" mStreamControls=");
466 if (mStreamControls == null) {
467 pw.println("null");
468 } else {
469 final int N = mStreamControls.size();
470 pw.print("<size "); pw.print(N); pw.println('>');
471 for (int i = 0; i < N; i++) {
472 final StreamControl sc = mStreamControls.valueAt(i);
473 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
474 if (sc.seekbarView != null) {
475 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
476 pw.print(" of "); pw.print(sc.seekbarView.getMax());
477 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
478 }
479 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
480 pw.println();
481 }
482 }
483 }
484
John Spurlockeb2727b2014-07-19 23:11:36 -0400485 private void initZenModePanel() {
486 mZenPanel.init(mZenController);
487 mZenPanel.setCallback(new ZenModePanel.Callback() {
488 @Override
489 public void onMoreSettings() {
490 if (mCallback != null) {
491 mCallback.onZenSettings();
492 }
493 }
494
495 @Override
496 public void onInteraction() {
497 resetTimeout();
498 }
499
500 @Override
501 public void onExpanded(boolean expanded) {
502 if (mZenPanelExpanded == expanded) return;
503 mZenPanelExpanded = expanded;
504 updateTimeoutDelay();
505 resetTimeout();
506 }
507 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400508 }
509
John Spurlock86005342014-05-23 11:58:00 -0400510 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800511 mPanel.setLayoutDirection(layoutDirection);
512 updateStates();
513 }
514
John Spurlock8c79d2e2014-07-24 15:15:25 -0400515 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800516 final IntentFilter filter = new IntentFilter();
517 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400518 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800519 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700520 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800521 public void onReceive(Context context, Intent intent) {
522 final String action = intent.getAction();
523
524 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
525 removeMessages(MSG_RINGER_MODE_CHANGED);
526 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
527 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400528
529 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
530 postDismiss(0);
531 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800532 }
533 }, filter);
534 }
535
536 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400537 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700538 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700539 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700540 // TODO do we need to support a distinct mute property for remote?
541 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400542 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700543 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400544 }
545 }
546
547 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400548 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700549 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700550 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700551 if (mStreamControls != null) {
552 StreamControl sc = mStreamControls.get(streamType);
553 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700554 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700555 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700556 }
557 }
558 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400559 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700560 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400561 }
562 }
563
564 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400565 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700566 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700567 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700568 if (mStreamControls != null) {
569 StreamControl sc = mStreamControls.get(streamType);
570 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700571 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700572 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700573 }
574 }
575 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400576 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700577 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400578 }
579 }
580
RoboErik19c95182014-06-23 15:38:48 -0700581 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700582 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700583 if (sc.controller != null) {
584 sc.controller.setVolumeTo(index, flags);
585 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700586 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700587 }
588 } else if (getStreamVolume(sc.streamType) != index) {
589 if (sc.streamType == STREAM_MASTER) {
590 mAudioManager.setMasterVolume(index, flags);
591 } else {
592 mAudioManager.setStreamVolume(sc.streamType, index, flags);
593 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400594 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800595 }
596
597 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700598 final Resources res = mContext.getResources();
599 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
600 Context.LAYOUT_INFLATER_SERVICE);
601
John Spurlockad494bc2014-07-19 15:56:19 -0400602 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700603
Amith Yamasani71def772011-10-12 12:25:24 -0700604 for (int i = 0; i < STREAMS.length; i++) {
605 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700606
607 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700608
609 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700610 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400611 sc.group = (ViewGroup) inflater.inflate(
612 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800613 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400614 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800615 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700616 sc.icon.setContentDescription(res.getString(streamRes.descRes));
617 sc.iconRes = streamRes.iconRes;
618 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800619 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400620 sc.icon.setClickable(isNotificationOrRing(streamType));
621 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400622 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400623 sc.icon.setOnClickListener(new OnClickListener() {
624 @Override
625 public void onClick(View v) {
626 resetTimeout();
627 toggle(sc);
628 }
629 });
John Spurlockb4782522014-08-22 14:54:46 -0400630 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400631 }
John Spurlock86005342014-05-23 11:58:00 -0400632 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400633 sc.suppressorView =
634 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
635 sc.suppressorView.setVisibility(View.GONE);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700636 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700637 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400638 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700639 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800640 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700641 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800642 }
643 }
644
John Spurlock7f1df5e2014-05-31 19:11:40 -0400645 private void toggle(StreamControl sc) {
646 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
647 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
648 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
649 } else {
650 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
651 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
652 }
653 }
654
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800655 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400656 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800657
Alan Viverette494fb7b2014-04-10 18:12:56 -0700658 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800659 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400660 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800661 mActiveStreamType = -1;
662 } else {
John Spurlock86005342014-05-23 11:58:00 -0400663 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800664 mActiveStreamType = activeStreamType;
665 active.group.setVisibility(View.VISIBLE);
666 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400667 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400668 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800669 }
670 }
671
John Spurlockad494bc2014-07-19 15:56:19 -0400672 private void updateSliderProgress(StreamControl sc, int progress) {
673 final boolean isRinger = isNotificationOrRing(sc.streamType);
674 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
675 progress = mLastRingerProgress;
676 }
677 if (progress < 0) {
678 progress = getStreamVolume(sc.streamType);
679 }
680 sc.seekbarView.setProgress(progress);
681 if (isRinger) {
682 mLastRingerProgress = progress;
683 }
684 }
685
686 private void updateSliderIcon(StreamControl sc, boolean muted) {
687 if (isNotificationOrRing(sc.streamType)) {
688 int ringerMode = mAudioManager.getRingerMode();
689 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
690 ringerMode = mLastRingerMode;
691 } else {
692 mLastRingerMode = ringerMode;
693 }
694 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
695 }
696 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
697 }
698
John Spurlockb4782522014-08-22 14:54:46 -0400699 private void updateSliderSupressor(StreamControl sc) {
700 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
701 ? mZenController.getEffectsSuppressor() : null;
702 if (suppressor == null) {
703 sc.seekbarView.setVisibility(View.VISIBLE);
704 sc.suppressorView.setVisibility(View.GONE);
705 } else {
706 sc.seekbarView.setVisibility(View.GONE);
707 sc.suppressorView.setVisibility(View.VISIBLE);
708 sc.suppressorView.setText(mContext.getString(com.android.systemui.R.string.muted_by,
709 getSuppressorCaption(suppressor)));
710 sc.icon.setImageResource(sc.iconSuppressedRes);
711 }
712 }
713
714 private String getSuppressorCaption(ComponentName suppressor) {
715 final PackageManager pm = mContext.getPackageManager();
716 try {
717 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
718 if (info != null) {
719 final CharSequence seq = info.loadLabel(pm);
720 if (seq != null) {
721 final String str = seq.toString().trim();
722 if (str.length() > 0) {
723 return str;
724 }
725 }
726 }
727 } catch (Throwable e) {
728 Log.w(TAG, "Error loading suppressor caption", e);
729 }
730 return suppressor.getPackageName();
731 }
732
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800733 /** Update the mute and progress state of a slider */
734 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400735 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800736 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800737 // Force reloading the image resource
738 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400739 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400740 updateSliderEnabled(sc, muted, false);
John Spurlockb4782522014-08-22 14:54:46 -0400741 updateSliderSupressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400742 }
743
John Spurlock5f640e42014-05-31 20:15:59 -0400744 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
745 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400746 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700747 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700748 // never disable touch interactions for remote playback, the muting is not tied to
749 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700750 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400751 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400752 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400753 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400754 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400755 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400756 } else if (fixedVolume ||
757 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
758 (sSafetyWarning != null)) {
759 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700760 } else {
761 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400762 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400763 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700764 }
John Spurlockae641c92014-06-30 18:11:40 -0400765 // show the silent hint when the disabled slider is touched in silent mode
766 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400767 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400768 sc.group.setOnTouchListener(null);
769 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400770 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400771 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400772 @Override
773 public boolean onTouch(View v, MotionEvent event) {
774 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400775 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400776 return false;
777 }
John Spurlockae641c92014-06-30 18:11:40 -0400778 };
779 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400780 }
781 }
782 }
783
John Spurlockae641c92014-06-30 18:11:40 -0400784 private void showSilentHint() {
785 if (mZenPanel != null) {
786 mZenPanel.showSilentHint();
787 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800788 }
789
John Spurlock7f1df5e2014-05-31 19:11:40 -0400790 private static boolean isNotificationOrRing(int streamType) {
791 return streamType == AudioManager.STREAM_RING
792 || streamType == AudioManager.STREAM_NOTIFICATION;
793 }
794
John Spurlockae641c92014-06-30 18:11:40 -0400795 public void setCallback(Callback callback) {
796 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400797 }
798
John Spurlock8845da72014-07-07 21:29:48 -0400799 private void updateTimeoutDelay() {
Selim Cinek1cf89062014-08-29 19:21:44 +0200800 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400801 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400802 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
803 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
804 : TIMEOUT_DELAY;
805 }
806
807 private boolean isZenPanelVisible() {
808 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400809 }
810
John Spurlockae641c92014-06-30 18:11:40 -0400811 private void setZenPanelVisible(boolean visible) {
812 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400813 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400814 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400815 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400816 resetTimeout();
817 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400818 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800819 }
John Spurlockea9938c2014-07-11 18:51:32 -0400820 if (changing) {
821 updateTimeoutDelay();
822 resetTimeout();
823 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800824 }
825
Eric Laurentfde16d52012-12-03 14:42:39 -0800826 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400827 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800828 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400829 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800830 updateSlider(sc);
831 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 }
833
John Spurlock45601d62014-08-07 17:40:50 -0400834 private void updateZenPanelVisible() {
835 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400836 }
837
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 public void postVolumeChanged(int streamType, int flags) {
839 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700840 synchronized (this) {
841 if (mStreamControls == null) {
842 createSliders();
843 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800844 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800845 removeMessages(MSG_FREE_RESOURCES);
846 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
847 }
848
RoboErik19c95182014-06-23 15:38:48 -0700849 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700850 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
851 synchronized (this) {
852 if (mStreamControls == null) {
853 createSliders();
854 }
855 }
856 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700857 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700858 }
859
860 public void postRemoteSliderVisibility(boolean visible) {
861 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700862 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700863 }
864
865 /**
866 * Called by AudioService when it has received new remote playback information that
867 * would affect the VolumePanel display (mainly volumes). The difference with
868 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
869 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
870 * displayed.
871 * This special code path is due to the fact that remote volume updates arrive to AudioService
872 * asynchronously. So after AudioService has sent the volume update (which should be treated
873 * as a request to update the volume), the application will likely set a new volume. If the UI
874 * is still up, we need to refresh the display to show this new value.
875 */
876 public void postHasNewRemotePlaybackInfo() {
877 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
878 // don't create or prevent resources to be freed, if they disappear, this update came too
879 // late and shouldn't warrant the panel to be displayed longer
880 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
881 }
882
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400883 public void postMasterVolumeChanged(int flags) {
884 postVolumeChanged(STREAM_MASTER, flags);
885 }
886
Mike Lockwoodce952c82011-11-14 10:47:42 -0800887 public void postMuteChanged(int streamType, int flags) {
888 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700889 synchronized (this) {
890 if (mStreamControls == null) {
891 createSliders();
892 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800893 }
894 removeMessages(MSG_FREE_RESOURCES);
895 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
896 }
897
898 public void postMasterMuteChanged(int flags) {
899 postMuteChanged(STREAM_MASTER, flags);
900 }
901
Eric Laurentfde16d52012-12-03 14:42:39 -0800902 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700903 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800904 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700905 }
906
John Spurlock1dad2722014-07-11 11:07:53 -0400907 public void postDismiss(long delay) {
908 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400909 }
910
911 public void postLayoutDirection(int layoutDirection) {
912 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400913 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400914 }
915
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 /**
917 * Override this if you have other work to do when the volume changes (for
918 * example, vibrating, playing a sound, etc.). Make sure to call through to
919 * the superclass implementation.
920 */
921 protected void onVolumeChanged(int streamType, int flags) {
922
John Spurlock86005342014-05-23 11:58:00 -0400923 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924
925 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700926 synchronized (this) {
927 if (mActiveStreamType != streamType) {
928 reorderSliders(streamType);
929 }
RoboErik19c95182014-06-23 15:38:48 -0700930 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800931 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 }
933
Marco Nelissen69f593c2009-07-28 09:55:04 -0700934 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 removeMessages(MSG_PLAY_SOUND);
936 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
937 }
938
939 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
940 removeMessages(MSG_PLAY_SOUND);
941 removeMessages(MSG_VIBRATE);
942 onStopSounds();
943 }
944
945 removeMessages(MSG_FREE_RESOURCES);
946 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800947 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800948 }
949
Mike Lockwoodce952c82011-11-14 10:47:42 -0800950 protected void onMuteChanged(int streamType, int flags) {
951
John Spurlock86005342014-05-23 11:58:00 -0400952 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800953
954 StreamControl sc = mStreamControls.get(streamType);
955 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400956 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800957 }
958
959 onVolumeChanged(streamType, flags);
960 }
961
RoboErik19c95182014-06-23 15:38:48 -0700962 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700963 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800964
Marco Nelissen69f593c2009-07-28 09:55:04 -0700965 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966
967 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400968 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800969 + ", flags: " + flags + "), index: " + index);
970 }
971
972 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800973
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400974 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700975 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800976
977 switch (streamType) {
978
979 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800980// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700981 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
982 mContext, RingtoneManager.TYPE_RINGTONE);
983 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700984 mRingIsSilent = true;
985 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986 break;
987 }
988
989 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800990 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800991 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
992 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
993 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
994 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800995 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800996 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800997 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800998 }
999 break;
1000 }
1001
1002 case AudioManager.STREAM_VOICE_CALL: {
1003 /*
1004 * For in-call voice call volume, there is no inaudible volume.
1005 * Rescale the UI control so the progress bar doesn't go all
1006 * the way to zero and don't show the mute icon.
1007 */
1008 index++;
1009 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 break;
1011 }
1012
1013 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001014 break;
1015 }
1016
1017 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001018 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1019 mContext, RingtoneManager.TYPE_NOTIFICATION);
1020 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001021 mRingIsSilent = true;
1022 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 break;
1024 }
1025
1026 case AudioManager.STREAM_BLUETOOTH_SCO: {
1027 /*
1028 * For in-call voice call volume, there is no inaudible volume.
1029 * Rescale the UI control so the progress bar doesn't go all
1030 * the way to zero and don't show the mute icon.
1031 */
1032 index++;
1033 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 break;
1035 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001036
RoboErik2811dd32014-08-12 09:48:13 -07001037 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001038 if (controller == null && sc != null) {
1039 // If we weren't passed one try using the last one set.
1040 controller = sc.controller;
1041 }
1042 if (controller == null) {
1043 // We still don't have one, ignore the command.
1044 Log.w(mTag, "sent remote volume change without a controller!");
1045 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001046 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001047 index = vi.getCurrentVolume();
1048 max = vi.getMaxVolume();
1049 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1050 // if the remote volume is fixed add the flag for the UI
1051 flags |= AudioManager.FLAG_FIXED_VOLUME;
1052 }
1053 }
John Spurlock86005342014-05-23 11:58:00 -04001054 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001055 break;
1056 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 }
1058
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001059 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001060 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001061 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001062 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001063 }
1064 sc.controller = controller;
1065 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001066 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001067 }
1068 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001069 if (sc.seekbarView.getMax() != max) {
1070 sc.seekbarView.setMax(max);
1071 }
John Spurlockad494bc2014-07-19 15:56:19 -04001072 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001073 updateSliderEnabled(sc, isMuted(streamType),
1074 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001075 }
1076
John Spurlock86005342014-05-23 11:58:00 -04001077 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001078 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001079 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1080 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001081 mDialog.show();
1082 if (mCallback != null) {
1083 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001084 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001085 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 }
1087
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001088 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001089 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001090 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001091 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001092 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001093 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1094 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001095
1096 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001097 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1098 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001099 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
1101
Selim Cinek62ea3402014-09-08 12:11:51 +02001102 private void announceDialogShown() {
1103 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1104 }
1105
John Spurlock86005342014-05-23 11:58:00 -04001106 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001107 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001108 }
1109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 protected void onPlaySound(int streamType, int flags) {
1111
1112 if (hasMessages(MSG_STOP_SOUNDS)) {
1113 removeMessages(MSG_STOP_SOUNDS);
1114 // Force stop right now
1115 onStopSounds();
1116 }
1117
1118 synchronized (this) {
1119 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001120 if (toneGen != null) {
1121 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1122 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1123 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001124 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 }
1126
1127 protected void onStopSounds() {
1128
1129 synchronized (this) {
1130 int numStreamTypes = AudioSystem.getNumStreamTypes();
1131 for (int i = numStreamTypes - 1; i >= 0; i--) {
1132 ToneGenerator toneGen = mToneGenerators[i];
1133 if (toneGen != null) {
1134 toneGen.stopTone();
1135 }
1136 }
1137 }
1138 }
1139
1140 protected void onVibrate() {
1141
1142 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001143 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 return;
1145 }
1146
John Spurlock7b414672014-07-18 13:02:39 -04001147 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001148 }
1149
RoboErik19c95182014-06-23 15:38:48 -07001150 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1151 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1152 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001153
John Spurlock86005342014-05-23 11:58:00 -04001154 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001155 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001156 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1157 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001158 }
RoboErik2811dd32014-08-12 09:48:13 -07001159 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001160 }
1161 } else {
John Spurlock86005342014-05-23 11:58:00 -04001162 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001163 }
1164
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001165 removeMessages(MSG_FREE_RESOURCES);
1166 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001167 resetTimeout();
1168 }
1169
1170 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001171 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1172 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001173 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001174 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001175 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001176 }
1177 }
1178
RoboErik19c95182014-06-23 15:38:48 -07001179 /**
1180 * Clear the current remote stream controller.
1181 */
1182 private void clearRemoteStreamController() {
1183 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001184 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001185 if (sc != null) {
1186 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001187 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001188 sc.controller = null;
1189 }
1190 }
1191 }
1192 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001193
1194 /**
RoboErik2811dd32014-08-12 09:48:13 -07001195 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1196 *
1197 * @param streamType can be a valid stream type value, or
1198 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001199 * @param visible
1200 */
1201 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001202 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001203 boolean isVisible = (visible == 1);
1204 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1205 StreamResources streamRes = STREAMS[i];
1206 if (streamRes.streamType == streamType) {
1207 streamRes.show = isVisible;
1208 if (!isVisible && (mActiveStreamType == streamType)) {
1209 mActiveStreamType = -1;
1210 }
1211 break;
1212 }
1213 }
1214 }
1215
Eric Laurentfde16d52012-12-03 14:42:39 -08001216 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001217 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1218 || isShowing()) {
1219 synchronized (sSafetyWarningLock) {
1220 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001221 return;
1222 }
John Spurlock35134602014-07-24 18:10:48 -04001223 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1224 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001225 }
1226 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001227 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001228 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1229 removeMessages(MSG_TIMEOUT);
1230 } else {
1231 updateTimeoutDelay();
1232 resetTimeout();
1233 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001234 }
1235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001236 /**
1237 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1238 */
1239 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001240 if (streamType == STREAM_MASTER) {
1241 // For devices that use the master volume setting only but still want to
1242 // play a volume-changed tone, direct the master volume pseudostream to
1243 // the system stream's tone generator.
1244 if (mPlayMasterStreamTones) {
1245 streamType = AudioManager.STREAM_SYSTEM;
1246 } else {
1247 return null;
1248 }
1249 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001250 synchronized (this) {
1251 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001252 try {
1253 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1254 } catch (RuntimeException e) {
1255 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001256 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001257 + "RuntimeException: " + e);
1258 }
1259 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001260 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001261 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001262 }
1263 }
1264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001265
1266 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001267 * Switch between icons because Bluetooth music is same as music volume, but with
1268 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001269 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001270 private void setMusicIcon(int resId, int resMuteId) {
1271 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1272 if (sc != null) {
1273 sc.iconRes = resId;
1274 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001275 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001276 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001277 }
1278
1279 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001280 synchronized (this) {
1281 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1282 if (mToneGenerators[i] != null) {
1283 mToneGenerators[i].release();
1284 }
1285 mToneGenerators[i] = null;
1286 }
1287 }
1288 }
1289
1290 @Override
1291 public void handleMessage(Message msg) {
1292 switch (msg.what) {
1293
1294 case MSG_VOLUME_CHANGED: {
1295 onVolumeChanged(msg.arg1, msg.arg2);
1296 break;
1297 }
1298
Mike Lockwoodce952c82011-11-14 10:47:42 -08001299 case MSG_MUTE_CHANGED: {
1300 onMuteChanged(msg.arg1, msg.arg2);
1301 break;
1302 }
1303
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001304 case MSG_FREE_RESOURCES: {
1305 onFreeResources();
1306 break;
1307 }
1308
1309 case MSG_STOP_SOUNDS: {
1310 onStopSounds();
1311 break;
1312 }
1313
1314 case MSG_PLAY_SOUND: {
1315 onPlaySound(msg.arg1, msg.arg2);
1316 break;
1317 }
1318
1319 case MSG_VIBRATE: {
1320 onVibrate();
1321 break;
1322 }
1323
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001324 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001325 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001326 mDialog.dismiss();
1327 clearRemoteStreamController();
1328 mActiveStreamType = -1;
1329 if (mCallback != null) {
1330 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001331 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001332 }
John Spurlock35134602014-07-24 18:10:48 -04001333 synchronized (sSafetyWarningLock) {
1334 if (sSafetyWarning != null) {
1335 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1336 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001337 }
1338 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001339 break;
1340 }
John Spurlockb4782522014-08-22 14:54:46 -04001341
1342 case MSG_RINGER_MODE_CHANGED:
1343 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001344 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001345 updateStates();
1346 }
1347 break;
1348 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001349
1350 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001351 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001352 break;
1353 }
1354
1355 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1356 onRemoteVolumeUpdateIfShown();
1357 break;
1358
1359 case MSG_SLIDER_VISIBILITY_CHANGED:
1360 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1361 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001362
1363 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001364 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001365 break;
John Spurlock86005342014-05-23 11:58:00 -04001366
1367 case MSG_LAYOUT_DIRECTION:
1368 setLayoutDirection(msg.arg1);
1369 break;
1370
John Spurlock45601d62014-08-07 17:40:50 -04001371 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1372 mZenModeAvailable = msg.arg1 != 0;
1373 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001374 break;
1375
1376 case MSG_USER_ACTIVITY:
1377 if (mCallback != null) {
1378 mCallback.onInteraction();
1379 }
John Spurlock86005342014-05-23 11:58:00 -04001380 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001381 }
1382 }
1383
John Spurlockae641c92014-06-30 18:11:40 -04001384 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001385 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001386 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001387 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1388 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001389 removeMessages(MSG_TIMEOUT);
1390 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1391 removeMessages(MSG_USER_ACTIVITY);
1392 sendEmptyMessage(MSG_USER_ACTIVITY);
1393 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001394 }
1395
John Spurlock1dad2722014-07-11 11:07:53 -04001396 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001397 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001398 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001399 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001400 }
1401
1402 public ZenModeController getZenController() {
1403 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001404 }
1405
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001406 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1407 @Override
1408 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1409 final Object tag = seekBar.getTag();
1410 if (fromUser && tag instanceof StreamControl) {
1411 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001412 setStreamVolume(sc, progress,
1413 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001414 }
1415 resetTimeout();
1416 }
1417
1418 @Override
1419 public void onStartTrackingTouch(SeekBar seekBar) {
1420 }
1421
1422 @Override
1423 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001424 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001425 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001426
John Spurlock86005342014-05-23 11:58:00 -04001427 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001428 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001429 public void onZenAvailableChanged(boolean available) {
1430 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001431 }
John Spurlockb4782522014-08-22 14:54:46 -04001432 @Override
1433 public void onEffectsSupressorChanged() {
1434 obtainMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED,
1435 mZenController.getEffectsSuppressor()).sendToTarget();
1436 }
John Spurlock86005342014-05-23 11:58:00 -04001437 };
RoboErik19c95182014-06-23 15:38:48 -07001438
1439 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001440 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001441 onRemoteVolumeUpdateIfShown();
1442 }
1443 };
John Spurlockae641c92014-06-30 18:11:40 -04001444
1445 public interface Callback {
1446 void onZenSettings();
1447 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001448 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001449 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001450}