blob: 51adaacd72f827e58d7b354c13c52351e3ce0572 [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
RoboErik2811dd32014-08-12 09:48:13 -0700128 // Pseudo stream type for remote volume
129 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400130
John Spurlock7b414672014-07-18 13:02:39 -0400131 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
132 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
133 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
134 .build();
135
John Spurlock4bebb1d2014-10-02 19:17:19 -0400136 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
137 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
John Spurlockbb4a7022014-11-08 12:40:19 -0500138 private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
139 private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
John Spurlock4bebb1d2014-10-02 19:17:19 -0400140
John Spurlock86005342014-05-23 11:58:00 -0400141 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400142 protected final Context mContext;
143 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400144 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700145 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700146 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400147 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400148 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400149 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400150 private float mDisabledAlpha;
151 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
152 private int mLastRingerProgress = 0;
John Spurlockbb4a7022014-11-08 12:40:19 -0500153 private int mDemoIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800154
John Spurlock86005342014-05-23 11:58:00 -0400155 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400157 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400158 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800159
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700160 /** The visible portion of the volume overlay */
161 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400162 /** Contains the slider and its touchable icons */
163 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400164 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400165 private ZenModePanel mZenPanel;
John Spurlock661f2cf2014-11-17 10:29:10 -0500166 /** The component currently suppressing notification stream effects */
167 private ComponentName mNotificationEffectsSuppressor;
John Spurlock86005342014-05-23 11:58:00 -0400168
John Spurlockae641c92014-06-30 18:11:40 -0400169 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800170
171 /** Currently active stream that shows up at the top of the list of sliders */
172 private int mActiveStreamType = -1;
173 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400174 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200175 private final AccessibilityManager mAccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -0500176 private final SecondaryIconTransition mSecondaryIconTransition;
John Spurlock661f2cf2014-11-17 10:29:10 -0500177 private final IconPulser mIconPulser;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800178
Amith Yamasani71def772011-10-12 12:25:24 -0700179 private enum StreamResources {
180 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
181 R.string.volume_icon_description_bluetooth,
John Spurlockbb4a7022014-11-08 12:40:19 -0500182 IC_AUDIO_BT,
183 IC_AUDIO_BT_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700184 false),
185 RingerStream(AudioManager.STREAM_RING,
186 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400187 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500188 com.android.systemui.R.drawable.ic_ringer_mute,
Amith Yamasani71def772011-10-12 12:25:24 -0700189 false),
190 VoiceStream(AudioManager.STREAM_VOICE_CALL,
191 R.string.volume_icon_description_incall,
John Spurlockbb4a7022014-11-08 12:40:19 -0500192 com.android.systemui.R.drawable.ic_audio_phone,
193 com.android.systemui.R.drawable.ic_audio_phone,
Amith Yamasani71def772011-10-12 12:25:24 -0700194 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700195 AlarmStream(AudioManager.STREAM_ALARM,
196 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400197 com.android.systemui.R.drawable.ic_audio_alarm,
198 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700199 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700200 MediaStream(AudioManager.STREAM_MUSIC,
201 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400202 IC_AUDIO_VOL,
203 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700204 true),
205 NotificationStream(AudioManager.STREAM_NOTIFICATION,
206 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400207 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlock661f2cf2014-11-17 10:29:10 -0500208 com.android.systemui.R.drawable.ic_ringer_mute,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400209 true),
RoboErik2811dd32014-08-12 09:48:13 -0700210 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700211 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlockcfe9fb62014-11-26 11:37:00 -0500212 com.android.systemui.R.drawable.ic_audio_remote,
213 com.android.systemui.R.drawable.ic_audio_remote,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700214 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700215
216 int streamType;
217 int descRes;
218 int iconRes;
219 int iconMuteRes;
220 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
221 boolean show;
222
223 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
224 this.streamType = streamType;
225 this.descRes = descRes;
226 this.iconRes = iconRes;
227 this.iconMuteRes = iconMuteRes;
228 this.show = show;
229 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700230 }
Amith Yamasani71def772011-10-12 12:25:24 -0700231
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800232 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700233 private static final StreamResources[] STREAMS = {
234 StreamResources.BluetoothSCOStream,
235 StreamResources.RingerStream,
236 StreamResources.VoiceStream,
237 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700238 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400239 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700240 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800241 };
242
243 /** Object that contains data for each slider */
244 private class StreamControl {
245 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700246 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800247 ViewGroup group;
248 ImageView icon;
249 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400250 TextView suppressorView;
John Spurlockbb4a7022014-11-08 12:40:19 -0500251 View divider;
252 ImageView secondaryIcon;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800253 int iconRes;
254 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400255 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800256 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800257
258 // Synchronize when accessing this
259 private ToneGenerator mToneGenerators[];
260 private Vibrator mVibrator;
John Spurlock661f2cf2014-11-17 10:29:10 -0500261 private boolean mHasVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800262
John Spurlock35134602014-07-24 18:10:48 -0400263 private static AlertDialog sSafetyWarning;
264 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700265
John Spurlock35134602014-07-24 18:10:48 -0400266 private static class SafetyWarning extends SystemUIDialog
267 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800268 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800269 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400270 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700271
John Spurlock2d28d6e2014-08-01 13:10:14 -0400272 private boolean mNewVolumeUp;
273
John Spurlock35134602014-07-24 18:10:48 -0400274 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
275 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700276 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800277 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400278 mAudioManager = audioManager;
279
280 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
281 setButton(DialogInterface.BUTTON_POSITIVE,
282 mContext.getString(com.android.internal.R.string.yes), this);
283 setButton(DialogInterface.BUTTON_NEGATIVE,
284 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
285 setOnDismissListener(this);
286
Eric Laurentc34dcc12012-09-10 13:51:52 -0700287 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400288 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700289 }
290
291 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400292 public boolean onKeyDown(int keyCode, KeyEvent event) {
293 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
294 mNewVolumeUp = true;
295 }
296 return super.onKeyDown(keyCode, event);
297 }
298
299 @Override
300 public boolean onKeyUp(int keyCode, KeyEvent event) {
301 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
302 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
303 mAudioManager.disableSafeMediaVolume();
304 dismiss();
305 }
306 return super.onKeyUp(keyCode, event);
307 }
308
309 @Override
John Spurlock35134602014-07-24 18:10:48 -0400310 public void onClick(DialogInterface dialog, int which) {
311 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700312 }
313
Alan Viverette494fb7b2014-04-10 18:12:56 -0700314 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700315 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400316 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800317 cleanUp();
318 }
319
320 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400321 synchronized (sSafetyWarningLock) {
322 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700323 }
John Spurlock1dad2722014-07-11 11:07:53 -0400324 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800325 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700326 }
John Spurlock35134602014-07-24 18:10:48 -0400327
328 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
329 @Override
330 public void onReceive(Context context, Intent intent) {
331 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
332 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
333 cancel();
334 cleanUp();
335 }
336 }
337 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700338 }
339
John Spurlockad680d42015-01-30 15:48:15 -0500340 protected LayoutParams getDialogLayoutParams(Window window, Resources res) {
341 final LayoutParams lp = window.getAttributes();
342 lp.token = null;
343 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
344 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
345 lp.format = PixelFormat.TRANSLUCENT;
346 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
347 lp.setTitle(TAG);
348 return lp;
349 }
350
John Spurlockeb2727b2014-07-19 23:11:36 -0400351 public VolumePanel(Context context, ZenModeController zenController) {
352 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400354 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800355 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200356 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
357 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500358 mSecondaryIconTransition = new SecondaryIconTransition();
John Spurlock661f2cf2014-11-17 10:29:10 -0500359 mIconPulser = new IconPulser(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360
John Spurlockeb2727b2014-07-19 23:11:36 -0400361 if (LOGD) Log.d(mTag, "new VolumePanel");
362
John Spurlockad494bc2014-07-19 15:56:19 -0400363 mDisabledAlpha = 0.5f;
364 if (mContext.getTheme() != null) {
365 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
366 new int[] { android.R.attr.disabledAlpha });
367 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
368 arr.recycle();
369 }
370
John Spurlockeb2727b2014-07-19 23:11:36 -0400371 mDialog = new Dialog(context) {
372 @Override
373 public boolean onTouchEvent(MotionEvent event) {
374 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400375 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400376 forceTimeout(0);
377 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700378 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400379 return false;
380 }
381 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700382
John Spurlockeb2727b2014-07-19 23:11:36 -0400383 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400384 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400385 mDialog.setCanceledOnTouchOutside(true);
386 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
387 mDialog.setOnDismissListener(new OnDismissListener() {
388 @Override
389 public void onDismiss(DialogInterface dialog) {
390 mActiveStreamType = -1;
391 mAudioManager.forceVolumeControlStream(mActiveStreamType);
392 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500393 mDemoIcon = 0;
394 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400395 }
396 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700397
John Spurlockeb2727b2014-07-19 23:11:36 -0400398 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700399
John Spurlockee5ad722015-03-03 16:17:21 -0500400 final Resources res = context.getResources();
John Spurlockad680d42015-01-30 15:48:15 -0500401 window.setAttributes(getDialogLayoutParams(window, res));
John Spurlock7e6809a2014-08-06 16:03:14 -0400402
403 updateWidth();
404
405 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
406 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
407 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
408 | LayoutParams.FLAG_NOT_TOUCH_MODAL
409 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
410 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400411 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400412 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400413 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400414 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400415 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400416 }
417 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700418
John Spurlock86005342014-05-23 11:58:00 -0400419 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
420 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400421 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
422 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700425 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
John Spurlock661f2cf2014-11-17 10:29:10 -0500426 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Amith Yamasani71def772011-10-12 12:25:24 -0700427 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700428
John Spurlockee5ad722015-03-03 16:17:21 -0500429 if (mZenController != null) {
John Spurlock45601d62014-08-07 17:40:50 -0400430 mZenModeAvailable = mZenController.isZenAvailable();
John Spurlock661f2cf2014-11-17 10:29:10 -0500431 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
John Spurlock45601d62014-08-07 17:40:50 -0400432 mZenController.addCallback(mZenCallback);
433 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700434
John Spurlock8c79d2e2014-07-24 15:15:25 -0400435 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800436 }
437
John Spurlock7e6809a2014-08-06 16:03:14 -0400438 public void onConfigurationChanged(Configuration newConfig) {
439 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400440 if (mZenPanel != null) {
441 mZenPanel.updateLocale();
442 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400443 }
444
445 private void updateWidth() {
446 final Resources res = mContext.getResources();
447 final LayoutParams lp = mDialog.getWindow().getAttributes();
448 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
449 lp.gravity =
450 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
451 mDialog.getWindow().setAttributes(lp);
452 }
453
John Spurlockad494bc2014-07-19 15:56:19 -0400454 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
455 pw.println("VolumePanel state:");
456 pw.print(" mTag="); pw.println(mTag);
457 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
458 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock661f2cf2014-11-17 10:29:10 -0500459 pw.print(" mHasVibrator="); pw.println(mHasVibrator);
John Spurlock45601d62014-08-07 17:40:50 -0400460 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400461 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
John Spurlock661f2cf2014-11-17 10:29:10 -0500462 pw.print(" mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
John Spurlockad494bc2014-07-19 15:56:19 -0400463 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
464 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
465 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
466 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
John Spurlockad494bc2014-07-19 15:56:19 -0400467 pw.print(" isShowing()="); pw.println(isShowing());
468 pw.print(" mCallback="); pw.println(mCallback);
469 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400470 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400471 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
472 pw.print(" mStreamControls=");
473 if (mStreamControls == null) {
474 pw.println("null");
475 } else {
476 final int N = mStreamControls.size();
477 pw.print("<size "); pw.print(N); pw.println('>');
478 for (int i = 0; i < N; i++) {
479 final StreamControl sc = mStreamControls.valueAt(i);
480 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
481 if (sc.seekbarView != null) {
482 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
483 pw.print(" of "); pw.print(sc.seekbarView.getMax());
484 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
485 }
486 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
487 pw.println();
488 }
489 }
John Spurlock530052a2014-11-30 16:26:19 -0500490 if (mZenPanel != null) {
491 mZenPanel.dump(fd, pw, args);
492 }
John Spurlockad494bc2014-07-19 15:56:19 -0400493 }
494
John Spurlockeb2727b2014-07-19 23:11:36 -0400495 private void initZenModePanel() {
496 mZenPanel.init(mZenController);
497 mZenPanel.setCallback(new ZenModePanel.Callback() {
498 @Override
499 public void onMoreSettings() {
500 if (mCallback != null) {
501 mCallback.onZenSettings();
502 }
503 }
504
505 @Override
506 public void onInteraction() {
507 resetTimeout();
508 }
509
510 @Override
511 public void onExpanded(boolean expanded) {
512 if (mZenPanelExpanded == expanded) return;
513 mZenPanelExpanded = expanded;
514 updateTimeoutDelay();
515 resetTimeout();
516 }
517 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400518 }
519
John Spurlock86005342014-05-23 11:58:00 -0400520 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800521 mPanel.setLayoutDirection(layoutDirection);
522 updateStates();
523 }
524
John Spurlock8c79d2e2014-07-24 15:15:25 -0400525 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800526 final IntentFilter filter = new IntentFilter();
527 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlockbcc10872014-11-28 15:29:21 -0500528 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400529 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800530 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700531 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800532 public void onReceive(Context context, Intent intent) {
533 final String action = intent.getAction();
534
535 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
536 removeMessages(MSG_RINGER_MODE_CHANGED);
John Spurlockbcc10872014-11-28 15:29:21 -0500537 sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
538 }
539
540 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
541 removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
542 sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800543 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400544
545 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
546 postDismiss(0);
547 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800548 }
549 }, filter);
550 }
551
552 private boolean isMuted(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500553 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700554 // TODO do we need to support a distinct mute property for remote?
555 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400556 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700557 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400558 }
559 }
560
561 private int getStreamMaxVolume(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500562 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700563 if (mStreamControls != null) {
564 StreamControl sc = mStreamControls.get(streamType);
565 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700566 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700567 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700568 }
569 }
570 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400571 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700572 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400573 }
574 }
575
576 private int getStreamVolume(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500577 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700578 if (mStreamControls != null) {
579 StreamControl sc = mStreamControls.get(streamType);
580 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700581 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700582 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700583 }
584 }
585 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400586 } else {
RoboErik5452e252015-02-06 15:33:53 -0800587 return mAudioManager.getLastAudibleStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400588 }
589 }
590
RoboErik19c95182014-06-23 15:38:48 -0700591 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700592 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700593 if (sc.controller != null) {
594 sc.controller.setVolumeTo(index, flags);
595 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700596 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700597 }
598 } else if (getStreamVolume(sc.streamType) != index) {
John Spurlockee5ad722015-03-03 16:17:21 -0500599 mAudioManager.setStreamVolume(sc.streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400600 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800601 }
602
603 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700604 final Resources res = mContext.getResources();
605 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
606 Context.LAYOUT_INFLATER_SERVICE);
607
John Spurlockad494bc2014-07-19 15:56:19 -0400608 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700609
John Spurlockbb4a7022014-11-08 12:40:19 -0500610 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700611 for (int i = 0; i < STREAMS.length; i++) {
612 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700613
614 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500615 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700616
617 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700618 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400619 sc.group = (ViewGroup) inflater.inflate(
620 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800621 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400622 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800623 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700624 sc.icon.setContentDescription(res.getString(streamRes.descRes));
625 sc.iconRes = streamRes.iconRes;
626 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800627 sc.icon.setImageResource(sc.iconRes);
John Spurlock661f2cf2014-11-17 10:29:10 -0500628 sc.icon.setClickable(isNotification && mHasVibrator);
John Spurlockbb4a7022014-11-08 12:40:19 -0500629 if (isNotification) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500630 if (mHasVibrator) {
631 sc.icon.setSoundEffectsEnabled(false);
632 sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
633 sc.icon.setOnClickListener(new OnClickListener() {
634 @Override
635 public void onClick(View v) {
636 resetTimeout();
637 toggleRinger(sc);
638 }
639 });
640 }
John Spurlockb4782522014-08-22 14:54:46 -0400641 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400642 }
John Spurlock86005342014-05-23 11:58:00 -0400643 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400644 sc.suppressorView =
645 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
646 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500647 final boolean showSecondary = !isNotification && notificationStream.show;
648 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
649 sc.secondaryIcon = (ImageView) sc.group
650 .findViewById(com.android.systemui.R.id.secondary_icon);
651 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
652 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
653 sc.secondaryIcon.setClickable(showSecondary);
654 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
655 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
656 if (showSecondary) {
657 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
658 @Override
659 public void onClick(View v) {
660 mSecondaryIconTransition.start(sc);
661 }
662 });
663 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700664 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700665 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400666 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700667 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800668 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700669 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800670 }
671 }
672
John Spurlock661f2cf2014-11-17 10:29:10 -0500673 private void toggleRinger(StreamControl sc) {
674 if (!mHasVibrator) return;
675 if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
676 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400677 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
678 } else {
John Spurlock661f2cf2014-11-17 10:29:10 -0500679 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400680 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
681 }
682 }
683
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800684 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400685 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800686
Alan Viverette494fb7b2014-04-10 18:12:56 -0700687 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800688 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400689 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800690 mActiveStreamType = -1;
691 } else {
John Spurlock86005342014-05-23 11:58:00 -0400692 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800693 mActiveStreamType = activeStreamType;
694 active.group.setVisibility(View.VISIBLE);
John Spurlock530052a2014-11-30 16:26:19 -0500695 updateSlider(active, true /*forceReloadIcon*/);
John Spurlock8845da72014-07-07 21:29:48 -0400696 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400697 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800698 }
699 }
700
John Spurlockad494bc2014-07-19 15:56:19 -0400701 private void updateSliderProgress(StreamControl sc, int progress) {
702 final boolean isRinger = isNotificationOrRing(sc.streamType);
John Spurlock661f2cf2014-11-17 10:29:10 -0500703 if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlockad494bc2014-07-19 15:56:19 -0400704 progress = mLastRingerProgress;
705 }
706 if (progress < 0) {
707 progress = getStreamVolume(sc.streamType);
708 }
709 sc.seekbarView.setProgress(progress);
710 if (isRinger) {
711 mLastRingerProgress = progress;
712 }
713 }
714
715 private void updateSliderIcon(StreamControl sc, boolean muted) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500716 ComponentName suppressor = null;
John Spurlockad494bc2014-07-19 15:56:19 -0400717 if (isNotificationOrRing(sc.streamType)) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500718 suppressor = mNotificationEffectsSuppressor;
719 int ringerMode = mAudioManager.getRingerModeInternal();
John Spurlockad494bc2014-07-19 15:56:19 -0400720 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
721 ringerMode = mLastRingerMode;
722 } else {
723 mLastRingerMode = ringerMode;
724 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500725 if (mHasVibrator) {
726 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
727 } else {
728 muted = false;
729 }
John Spurlockad494bc2014-07-19 15:56:19 -0400730 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500731 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
732 : suppressor != null ? sc.iconSuppressedRes
733 : muted ? sc.iconMuteRes
734 : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400735 }
736
John Spurlock661f2cf2014-11-17 10:29:10 -0500737 private void updateSliderSuppressor(StreamControl sc) {
John Spurlockb4782522014-08-22 14:54:46 -0400738 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
John Spurlock661f2cf2014-11-17 10:29:10 -0500739 ? mNotificationEffectsSuppressor : null;
John Spurlockb4782522014-08-22 14:54:46 -0400740 if (suppressor == null) {
741 sc.seekbarView.setVisibility(View.VISIBLE);
742 sc.suppressorView.setVisibility(View.GONE);
743 } else {
744 sc.seekbarView.setVisibility(View.GONE);
745 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500746 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400747 getSuppressorCaption(suppressor)));
John Spurlockb4782522014-08-22 14:54:46 -0400748 }
749 }
750
751 private String getSuppressorCaption(ComponentName suppressor) {
752 final PackageManager pm = mContext.getPackageManager();
753 try {
754 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
755 if (info != null) {
756 final CharSequence seq = info.loadLabel(pm);
757 if (seq != null) {
758 final String str = seq.toString().trim();
759 if (str.length() > 0) {
760 return str;
761 }
762 }
763 }
764 } catch (Throwable e) {
765 Log.w(TAG, "Error loading suppressor caption", e);
766 }
767 return suppressor.getPackageName();
768 }
769
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800770 /** Update the mute and progress state of a slider */
John Spurlock530052a2014-11-30 16:26:19 -0500771 private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
John Spurlockad494bc2014-07-19 15:56:19 -0400772 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800773 final boolean muted = isMuted(sc.streamType);
John Spurlock530052a2014-11-30 16:26:19 -0500774 if (forceReloadIcon) {
775 sc.icon.setImageDrawable(null);
776 }
John Spurlockad494bc2014-07-19 15:56:19 -0400777 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400778 updateSliderEnabled(sc, muted, false);
John Spurlock661f2cf2014-11-17 10:29:10 -0500779 updateSliderSuppressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400780 }
781
John Spurlock5f640e42014-05-31 20:15:59 -0400782 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
783 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400784 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700785 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700786 // never disable touch interactions for remote playback, the muting is not tied to
787 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700788 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock661f2cf2014-11-17 10:29:10 -0500789 } else if (isRinger && mNotificationEffectsSuppressor != null) {
790 sc.icon.setEnabled(true);
791 sc.icon.setAlpha(1f);
792 sc.icon.setClickable(false);
793 } else if (isRinger
794 && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400795 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400796 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400797 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400798 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400799 } else if (fixedVolume ||
John Spurlockee5ad722015-03-03 16:17:21 -0500800 (sc.streamType != mAudioManager.getUiSoundsStreamType() && !isRinger && muted) ||
John Spurlock4f0f1202014-08-05 13:28:33 -0400801 (sSafetyWarning != null)) {
802 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700803 } else {
804 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400805 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400806 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700807 }
John Spurlockae641c92014-06-30 18:11:40 -0400808 // show the silent hint when the disabled slider is touched in silent mode
809 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400810 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400811 sc.group.setOnTouchListener(null);
John Spurlock661f2cf2014-11-17 10:29:10 -0500812 sc.icon.setClickable(mHasVibrator);
John Spurlock5f640e42014-05-31 20:15:59 -0400813 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400814 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400815 @Override
816 public boolean onTouch(View v, MotionEvent event) {
817 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400818 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400819 return false;
820 }
John Spurlockae641c92014-06-30 18:11:40 -0400821 };
822 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400823 }
824 }
825 }
826
John Spurlockae641c92014-06-30 18:11:40 -0400827 private void showSilentHint() {
828 if (mZenPanel != null) {
829 mZenPanel.showSilentHint();
830 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800831 }
832
John Spurlock661f2cf2014-11-17 10:29:10 -0500833 private void showVibrateHint() {
834 final StreamControl active = mStreamControls.get(mActiveStreamType);
835 if (active != null) {
836 mIconPulser.start(active.icon);
837 if (!hasMessages(MSG_VIBRATE)) {
838 sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
839 }
840 }
841 }
842
John Spurlock7f1df5e2014-05-31 19:11:40 -0400843 private static boolean isNotificationOrRing(int streamType) {
844 return streamType == AudioManager.STREAM_RING
845 || streamType == AudioManager.STREAM_NOTIFICATION;
846 }
847
John Spurlockae641c92014-06-30 18:11:40 -0400848 public void setCallback(Callback callback) {
849 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400850 }
851
John Spurlock8845da72014-07-07 21:29:48 -0400852 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500853 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
854 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400855 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400856 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
857 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
858 : TIMEOUT_DELAY;
859 }
860
861 private boolean isZenPanelVisible() {
862 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400863 }
864
John Spurlockae641c92014-06-30 18:11:40 -0400865 private void setZenPanelVisible(boolean visible) {
866 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400867 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400868 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400869 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400870 resetTimeout();
871 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400872 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800873 }
John Spurlockea9938c2014-07-11 18:51:32 -0400874 if (changing) {
875 updateTimeoutDelay();
876 resetTimeout();
877 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800878 }
879
John Spurlock530052a2014-11-30 16:26:19 -0500880 private void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400881 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800882 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400883 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
John Spurlock530052a2014-11-30 16:26:19 -0500884 updateSlider(sc, true /*forceReloadIcon*/);
885 }
886 }
887
888 private void updateActiveSlider() {
889 final StreamControl active = mStreamControls.get(mActiveStreamType);
890 if (active != null) {
891 updateSlider(active, false /*forceReloadIcon*/);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800892 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893 }
894
John Spurlock45601d62014-08-07 17:40:50 -0400895 private void updateZenPanelVisible() {
896 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400897 }
898
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 public void postVolumeChanged(int streamType, int flags) {
900 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700901 synchronized (this) {
902 if (mStreamControls == null) {
903 createSliders();
904 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800905 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800906 removeMessages(MSG_FREE_RESOURCES);
907 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
908 }
909
RoboErik19c95182014-06-23 15:38:48 -0700910 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700911 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
912 synchronized (this) {
913 if (mStreamControls == null) {
914 createSliders();
915 }
916 }
917 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700918 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700919 }
920
921 public void postRemoteSliderVisibility(boolean visible) {
922 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700923 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700924 }
925
926 /**
927 * Called by AudioService when it has received new remote playback information that
928 * would affect the VolumePanel display (mainly volumes). The difference with
929 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
930 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
931 * displayed.
932 * This special code path is due to the fact that remote volume updates arrive to AudioService
933 * asynchronously. So after AudioService has sent the volume update (which should be treated
934 * as a request to update the volume), the application will likely set a new volume. If the UI
935 * is still up, we need to refresh the display to show this new value.
936 */
937 public void postHasNewRemotePlaybackInfo() {
938 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
939 // don't create or prevent resources to be freed, if they disappear, this update came too
940 // late and shouldn't warrant the panel to be displayed longer
941 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
942 }
943
Mike Lockwoodce952c82011-11-14 10:47:42 -0800944 public void postMuteChanged(int streamType, int flags) {
945 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700946 synchronized (this) {
947 if (mStreamControls == null) {
948 createSliders();
949 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800950 }
951 removeMessages(MSG_FREE_RESOURCES);
952 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
953 }
954
Eric Laurentfde16d52012-12-03 14:42:39 -0800955 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700956 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800957 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700958 }
959
John Spurlock1dad2722014-07-11 11:07:53 -0400960 public void postDismiss(long delay) {
961 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400962 }
963
964 public void postLayoutDirection(int layoutDirection) {
965 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400966 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400967 }
968
John Spurlock661f2cf2014-11-17 10:29:10 -0500969 private static String flagsToString(int flags) {
970 return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
971 }
972
973 private static String streamToString(int stream) {
John Spurlock61560172015-02-06 19:46:04 -0500974 return AudioSystem.streamToString(stream);
John Spurlock661f2cf2014-11-17 10:29:10 -0500975 }
976
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800977 /**
978 * Override this if you have other work to do when the volume changes (for
979 * example, vibrating, playing a sound, etc.). Make sure to call through to
980 * the superclass implementation.
981 */
982 protected void onVolumeChanged(int streamType, int flags) {
983
John Spurlock661f2cf2014-11-17 10:29:10 -0500984 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
985 + ", flags: " + flagsToString(flags) + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800986
987 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700988 synchronized (this) {
989 if (mActiveStreamType != streamType) {
990 reorderSliders(streamType);
991 }
RoboErik19c95182014-06-23 15:38:48 -0700992 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800993 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994 }
995
Marco Nelissen69f593c2009-07-28 09:55:04 -0700996 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 removeMessages(MSG_PLAY_SOUND);
998 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
999 }
1000
1001 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
1002 removeMessages(MSG_PLAY_SOUND);
1003 removeMessages(MSG_VIBRATE);
1004 onStopSounds();
1005 }
1006
1007 removeMessages(MSG_FREE_RESOURCES);
1008 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001009 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 }
1011
Mike Lockwoodce952c82011-11-14 10:47:42 -08001012 protected void onMuteChanged(int streamType, int flags) {
1013
John Spurlock661f2cf2014-11-17 10:29:10 -05001014 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
1015 + ", flags: " + flagsToString(flags) + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -08001016
1017 StreamControl sc = mStreamControls.get(streamType);
1018 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -04001019 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -08001020 }
1021
1022 onVolumeChanged(streamType, flags);
1023 }
1024
RoboErik19c95182014-06-23 15:38:48 -07001025 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001026 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001027
Marco Nelissen69f593c2009-07-28 09:55:04 -07001028 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001029
1030 if (LOGD) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001031 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
1032 + ", flags: " + flagsToString(flags) + "), index: " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 }
1034
1035 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001036
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001037 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001038 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039
1040 switch (streamType) {
1041
1042 case AudioManager.STREAM_RING: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001043 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1044 mContext, RingtoneManager.TYPE_RINGTONE);
1045 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001046 mRingIsSilent = true;
1047 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 break;
1049 }
1050
1051 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001052 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001053 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1054 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1055 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1056 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001057 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001058 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001059 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001060 }
1061 break;
1062 }
1063
1064 case AudioManager.STREAM_VOICE_CALL: {
1065 /*
1066 * For in-call voice call volume, there is no inaudible volume.
1067 * Rescale the UI control so the progress bar doesn't go all
1068 * the way to zero and don't show the mute icon.
1069 */
1070 index++;
1071 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 break;
1073 }
1074
1075 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 break;
1077 }
1078
1079 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001080 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1081 mContext, RingtoneManager.TYPE_NOTIFICATION);
1082 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001083 mRingIsSilent = true;
1084 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 break;
1086 }
1087
1088 case AudioManager.STREAM_BLUETOOTH_SCO: {
1089 /*
1090 * For in-call voice call volume, there is no inaudible volume.
1091 * Rescale the UI control so the progress bar doesn't go all
1092 * the way to zero and don't show the mute icon.
1093 */
1094 index++;
1095 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001096 break;
1097 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001098
RoboErik2811dd32014-08-12 09:48:13 -07001099 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001100 if (controller == null && sc != null) {
1101 // If we weren't passed one try using the last one set.
1102 controller = sc.controller;
1103 }
1104 if (controller == null) {
1105 // We still don't have one, ignore the command.
1106 Log.w(mTag, "sent remote volume change without a controller!");
1107 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001108 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001109 index = vi.getCurrentVolume();
1110 max = vi.getMaxVolume();
1111 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1112 // if the remote volume is fixed add the flag for the UI
1113 flags |= AudioManager.FLAG_FIXED_VOLUME;
1114 }
1115 }
John Spurlock86005342014-05-23 11:58:00 -04001116 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001117 break;
1118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 }
1120
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001121 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001122 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001123 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001124 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001125 }
1126 sc.controller = controller;
1127 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001128 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001129 }
1130 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001131 if (sc.seekbarView.getMax() != max) {
1132 sc.seekbarView.setMax(max);
1133 }
John Spurlockad494bc2014-07-19 15:56:19 -04001134 updateSliderProgress(sc, index);
John Spurlock661f2cf2014-11-17 10:29:10 -05001135 final boolean muted = isMuted(streamType);
1136 updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
1137 if (isNotificationOrRing(streamType)) {
1138 // check for secondary-icon transition completion
1139 if (mSecondaryIconTransition.isRunning()) {
1140 mSecondaryIconTransition.cancel(); // safe to reset
1141 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1142 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1143 }
1144 updateSliderIcon(sc, muted);
John Spurlockbb4a7022014-11-08 12:40:19 -05001145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 }
1147
John Spurlock86005342014-05-23 11:58:00 -04001148 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001149 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001150 // when the stream is for remote playback, use -1 to reset the stream type evaluation
John Spurlockee5ad722015-03-03 16:17:21 -05001151 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001152 mDialog.show();
1153 if (mCallback != null) {
1154 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001155 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001156 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001157 }
1158
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001159 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001160 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001161 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock661f2cf2014-11-17 10:29:10 -05001162 isNotificationOrRing(streamType) &&
1163 mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001164 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1165 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001166
John Spurlock661f2cf2014-11-17 10:29:10 -05001167 // Pulse the zen icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001168 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1169 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001170 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001171
1172 // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
1173 if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
1174 showVibrateHint();
1175 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
1177
Selim Cinek62ea3402014-09-08 12:11:51 +02001178 private void announceDialogShown() {
1179 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1180 }
1181
John Spurlock86005342014-05-23 11:58:00 -04001182 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001183 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001184 }
1185
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 protected void onPlaySound(int streamType, int flags) {
1187
1188 if (hasMessages(MSG_STOP_SOUNDS)) {
1189 removeMessages(MSG_STOP_SOUNDS);
1190 // Force stop right now
1191 onStopSounds();
1192 }
1193
1194 synchronized (this) {
1195 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001196 if (toneGen != null) {
1197 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1198 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1199 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
1202
1203 protected void onStopSounds() {
1204
1205 synchronized (this) {
1206 int numStreamTypes = AudioSystem.getNumStreamTypes();
1207 for (int i = numStreamTypes - 1; i >= 0; i--) {
1208 ToneGenerator toneGen = mToneGenerators[i];
1209 if (toneGen != null) {
1210 toneGen.stopTone();
1211 }
1212 }
1213 }
1214 }
1215
1216 protected void onVibrate() {
1217
1218 // Make sure we ended up in vibrate ringer mode
John Spurlock661f2cf2014-11-17 10:29:10 -05001219 if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001220 return;
1221 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001222 if (mVibrator != null) {
1223 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
1224 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001225 }
1226
RoboErik19c95182014-06-23 15:38:48 -07001227 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001228 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
1229 + flagsToString(flags) + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001230
John Spurlock86005342014-05-23 11:58:00 -04001231 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001232 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001233 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1234 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001235 }
RoboErik2811dd32014-08-12 09:48:13 -07001236 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001237 }
1238 } else {
John Spurlock86005342014-05-23 11:58:00 -04001239 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001240 }
1241
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001242 removeMessages(MSG_FREE_RESOURCES);
1243 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001244 resetTimeout();
1245 }
1246
1247 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001248 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1249 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001250 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001251 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001252 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001253 }
1254 }
1255
RoboErik19c95182014-06-23 15:38:48 -07001256 /**
1257 * Clear the current remote stream controller.
1258 */
1259 private void clearRemoteStreamController() {
1260 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001261 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001262 if (sc != null) {
1263 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001264 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001265 sc.controller = null;
1266 }
1267 }
1268 }
1269 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001270
1271 /**
RoboErik2811dd32014-08-12 09:48:13 -07001272 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1273 *
1274 * @param streamType can be a valid stream type value, or
1275 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001276 * @param visible
1277 */
1278 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001279 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001280 boolean isVisible = (visible == 1);
1281 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1282 StreamResources streamRes = STREAMS[i];
1283 if (streamRes.streamType == streamType) {
1284 streamRes.show = isVisible;
1285 if (!isVisible && (mActiveStreamType == streamType)) {
1286 mActiveStreamType = -1;
1287 }
1288 break;
1289 }
1290 }
1291 }
1292
Eric Laurentfde16d52012-12-03 14:42:39 -08001293 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001294 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1295 || isShowing()) {
1296 synchronized (sSafetyWarningLock) {
1297 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001298 return;
1299 }
John Spurlock35134602014-07-24 18:10:48 -04001300 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1301 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001302 }
1303 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001304 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001305 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1306 removeMessages(MSG_TIMEOUT);
1307 } else {
1308 updateTimeoutDelay();
1309 resetTimeout();
1310 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001311 }
1312
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 /**
1314 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1315 */
1316 private ToneGenerator getOrCreateToneGenerator(int streamType) {
1317 synchronized (this) {
1318 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001319 try {
1320 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1321 } catch (RuntimeException e) {
1322 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001323 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001324 + "RuntimeException: " + e);
1325 }
1326 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001327 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001328 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 }
1330 }
1331
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001332
1333 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001334 * Switch between icons because Bluetooth music is same as music volume, but with
1335 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001336 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001337 private void setMusicIcon(int resId, int resMuteId) {
1338 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1339 if (sc != null) {
1340 sc.iconRes = resId;
1341 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001342 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001343 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001344 }
1345
1346 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001347 synchronized (this) {
1348 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1349 if (mToneGenerators[i] != null) {
1350 mToneGenerators[i].release();
1351 }
1352 mToneGenerators[i] = null;
1353 }
1354 }
1355 }
1356
1357 @Override
1358 public void handleMessage(Message msg) {
1359 switch (msg.what) {
1360
1361 case MSG_VOLUME_CHANGED: {
1362 onVolumeChanged(msg.arg1, msg.arg2);
1363 break;
1364 }
1365
Mike Lockwoodce952c82011-11-14 10:47:42 -08001366 case MSG_MUTE_CHANGED: {
1367 onMuteChanged(msg.arg1, msg.arg2);
1368 break;
1369 }
1370
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001371 case MSG_FREE_RESOURCES: {
1372 onFreeResources();
1373 break;
1374 }
1375
1376 case MSG_STOP_SOUNDS: {
1377 onStopSounds();
1378 break;
1379 }
1380
1381 case MSG_PLAY_SOUND: {
1382 onPlaySound(msg.arg1, msg.arg2);
1383 break;
1384 }
1385
1386 case MSG_VIBRATE: {
1387 onVibrate();
1388 break;
1389 }
1390
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001391 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001392 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001393 mDialog.dismiss();
1394 clearRemoteStreamController();
1395 mActiveStreamType = -1;
1396 if (mCallback != null) {
1397 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001398 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001399 }
John Spurlock35134602014-07-24 18:10:48 -04001400 synchronized (sSafetyWarningLock) {
1401 if (sSafetyWarning != null) {
1402 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1403 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001404 }
1405 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001406 break;
1407 }
John Spurlockb4782522014-08-22 14:54:46 -04001408
1409 case MSG_RINGER_MODE_CHANGED:
John Spurlock661f2cf2014-11-17 10:29:10 -05001410 case MSG_INTERNAL_RINGER_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001411 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001412 if (isShowing()) {
John Spurlock530052a2014-11-30 16:26:19 -05001413 updateActiveSlider();
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001414 }
1415 break;
1416 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001417
1418 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001419 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001420 break;
1421 }
1422
1423 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1424 onRemoteVolumeUpdateIfShown();
1425 break;
1426
1427 case MSG_SLIDER_VISIBILITY_CHANGED:
1428 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1429 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001430
1431 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001432 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001433 break;
John Spurlock86005342014-05-23 11:58:00 -04001434
1435 case MSG_LAYOUT_DIRECTION:
1436 setLayoutDirection(msg.arg1);
1437 break;
1438
John Spurlock45601d62014-08-07 17:40:50 -04001439 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1440 mZenModeAvailable = msg.arg1 != 0;
1441 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001442 break;
1443
1444 case MSG_USER_ACTIVITY:
1445 if (mCallback != null) {
1446 mCallback.onInteraction();
1447 }
John Spurlock86005342014-05-23 11:58:00 -04001448 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001449 }
1450 }
1451
John Spurlockae641c92014-06-30 18:11:40 -04001452 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001453 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001454 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001455 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1456 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001457 removeMessages(MSG_TIMEOUT);
1458 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1459 removeMessages(MSG_USER_ACTIVITY);
1460 sendEmptyMessage(MSG_USER_ACTIVITY);
1461 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001462 }
1463
John Spurlock1dad2722014-07-11 11:07:53 -04001464 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001465 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001466 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001467 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001468 }
1469
1470 public ZenModeController getZenController() {
1471 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001472 }
1473
John Spurlockbb4a7022014-11-08 12:40:19 -05001474 @Override
1475 public void dispatchDemoCommand(String command, Bundle args) {
1476 if (!COMMAND_VOLUME.equals(command)) return;
1477 String icon = args.getString("icon");
1478 final String iconMute = args.getString("iconmute");
1479 final boolean mute = iconMute != null;
1480 icon = mute ? iconMute : icon;
1481 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1482 final StreamResources sr = StreamResources.valueOf(icon);
1483 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1484 final int forcedStreamType = StreamResources.MediaStream.streamType;
1485 mAudioManager.forceVolumeControlStream(forcedStreamType);
1486 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1487 AudioManager.FLAG_SHOW_UI);
1488 }
1489
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001490 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1491 @Override
1492 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1493 final Object tag = seekBar.getTag();
1494 if (fromUser && tag instanceof StreamControl) {
1495 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001496 setStreamVolume(sc, progress,
1497 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001498 }
1499 resetTimeout();
1500 }
1501
1502 @Override
1503 public void onStartTrackingTouch(SeekBar seekBar) {
1504 }
1505
1506 @Override
1507 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001508 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001509 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001510
John Spurlock86005342014-05-23 11:58:00 -04001511 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001512 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001513 public void onZenAvailableChanged(boolean available) {
1514 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001515 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001516
John Spurlockb4782522014-08-22 14:54:46 -04001517 @Override
1518 public void onEffectsSupressorChanged() {
John Spurlock661f2cf2014-11-17 10:29:10 -05001519 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
1520 sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
1521 }
John Spurlock86005342014-05-23 11:58:00 -04001522 };
RoboErik19c95182014-06-23 15:38:48 -07001523
1524 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001525 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001526 onRemoteVolumeUpdateIfShown();
1527 }
1528 };
John Spurlockae641c92014-06-30 18:11:40 -04001529
John Spurlockbb4a7022014-11-08 12:40:19 -05001530 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1531 implements Runnable {
1532 private static final int ANIMATION_TIME = 400;
1533 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1534
1535 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1536 private final int mFadeOutTime = mAnimationTime / 2;
1537 private final int mDelayTime = mAnimationTime / 3;
1538
1539 private final Interpolator mIconInterpolator =
1540 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1541
1542 private StreamControl mTarget;
1543
1544 public void start(StreamControl sc) {
1545 if (sc == null) throw new IllegalArgumentException();
John Spurlock530052a2014-11-30 16:26:19 -05001546 if (LOGD) Log.d(mTag, "Secondary icon animation start");
John Spurlockbb4a7022014-11-08 12:40:19 -05001547 if (mTarget != null) {
1548 cancel();
1549 }
1550 mTarget = sc;
1551 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1552 resetTimeout();
1553 mTarget.secondaryIcon.setClickable(false);
1554 final int N = mTarget.group.getChildCount();
1555 for (int i = 0; i < N; i++) {
1556 final View child = mTarget.group.getChildAt(i);
1557 if (child != mTarget.secondaryIcon) {
1558 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1559 }
1560 }
1561 mTarget.secondaryIcon.animate()
1562 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1563 .setInterpolator(mIconInterpolator)
1564 .setStartDelay(mDelayTime)
1565 .setDuration(mAnimationTime - mDelayTime)
1566 .setListener(this)
1567 .start();
1568 }
1569
1570 public boolean isRunning() {
1571 return mTarget != null;
1572 }
1573
1574 public void cancel() {
1575 if (mTarget == null) return;
1576 mTarget.secondaryIcon.setClickable(true);
1577 final int N = mTarget.group.getChildCount();
1578 for (int i = 0; i < N; i++) {
1579 final View child = mTarget.group.getChildAt(i);
1580 if (child != mTarget.secondaryIcon) {
1581 child.animate().cancel();
1582 child.setAlpha(1);
1583 }
1584 }
1585 mTarget.secondaryIcon.animate().cancel();
1586 mTarget.secondaryIcon.setTranslationX(0);
1587 mTarget = null;
1588 }
1589
1590 @Override
1591 public void onAnimationEnd(Animator animation) {
1592 if (mTarget == null) return;
1593 AsyncTask.execute(this);
1594 }
1595
1596 @Override
1597 public void run() {
1598 if (mTarget == null) return;
John Spurlock530052a2014-11-30 16:26:19 -05001599 if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
John Spurlockbb4a7022014-11-08 12:40:19 -05001600 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1601 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1602 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1603 }
1604 }
1605
John Spurlockae641c92014-06-30 18:11:40 -04001606 public interface Callback {
1607 void onZenSettings();
1608 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001609 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001610 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001611}