blob: d16b8181be18dbf2046e59f20bf2208734c062b9 [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;
John Spurlockb6e19e32015-03-10 21:33:44 -0400256 int minVolume;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800257 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258
259 // Synchronize when accessing this
260 private ToneGenerator mToneGenerators[];
261 private Vibrator mVibrator;
John Spurlock661f2cf2014-11-17 10:29:10 -0500262 private boolean mHasVibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800263
John Spurlock35134602014-07-24 18:10:48 -0400264 private static AlertDialog sSafetyWarning;
265 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700266
John Spurlock35134602014-07-24 18:10:48 -0400267 private static class SafetyWarning extends SystemUIDialog
268 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800269 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800270 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400271 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700272
John Spurlock2d28d6e2014-08-01 13:10:14 -0400273 private boolean mNewVolumeUp;
274
John Spurlock35134602014-07-24 18:10:48 -0400275 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
276 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700277 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800278 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400279 mAudioManager = audioManager;
280
281 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
282 setButton(DialogInterface.BUTTON_POSITIVE,
283 mContext.getString(com.android.internal.R.string.yes), this);
284 setButton(DialogInterface.BUTTON_NEGATIVE,
285 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
286 setOnDismissListener(this);
287
Eric Laurentc34dcc12012-09-10 13:51:52 -0700288 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400289 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700290 }
291
292 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400293 public boolean onKeyDown(int keyCode, KeyEvent event) {
294 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
295 mNewVolumeUp = true;
296 }
297 return super.onKeyDown(keyCode, event);
298 }
299
300 @Override
301 public boolean onKeyUp(int keyCode, KeyEvent event) {
302 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
303 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
304 mAudioManager.disableSafeMediaVolume();
305 dismiss();
306 }
307 return super.onKeyUp(keyCode, event);
308 }
309
310 @Override
John Spurlock35134602014-07-24 18:10:48 -0400311 public void onClick(DialogInterface dialog, int which) {
312 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700313 }
314
Alan Viverette494fb7b2014-04-10 18:12:56 -0700315 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700316 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400317 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800318 cleanUp();
319 }
320
321 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400322 synchronized (sSafetyWarningLock) {
323 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700324 }
John Spurlock1dad2722014-07-11 11:07:53 -0400325 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800326 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700327 }
John Spurlock35134602014-07-24 18:10:48 -0400328
329 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
330 @Override
331 public void onReceive(Context context, Intent intent) {
332 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
333 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
334 cancel();
335 cleanUp();
336 }
337 }
338 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700339 }
340
John Spurlockad680d42015-01-30 15:48:15 -0500341 protected LayoutParams getDialogLayoutParams(Window window, Resources res) {
342 final LayoutParams lp = window.getAttributes();
343 lp.token = null;
344 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
345 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
346 lp.format = PixelFormat.TRANSLUCENT;
347 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
348 lp.setTitle(TAG);
349 return lp;
350 }
351
John Spurlockeb2727b2014-07-19 23:11:36 -0400352 public VolumePanel(Context context, ZenModeController zenController) {
353 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800354 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400355 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800356 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200357 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
358 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500359 mSecondaryIconTransition = new SecondaryIconTransition();
John Spurlock661f2cf2014-11-17 10:29:10 -0500360 mIconPulser = new IconPulser(context);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361
John Spurlockeb2727b2014-07-19 23:11:36 -0400362 if (LOGD) Log.d(mTag, "new VolumePanel");
363
John Spurlockad494bc2014-07-19 15:56:19 -0400364 mDisabledAlpha = 0.5f;
365 if (mContext.getTheme() != null) {
366 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
367 new int[] { android.R.attr.disabledAlpha });
368 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
369 arr.recycle();
370 }
371
John Spurlockeb2727b2014-07-19 23:11:36 -0400372 mDialog = new Dialog(context) {
373 @Override
374 public boolean onTouchEvent(MotionEvent event) {
375 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400376 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400377 forceTimeout(0);
378 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700379 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400380 return false;
381 }
382 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700383
John Spurlockeb2727b2014-07-19 23:11:36 -0400384 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400385 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400386 mDialog.setCanceledOnTouchOutside(true);
387 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
388 mDialog.setOnDismissListener(new OnDismissListener() {
389 @Override
390 public void onDismiss(DialogInterface dialog) {
391 mActiveStreamType = -1;
392 mAudioManager.forceVolumeControlStream(mActiveStreamType);
393 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500394 mDemoIcon = 0;
395 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400396 }
397 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700398
John Spurlockeb2727b2014-07-19 23:11:36 -0400399 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700400
John Spurlockee5ad722015-03-03 16:17:21 -0500401 final Resources res = context.getResources();
John Spurlockad680d42015-01-30 15:48:15 -0500402 window.setAttributes(getDialogLayoutParams(window, res));
John Spurlock7e6809a2014-08-06 16:03:14 -0400403
404 updateWidth();
405
406 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
407 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
408 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
409 | LayoutParams.FLAG_NOT_TOUCH_MODAL
410 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
411 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400412 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400413 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400414 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400415 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400416 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400417 }
418 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700419
John Spurlock86005342014-05-23 11:58:00 -0400420 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
421 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400422 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
423 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800424
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700426 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
John Spurlock661f2cf2014-11-17 10:29:10 -0500427 mHasVibrator = mVibrator != null && mVibrator.hasVibrator();
Amith Yamasani71def772011-10-12 12:25:24 -0700428 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700429
John Spurlockee5ad722015-03-03 16:17:21 -0500430 if (mZenController != null) {
John Spurlock45601d62014-08-07 17:40:50 -0400431 mZenModeAvailable = mZenController.isZenAvailable();
John Spurlock661f2cf2014-11-17 10:29:10 -0500432 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
John Spurlock45601d62014-08-07 17:40:50 -0400433 mZenController.addCallback(mZenCallback);
434 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700435
John Spurlock8c79d2e2014-07-24 15:15:25 -0400436 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800437 }
438
John Spurlock7e6809a2014-08-06 16:03:14 -0400439 public void onConfigurationChanged(Configuration newConfig) {
440 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400441 if (mZenPanel != null) {
442 mZenPanel.updateLocale();
443 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400444 }
445
446 private void updateWidth() {
447 final Resources res = mContext.getResources();
448 final LayoutParams lp = mDialog.getWindow().getAttributes();
449 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
450 lp.gravity =
451 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
452 mDialog.getWindow().setAttributes(lp);
453 }
454
John Spurlockad494bc2014-07-19 15:56:19 -0400455 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
456 pw.println("VolumePanel state:");
457 pw.print(" mTag="); pw.println(mTag);
458 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
459 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock661f2cf2014-11-17 10:29:10 -0500460 pw.print(" mHasVibrator="); pw.println(mHasVibrator);
John Spurlock45601d62014-08-07 17:40:50 -0400461 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400462 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
John Spurlock661f2cf2014-11-17 10:29:10 -0500463 pw.print(" mNotificationEffectsSuppressor="); pw.println(mNotificationEffectsSuppressor);
John Spurlockad494bc2014-07-19 15:56:19 -0400464 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
465 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
466 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
467 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
John Spurlockad494bc2014-07-19 15:56:19 -0400468 pw.print(" isShowing()="); pw.println(isShowing());
469 pw.print(" mCallback="); pw.println(mCallback);
470 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400471 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400472 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
473 pw.print(" mStreamControls=");
474 if (mStreamControls == null) {
475 pw.println("null");
476 } else {
477 final int N = mStreamControls.size();
478 pw.print("<size "); pw.print(N); pw.println('>');
479 for (int i = 0; i < N; i++) {
480 final StreamControl sc = mStreamControls.valueAt(i);
481 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
482 if (sc.seekbarView != null) {
483 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
484 pw.print(" of "); pw.print(sc.seekbarView.getMax());
485 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
486 }
487 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
488 pw.println();
489 }
490 }
John Spurlock530052a2014-11-30 16:26:19 -0500491 if (mZenPanel != null) {
492 mZenPanel.dump(fd, pw, args);
493 }
John Spurlockad494bc2014-07-19 15:56:19 -0400494 }
495
John Spurlockeb2727b2014-07-19 23:11:36 -0400496 private void initZenModePanel() {
497 mZenPanel.init(mZenController);
498 mZenPanel.setCallback(new ZenModePanel.Callback() {
499 @Override
500 public void onMoreSettings() {
501 if (mCallback != null) {
502 mCallback.onZenSettings();
503 }
504 }
505
506 @Override
507 public void onInteraction() {
508 resetTimeout();
509 }
510
511 @Override
512 public void onExpanded(boolean expanded) {
513 if (mZenPanelExpanded == expanded) return;
514 mZenPanelExpanded = expanded;
515 updateTimeoutDelay();
516 resetTimeout();
517 }
518 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400519 }
520
John Spurlock86005342014-05-23 11:58:00 -0400521 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800522 mPanel.setLayoutDirection(layoutDirection);
523 updateStates();
524 }
525
John Spurlock8c79d2e2014-07-24 15:15:25 -0400526 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800527 final IntentFilter filter = new IntentFilter();
528 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlockbcc10872014-11-28 15:29:21 -0500529 filter.addAction(AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400530 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800531 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700532 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800533 public void onReceive(Context context, Intent intent) {
534 final String action = intent.getAction();
535
536 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
537 removeMessages(MSG_RINGER_MODE_CHANGED);
John Spurlockbcc10872014-11-28 15:29:21 -0500538 sendEmptyMessage(MSG_RINGER_MODE_CHANGED);
539 }
540
541 if (AudioManager.INTERNAL_RINGER_MODE_CHANGED_ACTION.equals(action)) {
542 removeMessages(MSG_INTERNAL_RINGER_MODE_CHANGED);
543 sendEmptyMessage(MSG_INTERNAL_RINGER_MODE_CHANGED);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800544 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400545
546 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
547 postDismiss(0);
548 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800549 }
550 }, filter);
551 }
552
553 private boolean isMuted(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500554 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700555 // TODO do we need to support a distinct mute property for remote?
556 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400557 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700558 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400559 }
560 }
561
John Spurlockb6e19e32015-03-10 21:33:44 -0400562 private int getStreamMinVolume(int streamType) {
563 if (streamType == STREAM_REMOTE_MUSIC) {
564 return 0;
565 } else {
566 return mAudioManager.getStreamMinVolume(streamType);
567 }
568 }
569
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400570 private int getStreamMaxVolume(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500571 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700572 if (mStreamControls != null) {
573 StreamControl sc = mStreamControls.get(streamType);
574 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700575 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700576 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700577 }
578 }
579 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400580 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700581 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400582 }
583 }
584
585 private int getStreamVolume(int streamType) {
John Spurlockee5ad722015-03-03 16:17:21 -0500586 if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700587 if (mStreamControls != null) {
588 StreamControl sc = mStreamControls.get(streamType);
589 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700590 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700591 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700592 }
593 }
594 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400595 } else {
RoboErik5452e252015-02-06 15:33:53 -0800596 return mAudioManager.getLastAudibleStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400597 }
598 }
599
RoboErik19c95182014-06-23 15:38:48 -0700600 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700601 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700602 if (sc.controller != null) {
603 sc.controller.setVolumeTo(index, flags);
604 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700605 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700606 }
607 } else if (getStreamVolume(sc.streamType) != index) {
John Spurlockee5ad722015-03-03 16:17:21 -0500608 mAudioManager.setStreamVolume(sc.streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400609 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800610 }
611
612 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700613 final Resources res = mContext.getResources();
614 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
615 Context.LAYOUT_INFLATER_SERVICE);
616
John Spurlockad494bc2014-07-19 15:56:19 -0400617 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700618
John Spurlockbb4a7022014-11-08 12:40:19 -0500619 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700620 for (int i = 0; i < STREAMS.length; i++) {
621 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700622
623 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500624 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700625
626 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700627 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400628 sc.group = (ViewGroup) inflater.inflate(
629 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800630 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400631 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800632 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700633 sc.icon.setContentDescription(res.getString(streamRes.descRes));
634 sc.iconRes = streamRes.iconRes;
635 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800636 sc.icon.setImageResource(sc.iconRes);
John Spurlock661f2cf2014-11-17 10:29:10 -0500637 sc.icon.setClickable(isNotification && mHasVibrator);
John Spurlockbb4a7022014-11-08 12:40:19 -0500638 if (isNotification) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500639 if (mHasVibrator) {
640 sc.icon.setSoundEffectsEnabled(false);
641 sc.iconMuteRes = com.android.systemui.R.drawable.ic_ringer_vibrate;
642 sc.icon.setOnClickListener(new OnClickListener() {
643 @Override
644 public void onClick(View v) {
645 resetTimeout();
646 toggleRinger(sc);
647 }
648 });
649 }
John Spurlockb4782522014-08-22 14:54:46 -0400650 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400651 }
John Spurlock86005342014-05-23 11:58:00 -0400652 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400653 sc.suppressorView =
654 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
655 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500656 final boolean showSecondary = !isNotification && notificationStream.show;
657 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
658 sc.secondaryIcon = (ImageView) sc.group
659 .findViewById(com.android.systemui.R.id.secondary_icon);
660 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
661 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
662 sc.secondaryIcon.setClickable(showSecondary);
663 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
664 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
665 if (showSecondary) {
666 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
667 @Override
668 public void onClick(View v) {
669 mSecondaryIconTransition.start(sc);
670 }
671 });
672 }
John Spurlockb6e19e32015-03-10 21:33:44 -0400673 sc.minVolume = getStreamMinVolume(streamType);
674 sc.seekbarView.setMax(getStreamMaxVolume(streamType) - sc.minVolume);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700675 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800676 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700677 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800678 }
679 }
680
John Spurlock661f2cf2014-11-17 10:29:10 -0500681 private void toggleRinger(StreamControl sc) {
682 if (!mHasVibrator) return;
683 if (mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_NORMAL) {
684 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_VIBRATE);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400685 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
686 } else {
John Spurlock661f2cf2014-11-17 10:29:10 -0500687 mAudioManager.setRingerModeInternal(AudioManager.RINGER_MODE_NORMAL);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400688 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
689 }
690 }
691
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800692 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400693 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800694
Alan Viverette494fb7b2014-04-10 18:12:56 -0700695 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800696 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400697 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800698 mActiveStreamType = -1;
699 } else {
John Spurlock86005342014-05-23 11:58:00 -0400700 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800701 mActiveStreamType = activeStreamType;
702 active.group.setVisibility(View.VISIBLE);
John Spurlock530052a2014-11-30 16:26:19 -0500703 updateSlider(active, true /*forceReloadIcon*/);
John Spurlock8845da72014-07-07 21:29:48 -0400704 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400705 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800706 }
707 }
708
John Spurlockad494bc2014-07-19 15:56:19 -0400709 private void updateSliderProgress(StreamControl sc, int progress) {
710 final boolean isRinger = isNotificationOrRing(sc.streamType);
John Spurlock661f2cf2014-11-17 10:29:10 -0500711 if (isRinger && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlockad494bc2014-07-19 15:56:19 -0400712 progress = mLastRingerProgress;
713 }
714 if (progress < 0) {
715 progress = getStreamVolume(sc.streamType);
716 }
John Spurlockb6e19e32015-03-10 21:33:44 -0400717 sc.seekbarView.setProgress(progress - sc.minVolume);
John Spurlockad494bc2014-07-19 15:56:19 -0400718 if (isRinger) {
719 mLastRingerProgress = progress;
720 }
721 }
722
723 private void updateSliderIcon(StreamControl sc, boolean muted) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500724 ComponentName suppressor = null;
John Spurlockad494bc2014-07-19 15:56:19 -0400725 if (isNotificationOrRing(sc.streamType)) {
John Spurlock661f2cf2014-11-17 10:29:10 -0500726 suppressor = mNotificationEffectsSuppressor;
727 int ringerMode = mAudioManager.getRingerModeInternal();
John Spurlockad494bc2014-07-19 15:56:19 -0400728 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
729 ringerMode = mLastRingerMode;
730 } else {
731 mLastRingerMode = ringerMode;
732 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500733 if (mHasVibrator) {
734 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
735 } else {
736 muted = false;
737 }
John Spurlockad494bc2014-07-19 15:56:19 -0400738 }
John Spurlock661f2cf2014-11-17 10:29:10 -0500739 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon
740 : suppressor != null ? sc.iconSuppressedRes
741 : muted ? sc.iconMuteRes
742 : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400743 }
744
John Spurlock661f2cf2014-11-17 10:29:10 -0500745 private void updateSliderSuppressor(StreamControl sc) {
John Spurlockb4782522014-08-22 14:54:46 -0400746 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
John Spurlock661f2cf2014-11-17 10:29:10 -0500747 ? mNotificationEffectsSuppressor : null;
John Spurlockb4782522014-08-22 14:54:46 -0400748 if (suppressor == null) {
749 sc.seekbarView.setVisibility(View.VISIBLE);
750 sc.suppressorView.setVisibility(View.GONE);
751 } else {
752 sc.seekbarView.setVisibility(View.GONE);
753 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500754 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400755 getSuppressorCaption(suppressor)));
John Spurlockb4782522014-08-22 14:54:46 -0400756 }
757 }
758
759 private String getSuppressorCaption(ComponentName suppressor) {
760 final PackageManager pm = mContext.getPackageManager();
761 try {
762 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
763 if (info != null) {
764 final CharSequence seq = info.loadLabel(pm);
765 if (seq != null) {
766 final String str = seq.toString().trim();
767 if (str.length() > 0) {
768 return str;
769 }
770 }
771 }
772 } catch (Throwable e) {
773 Log.w(TAG, "Error loading suppressor caption", e);
774 }
775 return suppressor.getPackageName();
776 }
777
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800778 /** Update the mute and progress state of a slider */
John Spurlock530052a2014-11-30 16:26:19 -0500779 private void updateSlider(StreamControl sc, boolean forceReloadIcon) {
John Spurlockad494bc2014-07-19 15:56:19 -0400780 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800781 final boolean muted = isMuted(sc.streamType);
John Spurlock530052a2014-11-30 16:26:19 -0500782 if (forceReloadIcon) {
783 sc.icon.setImageDrawable(null);
784 }
John Spurlockad494bc2014-07-19 15:56:19 -0400785 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400786 updateSliderEnabled(sc, muted, false);
John Spurlock661f2cf2014-11-17 10:29:10 -0500787 updateSliderSuppressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400788 }
789
John Spurlock5f640e42014-05-31 20:15:59 -0400790 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
791 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400792 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700793 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700794 // never disable touch interactions for remote playback, the muting is not tied to
795 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700796 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock661f2cf2014-11-17 10:29:10 -0500797 } else if (isRinger && mNotificationEffectsSuppressor != null) {
798 sc.icon.setEnabled(true);
799 sc.icon.setAlpha(1f);
800 sc.icon.setClickable(false);
801 } else if (isRinger
802 && mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400803 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400804 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400805 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400806 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400807 } else if (fixedVolume ||
John Spurlockee5ad722015-03-03 16:17:21 -0500808 (sc.streamType != mAudioManager.getUiSoundsStreamType() && !isRinger && muted) ||
John Spurlock4f0f1202014-08-05 13:28:33 -0400809 (sSafetyWarning != null)) {
810 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700811 } else {
812 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400813 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400814 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700815 }
John Spurlockae641c92014-06-30 18:11:40 -0400816 // show the silent hint when the disabled slider is touched in silent mode
817 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400818 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400819 sc.group.setOnTouchListener(null);
John Spurlock661f2cf2014-11-17 10:29:10 -0500820 sc.icon.setClickable(mHasVibrator);
John Spurlock5f640e42014-05-31 20:15:59 -0400821 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400822 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400823 @Override
824 public boolean onTouch(View v, MotionEvent event) {
825 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400826 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400827 return false;
828 }
John Spurlockae641c92014-06-30 18:11:40 -0400829 };
830 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400831 }
832 }
833 }
834
John Spurlockae641c92014-06-30 18:11:40 -0400835 private void showSilentHint() {
836 if (mZenPanel != null) {
837 mZenPanel.showSilentHint();
838 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800839 }
840
John Spurlock661f2cf2014-11-17 10:29:10 -0500841 private void showVibrateHint() {
842 final StreamControl active = mStreamControls.get(mActiveStreamType);
843 if (active != null) {
844 mIconPulser.start(active.icon);
845 if (!hasMessages(MSG_VIBRATE)) {
846 sendEmptyMessageDelayed(MSG_VIBRATE, VIBRATE_DELAY);
847 }
848 }
849 }
850
John Spurlock7f1df5e2014-05-31 19:11:40 -0400851 private static boolean isNotificationOrRing(int streamType) {
852 return streamType == AudioManager.STREAM_RING
853 || streamType == AudioManager.STREAM_NOTIFICATION;
854 }
855
John Spurlockae641c92014-06-30 18:11:40 -0400856 public void setCallback(Callback callback) {
857 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400858 }
859
John Spurlock8845da72014-07-07 21:29:48 -0400860 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500861 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
862 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400863 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400864 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
865 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
866 : TIMEOUT_DELAY;
867 }
868
869 private boolean isZenPanelVisible() {
870 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400871 }
872
John Spurlockae641c92014-06-30 18:11:40 -0400873 private void setZenPanelVisible(boolean visible) {
874 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400875 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400876 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400877 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400878 resetTimeout();
879 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400880 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800881 }
John Spurlockea9938c2014-07-11 18:51:32 -0400882 if (changing) {
883 updateTimeoutDelay();
884 resetTimeout();
885 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800886 }
887
John Spurlock530052a2014-11-30 16:26:19 -0500888 private void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400889 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800890 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400891 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
John Spurlock530052a2014-11-30 16:26:19 -0500892 updateSlider(sc, true /*forceReloadIcon*/);
893 }
894 }
895
896 private void updateActiveSlider() {
897 final StreamControl active = mStreamControls.get(mActiveStreamType);
898 if (active != null) {
899 updateSlider(active, false /*forceReloadIcon*/);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800900 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 }
902
John Spurlock45601d62014-08-07 17:40:50 -0400903 private void updateZenPanelVisible() {
904 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400905 }
906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 public void postVolumeChanged(int streamType, int flags) {
908 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700909 synchronized (this) {
910 if (mStreamControls == null) {
911 createSliders();
912 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800913 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800914 removeMessages(MSG_FREE_RESOURCES);
915 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
916 }
917
RoboErik19c95182014-06-23 15:38:48 -0700918 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700919 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
920 synchronized (this) {
921 if (mStreamControls == null) {
922 createSliders();
923 }
924 }
925 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700926 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700927 }
928
929 public void postRemoteSliderVisibility(boolean visible) {
930 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700931 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700932 }
933
934 /**
935 * Called by AudioService when it has received new remote playback information that
936 * would affect the VolumePanel display (mainly volumes). The difference with
937 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
938 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
939 * displayed.
940 * This special code path is due to the fact that remote volume updates arrive to AudioService
941 * asynchronously. So after AudioService has sent the volume update (which should be treated
942 * as a request to update the volume), the application will likely set a new volume. If the UI
943 * is still up, we need to refresh the display to show this new value.
944 */
945 public void postHasNewRemotePlaybackInfo() {
946 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
947 // don't create or prevent resources to be freed, if they disappear, this update came too
948 // late and shouldn't warrant the panel to be displayed longer
949 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
950 }
951
Mike Lockwoodce952c82011-11-14 10:47:42 -0800952 public void postMuteChanged(int streamType, int flags) {
953 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700954 synchronized (this) {
955 if (mStreamControls == null) {
956 createSliders();
957 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800958 }
959 removeMessages(MSG_FREE_RESOURCES);
960 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
961 }
962
Eric Laurentfde16d52012-12-03 14:42:39 -0800963 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700964 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800965 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700966 }
967
John Spurlock1dad2722014-07-11 11:07:53 -0400968 public void postDismiss(long delay) {
969 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400970 }
971
972 public void postLayoutDirection(int layoutDirection) {
973 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400974 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400975 }
976
John Spurlock661f2cf2014-11-17 10:29:10 -0500977 private static String flagsToString(int flags) {
978 return flags == 0 ? "0" : (flags + "=" + AudioManager.flagsToString(flags));
979 }
980
981 private static String streamToString(int stream) {
John Spurlock61560172015-02-06 19:46:04 -0500982 return AudioSystem.streamToString(stream);
John Spurlock661f2cf2014-11-17 10:29:10 -0500983 }
984
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 /**
986 * Override this if you have other work to do when the volume changes (for
987 * example, vibrating, playing a sound, etc.). Make sure to call through to
988 * the superclass implementation.
989 */
990 protected void onVolumeChanged(int streamType, int flags) {
991
John Spurlock661f2cf2014-11-17 10:29:10 -0500992 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamToString(streamType)
993 + ", flags: " + flagsToString(flags) + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800994
995 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700996 synchronized (this) {
997 if (mActiveStreamType != streamType) {
998 reorderSliders(streamType);
999 }
RoboErik19c95182014-06-23 15:38:48 -07001000 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -08001001 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 }
1003
Marco Nelissen69f593c2009-07-28 09:55:04 -07001004 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 removeMessages(MSG_PLAY_SOUND);
1006 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
1007 }
1008
1009 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
1010 removeMessages(MSG_PLAY_SOUND);
1011 removeMessages(MSG_VIBRATE);
1012 onStopSounds();
1013 }
1014
1015 removeMessages(MSG_FREE_RESOURCES);
1016 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001017 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001018 }
1019
Mike Lockwoodce952c82011-11-14 10:47:42 -08001020 protected void onMuteChanged(int streamType, int flags) {
1021
John Spurlock661f2cf2014-11-17 10:29:10 -05001022 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamToString(streamType)
1023 + ", flags: " + flagsToString(flags) + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -08001024
1025 StreamControl sc = mStreamControls.get(streamType);
1026 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -04001027 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -08001028 }
1029
1030 onVolumeChanged(streamType, flags);
1031 }
1032
RoboErik19c95182014-06-23 15:38:48 -07001033 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001034 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001035
Marco Nelissen69f593c2009-07-28 09:55:04 -07001036 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037
1038 if (LOGD) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001039 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamToString(streamType)
1040 + ", flags: " + flagsToString(flags) + "), index: " + index);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001041 }
1042
1043 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001044
John Spurlockb6e19e32015-03-10 21:33:44 -04001045 int max = getStreamMaxVolume(streamType) - getStreamMinVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001046 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001047
1048 switch (streamType) {
1049
1050 case AudioManager.STREAM_RING: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001051 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1052 mContext, RingtoneManager.TYPE_RINGTONE);
1053 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001054 mRingIsSilent = true;
1055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 break;
1057 }
1058
1059 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001060 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001061 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1062 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1063 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1064 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001065 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001067 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001068 }
1069 break;
1070 }
1071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001073 break;
1074 }
1075
1076 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001077 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1078 mContext, RingtoneManager.TYPE_NOTIFICATION);
1079 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001080 mRingIsSilent = true;
1081 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 break;
1083 }
1084
RoboErik2811dd32014-08-12 09:48:13 -07001085 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001086 if (controller == null && sc != null) {
1087 // If we weren't passed one try using the last one set.
1088 controller = sc.controller;
1089 }
1090 if (controller == null) {
1091 // We still don't have one, ignore the command.
1092 Log.w(mTag, "sent remote volume change without a controller!");
1093 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001094 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001095 index = vi.getCurrentVolume();
1096 max = vi.getMaxVolume();
1097 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1098 // if the remote volume is fixed add the flag for the UI
1099 flags |= AudioManager.FLAG_FIXED_VOLUME;
1100 }
1101 }
John Spurlock86005342014-05-23 11:58:00 -04001102 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001103 break;
1104 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 }
1106
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001107 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001108 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001109 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001110 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001111 }
1112 sc.controller = controller;
1113 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001114 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001115 }
1116 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001117 if (sc.seekbarView.getMax() != max) {
1118 sc.seekbarView.setMax(max);
1119 }
John Spurlockad494bc2014-07-19 15:56:19 -04001120 updateSliderProgress(sc, index);
John Spurlock661f2cf2014-11-17 10:29:10 -05001121 final boolean muted = isMuted(streamType);
1122 updateSliderEnabled(sc, muted, (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
1123 if (isNotificationOrRing(streamType)) {
1124 // check for secondary-icon transition completion
1125 if (mSecondaryIconTransition.isRunning()) {
1126 mSecondaryIconTransition.cancel(); // safe to reset
1127 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1128 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1129 }
1130 updateSliderIcon(sc, muted);
John Spurlockbb4a7022014-11-08 12:40:19 -05001131 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 }
1133
John Spurlock86005342014-05-23 11:58:00 -04001134 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001135 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001136 // when the stream is for remote playback, use -1 to reset the stream type evaluation
John Spurlockee5ad722015-03-03 16:17:21 -05001137 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001138 mDialog.show();
1139 if (mCallback != null) {
1140 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001141 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001142 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 }
1144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001145 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001146 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001147 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock661f2cf2014-11-17 10:29:10 -05001148 isNotificationOrRing(streamType) &&
1149 mAudioManager.getRingerModeInternal() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001150 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1151 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001152
John Spurlock661f2cf2014-11-17 10:29:10 -05001153 // Pulse the zen icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001154 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1155 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001156 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001157
1158 // Pulse the slider icon & vibrate if an adjustment down was suppressed due to vibrate mode.
1159 if ((flags & AudioManager.FLAG_SHOW_VIBRATE_HINT) != 0) {
1160 showVibrateHint();
1161 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001162 }
1163
Selim Cinek62ea3402014-09-08 12:11:51 +02001164 private void announceDialogShown() {
1165 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1166 }
1167
John Spurlock86005342014-05-23 11:58:00 -04001168 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001169 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001170 }
1171
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 protected void onPlaySound(int streamType, int flags) {
1173
1174 if (hasMessages(MSG_STOP_SOUNDS)) {
1175 removeMessages(MSG_STOP_SOUNDS);
1176 // Force stop right now
1177 onStopSounds();
1178 }
1179
1180 synchronized (this) {
1181 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001182 if (toneGen != null) {
1183 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1184 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1185 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 }
1188
1189 protected void onStopSounds() {
1190
1191 synchronized (this) {
1192 int numStreamTypes = AudioSystem.getNumStreamTypes();
1193 for (int i = numStreamTypes - 1; i >= 0; i--) {
1194 ToneGenerator toneGen = mToneGenerators[i];
1195 if (toneGen != null) {
1196 toneGen.stopTone();
1197 }
1198 }
1199 }
1200 }
1201
1202 protected void onVibrate() {
1203
1204 // Make sure we ended up in vibrate ringer mode
John Spurlock661f2cf2014-11-17 10:29:10 -05001205 if (mAudioManager.getRingerModeInternal() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 return;
1207 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001208 if (mVibrator != null) {
1209 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
1210 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001211 }
1212
RoboErik19c95182014-06-23 15:38:48 -07001213 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
John Spurlock661f2cf2014-11-17 10:29:10 -05001214 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: "
1215 + flagsToString(flags) + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001216
John Spurlock86005342014-05-23 11:58:00 -04001217 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001218 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001219 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1220 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001221 }
RoboErik2811dd32014-08-12 09:48:13 -07001222 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001223 }
1224 } else {
John Spurlock86005342014-05-23 11:58:00 -04001225 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001226 }
1227
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001228 removeMessages(MSG_FREE_RESOURCES);
1229 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001230 resetTimeout();
1231 }
1232
1233 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001234 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1235 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001236 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001237 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001238 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001239 }
1240 }
1241
RoboErik19c95182014-06-23 15:38:48 -07001242 /**
1243 * Clear the current remote stream controller.
1244 */
1245 private void clearRemoteStreamController() {
1246 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001247 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001248 if (sc != null) {
1249 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001250 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001251 sc.controller = null;
1252 }
1253 }
1254 }
1255 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001256
1257 /**
RoboErik2811dd32014-08-12 09:48:13 -07001258 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1259 *
1260 * @param streamType can be a valid stream type value, or
1261 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001262 * @param visible
1263 */
1264 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001265 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001266 boolean isVisible = (visible == 1);
1267 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1268 StreamResources streamRes = STREAMS[i];
1269 if (streamRes.streamType == streamType) {
1270 streamRes.show = isVisible;
1271 if (!isVisible && (mActiveStreamType == streamType)) {
1272 mActiveStreamType = -1;
1273 }
1274 break;
1275 }
1276 }
1277 }
1278
Eric Laurentfde16d52012-12-03 14:42:39 -08001279 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001280 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1281 || isShowing()) {
1282 synchronized (sSafetyWarningLock) {
1283 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001284 return;
1285 }
John Spurlock35134602014-07-24 18:10:48 -04001286 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1287 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001288 }
1289 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001290 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001291 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1292 removeMessages(MSG_TIMEOUT);
1293 } else {
1294 updateTimeoutDelay();
1295 resetTimeout();
1296 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001297 }
1298
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001299 /**
1300 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1301 */
1302 private ToneGenerator getOrCreateToneGenerator(int streamType) {
1303 synchronized (this) {
1304 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001305 try {
1306 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1307 } catch (RuntimeException e) {
1308 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001309 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001310 + "RuntimeException: " + e);
1311 }
1312 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001313 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001314 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 }
1316 }
1317
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001318
1319 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001320 * Switch between icons because Bluetooth music is same as music volume, but with
1321 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001323 private void setMusicIcon(int resId, int resMuteId) {
1324 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1325 if (sc != null) {
1326 sc.iconRes = resId;
1327 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001328 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001329 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001330 }
1331
1332 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001333 synchronized (this) {
1334 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1335 if (mToneGenerators[i] != null) {
1336 mToneGenerators[i].release();
1337 }
1338 mToneGenerators[i] = null;
1339 }
1340 }
1341 }
1342
1343 @Override
1344 public void handleMessage(Message msg) {
1345 switch (msg.what) {
1346
1347 case MSG_VOLUME_CHANGED: {
1348 onVolumeChanged(msg.arg1, msg.arg2);
1349 break;
1350 }
1351
Mike Lockwoodce952c82011-11-14 10:47:42 -08001352 case MSG_MUTE_CHANGED: {
1353 onMuteChanged(msg.arg1, msg.arg2);
1354 break;
1355 }
1356
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001357 case MSG_FREE_RESOURCES: {
1358 onFreeResources();
1359 break;
1360 }
1361
1362 case MSG_STOP_SOUNDS: {
1363 onStopSounds();
1364 break;
1365 }
1366
1367 case MSG_PLAY_SOUND: {
1368 onPlaySound(msg.arg1, msg.arg2);
1369 break;
1370 }
1371
1372 case MSG_VIBRATE: {
1373 onVibrate();
1374 break;
1375 }
1376
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001377 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001378 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001379 mDialog.dismiss();
1380 clearRemoteStreamController();
1381 mActiveStreamType = -1;
1382 if (mCallback != null) {
1383 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001384 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001385 }
John Spurlock35134602014-07-24 18:10:48 -04001386 synchronized (sSafetyWarningLock) {
1387 if (sSafetyWarning != null) {
1388 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1389 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001390 }
1391 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001392 break;
1393 }
John Spurlockb4782522014-08-22 14:54:46 -04001394
1395 case MSG_RINGER_MODE_CHANGED:
John Spurlock661f2cf2014-11-17 10:29:10 -05001396 case MSG_INTERNAL_RINGER_MODE_CHANGED:
John Spurlockb4782522014-08-22 14:54:46 -04001397 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001398 if (isShowing()) {
John Spurlock530052a2014-11-30 16:26:19 -05001399 updateActiveSlider();
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001400 }
1401 break;
1402 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001403
1404 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001405 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001406 break;
1407 }
1408
1409 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1410 onRemoteVolumeUpdateIfShown();
1411 break;
1412
1413 case MSG_SLIDER_VISIBILITY_CHANGED:
1414 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1415 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001416
1417 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001418 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001419 break;
John Spurlock86005342014-05-23 11:58:00 -04001420
1421 case MSG_LAYOUT_DIRECTION:
1422 setLayoutDirection(msg.arg1);
1423 break;
1424
John Spurlock45601d62014-08-07 17:40:50 -04001425 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1426 mZenModeAvailable = msg.arg1 != 0;
1427 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001428 break;
1429
1430 case MSG_USER_ACTIVITY:
1431 if (mCallback != null) {
1432 mCallback.onInteraction();
1433 }
John Spurlock86005342014-05-23 11:58:00 -04001434 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001435 }
1436 }
1437
John Spurlockae641c92014-06-30 18:11:40 -04001438 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001439 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001440 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001441 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1442 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001443 removeMessages(MSG_TIMEOUT);
1444 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1445 removeMessages(MSG_USER_ACTIVITY);
1446 sendEmptyMessage(MSG_USER_ACTIVITY);
1447 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001448 }
1449
John Spurlock1dad2722014-07-11 11:07:53 -04001450 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001451 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001452 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001453 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001454 }
1455
1456 public ZenModeController getZenController() {
1457 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001458 }
1459
John Spurlockbb4a7022014-11-08 12:40:19 -05001460 @Override
1461 public void dispatchDemoCommand(String command, Bundle args) {
1462 if (!COMMAND_VOLUME.equals(command)) return;
1463 String icon = args.getString("icon");
1464 final String iconMute = args.getString("iconmute");
1465 final boolean mute = iconMute != null;
1466 icon = mute ? iconMute : icon;
1467 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1468 final StreamResources sr = StreamResources.valueOf(icon);
1469 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1470 final int forcedStreamType = StreamResources.MediaStream.streamType;
1471 mAudioManager.forceVolumeControlStream(forcedStreamType);
1472 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1473 AudioManager.FLAG_SHOW_UI);
1474 }
1475
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001476 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1477 @Override
1478 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1479 final Object tag = seekBar.getTag();
1480 if (fromUser && tag instanceof StreamControl) {
1481 StreamControl sc = (StreamControl) tag;
John Spurlockb6e19e32015-03-10 21:33:44 -04001482 setStreamVolume(sc, progress + sc.minVolume,
John Spurlockac9ab5b2014-07-20 13:09:44 -04001483 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001484 }
1485 resetTimeout();
1486 }
1487
1488 @Override
1489 public void onStartTrackingTouch(SeekBar seekBar) {
1490 }
1491
1492 @Override
1493 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001494 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001495 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001496
John Spurlock86005342014-05-23 11:58:00 -04001497 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001498 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001499 public void onZenAvailableChanged(boolean available) {
1500 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001501 }
John Spurlock661f2cf2014-11-17 10:29:10 -05001502
John Spurlockb4782522014-08-22 14:54:46 -04001503 @Override
1504 public void onEffectsSupressorChanged() {
John Spurlock661f2cf2014-11-17 10:29:10 -05001505 mNotificationEffectsSuppressor = mZenController.getEffectsSuppressor();
1506 sendEmptyMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED);
1507 }
John Spurlock86005342014-05-23 11:58:00 -04001508 };
RoboErik19c95182014-06-23 15:38:48 -07001509
1510 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001511 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001512 onRemoteVolumeUpdateIfShown();
1513 }
1514 };
John Spurlockae641c92014-06-30 18:11:40 -04001515
John Spurlockbb4a7022014-11-08 12:40:19 -05001516 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1517 implements Runnable {
1518 private static final int ANIMATION_TIME = 400;
1519 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1520
1521 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1522 private final int mFadeOutTime = mAnimationTime / 2;
1523 private final int mDelayTime = mAnimationTime / 3;
1524
1525 private final Interpolator mIconInterpolator =
1526 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1527
1528 private StreamControl mTarget;
1529
1530 public void start(StreamControl sc) {
1531 if (sc == null) throw new IllegalArgumentException();
John Spurlock530052a2014-11-30 16:26:19 -05001532 if (LOGD) Log.d(mTag, "Secondary icon animation start");
John Spurlockbb4a7022014-11-08 12:40:19 -05001533 if (mTarget != null) {
1534 cancel();
1535 }
1536 mTarget = sc;
1537 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1538 resetTimeout();
1539 mTarget.secondaryIcon.setClickable(false);
1540 final int N = mTarget.group.getChildCount();
1541 for (int i = 0; i < N; i++) {
1542 final View child = mTarget.group.getChildAt(i);
1543 if (child != mTarget.secondaryIcon) {
1544 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1545 }
1546 }
1547 mTarget.secondaryIcon.animate()
1548 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1549 .setInterpolator(mIconInterpolator)
1550 .setStartDelay(mDelayTime)
1551 .setDuration(mAnimationTime - mDelayTime)
1552 .setListener(this)
1553 .start();
1554 }
1555
1556 public boolean isRunning() {
1557 return mTarget != null;
1558 }
1559
1560 public void cancel() {
1561 if (mTarget == null) return;
1562 mTarget.secondaryIcon.setClickable(true);
1563 final int N = mTarget.group.getChildCount();
1564 for (int i = 0; i < N; i++) {
1565 final View child = mTarget.group.getChildAt(i);
1566 if (child != mTarget.secondaryIcon) {
1567 child.animate().cancel();
1568 child.setAlpha(1);
1569 }
1570 }
1571 mTarget.secondaryIcon.animate().cancel();
1572 mTarget.secondaryIcon.setTranslationX(0);
1573 mTarget = null;
1574 }
1575
1576 @Override
1577 public void onAnimationEnd(Animator animation) {
1578 if (mTarget == null) return;
1579 AsyncTask.execute(this);
1580 }
1581
1582 @Override
1583 public void run() {
1584 if (mTarget == null) return;
John Spurlock530052a2014-11-30 16:26:19 -05001585 if (LOGD) Log.d(mTag, "Secondary icon animation complete, show notification slider");
John Spurlockbb4a7022014-11-08 12:40:19 -05001586 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1587 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1588 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1589 }
1590 }
1591
John Spurlockae641c92014-06-30 18:11:40 -04001592 public interface Callback {
1593 void onZenSettings();
1594 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001595 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001596 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001597}