blob: 31264eed49a43ae7419148897abf549864a5815a [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;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070041import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070043import android.media.VolumeProvider;
44import android.media.session.MediaController;
RoboErikd2b8c942014-08-19 11:23:40 -070045import android.media.session.MediaController.PlaybackInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070046import android.net.Uri;
John Spurlockbb4a7022014-11-08 12:40:19 -050047import android.os.AsyncTask;
48import android.os.Bundle;
John Spurlocka0457c22014-09-26 13:22:08 -040049import android.os.Debug;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080050import android.os.Handler;
51import android.os.Message;
52import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080053import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040054import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040055import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040056import android.view.LayoutInflater;
57import android.view.MotionEvent;
58import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040059import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040060import android.view.ViewGroup;
61import android.view.Window;
62import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070063import android.view.WindowManager.LayoutParams;
Selim Cinek62ea3402014-09-08 12:11:51 +020064import android.view.accessibility.AccessibilityEvent;
Selim Cinek9f6ceb12014-08-29 16:28:39 +020065import android.view.accessibility.AccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -050066import android.view.animation.AnimationUtils;
67import android.view.animation.Interpolator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080069import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080070import android.widget.SeekBar.OnSeekBarChangeListener;
John Spurlockb4782522014-08-22 14:54:46 -040071import android.widget.TextView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080072
John Spurlock86005342014-05-23 11:58:00 -040073import com.android.internal.R;
John Spurlockbb4a7022014-11-08 12:40:19 -050074import com.android.systemui.DemoMode;
John Spurlock35134602014-07-24 18:10:48 -040075import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040076import com.android.systemui.statusbar.policy.ZenModeController;
77
John Spurlockad494bc2014-07-19 15:56:19 -040078import java.io.FileDescriptor;
79import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
81/**
John Spurlock3346a802014-05-20 16:25:37 -040082 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070083 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 * @hide
85 */
John Spurlockbb4a7022014-11-08 12:40:19 -050086public class VolumePanel extends Handler implements DemoMode {
John Spurlockae641c92014-06-30 18:11:40 -040087 private static final String TAG = "VolumePanel";
88 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089
John Spurlock61560172015-02-06 19:46:04 -050090 private static final int PLAY_SOUND_DELAY = AudioSystem.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 /**
93 * The delay before vibrating. This small period exists so if the user is
94 * moving to silent mode, it will not emit a short vibrate (it normally
95 * would since vibrate is between normal mode and silent mode using hardware
96 * keys).
97 */
98 public static final int VIBRATE_DELAY = 300;
99
100 private static final int VIBRATE_DURATION = 300;
101 private static final int BEEP_DURATION = 150;
102 private static final int MAX_VOLUME = 100;
103 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800104 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -0400105 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -0400106 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -0400107 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400108 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800109
110 private static final int MSG_VOLUME_CHANGED = 0;
111 private static final int MSG_FREE_RESOURCES = 1;
112 private static final int MSG_PLAY_SOUND = 2;
113 private static final int MSG_STOP_SOUNDS = 3;
114 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800115 private static final int MSG_TIMEOUT = 5;
116 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800117 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700118 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
119 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
120 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700121 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400122 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400123 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400124 private static final int MSG_USER_ACTIVITY = 14;
John Spurlockb4782522014-08-22 14:54:46 -0400125 private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
John Spurlock530052a2014-11-30 16:26:19 -0500126 private static final int MSG_INTERNAL_RINGER_MODE_CHANGED = 16;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400128 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800129 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700130 // Pseudo stream type for remote volume
131 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400132
John Spurlock7b414672014-07-18 13:02:39 -0400133 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
134 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
135 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
136 .build();
137
John Spurlock4bebb1d2014-10-02 19:17:19 -0400138 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
139 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
John Spurlockbb4a7022014-11-08 12:40:19 -0500140 private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
141 private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
John Spurlock4bebb1d2014-10-02 19:17:19 -0400142
John Spurlock86005342014-05-23 11:58:00 -0400143 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400144 protected final Context mContext;
145 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400146 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700147 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700148 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400149 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400150 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400151 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400152 private float mDisabledAlpha;
153 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
154 private int mLastRingerProgress = 0;
John Spurlockbb4a7022014-11-08 12:40:19 -0500155 private int mDemoIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
Christopher Tatec4b78d22012-05-22 13:57:58 -0700157 // True if we want to play tones on the system stream when the master stream is specified.
158 private final boolean mPlayMasterStreamTones;
159
John Spurlock86005342014-05-23 11:58:00 -0400160
161 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400163 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400164 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800165
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700166 /** The visible portion of the volume overlay */
167 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400168 /** Contains the slider and its touchable icons */
169 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400170 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400171 private ZenModePanel mZenPanel;
John Spurlock661f2cf2014-11-17 10:29:10 -0500172 /** The component currently suppressing notification stream effects */
173 private ComponentName mNotificationEffectsSuppressor;
John Spurlock86005342014-05-23 11:58:00 -0400174
John Spurlockae641c92014-06-30 18:11:40 -0400175 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800176
177 /** Currently active stream that shows up at the top of the list of sliders */
178 private int mActiveStreamType = -1;
179 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400180 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200181 private final AccessibilityManager mAccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -0500182 private final SecondaryIconTransition mSecondaryIconTransition;
John Spurlock661f2cf2014-11-17 10:29:10 -0500183 private final IconPulser mIconPulser;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800184
Amith Yamasani71def772011-10-12 12:25:24 -0700185 private enum StreamResources {
186 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
187 R.string.volume_icon_description_bluetooth,
John Spurlockbb4a7022014-11-08 12:40:19 -0500188 IC_AUDIO_BT,
189 IC_AUDIO_BT_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700190 false),
191 RingerStream(AudioManager.STREAM_RING,
192 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400193 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500194 com.android.systemui.R.drawable.ic_ringer_mute,
Amith Yamasani71def772011-10-12 12:25:24 -0700195 false),
196 VoiceStream(AudioManager.STREAM_VOICE_CALL,
197 R.string.volume_icon_description_incall,
John Spurlockbb4a7022014-11-08 12:40:19 -0500198 com.android.systemui.R.drawable.ic_audio_phone,
199 com.android.systemui.R.drawable.ic_audio_phone,
Amith Yamasani71def772011-10-12 12:25:24 -0700200 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700201 AlarmStream(AudioManager.STREAM_ALARM,
202 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400203 com.android.systemui.R.drawable.ic_audio_alarm,
204 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700205 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700206 MediaStream(AudioManager.STREAM_MUSIC,
207 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400208 IC_AUDIO_VOL,
209 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700210 true),
211 NotificationStream(AudioManager.STREAM_NOTIFICATION,
212 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400213 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500214 com.android.systemui.R.drawable.ic_ringer_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400215 true),
216 // for now, use media resources for master volume
217 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700218 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlock4bebb1d2014-10-02 19:17:19 -0400219 IC_AUDIO_VOL,
220 IC_AUDIO_VOL_MUTE,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700221 false),
RoboErik2811dd32014-08-12 09:48:13 -0700222 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700223 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlockcfe9fb62014-11-26 11:37:00 -0500224 com.android.systemui.R.drawable.ic_audio_remote,
225 com.android.systemui.R.drawable.ic_audio_remote,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700226 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700227
228 int streamType;
229 int descRes;
230 int iconRes;
231 int iconMuteRes;
232 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
233 boolean show;
234
235 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
236 this.streamType = streamType;
237 this.descRes = descRes;
238 this.iconRes = iconRes;
239 this.iconMuteRes = iconMuteRes;
240 this.show = show;
241 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700242 }
Amith Yamasani71def772011-10-12 12:25:24 -0700243
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800244 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700245 private static final StreamResources[] STREAMS = {
246 StreamResources.BluetoothSCOStream,
247 StreamResources.RingerStream,
248 StreamResources.VoiceStream,
249 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700250 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400251 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700252 StreamResources.MasterStream,
253 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800254 };
255
256 /** Object that contains data for each slider */
257 private class StreamControl {
258 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700259 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800260 ViewGroup group;
261 ImageView icon;
262 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400263 TextView suppressorView;
John Spurlockbb4a7022014-11-08 12:40:19 -0500264 View divider;
265 ImageView secondaryIcon;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800266 int iconRes;
267 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400268 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800269 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800270
271 // Synchronize when accessing this
272 private ToneGenerator mToneGenerators[];
273 private Vibrator mVibrator;
John Spurlock661f2cf2014-11-17 10:29:10 -0500274 private boolean mHasVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275
John Spurlock35134602014-07-24 18:10:48 -0400276 private static AlertDialog sSafetyWarning;
277 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700278
John Spurlock35134602014-07-24 18:10:48 -0400279 private static class SafetyWarning extends SystemUIDialog
280 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800281 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800282 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400283 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700284
John Spurlock2d28d6e2014-08-01 13:10:14 -0400285 private boolean mNewVolumeUp;
286
John Spurlock35134602014-07-24 18:10:48 -0400287 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
288 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700289 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800290 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400291 mAudioManager = audioManager;
292
293 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
294 setButton(DialogInterface.BUTTON_POSITIVE,
295 mContext.getString(com.android.internal.R.string.yes), this);
296 setButton(DialogInterface.BUTTON_NEGATIVE,
297 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
298 setOnDismissListener(this);
299
Eric Laurentc34dcc12012-09-10 13:51:52 -0700300 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400301 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700302 }
303
304 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400305 public boolean onKeyDown(int keyCode, KeyEvent event) {
306 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
307 mNewVolumeUp = true;
308 }
309 return super.onKeyDown(keyCode, event);
310 }
311
312 @Override
313 public boolean onKeyUp(int keyCode, KeyEvent event) {
314 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
315 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
316 mAudioManager.disableSafeMediaVolume();
317 dismiss();
318 }
319 return super.onKeyUp(keyCode, event);
320 }
321
322 @Override
John Spurlock35134602014-07-24 18:10:48 -0400323 public void onClick(DialogInterface dialog, int which) {
324 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700325 }
326
Alan Viverette494fb7b2014-04-10 18:12:56 -0700327 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700328 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400329 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800330 cleanUp();
331 }
332
333 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400334 synchronized (sSafetyWarningLock) {
335 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700336 }
John Spurlock1dad2722014-07-11 11:07:53 -0400337 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800338 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700339 }
John Spurlock35134602014-07-24 18:10:48 -0400340
341 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
342 @Override
343 public void onReceive(Context context, Intent intent) {
344 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
345 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
346 cancel();
347 cleanUp();
348 }
349 }
350 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700351 }
352
John Spurlockad680d42015-01-30 15:48:15 -0500353 protected LayoutParams getDialogLayoutParams(Window window, Resources res) {
354 final LayoutParams lp = window.getAttributes();
355 lp.token = null;
356 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
357 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
358 lp.format = PixelFormat.TRANSLUCENT;
359 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
360 lp.setTitle(TAG);
361 return lp;
362 }
363
John Spurlockeb2727b2014-07-19 23:11:36 -0400364 public VolumePanel(Context context, ZenModeController zenController) {
365 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800366 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400367 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200369 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
370 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500371 mSecondaryIconTransition = new SecondaryIconTransition();
John Spurlock661f2cf2014-11-17 10:29:10 -0500372 mIconPulser = new IconPulser(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800373
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400374 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700375 final Resources res = context.getResources();
376 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400377 if (useMasterVolume) {
378 for (int i = 0; i < STREAMS.length; i++) {
379 StreamResources streamRes = STREAMS[i];
380 streamRes.show = (streamRes.streamType == STREAM_MASTER);
381 }
382 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400383 if (LOGD) Log.d(mTag, "new VolumePanel");
384
John Spurlockad494bc2014-07-19 15:56:19 -0400385 mDisabledAlpha = 0.5f;
386 if (mContext.getTheme() != null) {
387 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
388 new int[] { android.R.attr.disabledAlpha });
389 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
390 arr.recycle();
391 }
392
John Spurlockeb2727b2014-07-19 23:11:36 -0400393 mDialog = new Dialog(context) {
394 @Override
395 public boolean onTouchEvent(MotionEvent event) {
396 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400397 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400398 forceTimeout(0);
399 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700400 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400401 return false;
402 }
403 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700404
John Spurlockeb2727b2014-07-19 23:11:36 -0400405 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400406 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400407 mDialog.setCanceledOnTouchOutside(true);
408 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
409 mDialog.setOnDismissListener(new OnDismissListener() {
410 @Override
411 public void onDismiss(DialogInterface dialog) {
412 mActiveStreamType = -1;
413 mAudioManager.forceVolumeControlStream(mActiveStreamType);
414 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500415 mDemoIcon = 0;
416 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400417 }
418 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700419
John Spurlockeb2727b2014-07-19 23:11:36 -0400420 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700421
John Spurlockad680d42015-01-30 15:48:15 -0500422 window.setAttributes(getDialogLayoutParams(window, res));
John Spurlock7e6809a2014-08-06 16:03:14 -0400423
424 updateWidth();
425
426 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
427 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
428 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
429 | LayoutParams.FLAG_NOT_TOUCH_MODAL
430 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
431 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400432 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400433 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400434 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400435 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400436 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400437 }
438 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700439
John Spurlock86005342014-05-23 11:58:00 -0400440 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
441 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400442 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
443 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800444
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700446 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
John Spurlock661f2cf2014-11-17 10:29:10 -0500447 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Amith Yamasani71def772011-10-12 12:25:24 -0700448 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700449
John Spurlock45601d62014-08-07 17:40:50 -0400450 if (mZenController != null && !useMasterVolume) {
451 mZenModeAvailable = mZenController.isZenAvailable();
John Spurlock661f2cf2014-11-17 10:29:10 -0500452 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
John Spurlock45601d62014-08-07 17:40:50 -0400453 mZenController.addCallback(mZenCallback);
454 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700455
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700456 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
457 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700458 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
459
John Spurlock8c79d2e2014-07-24 15:15:25 -0400460 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800461 }
462
John Spurlock7e6809a2014-08-06 16:03:14 -0400463 public void onConfigurationChanged(Configuration newConfig) {
464 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400465 if (mZenPanel != null) {
466 mZenPanel.updateLocale();
467 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400468 }
469
470 private void updateWidth() {
471 final Resources res = mContext.getResources();
472 final LayoutParams lp = mDialog.getWindow().getAttributes();
473 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
474 lp.gravity =
475 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
476 mDialog.getWindow().setAttributes(lp);
477 }
478
John Spurlockad494bc2014-07-19 15:56:19 -0400479 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
480 pw.println("VolumePanel state:");
481 pw.print(" mTag="); pw.println(mTag);
482 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
483 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock661f2cf2014-11-17 10:29:10 -0500484 pw.print(" mHasVibrator="); pw.println(mHasVibrator);
John Spurlock45601d62014-08-07 17:40:50 -0400485 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400486 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
John Spurlock661f2cf2014-11-17 10:29:10 -0500487 pw.print(" mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
John Spurlockad494bc2014-07-19 15:56:19 -0400488 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
489 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
490 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
491 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
492 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
493 pw.print(" isShowing()="); pw.println(isShowing());
494 pw.print(" mCallback="); pw.println(mCallback);
495 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400496 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400497 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
498 pw.print(" mStreamControls=");
499 if (mStreamControls == null) {
500 pw.println("null");
501 } else {
502 final int N = mStreamControls.size();
503 pw.print("<size "); pw.print(N); pw.println('>');
504 for (int i = 0; i < N; i++) {
505 final StreamControl sc = mStreamControls.valueAt(i);
506 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
507 if (sc.seekbarView != null) {
508 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
509 pw.print(" of "); pw.print(sc.seekbarView.getMax());
510 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
511 }
512 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
513 pw.println();
514 }
515 }
John Spurlock530052a2014-11-30 16:26:19 -0500516 if (mZenPanel != null) {
517 mZenPanel.dump(fd, pw, args);
518 }
John Spurlockad494bc2014-07-19 15:56:19 -0400519 }
520
John Spurlockeb2727b2014-07-19 23:11:36 -0400521 private void initZenModePanel() {
522 mZenPanel.init(mZenController);
523 mZenPanel.setCallback(new ZenModePanel.Callback() {
524 @Override
525 public void onMoreSettings() {
526 if (mCallback != null) {
527 mCallback.onZenSettings();
528 }
529 }
530
531 @Override
532 public void onInteraction() {
533 resetTimeout();
534 }
535
536 @Override
537 public void onExpanded(boolean expanded) {
538 if (mZenPanelExpanded == expanded) return;
539 mZenPanelExpanded = expanded;
540 updateTimeoutDelay();
541 resetTimeout();
542 }
543 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400544 }
545
John Spurlock86005342014-05-23 11:58:00 -0400546 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800547 mPanel.setLayoutDirection(layoutDirection);
548 updateStates();
549 }
550
John Spurlock8c79d2e2014-07-24 15:15:25 -0400551 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800552 final IntentFilter filter = new IntentFilter();
553 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlockbcc10872014-11-28 15:29:21 -0500554 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400555 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800556 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700557 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800558 public void onReceive(Context context, Intent intent) {
559 final String action = intent.getAction();
560
561 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
562 removeMessages(MSG_RINGER_MODE_CHANGED);
John Spurlockbcc10872014-11-28 15:29:21 -0500563 sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
564 }
565
566 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
567 removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
568 sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800569 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400570
571 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
572 postDismiss(0);
573 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800574 }
575 }, filter);
576 }
577
578 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400579 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700580 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700581 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700582 // TODO do we need to support a distinct mute property for remote?
583 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400584 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700585 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400586 }
587 }
588
589 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400590 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700591 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700592 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700593 if (mStreamControls != null) {
594 StreamControl sc = mStreamControls.get(streamType);
595 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700596 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700597 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700598 }
599 }
600 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400601 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700602 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400603 }
604 }
605
606 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400607 if (streamType == STREAM_MASTER) {
RoboErik5452e252015-02-06 15:33:53 -0800608 return mAudioManager.getLastAudibleMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700609 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700610 if (mStreamControls != null) {
611 StreamControl sc = mStreamControls.get(streamType);
612 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700613 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700614 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700615 }
616 }
617 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400618 } else {
RoboErik5452e252015-02-06 15:33:53 -0800619 return mAudioManager.getLastAudibleStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400620 }
621 }
622
RoboErik19c95182014-06-23 15:38:48 -0700623 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700624 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700625 if (sc.controller != null) {
626 sc.controller.setVolumeTo(index, flags);
627 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700628 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700629 }
630 } else if (getStreamVolume(sc.streamType) != index) {
631 if (sc.streamType == STREAM_MASTER) {
632 mAudioManager.setMasterVolume(index, flags);
633 } else {
634 mAudioManager.setStreamVolume(sc.streamType, index, flags);
635 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400636 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800637 }
638
639 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700640 final Resources res = mContext.getResources();
641 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
642 Context.LAYOUT_INFLATER_SERVICE);
643
John Spurlockad494bc2014-07-19 15:56:19 -0400644 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700645
John Spurlockbb4a7022014-11-08 12:40:19 -0500646 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700647 for (int i = 0; i < STREAMS.length; i++) {
648 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700649
650 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500651 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700652
653 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700654 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400655 sc.group = (ViewGroup) inflater.inflate(
656 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800657 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400658 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800659 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700660 sc.icon.setContentDescription(res.getString(streamRes.descRes));
661 sc.iconRes = streamRes.iconRes;
662 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800663 sc.icon.setImageResource(sc.iconRes);
John Spurlock661f2cf2014-11-17 10:29:10 -0500664 sc.icon.setClickable(isNotification && mHasVibrator);
John Spurlockbb4a7022014-11-08 12:40:19 -0500665 if (isNotification) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500666 if (mHasVibrator) {
667 sc.icon.setSoundEffectsEnabled(false);
668 sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
669 sc.icon.setOnClickListener(new OnClickListener() {
670 @Override
671 public void onClick(View v) {
672 resetTimeout();
673 toggleRinger(sc);
674 }
675 });
676 }
John Spurlockb4782522014-08-22 14:54:46 -0400677 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400678 }
John Spurlock86005342014-05-23 11:58:00 -0400679 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400680 sc.suppressorView =
681 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
682 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500683 final boolean showSecondary = !isNotification && notificationStream.show;
684 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
685 sc.secondaryIcon = (ImageView) sc.group
686 .findViewById(com.android.systemui.R.id.secondary_icon);
687 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
688 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
689 sc.secondaryIcon.setClickable(showSecondary);
690 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
691 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
692 if (showSecondary) {
693 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
694 @Override
695 public void onClick(View v) {
696 mSecondaryIconTransition.start(sc);
697 }
698 });
699 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700700 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700701 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400702 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700703 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800704 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700705 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800706 }
707 }
708
John Spurlock661f2cf2014-11-17 10:29:10 -0500709 private void toggleRinger(StreamControl sc) {
710 if (!mHasVibrator) return;
711 if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
712 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400713 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
714 } else {
John Spurlock661f2cf2014-11-17 10:29:10 -0500715 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400716 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
717 }
718 }
719
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800720 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400721 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800722
Alan Viverette494fb7b2014-04-10 18:12:56 -0700723 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800724 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400725 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800726 mActiveStreamType = -1;
727 } else {
John Spurlock86005342014-05-23 11:58:00 -0400728 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800729 mActiveStreamType = activeStreamType;
730 active.group.setVisibility(View.VISIBLE);
John Spurlock530052a2014-11-30 16:26:19 -0500731 updateSlider(active, true /*forceReloadIcon*/);
John Spurlock8845da72014-07-07 21:29:48 -0400732 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400733 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800734 }
735 }
736
John Spurlockad494bc2014-07-19 15:56:19 -0400737 private void updateSliderProgress(StreamControl sc, int progress) {
738 final boolean isRinger = isNotificationOrRing(sc.streamType);
John Spurlock661f2cf2014-11-17 10:29:10 -0500739 if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlockad494bc2014-07-19 15:56:19 -0400740 progress = mLastRingerProgress;
741 }
742 if (progress < 0) {
743 progress = getStreamVolume(sc.streamType);
744 }
745 sc.seekbarView.setProgress(progress);
746 if (isRinger) {
747 mLastRingerProgress = progress;
748 }
749 }
750
751 private void updateSliderIcon(StreamControl sc, boolean muted) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500752 ComponentName suppressor = null;
John Spurlockad494bc2014-07-19 15:56:19 -0400753 if (isNotificationOrRing(sc.streamType)) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500754 suppressor = mNotificationEffectsSuppressor;
755 int ringerMode = mAudioManager.getRingerModeInternal();
John Spurlockad494bc2014-07-19 15:56:19 -0400756 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
757 ringerMode = mLastRingerMode;
758 } else {
759 mLastRingerMode = ringerMode;
760 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500761 if (mHasVibrator) {
762 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
763 } else {
764 muted = false;
765 }
John Spurlockad494bc2014-07-19 15:56:19 -0400766 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500767 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
768 : suppressor != null ? sc.iconSuppressedRes
769 : muted ? sc.iconMuteRes
770 : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400771 }
772
John Spurlock661f2cf2014-11-17 10:29:10 -0500773 private void updateSliderSuppressor(StreamControl sc) {
John Spurlockb4782522014-08-22 14:54:46 -0400774 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
John Spurlock661f2cf2014-11-17 10:29:10 -0500775 ? mNotificationEffectsSuppressor : null;
John Spurlockb4782522014-08-22 14:54:46 -0400776 if (suppressor == null) {
777 sc.seekbarView.setVisibility(View.VISIBLE);
778 sc.suppressorView.setVisibility(View.GONE);
779 } else {
780 sc.seekbarView.setVisibility(View.GONE);
781 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500782 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400783 getSuppressorCaption(suppressor)));
John Spurlockb4782522014-08-22 14:54:46 -0400784 }
785 }
786
787 private String getSuppressorCaption(ComponentName suppressor) {
788 final PackageManager pm = mContext.getPackageManager();
789 try {
790 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
791 if (info != null) {
792 final CharSequence seq = info.loadLabel(pm);
793 if (seq != null) {
794 final String str = seq.toString().trim();
795 if (str.length() > 0) {
796 return str;
797 }
798 }
799 }
800 } catch (Throwable e) {
801 Log.w(TAG, "Error loading suppressor caption", e);
802 }
803 return suppressor.getPackageName();
804 }
805
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800806 /** Update the mute and progress state of a slider */
John Spurlock530052a2014-11-30 16:26:19 -0500807 private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
John Spurlockad494bc2014-07-19 15:56:19 -0400808 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800809 final boolean muted = isMuted(sc.streamType);
John Spurlock530052a2014-11-30 16:26:19 -0500810 if (forceReloadIcon) {
811 sc.icon.setImageDrawable(null);
812 }
John Spurlockad494bc2014-07-19 15:56:19 -0400813 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400814 updateSliderEnabled(sc, muted, false);
John Spurlock661f2cf2014-11-17 10:29:10 -0500815 updateSliderSuppressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400816 }
817
John Spurlock5f640e42014-05-31 20:15:59 -0400818 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
819 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400820 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700821 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700822 // never disable touch interactions for remote playback, the muting is not tied to
823 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700824 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock661f2cf2014-11-17 10:29:10 -0500825 } else if (isRinger && mNotificationEffectsSuppressor != null) {
826 sc.icon.setEnabled(true);
827 sc.icon.setAlpha(1f);
828 sc.icon.setClickable(false);
829 } else if (isRinger
830 && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400831 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400832 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400833 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400834 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400835 } else if (fixedVolume ||
John Spurlock5eb21f32014-11-19 11:54:53 -0500836 (sc.streamType != mAudioManager.getMasterStreamType() && !isRinger && muted) ||
John Spurlock4f0f1202014-08-05 13:28:33 -0400837 (sSafetyWarning != null)) {
838 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700839 } else {
840 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400841 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400842 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700843 }
John Spurlockae641c92014-06-30 18:11:40 -0400844 // show the silent hint when the disabled slider is touched in silent mode
845 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400846 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400847 sc.group.setOnTouchListener(null);
John Spurlock661f2cf2014-11-17 10:29:10 -0500848 sc.icon.setClickable(mHasVibrator);
John Spurlock5f640e42014-05-31 20:15:59 -0400849 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400850 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400851 @Override
852 public boolean onTouch(View v, MotionEvent event) {
853 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400854 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400855 return false;
856 }
John Spurlockae641c92014-06-30 18:11:40 -0400857 };
858 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400859 }
860 }
861 }
862
John Spurlockae641c92014-06-30 18:11:40 -0400863 private void showSilentHint() {
864 if (mZenPanel != null) {
865 mZenPanel.showSilentHint();
866 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800867 }
868
John Spurlock661f2cf2014-11-17 10:29:10 -0500869 private void showVibrateHint() {
870 final StreamControl active = mStreamControls.get(mActiveStreamType);
871 if (active != null) {
872 mIconPulser.start(active.icon);
873 if (!hasMessages(MSG_VIBRATE)) {
874 sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
875 }
876 }
877 }
878
John Spurlock7f1df5e2014-05-31 19:11:40 -0400879 private static boolean isNotificationOrRing(int streamType) {
880 return streamType == AudioManager.STREAM_RING
881 || streamType == AudioManager.STREAM_NOTIFICATION;
882 }
883
John Spurlockae641c92014-06-30 18:11:40 -0400884 public void setCallback(Callback callback) {
885 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400886 }
887
John Spurlock8845da72014-07-07 21:29:48 -0400888 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500889 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
890 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400891 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400892 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
893 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
894 : TIMEOUT_DELAY;
895 }
896
897 private boolean isZenPanelVisible() {
898 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400899 }
900
John Spurlockae641c92014-06-30 18:11:40 -0400901 private void setZenPanelVisible(boolean visible) {
902 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400903 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400904 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400905 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400906 resetTimeout();
907 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400908 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800909 }
John Spurlockea9938c2014-07-11 18:51:32 -0400910 if (changing) {
911 updateTimeoutDelay();
912 resetTimeout();
913 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800914 }
915
John Spurlock530052a2014-11-30 16:26:19 -0500916 private void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400917 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800918 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400919 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
John Spurlock530052a2014-11-30 16:26:19 -0500920 updateSlider(sc, true /*forceReloadIcon*/);
921 }
922 }
923
924 private void updateActiveSlider() {
925 final StreamControl active = mStreamControls.get(mActiveStreamType);
926 if (active != null) {
927 updateSlider(active, false /*forceReloadIcon*/);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800928 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800929 }
930
John Spurlock45601d62014-08-07 17:40:50 -0400931 private void updateZenPanelVisible() {
932 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400933 }
934
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 public void postVolumeChanged(int streamType, int flags) {
936 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700937 synchronized (this) {
938 if (mStreamControls == null) {
939 createSliders();
940 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800941 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800942 removeMessages(MSG_FREE_RESOURCES);
943 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
944 }
945
RoboErik19c95182014-06-23 15:38:48 -0700946 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700947 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
948 synchronized (this) {
949 if (mStreamControls == null) {
950 createSliders();
951 }
952 }
953 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700954 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700955 }
956
957 public void postRemoteSliderVisibility(boolean visible) {
958 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700959 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700960 }
961
962 /**
963 * Called by AudioService when it has received new remote playback information that
964 * would affect the VolumePanel display (mainly volumes). The difference with
965 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
966 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
967 * displayed.
968 * This special code path is due to the fact that remote volume updates arrive to AudioService
969 * asynchronously. So after AudioService has sent the volume update (which should be treated
970 * as a request to update the volume), the application will likely set a new volume. If the UI
971 * is still up, we need to refresh the display to show this new value.
972 */
973 public void postHasNewRemotePlaybackInfo() {
974 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
975 // don't create or prevent resources to be freed, if they disappear, this update came too
976 // late and shouldn't warrant the panel to be displayed longer
977 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
978 }
979
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400980 public void postMasterVolumeChanged(int flags) {
981 postVolumeChanged(STREAM_MASTER, flags);
982 }
983
Mike Lockwoodce952c82011-11-14 10:47:42 -0800984 public void postMuteChanged(int streamType, int flags) {
985 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700986 synchronized (this) {
987 if (mStreamControls == null) {
988 createSliders();
989 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800990 }
991 removeMessages(MSG_FREE_RESOURCES);
992 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
993 }
994
995 public void postMasterMuteChanged(int flags) {
996 postMuteChanged(STREAM_MASTER, flags);
997 }
998
Eric Laurentfde16d52012-12-03 14:42:39 -0800999 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -07001000 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -08001001 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001002 }
1003
John Spurlock1dad2722014-07-11 11:07:53 -04001004 public void postDismiss(long delay) {
1005 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -04001006 }
1007
1008 public void postLayoutDirection(int layoutDirection) {
1009 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -04001010 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -04001011 }
1012
John Spurlock661f2cf2014-11-17 10:29:10 -05001013 private static String flagsToString(int flags) {
1014 return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
1015 }
1016
1017 private static String streamToString(int stream) {
John Spurlock61560172015-02-06 19:46:04 -05001018 return AudioSystem.streamToString(stream);
John Spurlock661f2cf2014-11-17 10:29:10 -05001019 }
1020
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001021 /**
1022 * Override this if you have other work to do when the volume changes (for
1023 * example, vibrating, playing a sound, etc.). Make sure to call through to
1024 * the superclass implementation.
1025 */
1026 protected void onVolumeChanged(int streamType, int flags) {
1027
John Spurlock661f2cf2014-11-17 10:29:10 -05001028 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
1029 + ", flags: " + flagsToString(flags) + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030
1031 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -07001032 synchronized (this) {
1033 if (mActiveStreamType != streamType) {
1034 reorderSliders(streamType);
1035 }
RoboErik19c95182014-06-23 15:38:48 -07001036 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -08001037 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039
Marco Nelissen69f593c2009-07-28 09:55:04 -07001040 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 removeMessages(MSG_PLAY_SOUND);
1042 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
1043 }
1044
1045 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
1046 removeMessages(MSG_PLAY_SOUND);
1047 removeMessages(MSG_VIBRATE);
1048 onStopSounds();
1049 }
1050
1051 removeMessages(MSG_FREE_RESOURCES);
1052 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001053 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 }
1055
Mike Lockwoodce952c82011-11-14 10:47:42 -08001056 protected void onMuteChanged(int streamType, int flags) {
1057
John Spurlock661f2cf2014-11-17 10:29:10 -05001058 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
1059 + ", flags: " + flagsToString(flags) + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -08001060
1061 StreamControl sc = mStreamControls.get(streamType);
1062 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -04001063 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -08001064 }
1065
1066 onVolumeChanged(streamType, flags);
1067 }
1068
RoboErik19c95182014-06-23 15:38:48 -07001069 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001070 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001071
Marco Nelissen69f593c2009-07-28 09:55:04 -07001072 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073
1074 if (LOGD) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001075 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
1076 + ", flags: " + flagsToString(flags) + "), index: " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078
1079 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001080
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001081 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001082 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083
1084 switch (streamType) {
1085
1086 case AudioManager.STREAM_RING: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001087 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1088 mContext, RingtoneManager.TYPE_RINGTONE);
1089 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001090 mRingIsSilent = true;
1091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 break;
1093 }
1094
1095 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001096 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001097 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1098 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1099 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1100 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001101 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001103 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 }
1105 break;
1106 }
1107
1108 case AudioManager.STREAM_VOICE_CALL: {
1109 /*
1110 * For in-call voice call volume, there is no inaudible volume.
1111 * Rescale the UI control so the progress bar doesn't go all
1112 * the way to zero and don't show the mute icon.
1113 */
1114 index++;
1115 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001116 break;
1117 }
1118
1119 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001120 break;
1121 }
1122
1123 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001124 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1125 mContext, RingtoneManager.TYPE_NOTIFICATION);
1126 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001127 mRingIsSilent = true;
1128 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 break;
1130 }
1131
1132 case AudioManager.STREAM_BLUETOOTH_SCO: {
1133 /*
1134 * For in-call voice call volume, there is no inaudible volume.
1135 * Rescale the UI control so the progress bar doesn't go all
1136 * the way to zero and don't show the mute icon.
1137 */
1138 index++;
1139 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001140 break;
1141 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001142
RoboErik2811dd32014-08-12 09:48:13 -07001143 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001144 if (controller == null && sc != null) {
1145 // If we weren't passed one try using the last one set.
1146 controller = sc.controller;
1147 }
1148 if (controller == null) {
1149 // We still don't have one, ignore the command.
1150 Log.w(mTag, "sent remote volume change without a controller!");
1151 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001152 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001153 index = vi.getCurrentVolume();
1154 max = vi.getMaxVolume();
1155 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1156 // if the remote volume is fixed add the flag for the UI
1157 flags |= AudioManager.FLAG_FIXED_VOLUME;
1158 }
1159 }
John Spurlock86005342014-05-23 11:58:00 -04001160 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001161 break;
1162 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001163 }
1164
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001165 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001166 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001167 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001168 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001169 }
1170 sc.controller = controller;
1171 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001172 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001173 }
1174 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001175 if (sc.seekbarView.getMax() != max) {
1176 sc.seekbarView.setMax(max);
1177 }
John Spurlockad494bc2014-07-19 15:56:19 -04001178 updateSliderProgress(sc, index);
John Spurlock661f2cf2014-11-17 10:29:10 -05001179 final boolean muted = isMuted(streamType);
1180 updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
1181 if (isNotificationOrRing(streamType)) {
1182 // check for secondary-icon transition completion
1183 if (mSecondaryIconTransition.isRunning()) {
1184 mSecondaryIconTransition.cancel(); // safe to reset
1185 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1186 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1187 }
1188 updateSliderIcon(sc, muted);
John Spurlockbb4a7022014-11-08 12:40:19 -05001189 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 }
1191
John Spurlock86005342014-05-23 11:58:00 -04001192 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001193 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001194 // when the stream is for remote playback, use -1 to reset the stream type evaluation
John Spurlocke506c852014-11-26 10:11:19 -05001195 if (stream != STREAM_MASTER) {
1196 mAudioManager.forceVolumeControlStream(stream);
1197 }
John Spurlockeb2727b2014-07-19 23:11:36 -04001198 mDialog.show();
1199 if (mCallback != null) {
1200 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001201 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001202 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 }
1204
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001205 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001206 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001207 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock661f2cf2014-11-17 10:29:10 -05001208 isNotificationOrRing(streamType) &&
1209 mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1211 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001212
John Spurlock661f2cf2014-11-17 10:29:10 -05001213 // Pulse the zen icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001214 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1215 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001216 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001217
1218 // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
1219 if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
1220 showVibrateHint();
1221 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001222 }
1223
Selim Cinek62ea3402014-09-08 12:11:51 +02001224 private void announceDialogShown() {
1225 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1226 }
1227
John Spurlock86005342014-05-23 11:58:00 -04001228 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001229 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001230 }
1231
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001232 protected void onPlaySound(int streamType, int flags) {
1233
1234 if (hasMessages(MSG_STOP_SOUNDS)) {
1235 removeMessages(MSG_STOP_SOUNDS);
1236 // Force stop right now
1237 onStopSounds();
1238 }
1239
1240 synchronized (this) {
1241 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001242 if (toneGen != null) {
1243 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1244 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1245 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001246 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001247 }
1248
1249 protected void onStopSounds() {
1250
1251 synchronized (this) {
1252 int numStreamTypes = AudioSystem.getNumStreamTypes();
1253 for (int i = numStreamTypes - 1; i >= 0; i--) {
1254 ToneGenerator toneGen = mToneGenerators[i];
1255 if (toneGen != null) {
1256 toneGen.stopTone();
1257 }
1258 }
1259 }
1260 }
1261
1262 protected void onVibrate() {
1263
1264 // Make sure we ended up in vibrate ringer mode
John Spurlock661f2cf2014-11-17 10:29:10 -05001265 if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001266 return;
1267 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001268 if (mVibrator != null) {
1269 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
1270 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 }
1272
RoboErik19c95182014-06-23 15:38:48 -07001273 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001274 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
1275 + flagsToString(flags) + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001276
John Spurlock86005342014-05-23 11:58:00 -04001277 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001278 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001279 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1280 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001281 }
RoboErik2811dd32014-08-12 09:48:13 -07001282 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001283 }
1284 } else {
John Spurlock86005342014-05-23 11:58:00 -04001285 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001286 }
1287
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001288 removeMessages(MSG_FREE_RESOURCES);
1289 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001290 resetTimeout();
1291 }
1292
1293 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001294 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1295 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001296 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001297 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001298 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001299 }
1300 }
1301
RoboErik19c95182014-06-23 15:38:48 -07001302 /**
1303 * Clear the current remote stream controller.
1304 */
1305 private void clearRemoteStreamController() {
1306 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001307 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001308 if (sc != null) {
1309 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001310 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001311 sc.controller = null;
1312 }
1313 }
1314 }
1315 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001316
1317 /**
RoboErik2811dd32014-08-12 09:48:13 -07001318 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1319 *
1320 * @param streamType can be a valid stream type value, or
1321 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001322 * @param visible
1323 */
1324 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001325 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001326 boolean isVisible = (visible == 1);
1327 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1328 StreamResources streamRes = STREAMS[i];
1329 if (streamRes.streamType == streamType) {
1330 streamRes.show = isVisible;
1331 if (!isVisible && (mActiveStreamType == streamType)) {
1332 mActiveStreamType = -1;
1333 }
1334 break;
1335 }
1336 }
1337 }
1338
Eric Laurentfde16d52012-12-03 14:42:39 -08001339 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001340 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1341 || isShowing()) {
1342 synchronized (sSafetyWarningLock) {
1343 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001344 return;
1345 }
John Spurlock35134602014-07-24 18:10:48 -04001346 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1347 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001348 }
1349 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001350 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001351 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1352 removeMessages(MSG_TIMEOUT);
1353 } else {
1354 updateTimeoutDelay();
1355 resetTimeout();
1356 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001357 }
1358
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001359 /**
1360 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1361 */
1362 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001363 if (streamType == STREAM_MASTER) {
1364 // For devices that use the master volume setting only but still want to
1365 // play a volume-changed tone, direct the master volume pseudostream to
1366 // the system stream's tone generator.
1367 if (mPlayMasterStreamTones) {
1368 streamType = AudioManager.STREAM_SYSTEM;
1369 } else {
1370 return null;
1371 }
1372 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001373 synchronized (this) {
1374 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001375 try {
1376 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1377 } catch (RuntimeException e) {
1378 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001379 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001380 + "RuntimeException: " + e);
1381 }
1382 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001383 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001384 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001385 }
1386 }
1387
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001388
1389 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001390 * Switch between icons because Bluetooth music is same as music volume, but with
1391 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001392 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001393 private void setMusicIcon(int resId, int resMuteId) {
1394 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1395 if (sc != null) {
1396 sc.iconRes = resId;
1397 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001398 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001399 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001400 }
1401
1402 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001403 synchronized (this) {
1404 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1405 if (mToneGenerators[i] != null) {
1406 mToneGenerators[i].release();
1407 }
1408 mToneGenerators[i] = null;
1409 }
1410 }
1411 }
1412
1413 @Override
1414 public void handleMessage(Message msg) {
1415 switch (msg.what) {
1416
1417 case MSG_VOLUME_CHANGED: {
1418 onVolumeChanged(msg.arg1, msg.arg2);
1419 break;
1420 }
1421
Mike Lockwoodce952c82011-11-14 10:47:42 -08001422 case MSG_MUTE_CHANGED: {
1423 onMuteChanged(msg.arg1, msg.arg2);
1424 break;
1425 }
1426
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 case MSG_FREE_RESOURCES: {
1428 onFreeResources();
1429 break;
1430 }
1431
1432 case MSG_STOP_SOUNDS: {
1433 onStopSounds();
1434 break;
1435 }
1436
1437 case MSG_PLAY_SOUND: {
1438 onPlaySound(msg.arg1, msg.arg2);
1439 break;
1440 }
1441
1442 case MSG_VIBRATE: {
1443 onVibrate();
1444 break;
1445 }
1446
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001447 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001448 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001449 mDialog.dismiss();
1450 clearRemoteStreamController();
1451 mActiveStreamType = -1;
1452 if (mCallback != null) {
1453 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001454 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001455 }
John Spurlock35134602014-07-24 18:10:48 -04001456 synchronized (sSafetyWarningLock) {
1457 if (sSafetyWarning != null) {
1458 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1459 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001460 }
1461 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001462 break;
1463 }
John Spurlockb4782522014-08-22 14:54:46 -04001464
1465 case MSG_RINGER_MODE_CHANGED:
John Spurlock661f2cf2014-11-17 10:29:10 -05001466 case MSG_INTERNAL_RINGER_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001467 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001468 if (isShowing()) {
John Spurlock530052a2014-11-30 16:26:19 -05001469 updateActiveSlider();
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001470 }
1471 break;
1472 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001473
1474 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001475 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001476 break;
1477 }
1478
1479 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1480 onRemoteVolumeUpdateIfShown();
1481 break;
1482
1483 case MSG_SLIDER_VISIBILITY_CHANGED:
1484 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1485 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001486
1487 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001488 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001489 break;
John Spurlock86005342014-05-23 11:58:00 -04001490
1491 case MSG_LAYOUT_DIRECTION:
1492 setLayoutDirection(msg.arg1);
1493 break;
1494
John Spurlock45601d62014-08-07 17:40:50 -04001495 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1496 mZenModeAvailable = msg.arg1 != 0;
1497 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001498 break;
1499
1500 case MSG_USER_ACTIVITY:
1501 if (mCallback != null) {
1502 mCallback.onInteraction();
1503 }
John Spurlock86005342014-05-23 11:58:00 -04001504 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001505 }
1506 }
1507
John Spurlockae641c92014-06-30 18:11:40 -04001508 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001509 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001510 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001511 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1512 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001513 removeMessages(MSG_TIMEOUT);
1514 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1515 removeMessages(MSG_USER_ACTIVITY);
1516 sendEmptyMessage(MSG_USER_ACTIVITY);
1517 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001518 }
1519
John Spurlock1dad2722014-07-11 11:07:53 -04001520 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001521 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001522 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001523 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001524 }
1525
1526 public ZenModeController getZenController() {
1527 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001528 }
1529
John Spurlockbb4a7022014-11-08 12:40:19 -05001530 @Override
1531 public void dispatchDemoCommand(String command, Bundle args) {
1532 if (!COMMAND_VOLUME.equals(command)) return;
1533 String icon = args.getString("icon");
1534 final String iconMute = args.getString("iconmute");
1535 final boolean mute = iconMute != null;
1536 icon = mute ? iconMute : icon;
1537 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1538 final StreamResources sr = StreamResources.valueOf(icon);
1539 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1540 final int forcedStreamType = StreamResources.MediaStream.streamType;
1541 mAudioManager.forceVolumeControlStream(forcedStreamType);
1542 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1543 AudioManager.FLAG_SHOW_UI);
1544 }
1545
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001546 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1547 @Override
1548 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1549 final Object tag = seekBar.getTag();
1550 if (fromUser && tag instanceof StreamControl) {
1551 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001552 setStreamVolume(sc, progress,
1553 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001554 }
1555 resetTimeout();
1556 }
1557
1558 @Override
1559 public void onStartTrackingTouch(SeekBar seekBar) {
1560 }
1561
1562 @Override
1563 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001564 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001565 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001566
John Spurlock86005342014-05-23 11:58:00 -04001567 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001568 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001569 public void onZenAvailableChanged(boolean available) {
1570 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001571 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001572
John Spurlockb4782522014-08-22 14:54:46 -04001573 @Override
1574 public void onEffectsSupressorChanged() {
John Spurlock661f2cf2014-11-17 10:29:10 -05001575 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
1576 sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
1577 }
John Spurlock86005342014-05-23 11:58:00 -04001578 };
RoboErik19c95182014-06-23 15:38:48 -07001579
1580 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001581 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001582 onRemoteVolumeUpdateIfShown();
1583 }
1584 };
John Spurlockae641c92014-06-30 18:11:40 -04001585
John Spurlockbb4a7022014-11-08 12:40:19 -05001586 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1587 implements Runnable {
1588 private static final int ANIMATION_TIME = 400;
1589 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1590
1591 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1592 private final int mFadeOutTime = mAnimationTime / 2;
1593 private final int mDelayTime = mAnimationTime / 3;
1594
1595 private final Interpolator mIconInterpolator =
1596 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1597
1598 private StreamControl mTarget;
1599
1600 public void start(StreamControl sc) {
1601 if (sc == null) throw new IllegalArgumentException();
John Spurlock530052a2014-11-30 16:26:19 -05001602 if (LOGD) Log.d(mTag, "Secondary icon animation start");
John Spurlockbb4a7022014-11-08 12:40:19 -05001603 if (mTarget != null) {
1604 cancel();
1605 }
1606 mTarget = sc;
1607 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1608 resetTimeout();
1609 mTarget.secondaryIcon.setClickable(false);
1610 final int N = mTarget.group.getChildCount();
1611 for (int i = 0; i < N; i++) {
1612 final View child = mTarget.group.getChildAt(i);
1613 if (child != mTarget.secondaryIcon) {
1614 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1615 }
1616 }
1617 mTarget.secondaryIcon.animate()
1618 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1619 .setInterpolator(mIconInterpolator)
1620 .setStartDelay(mDelayTime)
1621 .setDuration(mAnimationTime - mDelayTime)
1622 .setListener(this)
1623 .start();
1624 }
1625
1626 public boolean isRunning() {
1627 return mTarget != null;
1628 }
1629
1630 public void cancel() {
1631 if (mTarget == null) return;
1632 mTarget.secondaryIcon.setClickable(true);
1633 final int N = mTarget.group.getChildCount();
1634 for (int i = 0; i < N; i++) {
1635 final View child = mTarget.group.getChildAt(i);
1636 if (child != mTarget.secondaryIcon) {
1637 child.animate().cancel();
1638 child.setAlpha(1);
1639 }
1640 }
1641 mTarget.secondaryIcon.animate().cancel();
1642 mTarget.secondaryIcon.setTranslationX(0);
1643 mTarget = null;
1644 }
1645
1646 @Override
1647 public void onAnimationEnd(Animator animation) {
1648 if (mTarget == null) return;
1649 AsyncTask.execute(this);
1650 }
1651
1652 @Override
1653 public void run() {
1654 if (mTarget == null) return;
John Spurlock530052a2014-11-30 16:26:19 -05001655 if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
John Spurlockbb4a7022014-11-08 12:40:19 -05001656 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1657 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1658 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1659 }
1660 }
1661
John Spurlockae641c92014-06-30 18:11:40 -04001662 public interface Callback {
1663 void onZenSettings();
1664 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001665 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001666 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001667}