blob: e79258ef29450686856e5d42e0398af8c58f2a58 [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 Spurlock661f2cf2014-11-17 10:29:10 -0500127 private static final int MSG_ZEN_MODE_CHANGED = 16;
128 private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 17;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400130 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800131 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700132 // Pseudo stream type for remote volume
133 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400134
John Spurlock7b414672014-07-18 13:02:39 -0400135 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
136 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
137 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
138 .build();
139
John Spurlock4bebb1d2014-10-02 19:17:19 -0400140 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
141 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
John Spurlockbb4a7022014-11-08 12:40:19 -0500142 private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
143 private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
John Spurlock4bebb1d2014-10-02 19:17:19 -0400144
John Spurlock86005342014-05-23 11:58:00 -0400145 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400146 protected final Context mContext;
147 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400148 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700149 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700150 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400151 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400152 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400153 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400154 private float mDisabledAlpha;
155 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
156 private int mLastRingerProgress = 0;
John Spurlockbb4a7022014-11-08 12:40:19 -0500157 private int mDemoIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800158
Christopher Tatec4b78d22012-05-22 13:57:58 -0700159 // True if we want to play tones on the system stream when the master stream is specified.
160 private final boolean mPlayMasterStreamTones;
161
John Spurlock86005342014-05-23 11:58:00 -0400162
163 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800164 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400165 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400166 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800167
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700168 /** The visible portion of the volume overlay */
169 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400170 /** Contains the slider and its touchable icons */
171 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400172 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400173 private ZenModePanel mZenPanel;
John Spurlock661f2cf2014-11-17 10:29:10 -0500174 /** The component currently suppressing notification stream effects */
175 private ComponentName mNotificationEffectsSuppressor;
John Spurlock86005342014-05-23 11:58:00 -0400176
John Spurlockae641c92014-06-30 18:11:40 -0400177 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800178
179 /** Currently active stream that shows up at the top of the list of sliders */
180 private int mActiveStreamType = -1;
181 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400182 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200183 private final AccessibilityManager mAccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -0500184 private final SecondaryIconTransition mSecondaryIconTransition;
John Spurlock661f2cf2014-11-17 10:29:10 -0500185 private final IconPulser mIconPulser;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800186
Amith Yamasani71def772011-10-12 12:25:24 -0700187 private enum StreamResources {
188 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
189 R.string.volume_icon_description_bluetooth,
John Spurlockbb4a7022014-11-08 12:40:19 -0500190 IC_AUDIO_BT,
191 IC_AUDIO_BT_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700192 false),
193 RingerStream(AudioManager.STREAM_RING,
194 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400195 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500196 com.android.systemui.R.drawable.ic_ringer_mute,
Amith Yamasani71def772011-10-12 12:25:24 -0700197 false),
198 VoiceStream(AudioManager.STREAM_VOICE_CALL,
199 R.string.volume_icon_description_incall,
John Spurlockbb4a7022014-11-08 12:40:19 -0500200 com.android.systemui.R.drawable.ic_audio_phone,
201 com.android.systemui.R.drawable.ic_audio_phone,
Amith Yamasani71def772011-10-12 12:25:24 -0700202 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700203 AlarmStream(AudioManager.STREAM_ALARM,
204 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400205 com.android.systemui.R.drawable.ic_audio_alarm,
206 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700207 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700208 MediaStream(AudioManager.STREAM_MUSIC,
209 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400210 IC_AUDIO_VOL,
211 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700212 true),
213 NotificationStream(AudioManager.STREAM_NOTIFICATION,
214 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400215 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500216 com.android.systemui.R.drawable.ic_ringer_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400217 true),
218 // for now, use media resources for master volume
219 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700220 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlock4bebb1d2014-10-02 19:17:19 -0400221 IC_AUDIO_VOL,
222 IC_AUDIO_VOL_MUTE,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700223 false),
RoboErik2811dd32014-08-12 09:48:13 -0700224 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700225 R.string.volume_icon_description_media, //FIXME should have its own description
226 R.drawable.ic_media_route_on_holo_dark,
227 R.drawable.ic_media_route_disabled_holo_dark,
228 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700229
230 int streamType;
231 int descRes;
232 int iconRes;
233 int iconMuteRes;
234 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
235 boolean show;
236
237 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
238 this.streamType = streamType;
239 this.descRes = descRes;
240 this.iconRes = iconRes;
241 this.iconMuteRes = iconMuteRes;
242 this.show = show;
243 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700244 }
Amith Yamasani71def772011-10-12 12:25:24 -0700245
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800246 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700247 private static final StreamResources[] STREAMS = {
248 StreamResources.BluetoothSCOStream,
249 StreamResources.RingerStream,
250 StreamResources.VoiceStream,
251 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700252 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400253 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700254 StreamResources.MasterStream,
255 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800256 };
257
258 /** Object that contains data for each slider */
259 private class StreamControl {
260 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700261 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800262 ViewGroup group;
263 ImageView icon;
264 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400265 TextView suppressorView;
John Spurlockbb4a7022014-11-08 12:40:19 -0500266 View divider;
267 ImageView secondaryIcon;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800268 int iconRes;
269 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400270 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800271 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272
273 // Synchronize when accessing this
274 private ToneGenerator mToneGenerators[];
275 private Vibrator mVibrator;
John Spurlock661f2cf2014-11-17 10:29:10 -0500276 private boolean mHasVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277
John Spurlock35134602014-07-24 18:10:48 -0400278 private static AlertDialog sSafetyWarning;
279 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700280
John Spurlock35134602014-07-24 18:10:48 -0400281 private static class SafetyWarning extends SystemUIDialog
282 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800283 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800284 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400285 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700286
John Spurlock2d28d6e2014-08-01 13:10:14 -0400287 private boolean mNewVolumeUp;
288
John Spurlock35134602014-07-24 18:10:48 -0400289 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
290 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700291 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800292 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400293 mAudioManager = audioManager;
294
295 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
296 setButton(DialogInterface.BUTTON_POSITIVE,
297 mContext.getString(com.android.internal.R.string.yes), this);
298 setButton(DialogInterface.BUTTON_NEGATIVE,
299 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
300 setOnDismissListener(this);
301
Eric Laurentc34dcc12012-09-10 13:51:52 -0700302 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400303 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700304 }
305
306 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400307 public boolean onKeyDown(int keyCode, KeyEvent event) {
308 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
309 mNewVolumeUp = true;
310 }
311 return super.onKeyDown(keyCode, event);
312 }
313
314 @Override
315 public boolean onKeyUp(int keyCode, KeyEvent event) {
316 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
317 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
318 mAudioManager.disableSafeMediaVolume();
319 dismiss();
320 }
321 return super.onKeyUp(keyCode, event);
322 }
323
324 @Override
John Spurlock35134602014-07-24 18:10:48 -0400325 public void onClick(DialogInterface dialog, int which) {
326 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700327 }
328
Alan Viverette494fb7b2014-04-10 18:12:56 -0700329 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700330 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400331 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800332 cleanUp();
333 }
334
335 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400336 synchronized (sSafetyWarningLock) {
337 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700338 }
John Spurlock1dad2722014-07-11 11:07:53 -0400339 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800340 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700341 }
John Spurlock35134602014-07-24 18:10:48 -0400342
343 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
344 @Override
345 public void onReceive(Context context, Intent intent) {
346 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
347 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
348 cancel();
349 cleanUp();
350 }
351 }
352 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700353 }
354
John Spurlockeb2727b2014-07-19 23:11:36 -0400355 public VolumePanel(Context context, ZenModeController zenController) {
356 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400358 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800359 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200360 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
361 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500362 mSecondaryIconTransition = new SecondaryIconTransition();
John Spurlock661f2cf2014-11-17 10:29:10 -0500363 mIconPulser = new IconPulser(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800364
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400365 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700366 final Resources res = context.getResources();
367 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400368 if (useMasterVolume) {
369 for (int i = 0; i < STREAMS.length; i++) {
370 StreamResources streamRes = STREAMS[i];
371 streamRes.show = (streamRes.streamType == STREAM_MASTER);
372 }
373 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400374 if (LOGD) Log.d(mTag, "new VolumePanel");
375
John Spurlockad494bc2014-07-19 15:56:19 -0400376 mDisabledAlpha = 0.5f;
377 if (mContext.getTheme() != null) {
378 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
379 new int[] { android.R.attr.disabledAlpha });
380 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
381 arr.recycle();
382 }
383
John Spurlockeb2727b2014-07-19 23:11:36 -0400384 mDialog = new Dialog(context) {
385 @Override
386 public boolean onTouchEvent(MotionEvent event) {
387 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400388 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400389 forceTimeout(0);
390 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700391 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400392 return false;
393 }
394 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700395
John Spurlockeb2727b2014-07-19 23:11:36 -0400396 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400397 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400398 mDialog.setCanceledOnTouchOutside(true);
399 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
400 mDialog.setOnDismissListener(new OnDismissListener() {
401 @Override
402 public void onDismiss(DialogInterface dialog) {
403 mActiveStreamType = -1;
404 mAudioManager.forceVolumeControlStream(mActiveStreamType);
405 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500406 mDemoIcon = 0;
407 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400408 }
409 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700410
John Spurlockeb2727b2014-07-19 23:11:36 -0400411 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700412
John Spurlock7e6809a2014-08-06 16:03:14 -0400413 final LayoutParams lp = window.getAttributes();
414 lp.token = null;
415 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
416 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
417 lp.format = PixelFormat.TRANSLUCENT;
418 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
419 lp.setTitle(TAG);
420 window.setAttributes(lp);
421
422 updateWidth();
423
424 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
425 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
426 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
427 | LayoutParams.FLAG_NOT_TOUCH_MODAL
428 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
429 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400430 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400431 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400432 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400433 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400434 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400435 }
436 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700437
John Spurlock86005342014-05-23 11:58:00 -0400438 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
439 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400440 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
441 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800442
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800443 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700444 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
John Spurlock661f2cf2014-11-17 10:29:10 -0500445 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Amith Yamasani71def772011-10-12 12:25:24 -0700446 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700447
John Spurlock45601d62014-08-07 17:40:50 -0400448 if (mZenController != null && !useMasterVolume) {
449 mZenModeAvailable = mZenController.isZenAvailable();
John Spurlock661f2cf2014-11-17 10:29:10 -0500450 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
John Spurlock45601d62014-08-07 17:40:50 -0400451 mZenController.addCallback(mZenCallback);
452 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700453
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700454 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
455 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700456 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
457
John Spurlock8c79d2e2014-07-24 15:15:25 -0400458 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800459 }
460
John Spurlock7e6809a2014-08-06 16:03:14 -0400461 public void onConfigurationChanged(Configuration newConfig) {
462 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400463 if (mZenPanel != null) {
464 mZenPanel.updateLocale();
465 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400466 }
467
468 private void updateWidth() {
469 final Resources res = mContext.getResources();
470 final LayoutParams lp = mDialog.getWindow().getAttributes();
471 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
472 lp.gravity =
473 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
474 mDialog.getWindow().setAttributes(lp);
475 }
476
John Spurlockad494bc2014-07-19 15:56:19 -0400477 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
478 pw.println("VolumePanel state:");
479 pw.print(" mTag="); pw.println(mTag);
480 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
481 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock661f2cf2014-11-17 10:29:10 -0500482 pw.print(" mHasVibrator="); pw.println(mHasVibrator);
John Spurlock45601d62014-08-07 17:40:50 -0400483 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400484 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
John Spurlock661f2cf2014-11-17 10:29:10 -0500485 pw.print(" mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
John Spurlockad494bc2014-07-19 15:56:19 -0400486 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
487 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
488 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
489 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
490 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
491 pw.print(" isShowing()="); pw.println(isShowing());
492 pw.print(" mCallback="); pw.println(mCallback);
493 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400494 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400495 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
496 pw.print(" mStreamControls=");
497 if (mStreamControls == null) {
498 pw.println("null");
499 } else {
500 final int N = mStreamControls.size();
501 pw.print("<size "); pw.print(N); pw.println('>');
502 for (int i = 0; i < N; i++) {
503 final StreamControl sc = mStreamControls.valueAt(i);
504 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
505 if (sc.seekbarView != null) {
506 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
507 pw.print(" of "); pw.print(sc.seekbarView.getMax());
508 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
509 }
510 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
511 pw.println();
512 }
513 }
514 }
515
John Spurlockeb2727b2014-07-19 23:11:36 -0400516 private void initZenModePanel() {
517 mZenPanel.init(mZenController);
518 mZenPanel.setCallback(new ZenModePanel.Callback() {
519 @Override
520 public void onMoreSettings() {
521 if (mCallback != null) {
522 mCallback.onZenSettings();
523 }
524 }
525
526 @Override
527 public void onInteraction() {
528 resetTimeout();
529 }
530
531 @Override
532 public void onExpanded(boolean expanded) {
533 if (mZenPanelExpanded == expanded) return;
534 mZenPanelExpanded = expanded;
535 updateTimeoutDelay();
536 resetTimeout();
537 }
538 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400539 }
540
John Spurlock86005342014-05-23 11:58:00 -0400541 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800542 mPanel.setLayoutDirection(layoutDirection);
543 updateStates();
544 }
545
John Spurlock8c79d2e2014-07-24 15:15:25 -0400546 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800547 final IntentFilter filter = new IntentFilter();
548 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlockbcc10872014-11-28 15:29:21 -0500549 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400550 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800551 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700552 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800553 public void onReceive(Context context, Intent intent) {
554 final String action = intent.getAction();
555
556 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
557 removeMessages(MSG_RINGER_MODE_CHANGED);
John Spurlockbcc10872014-11-28 15:29:21 -0500558 sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
559 }
560
561 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
562 removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
563 sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800564 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400565
566 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
567 postDismiss(0);
568 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800569 }
570 }, filter);
571 }
572
573 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400574 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700575 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700576 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700577 // TODO do we need to support a distinct mute property for remote?
578 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400579 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700580 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400581 }
582 }
583
584 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400585 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700586 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700587 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700588 if (mStreamControls != null) {
589 StreamControl sc = mStreamControls.get(streamType);
590 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700591 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700592 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700593 }
594 }
595 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400596 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700597 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400598 }
599 }
600
601 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400602 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700603 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700604 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700605 if (mStreamControls != null) {
606 StreamControl sc = mStreamControls.get(streamType);
607 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700608 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700609 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700610 }
611 }
612 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400613 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700614 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400615 }
616 }
617
RoboErik19c95182014-06-23 15:38:48 -0700618 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700619 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700620 if (sc.controller != null) {
621 sc.controller.setVolumeTo(index, flags);
622 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700623 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700624 }
625 } else if (getStreamVolume(sc.streamType) != index) {
626 if (sc.streamType == STREAM_MASTER) {
627 mAudioManager.setMasterVolume(index, flags);
628 } else {
629 mAudioManager.setStreamVolume(sc.streamType, index, flags);
630 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400631 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800632 }
633
634 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700635 final Resources res = mContext.getResources();
636 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
637 Context.LAYOUT_INFLATER_SERVICE);
638
John Spurlockad494bc2014-07-19 15:56:19 -0400639 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700640
John Spurlockbb4a7022014-11-08 12:40:19 -0500641 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700642 for (int i = 0; i < STREAMS.length; i++) {
643 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700644
645 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500646 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700647
648 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700649 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400650 sc.group = (ViewGroup) inflater.inflate(
651 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800652 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400653 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800654 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700655 sc.icon.setContentDescription(res.getString(streamRes.descRes));
656 sc.iconRes = streamRes.iconRes;
657 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800658 sc.icon.setImageResource(sc.iconRes);
John Spurlock661f2cf2014-11-17 10:29:10 -0500659 sc.icon.setClickable(isNotification && mHasVibrator);
John Spurlockbb4a7022014-11-08 12:40:19 -0500660 if (isNotification) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500661 if (mHasVibrator) {
662 sc.icon.setSoundEffectsEnabled(false);
663 sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
664 sc.icon.setOnClickListener(new OnClickListener() {
665 @Override
666 public void onClick(View v) {
667 resetTimeout();
668 toggleRinger(sc);
669 }
670 });
671 }
John Spurlockb4782522014-08-22 14:54:46 -0400672 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400673 }
John Spurlock86005342014-05-23 11:58:00 -0400674 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400675 sc.suppressorView =
676 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
677 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500678 final boolean showSecondary = !isNotification && notificationStream.show;
679 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
680 sc.secondaryIcon = (ImageView) sc.group
681 .findViewById(com.android.systemui.R.id.secondary_icon);
682 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
683 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
684 sc.secondaryIcon.setClickable(showSecondary);
685 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
686 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
687 if (showSecondary) {
688 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
689 @Override
690 public void onClick(View v) {
691 mSecondaryIconTransition.start(sc);
692 }
693 });
694 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700695 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700696 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400697 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700698 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800699 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700700 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800701 }
702 }
703
John Spurlock661f2cf2014-11-17 10:29:10 -0500704 private void toggleRinger(StreamControl sc) {
705 if (!mHasVibrator) return;
706 if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
707 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400708 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
709 } else {
John Spurlock661f2cf2014-11-17 10:29:10 -0500710 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400711 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
712 }
713 }
714
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800715 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400716 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800717
Alan Viverette494fb7b2014-04-10 18:12:56 -0700718 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800719 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400720 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800721 mActiveStreamType = -1;
722 } else {
John Spurlock86005342014-05-23 11:58:00 -0400723 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800724 mActiveStreamType = activeStreamType;
725 active.group.setVisibility(View.VISIBLE);
726 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400727 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400728 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800729 }
730 }
731
John Spurlockad494bc2014-07-19 15:56:19 -0400732 private void updateSliderProgress(StreamControl sc, int progress) {
733 final boolean isRinger = isNotificationOrRing(sc.streamType);
John Spurlock661f2cf2014-11-17 10:29:10 -0500734 if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlockad494bc2014-07-19 15:56:19 -0400735 progress = mLastRingerProgress;
736 }
737 if (progress < 0) {
738 progress = getStreamVolume(sc.streamType);
739 }
740 sc.seekbarView.setProgress(progress);
741 if (isRinger) {
742 mLastRingerProgress = progress;
743 }
744 }
745
746 private void updateSliderIcon(StreamControl sc, boolean muted) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500747 ComponentName suppressor = null;
John Spurlockad494bc2014-07-19 15:56:19 -0400748 if (isNotificationOrRing(sc.streamType)) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500749 suppressor = mNotificationEffectsSuppressor;
750 int ringerMode = mAudioManager.getRingerModeInternal();
John Spurlockad494bc2014-07-19 15:56:19 -0400751 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
752 ringerMode = mLastRingerMode;
753 } else {
754 mLastRingerMode = ringerMode;
755 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500756 if (mHasVibrator) {
757 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
758 } else {
759 muted = false;
760 }
John Spurlockad494bc2014-07-19 15:56:19 -0400761 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500762 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
763 : suppressor != null ? sc.iconSuppressedRes
764 : muted ? sc.iconMuteRes
765 : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400766 }
767
John Spurlock661f2cf2014-11-17 10:29:10 -0500768 private void updateSliderSuppressor(StreamControl sc) {
John Spurlockb4782522014-08-22 14:54:46 -0400769 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
John Spurlock661f2cf2014-11-17 10:29:10 -0500770 ? mNotificationEffectsSuppressor : null;
John Spurlockb4782522014-08-22 14:54:46 -0400771 if (suppressor == null) {
772 sc.seekbarView.setVisibility(View.VISIBLE);
773 sc.suppressorView.setVisibility(View.GONE);
774 } else {
775 sc.seekbarView.setVisibility(View.GONE);
776 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500777 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400778 getSuppressorCaption(suppressor)));
John Spurlockb4782522014-08-22 14:54:46 -0400779 }
780 }
781
782 private String getSuppressorCaption(ComponentName suppressor) {
783 final PackageManager pm = mContext.getPackageManager();
784 try {
785 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
786 if (info != null) {
787 final CharSequence seq = info.loadLabel(pm);
788 if (seq != null) {
789 final String str = seq.toString().trim();
790 if (str.length() > 0) {
791 return str;
792 }
793 }
794 }
795 } catch (Throwable e) {
796 Log.w(TAG, "Error loading suppressor caption", e);
797 }
798 return suppressor.getPackageName();
799 }
800
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800801 /** Update the mute and progress state of a slider */
802 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400803 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800804 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800805 // Force reloading the image resource
806 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400807 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400808 updateSliderEnabled(sc, muted, false);
John Spurlock661f2cf2014-11-17 10:29:10 -0500809 updateSliderSuppressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400810 }
811
John Spurlock5f640e42014-05-31 20:15:59 -0400812 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
813 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400814 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700815 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700816 // never disable touch interactions for remote playback, the muting is not tied to
817 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700818 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock661f2cf2014-11-17 10:29:10 -0500819 } else if (isRinger && mNotificationEffectsSuppressor != null) {
820 sc.icon.setEnabled(true);
821 sc.icon.setAlpha(1f);
822 sc.icon.setClickable(false);
823 } else if (isRinger
824 && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400825 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400826 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400827 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400828 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400829 } else if (fixedVolume ||
John Spurlock5eb21f32014-11-19 11:54:53 -0500830 (sc.streamType != mAudioManager.getMasterStreamType() && !isRinger && muted) ||
John Spurlock4f0f1202014-08-05 13:28:33 -0400831 (sSafetyWarning != null)) {
832 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700833 } else {
834 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400835 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400836 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700837 }
John Spurlockae641c92014-06-30 18:11:40 -0400838 // show the silent hint when the disabled slider is touched in silent mode
839 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400840 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400841 sc.group.setOnTouchListener(null);
John Spurlock661f2cf2014-11-17 10:29:10 -0500842 sc.icon.setClickable(mHasVibrator);
John Spurlock5f640e42014-05-31 20:15:59 -0400843 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400844 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400845 @Override
846 public boolean onTouch(View v, MotionEvent event) {
847 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400848 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400849 return false;
850 }
John Spurlockae641c92014-06-30 18:11:40 -0400851 };
852 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400853 }
854 }
855 }
856
John Spurlockae641c92014-06-30 18:11:40 -0400857 private void showSilentHint() {
858 if (mZenPanel != null) {
859 mZenPanel.showSilentHint();
860 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800861 }
862
John Spurlock661f2cf2014-11-17 10:29:10 -0500863 private void showVibrateHint() {
864 final StreamControl active = mStreamControls.get(mActiveStreamType);
865 if (active != null) {
866 mIconPulser.start(active.icon);
867 if (!hasMessages(MSG_VIBRATE)) {
868 sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
869 }
870 }
871 }
872
John Spurlock7f1df5e2014-05-31 19:11:40 -0400873 private static boolean isNotificationOrRing(int streamType) {
874 return streamType == AudioManager.STREAM_RING
875 || streamType == AudioManager.STREAM_NOTIFICATION;
876 }
877
John Spurlockae641c92014-06-30 18:11:40 -0400878 public void setCallback(Callback callback) {
879 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400880 }
881
John Spurlock8845da72014-07-07 21:29:48 -0400882 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500883 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
884 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400885 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400886 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
887 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
888 : TIMEOUT_DELAY;
889 }
890
891 private boolean isZenPanelVisible() {
892 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400893 }
894
John Spurlockae641c92014-06-30 18:11:40 -0400895 private void setZenPanelVisible(boolean visible) {
896 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400897 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400898 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400899 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400900 resetTimeout();
901 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400902 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800903 }
John Spurlockea9938c2014-07-11 18:51:32 -0400904 if (changing) {
905 updateTimeoutDelay();
906 resetTimeout();
907 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800908 }
909
Eric Laurentfde16d52012-12-03 14:42:39 -0800910 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400911 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800912 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400913 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800914 updateSlider(sc);
915 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 }
917
John Spurlock45601d62014-08-07 17:40:50 -0400918 private void updateZenPanelVisible() {
919 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400920 }
921
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 public void postVolumeChanged(int streamType, int flags) {
923 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700924 synchronized (this) {
925 if (mStreamControls == null) {
926 createSliders();
927 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 removeMessages(MSG_FREE_RESOURCES);
930 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
931 }
932
RoboErik19c95182014-06-23 15:38:48 -0700933 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700934 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
935 synchronized (this) {
936 if (mStreamControls == null) {
937 createSliders();
938 }
939 }
940 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700941 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700942 }
943
944 public void postRemoteSliderVisibility(boolean visible) {
945 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700946 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700947 }
948
949 /**
950 * Called by AudioService when it has received new remote playback information that
951 * would affect the VolumePanel display (mainly volumes). The difference with
952 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
953 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
954 * displayed.
955 * This special code path is due to the fact that remote volume updates arrive to AudioService
956 * asynchronously. So after AudioService has sent the volume update (which should be treated
957 * as a request to update the volume), the application will likely set a new volume. If the UI
958 * is still up, we need to refresh the display to show this new value.
959 */
960 public void postHasNewRemotePlaybackInfo() {
961 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
962 // don't create or prevent resources to be freed, if they disappear, this update came too
963 // late and shouldn't warrant the panel to be displayed longer
964 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
965 }
966
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400967 public void postMasterVolumeChanged(int flags) {
968 postVolumeChanged(STREAM_MASTER, flags);
969 }
970
Mike Lockwoodce952c82011-11-14 10:47:42 -0800971 public void postMuteChanged(int streamType, int flags) {
972 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700973 synchronized (this) {
974 if (mStreamControls == null) {
975 createSliders();
976 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800977 }
978 removeMessages(MSG_FREE_RESOURCES);
979 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
980 }
981
982 public void postMasterMuteChanged(int flags) {
983 postMuteChanged(STREAM_MASTER, flags);
984 }
985
Eric Laurentfde16d52012-12-03 14:42:39 -0800986 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700987 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800988 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700989 }
990
John Spurlock1dad2722014-07-11 11:07:53 -0400991 public void postDismiss(long delay) {
992 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400993 }
994
995 public void postLayoutDirection(int layoutDirection) {
996 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400997 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400998 }
999
John Spurlock661f2cf2014-11-17 10:29:10 -05001000 private static String flagsToString(int flags) {
1001 return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
1002 }
1003
1004 private static String streamToString(int stream) {
1005 return AudioService.streamToString(stream);
1006 }
1007
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 /**
1009 * Override this if you have other work to do when the volume changes (for
1010 * example, vibrating, playing a sound, etc.). Make sure to call through to
1011 * the superclass implementation.
1012 */
1013 protected void onVolumeChanged(int streamType, int flags) {
1014
John Spurlock661f2cf2014-11-17 10:29:10 -05001015 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
1016 + ", flags: " + flagsToString(flags) + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017
1018 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -07001019 synchronized (this) {
1020 if (mActiveStreamType != streamType) {
1021 reorderSliders(streamType);
1022 }
RoboErik19c95182014-06-23 15:38:48 -07001023 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -08001024 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001025 }
1026
Marco Nelissen69f593c2009-07-28 09:55:04 -07001027 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 removeMessages(MSG_PLAY_SOUND);
1029 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
1030 }
1031
1032 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
1033 removeMessages(MSG_PLAY_SOUND);
1034 removeMessages(MSG_VIBRATE);
1035 onStopSounds();
1036 }
1037
1038 removeMessages(MSG_FREE_RESOURCES);
1039 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001040 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
1042
Mike Lockwoodce952c82011-11-14 10:47:42 -08001043 protected void onMuteChanged(int streamType, int flags) {
1044
John Spurlock661f2cf2014-11-17 10:29:10 -05001045 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
1046 + ", flags: " + flagsToString(flags) + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -08001047
1048 StreamControl sc = mStreamControls.get(streamType);
1049 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -04001050 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -08001051 }
1052
1053 onVolumeChanged(streamType, flags);
1054 }
1055
RoboErik19c95182014-06-23 15:38:48 -07001056 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001057 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001058
Marco Nelissen69f593c2009-07-28 09:55:04 -07001059 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060
1061 if (LOGD) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001062 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
1063 + ", flags: " + flagsToString(flags) + "), index: " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001064 }
1065
1066 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001067
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001068 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001069 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070
1071 switch (streamType) {
1072
1073 case AudioManager.STREAM_RING: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001074 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1075 mContext, RingtoneManager.TYPE_RINGTONE);
1076 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001077 mRingIsSilent = true;
1078 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 break;
1080 }
1081
1082 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001083 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001084 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1085 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1086 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1087 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001088 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001089 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001090 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 }
1092 break;
1093 }
1094
1095 case AudioManager.STREAM_VOICE_CALL: {
1096 /*
1097 * For in-call voice call volume, there is no inaudible volume.
1098 * Rescale the UI control so the progress bar doesn't go all
1099 * the way to zero and don't show the mute icon.
1100 */
1101 index++;
1102 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001103 break;
1104 }
1105
1106 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107 break;
1108 }
1109
1110 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001111 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1112 mContext, RingtoneManager.TYPE_NOTIFICATION);
1113 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001114 mRingIsSilent = true;
1115 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 break;
1117 }
1118
1119 case AudioManager.STREAM_BLUETOOTH_SCO: {
1120 /*
1121 * For in-call voice call volume, there is no inaudible volume.
1122 * Rescale the UI control so the progress bar doesn't go all
1123 * the way to zero and don't show the mute icon.
1124 */
1125 index++;
1126 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001127 break;
1128 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001129
RoboErik2811dd32014-08-12 09:48:13 -07001130 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001131 if (controller == null && sc != null) {
1132 // If we weren't passed one try using the last one set.
1133 controller = sc.controller;
1134 }
1135 if (controller == null) {
1136 // We still don't have one, ignore the command.
1137 Log.w(mTag, "sent remote volume change without a controller!");
1138 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001139 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001140 index = vi.getCurrentVolume();
1141 max = vi.getMaxVolume();
1142 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1143 // if the remote volume is fixed add the flag for the UI
1144 flags |= AudioManager.FLAG_FIXED_VOLUME;
1145 }
1146 }
John Spurlock86005342014-05-23 11:58:00 -04001147 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001148 break;
1149 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 }
1151
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001152 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001153 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001154 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001155 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001156 }
1157 sc.controller = controller;
1158 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001159 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001160 }
1161 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001162 if (sc.seekbarView.getMax() != max) {
1163 sc.seekbarView.setMax(max);
1164 }
John Spurlockad494bc2014-07-19 15:56:19 -04001165 updateSliderProgress(sc, index);
John Spurlock661f2cf2014-11-17 10:29:10 -05001166 final boolean muted = isMuted(streamType);
1167 updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
1168 if (isNotificationOrRing(streamType)) {
1169 // check for secondary-icon transition completion
1170 if (mSecondaryIconTransition.isRunning()) {
1171 mSecondaryIconTransition.cancel(); // safe to reset
1172 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1173 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1174 }
1175 updateSliderIcon(sc, muted);
John Spurlockbb4a7022014-11-08 12:40:19 -05001176 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 }
1178
John Spurlock86005342014-05-23 11:58:00 -04001179 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001180 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001181 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1182 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001183 mDialog.show();
1184 if (mCallback != null) {
1185 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001186 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001187 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 }
1189
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001191 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001192 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock661f2cf2014-11-17 10:29:10 -05001193 isNotificationOrRing(streamType) &&
1194 mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001195 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1196 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001197
John Spurlock661f2cf2014-11-17 10:29:10 -05001198 // Pulse the zen icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001199 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1200 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001201 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001202
1203 // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
1204 if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
1205 showVibrateHint();
1206 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001207 }
1208
Selim Cinek62ea3402014-09-08 12:11:51 +02001209 private void announceDialogShown() {
1210 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1211 }
1212
John Spurlock86005342014-05-23 11:58:00 -04001213 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001214 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001215 }
1216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 protected void onPlaySound(int streamType, int flags) {
1218
1219 if (hasMessages(MSG_STOP_SOUNDS)) {
1220 removeMessages(MSG_STOP_SOUNDS);
1221 // Force stop right now
1222 onStopSounds();
1223 }
1224
1225 synchronized (this) {
1226 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001227 if (toneGen != null) {
1228 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1229 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1230 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001231 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 }
1233
1234 protected void onStopSounds() {
1235
1236 synchronized (this) {
1237 int numStreamTypes = AudioSystem.getNumStreamTypes();
1238 for (int i = numStreamTypes - 1; i >= 0; i--) {
1239 ToneGenerator toneGen = mToneGenerators[i];
1240 if (toneGen != null) {
1241 toneGen.stopTone();
1242 }
1243 }
1244 }
1245 }
1246
1247 protected void onVibrate() {
1248
1249 // Make sure we ended up in vibrate ringer mode
John Spurlock661f2cf2014-11-17 10:29:10 -05001250 if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001251 return;
1252 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001253 if (mVibrator != null) {
1254 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
1255 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 }
1257
RoboErik19c95182014-06-23 15:38:48 -07001258 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001259 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
1260 + flagsToString(flags) + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001261
John Spurlock86005342014-05-23 11:58:00 -04001262 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001263 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001264 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1265 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001266 }
RoboErik2811dd32014-08-12 09:48:13 -07001267 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001268 }
1269 } else {
John Spurlock86005342014-05-23 11:58:00 -04001270 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001271 }
1272
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001273 removeMessages(MSG_FREE_RESOURCES);
1274 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001275 resetTimeout();
1276 }
1277
1278 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001279 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1280 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001281 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001282 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001283 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001284 }
1285 }
1286
RoboErik19c95182014-06-23 15:38:48 -07001287 /**
1288 * Clear the current remote stream controller.
1289 */
1290 private void clearRemoteStreamController() {
1291 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001292 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001293 if (sc != null) {
1294 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001295 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001296 sc.controller = null;
1297 }
1298 }
1299 }
1300 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001301
1302 /**
RoboErik2811dd32014-08-12 09:48:13 -07001303 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1304 *
1305 * @param streamType can be a valid stream type value, or
1306 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001307 * @param visible
1308 */
1309 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001310 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001311 boolean isVisible = (visible == 1);
1312 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1313 StreamResources streamRes = STREAMS[i];
1314 if (streamRes.streamType == streamType) {
1315 streamRes.show = isVisible;
1316 if (!isVisible && (mActiveStreamType == streamType)) {
1317 mActiveStreamType = -1;
1318 }
1319 break;
1320 }
1321 }
1322 }
1323
Eric Laurentfde16d52012-12-03 14:42:39 -08001324 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001325 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1326 || isShowing()) {
1327 synchronized (sSafetyWarningLock) {
1328 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001329 return;
1330 }
John Spurlock35134602014-07-24 18:10:48 -04001331 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1332 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001333 }
1334 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001335 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001336 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1337 removeMessages(MSG_TIMEOUT);
1338 } else {
1339 updateTimeoutDelay();
1340 resetTimeout();
1341 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001342 }
1343
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 /**
1345 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1346 */
1347 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001348 if (streamType == STREAM_MASTER) {
1349 // For devices that use the master volume setting only but still want to
1350 // play a volume-changed tone, direct the master volume pseudostream to
1351 // the system stream's tone generator.
1352 if (mPlayMasterStreamTones) {
1353 streamType = AudioManager.STREAM_SYSTEM;
1354 } else {
1355 return null;
1356 }
1357 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001358 synchronized (this) {
1359 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001360 try {
1361 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1362 } catch (RuntimeException e) {
1363 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001364 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001365 + "RuntimeException: " + e);
1366 }
1367 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001368 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001369 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001370 }
1371 }
1372
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373
1374 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001375 * Switch between icons because Bluetooth music is same as music volume, but with
1376 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001377 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001378 private void setMusicIcon(int resId, int resMuteId) {
1379 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1380 if (sc != null) {
1381 sc.iconRes = resId;
1382 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001383 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001384 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 }
1386
1387 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388 synchronized (this) {
1389 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1390 if (mToneGenerators[i] != null) {
1391 mToneGenerators[i].release();
1392 }
1393 mToneGenerators[i] = null;
1394 }
1395 }
1396 }
1397
1398 @Override
1399 public void handleMessage(Message msg) {
1400 switch (msg.what) {
1401
1402 case MSG_VOLUME_CHANGED: {
1403 onVolumeChanged(msg.arg1, msg.arg2);
1404 break;
1405 }
1406
Mike Lockwoodce952c82011-11-14 10:47:42 -08001407 case MSG_MUTE_CHANGED: {
1408 onMuteChanged(msg.arg1, msg.arg2);
1409 break;
1410 }
1411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001412 case MSG_FREE_RESOURCES: {
1413 onFreeResources();
1414 break;
1415 }
1416
1417 case MSG_STOP_SOUNDS: {
1418 onStopSounds();
1419 break;
1420 }
1421
1422 case MSG_PLAY_SOUND: {
1423 onPlaySound(msg.arg1, msg.arg2);
1424 break;
1425 }
1426
1427 case MSG_VIBRATE: {
1428 onVibrate();
1429 break;
1430 }
1431
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001432 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001433 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001434 mDialog.dismiss();
1435 clearRemoteStreamController();
1436 mActiveStreamType = -1;
1437 if (mCallback != null) {
1438 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001439 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001440 }
John Spurlock35134602014-07-24 18:10:48 -04001441 synchronized (sSafetyWarningLock) {
1442 if (sSafetyWarning != null) {
1443 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1444 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001445 }
1446 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001447 break;
1448 }
John Spurlockb4782522014-08-22 14:54:46 -04001449
John Spurlock661f2cf2014-11-17 10:29:10 -05001450 case MSG_ZEN_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001451 case MSG_RINGER_MODE_CHANGED:
John Spurlock661f2cf2014-11-17 10:29:10 -05001452 case MSG_INTERNAL_RINGER_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001453 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001454 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001455 updateStates();
1456 }
1457 break;
1458 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001459
1460 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001461 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001462 break;
1463 }
1464
1465 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1466 onRemoteVolumeUpdateIfShown();
1467 break;
1468
1469 case MSG_SLIDER_VISIBILITY_CHANGED:
1470 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1471 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001472
1473 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001474 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001475 break;
John Spurlock86005342014-05-23 11:58:00 -04001476
1477 case MSG_LAYOUT_DIRECTION:
1478 setLayoutDirection(msg.arg1);
1479 break;
1480
John Spurlock45601d62014-08-07 17:40:50 -04001481 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1482 mZenModeAvailable = msg.arg1 != 0;
1483 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001484 break;
1485
1486 case MSG_USER_ACTIVITY:
1487 if (mCallback != null) {
1488 mCallback.onInteraction();
1489 }
John Spurlock86005342014-05-23 11:58:00 -04001490 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001491 }
1492 }
1493
John Spurlockae641c92014-06-30 18:11:40 -04001494 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001495 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001496 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001497 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1498 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001499 removeMessages(MSG_TIMEOUT);
1500 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1501 removeMessages(MSG_USER_ACTIVITY);
1502 sendEmptyMessage(MSG_USER_ACTIVITY);
1503 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001504 }
1505
John Spurlock1dad2722014-07-11 11:07:53 -04001506 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001507 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001508 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001509 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001510 }
1511
1512 public ZenModeController getZenController() {
1513 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001514 }
1515
John Spurlockbb4a7022014-11-08 12:40:19 -05001516 @Override
1517 public void dispatchDemoCommand(String command, Bundle args) {
1518 if (!COMMAND_VOLUME.equals(command)) return;
1519 String icon = args.getString("icon");
1520 final String iconMute = args.getString("iconmute");
1521 final boolean mute = iconMute != null;
1522 icon = mute ? iconMute : icon;
1523 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1524 final StreamResources sr = StreamResources.valueOf(icon);
1525 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1526 final int forcedStreamType = StreamResources.MediaStream.streamType;
1527 mAudioManager.forceVolumeControlStream(forcedStreamType);
1528 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1529 AudioManager.FLAG_SHOW_UI);
1530 }
1531
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001532 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1533 @Override
1534 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1535 final Object tag = seekBar.getTag();
1536 if (fromUser && tag instanceof StreamControl) {
1537 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001538 setStreamVolume(sc, progress,
1539 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001540 }
1541 resetTimeout();
1542 }
1543
1544 @Override
1545 public void onStartTrackingTouch(SeekBar seekBar) {
1546 }
1547
1548 @Override
1549 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001550 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001551 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001552
John Spurlock86005342014-05-23 11:58:00 -04001553 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001554 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001555 public void onZenAvailableChanged(boolean available) {
1556 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001557 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001558
John Spurlockb4782522014-08-22 14:54:46 -04001559 @Override
1560 public void onEffectsSupressorChanged() {
John Spurlock661f2cf2014-11-17 10:29:10 -05001561 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
1562 sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
1563 }
1564
1565 public void onZenChanged(int zen) {
1566 sendEmptyMessage(MSG_ZEN_MODE_CHANGED);
John Spurlockb4782522014-08-22 14:54:46 -04001567 }
John Spurlock86005342014-05-23 11:58:00 -04001568 };
RoboErik19c95182014-06-23 15:38:48 -07001569
1570 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001571 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001572 onRemoteVolumeUpdateIfShown();
1573 }
1574 };
John Spurlockae641c92014-06-30 18:11:40 -04001575
John Spurlockbb4a7022014-11-08 12:40:19 -05001576 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1577 implements Runnable {
1578 private static final int ANIMATION_TIME = 400;
1579 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1580
1581 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1582 private final int mFadeOutTime = mAnimationTime / 2;
1583 private final int mDelayTime = mAnimationTime / 3;
1584
1585 private final Interpolator mIconInterpolator =
1586 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1587
1588 private StreamControl mTarget;
1589
1590 public void start(StreamControl sc) {
1591 if (sc == null) throw new IllegalArgumentException();
1592 if (mTarget != null) {
1593 cancel();
1594 }
1595 mTarget = sc;
1596 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1597 resetTimeout();
1598 mTarget.secondaryIcon.setClickable(false);
1599 final int N = mTarget.group.getChildCount();
1600 for (int i = 0; i < N; i++) {
1601 final View child = mTarget.group.getChildAt(i);
1602 if (child != mTarget.secondaryIcon) {
1603 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1604 }
1605 }
1606 mTarget.secondaryIcon.animate()
1607 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1608 .setInterpolator(mIconInterpolator)
1609 .setStartDelay(mDelayTime)
1610 .setDuration(mAnimationTime - mDelayTime)
1611 .setListener(this)
1612 .start();
1613 }
1614
1615 public boolean isRunning() {
1616 return mTarget != null;
1617 }
1618
1619 public void cancel() {
1620 if (mTarget == null) return;
1621 mTarget.secondaryIcon.setClickable(true);
1622 final int N = mTarget.group.getChildCount();
1623 for (int i = 0; i < N; i++) {
1624 final View child = mTarget.group.getChildAt(i);
1625 if (child != mTarget.secondaryIcon) {
1626 child.animate().cancel();
1627 child.setAlpha(1);
1628 }
1629 }
1630 mTarget.secondaryIcon.animate().cancel();
1631 mTarget.secondaryIcon.setTranslationX(0);
1632 mTarget = null;
1633 }
1634
1635 @Override
1636 public void onAnimationEnd(Animator animation) {
1637 if (mTarget == null) return;
1638 AsyncTask.execute(this);
1639 }
1640
1641 @Override
1642 public void run() {
1643 if (mTarget == null) return;
1644 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1645 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1646 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1647 }
1648 }
1649
John Spurlockae641c92014-06-30 18:11:40 -04001650 public interface Callback {
1651 void onZenSettings();
1652 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001653 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001654 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001655}