blob: acdcfc1907400b263aa06723e9ca5f1db525c0a7 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
John Spurlockbb4a7022014-11-08 12:40:19 -050019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Eric Laurentc34dcc12012-09-10 13:51:52 -070022import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080024import android.content.BroadcastReceiver;
John Spurlockb4782522014-08-22 14:54:46 -040025import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080027import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040028import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080030import android.content.IntentFilter;
John Spurlockb4782522014-08-22 14:54:46 -040031import android.content.pm.PackageManager;
John Spurlockb4782522014-08-22 14:54:46 -040032import android.content.pm.ServiceInfo;
John Spurlock7e6809a2014-08-06 16:03:14 -040033import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040035import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040036import android.graphics.PixelFormat;
37import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040038import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.media.AudioManager;
40import android.media.AudioService;
41import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070042import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070044import android.media.VolumeProvider;
45import android.media.session.MediaController;
RoboErikd2b8c942014-08-19 11:23:40 -070046import android.media.session.MediaController.PlaybackInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070047import android.net.Uri;
John Spurlockbb4a7022014-11-08 12:40:19 -050048import android.os.AsyncTask;
49import android.os.Bundle;
John Spurlocka0457c22014-09-26 13:22:08 -040050import android.os.Debug;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Handler;
52import android.os.Message;
53import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040055import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040056import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040057import android.view.LayoutInflater;
58import android.view.MotionEvent;
59import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040060import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040061import android.view.ViewGroup;
62import android.view.Window;
63import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070064import android.view.WindowManager.LayoutParams;
Selim Cinek62ea3402014-09-08 12:11:51 +020065import android.view.accessibility.AccessibilityEvent;
Selim Cinek9f6ceb12014-08-29 16:28:39 +020066import android.view.accessibility.AccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -050067import android.view.animation.AnimationUtils;
68import android.view.animation.Interpolator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080070import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080071import android.widget.SeekBar.OnSeekBarChangeListener;
John Spurlockb4782522014-08-22 14:54:46 -040072import android.widget.TextView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080073
John Spurlock86005342014-05-23 11:58:00 -040074import com.android.internal.R;
John Spurlockbb4a7022014-11-08 12:40:19 -050075import com.android.systemui.DemoMode;
John Spurlock35134602014-07-24 18:10:48 -040076import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040077import com.android.systemui.statusbar.policy.ZenModeController;
78
John Spurlockad494bc2014-07-19 15:56:19 -040079import java.io.FileDescriptor;
80import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82/**
John Spurlock3346a802014-05-20 16:25:37 -040083 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070084 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 * @hide
86 */
John Spurlockbb4a7022014-11-08 12:40:19 -050087public class VolumePanel extends Handler implements DemoMode {
John Spurlockae641c92014-06-30 18:11:40 -040088 private static final String TAG = "VolumePanel";
89 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
John Spurlock3346a802014-05-20 16:25:37 -040091 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
93 /**
94 * The delay before vibrating. This small period exists so if the user is
95 * moving to silent mode, it will not emit a short vibrate (it normally
96 * would since vibrate is between normal mode and silent mode using hardware
97 * keys).
98 */
99 public static final int VIBRATE_DELAY = 300;
100
101 private static final int VIBRATE_DURATION = 300;
102 private static final int BEEP_DURATION = 150;
103 private static final int MAX_VOLUME = 100;
104 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800105 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -0400106 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -0400107 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -0400108 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400109 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 private static final int MSG_VOLUME_CHANGED = 0;
112 private static final int MSG_FREE_RESOURCES = 1;
113 private static final int MSG_PLAY_SOUND = 2;
114 private static final int MSG_STOP_SOUNDS = 3;
115 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800116 private static final int MSG_TIMEOUT = 5;
117 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800118 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700119 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
120 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
121 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700122 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400123 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400124 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400125 private static final int MSG_USER_ACTIVITY = 14;
John Spurlockb4782522014-08-22 14:54:46 -0400126 private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
John Spurlock530052a2014-11-30 16:26:19 -0500127 private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800128
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400129 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800130 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700131 // Pseudo stream type for remote volume
132 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400133
John Spurlock7b414672014-07-18 13:02:39 -0400134 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
135 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
136 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
137 .build();
138
John Spurlock4bebb1d2014-10-02 19:17:19 -0400139 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
140 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
John Spurlockbb4a7022014-11-08 12:40:19 -0500141 private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
142 private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
John Spurlock4bebb1d2014-10-02 19:17:19 -0400143
John Spurlock86005342014-05-23 11:58:00 -0400144 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400145 protected final Context mContext;
146 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400147 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700148 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700149 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400150 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400151 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400152 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400153 private float mDisabledAlpha;
154 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
155 private int mLastRingerProgress = 0;
John Spurlockbb4a7022014-11-08 12:40:19 -0500156 private int mDemoIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157
Christopher Tatec4b78d22012-05-22 13:57:58 -0700158 // True if we want to play tones on the system stream when the master stream is specified.
159 private final boolean mPlayMasterStreamTones;
160
John Spurlock86005342014-05-23 11:58:00 -0400161
162 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400164 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400165 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800166
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700167 /** The visible portion of the volume overlay */
168 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400169 /** Contains the slider and its touchable icons */
170 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400171 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400172 private ZenModePanel mZenPanel;
John Spurlock661f2cf2014-11-17 10:29:10 -0500173 /** The component currently suppressing notification stream effects */
174 private ComponentName mNotificationEffectsSuppressor;
John Spurlock86005342014-05-23 11:58:00 -0400175
John Spurlockae641c92014-06-30 18:11:40 -0400176 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800177
178 /** Currently active stream that shows up at the top of the list of sliders */
179 private int mActiveStreamType = -1;
180 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400181 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200182 private final AccessibilityManager mAccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -0500183 private final SecondaryIconTransition mSecondaryIconTransition;
John Spurlock661f2cf2014-11-17 10:29:10 -0500184 private final IconPulser mIconPulser;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800185
Amith Yamasani71def772011-10-12 12:25:24 -0700186 private enum StreamResources {
187 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
188 R.string.volume_icon_description_bluetooth,
John Spurlockbb4a7022014-11-08 12:40:19 -0500189 IC_AUDIO_BT,
190 IC_AUDIO_BT_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700191 false),
192 RingerStream(AudioManager.STREAM_RING,
193 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400194 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500195 com.android.systemui.R.drawable.ic_ringer_mute,
Amith Yamasani71def772011-10-12 12:25:24 -0700196 false),
197 VoiceStream(AudioManager.STREAM_VOICE_CALL,
198 R.string.volume_icon_description_incall,
John Spurlockbb4a7022014-11-08 12:40:19 -0500199 com.android.systemui.R.drawable.ic_audio_phone,
200 com.android.systemui.R.drawable.ic_audio_phone,
Amith Yamasani71def772011-10-12 12:25:24 -0700201 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700202 AlarmStream(AudioManager.STREAM_ALARM,
203 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400204 com.android.systemui.R.drawable.ic_audio_alarm,
205 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700206 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700207 MediaStream(AudioManager.STREAM_MUSIC,
208 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400209 IC_AUDIO_VOL,
210 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700211 true),
212 NotificationStream(AudioManager.STREAM_NOTIFICATION,
213 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400214 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500215 com.android.systemui.R.drawable.ic_ringer_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400216 true),
217 // for now, use media resources for master volume
218 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700219 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlock4bebb1d2014-10-02 19:17:19 -0400220 IC_AUDIO_VOL,
221 IC_AUDIO_VOL_MUTE,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700222 false),
RoboErik2811dd32014-08-12 09:48:13 -0700223 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700224 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlockcfe9fb62014-11-26 11:37:00 -0500225 com.android.systemui.R.drawable.ic_audio_remote,
226 com.android.systemui.R.drawable.ic_audio_remote,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700227 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700228
229 int streamType;
230 int descRes;
231 int iconRes;
232 int iconMuteRes;
233 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
234 boolean show;
235
236 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
237 this.streamType = streamType;
238 this.descRes = descRes;
239 this.iconRes = iconRes;
240 this.iconMuteRes = iconMuteRes;
241 this.show = show;
242 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700243 }
Amith Yamasani71def772011-10-12 12:25:24 -0700244
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800245 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700246 private static final StreamResources[] STREAMS = {
247 StreamResources.BluetoothSCOStream,
248 StreamResources.RingerStream,
249 StreamResources.VoiceStream,
250 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700251 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400252 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700253 StreamResources.MasterStream,
254 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800255 };
256
257 /** Object that contains data for each slider */
258 private class StreamControl {
259 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700260 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800261 ViewGroup group;
262 ImageView icon;
263 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400264 TextView suppressorView;
John Spurlockbb4a7022014-11-08 12:40:19 -0500265 View divider;
266 ImageView secondaryIcon;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800267 int iconRes;
268 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400269 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800270 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800271
272 // Synchronize when accessing this
273 private ToneGenerator mToneGenerators[];
274 private Vibrator mVibrator;
John Spurlock661f2cf2014-11-17 10:29:10 -0500275 private boolean mHasVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800276
John Spurlock35134602014-07-24 18:10:48 -0400277 private static AlertDialog sSafetyWarning;
278 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700279
John Spurlock35134602014-07-24 18:10:48 -0400280 private static class SafetyWarning extends SystemUIDialog
281 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800282 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800283 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400284 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700285
John Spurlock2d28d6e2014-08-01 13:10:14 -0400286 private boolean mNewVolumeUp;
287
John Spurlock35134602014-07-24 18:10:48 -0400288 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
289 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700290 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800291 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400292 mAudioManager = audioManager;
293
294 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
295 setButton(DialogInterface.BUTTON_POSITIVE,
296 mContext.getString(com.android.internal.R.string.yes), this);
297 setButton(DialogInterface.BUTTON_NEGATIVE,
298 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
299 setOnDismissListener(this);
300
Eric Laurentc34dcc12012-09-10 13:51:52 -0700301 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400302 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700303 }
304
305 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400306 public boolean onKeyDown(int keyCode, KeyEvent event) {
307 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
308 mNewVolumeUp = true;
309 }
310 return super.onKeyDown(keyCode, event);
311 }
312
313 @Override
314 public boolean onKeyUp(int keyCode, KeyEvent event) {
315 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
316 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
317 mAudioManager.disableSafeMediaVolume();
318 dismiss();
319 }
320 return super.onKeyUp(keyCode, event);
321 }
322
323 @Override
John Spurlock35134602014-07-24 18:10:48 -0400324 public void onClick(DialogInterface dialog, int which) {
325 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700326 }
327
Alan Viverette494fb7b2014-04-10 18:12:56 -0700328 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700329 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400330 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800331 cleanUp();
332 }
333
334 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400335 synchronized (sSafetyWarningLock) {
336 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700337 }
John Spurlock1dad2722014-07-11 11:07:53 -0400338 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800339 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700340 }
John Spurlock35134602014-07-24 18:10:48 -0400341
342 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
343 @Override
344 public void onReceive(Context context, Intent intent) {
345 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
346 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
347 cancel();
348 cleanUp();
349 }
350 }
351 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700352 }
353
John Spurlockeb2727b2014-07-19 23:11:36 -0400354 public VolumePanel(Context context, ZenModeController zenController) {
355 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400357 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200359 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
360 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500361 mSecondaryIconTransition = new SecondaryIconTransition();
John Spurlock661f2cf2014-11-17 10:29:10 -0500362 mIconPulser = new IconPulser(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400364 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700365 final Resources res = context.getResources();
366 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400367 if (useMasterVolume) {
368 for (int i = 0; i < STREAMS.length; i++) {
369 StreamResources streamRes = STREAMS[i];
370 streamRes.show = (streamRes.streamType == STREAM_MASTER);
371 }
372 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400373 if (LOGD) Log.d(mTag, "new VolumePanel");
374
John Spurlockad494bc2014-07-19 15:56:19 -0400375 mDisabledAlpha = 0.5f;
376 if (mContext.getTheme() != null) {
377 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
378 new int[] { android.R.attr.disabledAlpha });
379 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
380 arr.recycle();
381 }
382
John Spurlockeb2727b2014-07-19 23:11:36 -0400383 mDialog = new Dialog(context) {
384 @Override
385 public boolean onTouchEvent(MotionEvent event) {
386 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400387 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400388 forceTimeout(0);
389 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700390 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400391 return false;
392 }
393 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700394
John Spurlockeb2727b2014-07-19 23:11:36 -0400395 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400396 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400397 mDialog.setCanceledOnTouchOutside(true);
398 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
399 mDialog.setOnDismissListener(new OnDismissListener() {
400 @Override
401 public void onDismiss(DialogInterface dialog) {
402 mActiveStreamType = -1;
403 mAudioManager.forceVolumeControlStream(mActiveStreamType);
404 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500405 mDemoIcon = 0;
406 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400407 }
408 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700409
John Spurlockeb2727b2014-07-19 23:11:36 -0400410 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700411
John Spurlock7e6809a2014-08-06 16:03:14 -0400412 final LayoutParams lp = window.getAttributes();
413 lp.token = null;
414 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
415 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
416 lp.format = PixelFormat.TRANSLUCENT;
417 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
418 lp.setTitle(TAG);
419 window.setAttributes(lp);
420
421 updateWidth();
422
423 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
424 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
425 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
426 | LayoutParams.FLAG_NOT_TOUCH_MODAL
427 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
428 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400429 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400430 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400431 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400432 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400433 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400434 }
435 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700436
John Spurlock86005342014-05-23 11:58:00 -0400437 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
438 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400439 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
440 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800441
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800442 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700443 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
John Spurlock661f2cf2014-11-17 10:29:10 -0500444 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Amith Yamasani71def772011-10-12 12:25:24 -0700445 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700446
John Spurlock45601d62014-08-07 17:40:50 -0400447 if (mZenController != null && !useMasterVolume) {
448 mZenModeAvailable = mZenController.isZenAvailable();
John Spurlock661f2cf2014-11-17 10:29:10 -0500449 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
John Spurlock45601d62014-08-07 17:40:50 -0400450 mZenController.addCallback(mZenCallback);
451 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700452
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700453 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
454 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700455 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
456
John Spurlock8c79d2e2014-07-24 15:15:25 -0400457 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800458 }
459
John Spurlock7e6809a2014-08-06 16:03:14 -0400460 public void onConfigurationChanged(Configuration newConfig) {
461 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400462 if (mZenPanel != null) {
463 mZenPanel.updateLocale();
464 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400465 }
466
467 private void updateWidth() {
468 final Resources res = mContext.getResources();
469 final LayoutParams lp = mDialog.getWindow().getAttributes();
470 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
471 lp.gravity =
472 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
473 mDialog.getWindow().setAttributes(lp);
474 }
475
John Spurlockad494bc2014-07-19 15:56:19 -0400476 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
477 pw.println("VolumePanel state:");
478 pw.print(" mTag="); pw.println(mTag);
479 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
480 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock661f2cf2014-11-17 10:29:10 -0500481 pw.print(" mHasVibrator="); pw.println(mHasVibrator);
John Spurlock45601d62014-08-07 17:40:50 -0400482 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400483 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
John Spurlock661f2cf2014-11-17 10:29:10 -0500484 pw.print(" mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
John Spurlockad494bc2014-07-19 15:56:19 -0400485 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
486 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
487 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
488 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
489 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
490 pw.print(" isShowing()="); pw.println(isShowing());
491 pw.print(" mCallback="); pw.println(mCallback);
492 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400493 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400494 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
495 pw.print(" mStreamControls=");
496 if (mStreamControls == null) {
497 pw.println("null");
498 } else {
499 final int N = mStreamControls.size();
500 pw.print("<size "); pw.print(N); pw.println('>');
501 for (int i = 0; i < N; i++) {
502 final StreamControl sc = mStreamControls.valueAt(i);
503 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
504 if (sc.seekbarView != null) {
505 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
506 pw.print(" of "); pw.print(sc.seekbarView.getMax());
507 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
508 }
509 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
510 pw.println();
511 }
512 }
John Spurlock530052a2014-11-30 16:26:19 -0500513 if (mZenPanel != null) {
514 mZenPanel.dump(fd, pw, args);
515 }
John Spurlockad494bc2014-07-19 15:56:19 -0400516 }
517
John Spurlockeb2727b2014-07-19 23:11:36 -0400518 private void initZenModePanel() {
519 mZenPanel.init(mZenController);
520 mZenPanel.setCallback(new ZenModePanel.Callback() {
521 @Override
522 public void onMoreSettings() {
523 if (mCallback != null) {
524 mCallback.onZenSettings();
525 }
526 }
527
528 @Override
529 public void onInteraction() {
530 resetTimeout();
531 }
532
533 @Override
534 public void onExpanded(boolean expanded) {
535 if (mZenPanelExpanded == expanded) return;
536 mZenPanelExpanded = expanded;
537 updateTimeoutDelay();
538 resetTimeout();
539 }
540 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400541 }
542
John Spurlock86005342014-05-23 11:58:00 -0400543 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800544 mPanel.setLayoutDirection(layoutDirection);
545 updateStates();
546 }
547
John Spurlock8c79d2e2014-07-24 15:15:25 -0400548 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800549 final IntentFilter filter = new IntentFilter();
550 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlockbcc10872014-11-28 15:29:21 -0500551 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400552 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800553 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700554 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800555 public void onReceive(Context context, Intent intent) {
556 final String action = intent.getAction();
557
558 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
559 removeMessages(MSG_RINGER_MODE_CHANGED);
John Spurlockbcc10872014-11-28 15:29:21 -0500560 sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
561 }
562
563 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
564 removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
565 sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800566 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400567
568 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
569 postDismiss(0);
570 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800571 }
572 }, filter);
573 }
574
575 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400576 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700577 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700578 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700579 // TODO do we need to support a distinct mute property for remote?
580 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400581 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700582 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400583 }
584 }
585
586 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400587 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700588 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700589 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700590 if (mStreamControls != null) {
591 StreamControl sc = mStreamControls.get(streamType);
592 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700593 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700594 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700595 }
596 }
597 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400598 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700599 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400600 }
601 }
602
603 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400604 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700605 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700606 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700607 if (mStreamControls != null) {
608 StreamControl sc = mStreamControls.get(streamType);
609 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700610 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700611 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700612 }
613 }
614 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400615 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700616 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400617 }
618 }
619
RoboErik19c95182014-06-23 15:38:48 -0700620 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700621 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700622 if (sc.controller != null) {
623 sc.controller.setVolumeTo(index, flags);
624 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700625 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700626 }
627 } else if (getStreamVolume(sc.streamType) != index) {
628 if (sc.streamType == STREAM_MASTER) {
629 mAudioManager.setMasterVolume(index, flags);
630 } else {
631 mAudioManager.setStreamVolume(sc.streamType, index, flags);
632 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400633 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800634 }
635
636 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700637 final Resources res = mContext.getResources();
638 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
639 Context.LAYOUT_INFLATER_SERVICE);
640
John Spurlockad494bc2014-07-19 15:56:19 -0400641 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700642
John Spurlockbb4a7022014-11-08 12:40:19 -0500643 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700644 for (int i = 0; i < STREAMS.length; i++) {
645 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700646
647 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500648 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700649
650 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700651 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400652 sc.group = (ViewGroup) inflater.inflate(
653 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800654 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400655 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800656 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700657 sc.icon.setContentDescription(res.getString(streamRes.descRes));
658 sc.iconRes = streamRes.iconRes;
659 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800660 sc.icon.setImageResource(sc.iconRes);
John Spurlock661f2cf2014-11-17 10:29:10 -0500661 sc.icon.setClickable(isNotification && mHasVibrator);
John Spurlockbb4a7022014-11-08 12:40:19 -0500662 if (isNotification) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500663 if (mHasVibrator) {
664 sc.icon.setSoundEffectsEnabled(false);
665 sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
666 sc.icon.setOnClickListener(new OnClickListener() {
667 @Override
668 public void onClick(View v) {
669 resetTimeout();
670 toggleRinger(sc);
671 }
672 });
673 }
John Spurlockb4782522014-08-22 14:54:46 -0400674 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400675 }
John Spurlock86005342014-05-23 11:58:00 -0400676 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400677 sc.suppressorView =
678 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
679 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500680 final boolean showSecondary = !isNotification && notificationStream.show;
681 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
682 sc.secondaryIcon = (ImageView) sc.group
683 .findViewById(com.android.systemui.R.id.secondary_icon);
684 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
685 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
686 sc.secondaryIcon.setClickable(showSecondary);
687 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
688 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
689 if (showSecondary) {
690 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
691 @Override
692 public void onClick(View v) {
693 mSecondaryIconTransition.start(sc);
694 }
695 });
696 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700697 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700698 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400699 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700700 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800701 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700702 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800703 }
704 }
705
John Spurlock661f2cf2014-11-17 10:29:10 -0500706 private void toggleRinger(StreamControl sc) {
707 if (!mHasVibrator) return;
708 if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
709 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400710 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
711 } else {
John Spurlock661f2cf2014-11-17 10:29:10 -0500712 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400713 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
714 }
715 }
716
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800717 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400718 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800719
Alan Viverette494fb7b2014-04-10 18:12:56 -0700720 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800721 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400722 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800723 mActiveStreamType = -1;
724 } else {
John Spurlock86005342014-05-23 11:58:00 -0400725 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800726 mActiveStreamType = activeStreamType;
727 active.group.setVisibility(View.VISIBLE);
John Spurlock530052a2014-11-30 16:26:19 -0500728 updateSlider(active, true /*forceReloadIcon*/);
John Spurlock8845da72014-07-07 21:29:48 -0400729 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400730 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800731 }
732 }
733
John Spurlockad494bc2014-07-19 15:56:19 -0400734 private void updateSliderProgress(StreamControl sc, int progress) {
735 final boolean isRinger = isNotificationOrRing(sc.streamType);
John Spurlock661f2cf2014-11-17 10:29:10 -0500736 if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlockad494bc2014-07-19 15:56:19 -0400737 progress = mLastRingerProgress;
738 }
739 if (progress < 0) {
740 progress = getStreamVolume(sc.streamType);
741 }
742 sc.seekbarView.setProgress(progress);
743 if (isRinger) {
744 mLastRingerProgress = progress;
745 }
746 }
747
748 private void updateSliderIcon(StreamControl sc, boolean muted) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500749 ComponentName suppressor = null;
John Spurlockad494bc2014-07-19 15:56:19 -0400750 if (isNotificationOrRing(sc.streamType)) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500751 suppressor = mNotificationEffectsSuppressor;
752 int ringerMode = mAudioManager.getRingerModeInternal();
John Spurlockad494bc2014-07-19 15:56:19 -0400753 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
754 ringerMode = mLastRingerMode;
755 } else {
756 mLastRingerMode = ringerMode;
757 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500758 if (mHasVibrator) {
759 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
760 } else {
761 muted = false;
762 }
John Spurlockad494bc2014-07-19 15:56:19 -0400763 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500764 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
765 : suppressor != null ? sc.iconSuppressedRes
766 : muted ? sc.iconMuteRes
767 : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400768 }
769
John Spurlock661f2cf2014-11-17 10:29:10 -0500770 private void updateSliderSuppressor(StreamControl sc) {
John Spurlockb4782522014-08-22 14:54:46 -0400771 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
John Spurlock661f2cf2014-11-17 10:29:10 -0500772 ? mNotificationEffectsSuppressor : null;
John Spurlockb4782522014-08-22 14:54:46 -0400773 if (suppressor == null) {
774 sc.seekbarView.setVisibility(View.VISIBLE);
775 sc.suppressorView.setVisibility(View.GONE);
776 } else {
777 sc.seekbarView.setVisibility(View.GONE);
778 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500779 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400780 getSuppressorCaption(suppressor)));
John Spurlockb4782522014-08-22 14:54:46 -0400781 }
782 }
783
784 private String getSuppressorCaption(ComponentName suppressor) {
785 final PackageManager pm = mContext.getPackageManager();
786 try {
787 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
788 if (info != null) {
789 final CharSequence seq = info.loadLabel(pm);
790 if (seq != null) {
791 final String str = seq.toString().trim();
792 if (str.length() > 0) {
793 return str;
794 }
795 }
796 }
797 } catch (Throwable e) {
798 Log.w(TAG, "Error loading suppressor caption", e);
799 }
800 return suppressor.getPackageName();
801 }
802
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800803 /** Update the mute and progress state of a slider */
John Spurlock530052a2014-11-30 16:26:19 -0500804 private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
John Spurlockad494bc2014-07-19 15:56:19 -0400805 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800806 final boolean muted = isMuted(sc.streamType);
John Spurlock530052a2014-11-30 16:26:19 -0500807 if (forceReloadIcon) {
808 sc.icon.setImageDrawable(null);
809 }
John Spurlockad494bc2014-07-19 15:56:19 -0400810 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400811 updateSliderEnabled(sc, muted, false);
John Spurlock661f2cf2014-11-17 10:29:10 -0500812 updateSliderSuppressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400813 }
814
John Spurlock5f640e42014-05-31 20:15:59 -0400815 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
816 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400817 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700818 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700819 // never disable touch interactions for remote playback, the muting is not tied to
820 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700821 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock661f2cf2014-11-17 10:29:10 -0500822 } else if (isRinger && mNotificationEffectsSuppressor != null) {
823 sc.icon.setEnabled(true);
824 sc.icon.setAlpha(1f);
825 sc.icon.setClickable(false);
826 } else if (isRinger
827 && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400828 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400829 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400830 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400831 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400832 } else if (fixedVolume ||
John Spurlock5eb21f32014-11-19 11:54:53 -0500833 (sc.streamType != mAudioManager.getMasterStreamType() && !isRinger && muted) ||
John Spurlock4f0f1202014-08-05 13:28:33 -0400834 (sSafetyWarning != null)) {
835 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700836 } else {
837 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400838 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400839 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700840 }
John Spurlockae641c92014-06-30 18:11:40 -0400841 // show the silent hint when the disabled slider is touched in silent mode
842 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400843 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400844 sc.group.setOnTouchListener(null);
John Spurlock661f2cf2014-11-17 10:29:10 -0500845 sc.icon.setClickable(mHasVibrator);
John Spurlock5f640e42014-05-31 20:15:59 -0400846 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400847 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400848 @Override
849 public boolean onTouch(View v, MotionEvent event) {
850 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400851 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400852 return false;
853 }
John Spurlockae641c92014-06-30 18:11:40 -0400854 };
855 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400856 }
857 }
858 }
859
John Spurlockae641c92014-06-30 18:11:40 -0400860 private void showSilentHint() {
861 if (mZenPanel != null) {
862 mZenPanel.showSilentHint();
863 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800864 }
865
John Spurlock661f2cf2014-11-17 10:29:10 -0500866 private void showVibrateHint() {
867 final StreamControl active = mStreamControls.get(mActiveStreamType);
868 if (active != null) {
869 mIconPulser.start(active.icon);
870 if (!hasMessages(MSG_VIBRATE)) {
871 sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
872 }
873 }
874 }
875
John Spurlock7f1df5e2014-05-31 19:11:40 -0400876 private static boolean isNotificationOrRing(int streamType) {
877 return streamType == AudioManager.STREAM_RING
878 || streamType == AudioManager.STREAM_NOTIFICATION;
879 }
880
John Spurlockae641c92014-06-30 18:11:40 -0400881 public void setCallback(Callback callback) {
882 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400883 }
884
John Spurlock8845da72014-07-07 21:29:48 -0400885 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500886 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
887 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400888 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400889 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
890 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
891 : TIMEOUT_DELAY;
892 }
893
894 private boolean isZenPanelVisible() {
895 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400896 }
897
John Spurlockae641c92014-06-30 18:11:40 -0400898 private void setZenPanelVisible(boolean visible) {
899 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400900 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400901 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400902 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400903 resetTimeout();
904 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400905 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800906 }
John Spurlockea9938c2014-07-11 18:51:32 -0400907 if (changing) {
908 updateTimeoutDelay();
909 resetTimeout();
910 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800911 }
912
John Spurlock530052a2014-11-30 16:26:19 -0500913 private void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400914 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800915 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400916 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
John Spurlock530052a2014-11-30 16:26:19 -0500917 updateSlider(sc, true /*forceReloadIcon*/);
918 }
919 }
920
921 private void updateActiveSlider() {
922 final StreamControl active = mStreamControls.get(mActiveStreamType);
923 if (active != null) {
924 updateSlider(active, false /*forceReloadIcon*/);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800925 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800926 }
927
John Spurlock45601d62014-08-07 17:40:50 -0400928 private void updateZenPanelVisible() {
929 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400930 }
931
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 public void postVolumeChanged(int streamType, int flags) {
933 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700934 synchronized (this) {
935 if (mStreamControls == null) {
936 createSliders();
937 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800938 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 removeMessages(MSG_FREE_RESOURCES);
940 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
941 }
942
RoboErik19c95182014-06-23 15:38:48 -0700943 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700944 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
945 synchronized (this) {
946 if (mStreamControls == null) {
947 createSliders();
948 }
949 }
950 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700951 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700952 }
953
954 public void postRemoteSliderVisibility(boolean visible) {
955 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700956 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700957 }
958
959 /**
960 * Called by AudioService when it has received new remote playback information that
961 * would affect the VolumePanel display (mainly volumes). The difference with
962 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
963 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
964 * displayed.
965 * This special code path is due to the fact that remote volume updates arrive to AudioService
966 * asynchronously. So after AudioService has sent the volume update (which should be treated
967 * as a request to update the volume), the application will likely set a new volume. If the UI
968 * is still up, we need to refresh the display to show this new value.
969 */
970 public void postHasNewRemotePlaybackInfo() {
971 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
972 // don't create or prevent resources to be freed, if they disappear, this update came too
973 // late and shouldn't warrant the panel to be displayed longer
974 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
975 }
976
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400977 public void postMasterVolumeChanged(int flags) {
978 postVolumeChanged(STREAM_MASTER, flags);
979 }
980
Mike Lockwoodce952c82011-11-14 10:47:42 -0800981 public void postMuteChanged(int streamType, int flags) {
982 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700983 synchronized (this) {
984 if (mStreamControls == null) {
985 createSliders();
986 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800987 }
988 removeMessages(MSG_FREE_RESOURCES);
989 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
990 }
991
992 public void postMasterMuteChanged(int flags) {
993 postMuteChanged(STREAM_MASTER, flags);
994 }
995
Eric Laurentfde16d52012-12-03 14:42:39 -0800996 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700997 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800998 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700999 }
1000
John Spurlock1dad2722014-07-11 11:07:53 -04001001 public void postDismiss(long delay) {
1002 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -04001003 }
1004
1005 public void postLayoutDirection(int layoutDirection) {
1006 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -04001007 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -04001008 }
1009
John Spurlock661f2cf2014-11-17 10:29:10 -05001010 private static String flagsToString(int flags) {
1011 return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
1012 }
1013
1014 private static String streamToString(int stream) {
1015 return AudioService.streamToString(stream);
1016 }
1017
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 /**
1019 * Override this if you have other work to do when the volume changes (for
1020 * example, vibrating, playing a sound, etc.). Make sure to call through to
1021 * the superclass implementation.
1022 */
1023 protected void onVolumeChanged(int streamType, int flags) {
1024
John Spurlock661f2cf2014-11-17 10:29:10 -05001025 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
1026 + ", flags: " + flagsToString(flags) + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001027
1028 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -07001029 synchronized (this) {
1030 if (mActiveStreamType != streamType) {
1031 reorderSliders(streamType);
1032 }
RoboErik19c95182014-06-23 15:38:48 -07001033 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -08001034 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001035 }
1036
Marco Nelissen69f593c2009-07-28 09:55:04 -07001037 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 removeMessages(MSG_PLAY_SOUND);
1039 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
1040 }
1041
1042 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
1043 removeMessages(MSG_PLAY_SOUND);
1044 removeMessages(MSG_VIBRATE);
1045 onStopSounds();
1046 }
1047
1048 removeMessages(MSG_FREE_RESOURCES);
1049 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001050 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 }
1052
Mike Lockwoodce952c82011-11-14 10:47:42 -08001053 protected void onMuteChanged(int streamType, int flags) {
1054
John Spurlock661f2cf2014-11-17 10:29:10 -05001055 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
1056 + ", flags: " + flagsToString(flags) + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -08001057
1058 StreamControl sc = mStreamControls.get(streamType);
1059 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -04001060 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -08001061 }
1062
1063 onVolumeChanged(streamType, flags);
1064 }
1065
RoboErik19c95182014-06-23 15:38:48 -07001066 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001067 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001068
Marco Nelissen69f593c2009-07-28 09:55:04 -07001069 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070
1071 if (LOGD) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001072 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
1073 + ", flags: " + flagsToString(flags) + "), index: " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
1075
1076 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001077
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001078 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001079 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001080
1081 switch (streamType) {
1082
1083 case AudioManager.STREAM_RING: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001084 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1085 mContext, RingtoneManager.TYPE_RINGTONE);
1086 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001087 mRingIsSilent = true;
1088 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 break;
1090 }
1091
1092 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001093 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001094 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1095 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1096 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1097 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001098 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001099 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001100 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 }
1102 break;
1103 }
1104
1105 case AudioManager.STREAM_VOICE_CALL: {
1106 /*
1107 * For in-call voice call volume, there is no inaudible volume.
1108 * Rescale the UI control so the progress bar doesn't go all
1109 * the way to zero and don't show the mute icon.
1110 */
1111 index++;
1112 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001113 break;
1114 }
1115
1116 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001117 break;
1118 }
1119
1120 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001121 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1122 mContext, RingtoneManager.TYPE_NOTIFICATION);
1123 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001124 mRingIsSilent = true;
1125 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001126 break;
1127 }
1128
1129 case AudioManager.STREAM_BLUETOOTH_SCO: {
1130 /*
1131 * For in-call voice call volume, there is no inaudible volume.
1132 * Rescale the UI control so the progress bar doesn't go all
1133 * the way to zero and don't show the mute icon.
1134 */
1135 index++;
1136 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001137 break;
1138 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001139
RoboErik2811dd32014-08-12 09:48:13 -07001140 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001141 if (controller == null && sc != null) {
1142 // If we weren't passed one try using the last one set.
1143 controller = sc.controller;
1144 }
1145 if (controller == null) {
1146 // We still don't have one, ignore the command.
1147 Log.w(mTag, "sent remote volume change without a controller!");
1148 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001149 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001150 index = vi.getCurrentVolume();
1151 max = vi.getMaxVolume();
1152 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1153 // if the remote volume is fixed add the flag for the UI
1154 flags |= AudioManager.FLAG_FIXED_VOLUME;
1155 }
1156 }
John Spurlock86005342014-05-23 11:58:00 -04001157 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001158 break;
1159 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001160 }
1161
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001162 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001163 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001164 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001165 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001166 }
1167 sc.controller = controller;
1168 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001169 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001170 }
1171 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001172 if (sc.seekbarView.getMax() != max) {
1173 sc.seekbarView.setMax(max);
1174 }
John Spurlockad494bc2014-07-19 15:56:19 -04001175 updateSliderProgress(sc, index);
John Spurlock661f2cf2014-11-17 10:29:10 -05001176 final boolean muted = isMuted(streamType);
1177 updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
1178 if (isNotificationOrRing(streamType)) {
1179 // check for secondary-icon transition completion
1180 if (mSecondaryIconTransition.isRunning()) {
1181 mSecondaryIconTransition.cancel(); // safe to reset
1182 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1183 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1184 }
1185 updateSliderIcon(sc, muted);
John Spurlockbb4a7022014-11-08 12:40:19 -05001186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 }
1188
John Spurlock86005342014-05-23 11:58:00 -04001189 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001190 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001191 // when the stream is for remote playback, use -1 to reset the stream type evaluation
John Spurlocke506c852014-11-26 10:11:19 -05001192 if (stream != STREAM_MASTER) {
1193 mAudioManager.forceVolumeControlStream(stream);
1194 }
John Spurlockeb2727b2014-07-19 23:11:36 -04001195 mDialog.show();
1196 if (mCallback != null) {
1197 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001198 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001199 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 }
1201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001203 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001204 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock661f2cf2014-11-17 10:29:10 -05001205 isNotificationOrRing(streamType) &&
1206 mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1208 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001209
John Spurlock661f2cf2014-11-17 10:29:10 -05001210 // Pulse the zen icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001211 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1212 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001213 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001214
1215 // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
1216 if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
1217 showVibrateHint();
1218 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001219 }
1220
Selim Cinek62ea3402014-09-08 12:11:51 +02001221 private void announceDialogShown() {
1222 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1223 }
1224
John Spurlock86005342014-05-23 11:58:00 -04001225 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001226 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001227 }
1228
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001229 protected void onPlaySound(int streamType, int flags) {
1230
1231 if (hasMessages(MSG_STOP_SOUNDS)) {
1232 removeMessages(MSG_STOP_SOUNDS);
1233 // Force stop right now
1234 onStopSounds();
1235 }
1236
1237 synchronized (this) {
1238 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001239 if (toneGen != null) {
1240 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1241 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1242 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 }
1245
1246 protected void onStopSounds() {
1247
1248 synchronized (this) {
1249 int numStreamTypes = AudioSystem.getNumStreamTypes();
1250 for (int i = numStreamTypes - 1; i >= 0; i--) {
1251 ToneGenerator toneGen = mToneGenerators[i];
1252 if (toneGen != null) {
1253 toneGen.stopTone();
1254 }
1255 }
1256 }
1257 }
1258
1259 protected void onVibrate() {
1260
1261 // Make sure we ended up in vibrate ringer mode
John Spurlock661f2cf2014-11-17 10:29:10 -05001262 if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 return;
1264 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001265 if (mVibrator != null) {
1266 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
1267 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001268 }
1269
RoboErik19c95182014-06-23 15:38:48 -07001270 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001271 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
1272 + flagsToString(flags) + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001273
John Spurlock86005342014-05-23 11:58:00 -04001274 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001275 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001276 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1277 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001278 }
RoboErik2811dd32014-08-12 09:48:13 -07001279 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001280 }
1281 } else {
John Spurlock86005342014-05-23 11:58:00 -04001282 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001283 }
1284
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001285 removeMessages(MSG_FREE_RESOURCES);
1286 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001287 resetTimeout();
1288 }
1289
1290 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001291 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1292 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001293 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001294 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001295 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001296 }
1297 }
1298
RoboErik19c95182014-06-23 15:38:48 -07001299 /**
1300 * Clear the current remote stream controller.
1301 */
1302 private void clearRemoteStreamController() {
1303 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001304 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001305 if (sc != null) {
1306 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001307 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001308 sc.controller = null;
1309 }
1310 }
1311 }
1312 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001313
1314 /**
RoboErik2811dd32014-08-12 09:48:13 -07001315 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1316 *
1317 * @param streamType can be a valid stream type value, or
1318 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001319 * @param visible
1320 */
1321 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001322 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001323 boolean isVisible = (visible == 1);
1324 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1325 StreamResources streamRes = STREAMS[i];
1326 if (streamRes.streamType == streamType) {
1327 streamRes.show = isVisible;
1328 if (!isVisible && (mActiveStreamType == streamType)) {
1329 mActiveStreamType = -1;
1330 }
1331 break;
1332 }
1333 }
1334 }
1335
Eric Laurentfde16d52012-12-03 14:42:39 -08001336 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001337 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1338 || isShowing()) {
1339 synchronized (sSafetyWarningLock) {
1340 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001341 return;
1342 }
John Spurlock35134602014-07-24 18:10:48 -04001343 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1344 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001345 }
1346 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001347 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001348 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1349 removeMessages(MSG_TIMEOUT);
1350 } else {
1351 updateTimeoutDelay();
1352 resetTimeout();
1353 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001354 }
1355
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001356 /**
1357 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1358 */
1359 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001360 if (streamType == STREAM_MASTER) {
1361 // For devices that use the master volume setting only but still want to
1362 // play a volume-changed tone, direct the master volume pseudostream to
1363 // the system stream's tone generator.
1364 if (mPlayMasterStreamTones) {
1365 streamType = AudioManager.STREAM_SYSTEM;
1366 } else {
1367 return null;
1368 }
1369 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 synchronized (this) {
1371 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001372 try {
1373 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1374 } catch (RuntimeException e) {
1375 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001376 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001377 + "RuntimeException: " + e);
1378 }
1379 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001380 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001381 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001382 }
1383 }
1384
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385
1386 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001387 * Switch between icons because Bluetooth music is same as music volume, but with
1388 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001389 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001390 private void setMusicIcon(int resId, int resMuteId) {
1391 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1392 if (sc != null) {
1393 sc.iconRes = resId;
1394 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001395 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001396 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001397 }
1398
1399 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 synchronized (this) {
1401 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1402 if (mToneGenerators[i] != null) {
1403 mToneGenerators[i].release();
1404 }
1405 mToneGenerators[i] = null;
1406 }
1407 }
1408 }
1409
1410 @Override
1411 public void handleMessage(Message msg) {
1412 switch (msg.what) {
1413
1414 case MSG_VOLUME_CHANGED: {
1415 onVolumeChanged(msg.arg1, msg.arg2);
1416 break;
1417 }
1418
Mike Lockwoodce952c82011-11-14 10:47:42 -08001419 case MSG_MUTE_CHANGED: {
1420 onMuteChanged(msg.arg1, msg.arg2);
1421 break;
1422 }
1423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001424 case MSG_FREE_RESOURCES: {
1425 onFreeResources();
1426 break;
1427 }
1428
1429 case MSG_STOP_SOUNDS: {
1430 onStopSounds();
1431 break;
1432 }
1433
1434 case MSG_PLAY_SOUND: {
1435 onPlaySound(msg.arg1, msg.arg2);
1436 break;
1437 }
1438
1439 case MSG_VIBRATE: {
1440 onVibrate();
1441 break;
1442 }
1443
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001444 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001445 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001446 mDialog.dismiss();
1447 clearRemoteStreamController();
1448 mActiveStreamType = -1;
1449 if (mCallback != null) {
1450 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001451 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001452 }
John Spurlock35134602014-07-24 18:10:48 -04001453 synchronized (sSafetyWarningLock) {
1454 if (sSafetyWarning != null) {
1455 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1456 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001457 }
1458 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001459 break;
1460 }
John Spurlockb4782522014-08-22 14:54:46 -04001461
1462 case MSG_RINGER_MODE_CHANGED:
John Spurlock661f2cf2014-11-17 10:29:10 -05001463 case MSG_INTERNAL_RINGER_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001464 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001465 if (isShowing()) {
John Spurlock530052a2014-11-30 16:26:19 -05001466 updateActiveSlider();
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001467 }
1468 break;
1469 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001470
1471 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001472 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001473 break;
1474 }
1475
1476 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1477 onRemoteVolumeUpdateIfShown();
1478 break;
1479
1480 case MSG_SLIDER_VISIBILITY_CHANGED:
1481 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1482 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001483
1484 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001485 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001486 break;
John Spurlock86005342014-05-23 11:58:00 -04001487
1488 case MSG_LAYOUT_DIRECTION:
1489 setLayoutDirection(msg.arg1);
1490 break;
1491
John Spurlock45601d62014-08-07 17:40:50 -04001492 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1493 mZenModeAvailable = msg.arg1 != 0;
1494 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001495 break;
1496
1497 case MSG_USER_ACTIVITY:
1498 if (mCallback != null) {
1499 mCallback.onInteraction();
1500 }
John Spurlock86005342014-05-23 11:58:00 -04001501 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001502 }
1503 }
1504
John Spurlockae641c92014-06-30 18:11:40 -04001505 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001506 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001507 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001508 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1509 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001510 removeMessages(MSG_TIMEOUT);
1511 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1512 removeMessages(MSG_USER_ACTIVITY);
1513 sendEmptyMessage(MSG_USER_ACTIVITY);
1514 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001515 }
1516
John Spurlock1dad2722014-07-11 11:07:53 -04001517 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001518 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001519 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001520 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001521 }
1522
1523 public ZenModeController getZenController() {
1524 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001525 }
1526
John Spurlockbb4a7022014-11-08 12:40:19 -05001527 @Override
1528 public void dispatchDemoCommand(String command, Bundle args) {
1529 if (!COMMAND_VOLUME.equals(command)) return;
1530 String icon = args.getString("icon");
1531 final String iconMute = args.getString("iconmute");
1532 final boolean mute = iconMute != null;
1533 icon = mute ? iconMute : icon;
1534 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1535 final StreamResources sr = StreamResources.valueOf(icon);
1536 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1537 final int forcedStreamType = StreamResources.MediaStream.streamType;
1538 mAudioManager.forceVolumeControlStream(forcedStreamType);
1539 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1540 AudioManager.FLAG_SHOW_UI);
1541 }
1542
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001543 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1544 @Override
1545 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1546 final Object tag = seekBar.getTag();
1547 if (fromUser && tag instanceof StreamControl) {
1548 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001549 setStreamVolume(sc, progress,
1550 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001551 }
1552 resetTimeout();
1553 }
1554
1555 @Override
1556 public void onStartTrackingTouch(SeekBar seekBar) {
1557 }
1558
1559 @Override
1560 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001561 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001562 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001563
John Spurlock86005342014-05-23 11:58:00 -04001564 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001565 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001566 public void onZenAvailableChanged(boolean available) {
1567 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001568 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001569
John Spurlockb4782522014-08-22 14:54:46 -04001570 @Override
1571 public void onEffectsSupressorChanged() {
John Spurlock661f2cf2014-11-17 10:29:10 -05001572 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
1573 sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
1574 }
John Spurlock86005342014-05-23 11:58:00 -04001575 };
RoboErik19c95182014-06-23 15:38:48 -07001576
1577 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001578 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001579 onRemoteVolumeUpdateIfShown();
1580 }
1581 };
John Spurlockae641c92014-06-30 18:11:40 -04001582
John Spurlockbb4a7022014-11-08 12:40:19 -05001583 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1584 implements Runnable {
1585 private static final int ANIMATION_TIME = 400;
1586 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1587
1588 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1589 private final int mFadeOutTime = mAnimationTime / 2;
1590 private final int mDelayTime = mAnimationTime / 3;
1591
1592 private final Interpolator mIconInterpolator =
1593 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1594
1595 private StreamControl mTarget;
1596
1597 public void start(StreamControl sc) {
1598 if (sc == null) throw new IllegalArgumentException();
John Spurlock530052a2014-11-30 16:26:19 -05001599 if (LOGD) Log.d(mTag, "Secondary icon animation start");
John Spurlockbb4a7022014-11-08 12:40:19 -05001600 if (mTarget != null) {
1601 cancel();
1602 }
1603 mTarget = sc;
1604 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1605 resetTimeout();
1606 mTarget.secondaryIcon.setClickable(false);
1607 final int N = mTarget.group.getChildCount();
1608 for (int i = 0; i < N; i++) {
1609 final View child = mTarget.group.getChildAt(i);
1610 if (child != mTarget.secondaryIcon) {
1611 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1612 }
1613 }
1614 mTarget.secondaryIcon.animate()
1615 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1616 .setInterpolator(mIconInterpolator)
1617 .setStartDelay(mDelayTime)
1618 .setDuration(mAnimationTime - mDelayTime)
1619 .setListener(this)
1620 .start();
1621 }
1622
1623 public boolean isRunning() {
1624 return mTarget != null;
1625 }
1626
1627 public void cancel() {
1628 if (mTarget == null) return;
1629 mTarget.secondaryIcon.setClickable(true);
1630 final int N = mTarget.group.getChildCount();
1631 for (int i = 0; i < N; i++) {
1632 final View child = mTarget.group.getChildAt(i);
1633 if (child != mTarget.secondaryIcon) {
1634 child.animate().cancel();
1635 child.setAlpha(1);
1636 }
1637 }
1638 mTarget.secondaryIcon.animate().cancel();
1639 mTarget.secondaryIcon.setTranslationX(0);
1640 mTarget = null;
1641 }
1642
1643 @Override
1644 public void onAnimationEnd(Animator animation) {
1645 if (mTarget == null) return;
1646 AsyncTask.execute(this);
1647 }
1648
1649 @Override
1650 public void run() {
1651 if (mTarget == null) return;
John Spurlock530052a2014-11-30 16:26:19 -05001652 if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
John Spurlockbb4a7022014-11-08 12:40:19 -05001653 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1654 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1655 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1656 }
1657 }
1658
John Spurlockae641c92014-06-30 18:11:40 -04001659 public interface Callback {
1660 void onZenSettings();
1661 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001662 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001664}