blob: f12053cf5242c978ac5d46b2ebb06cffee7ab82b [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
John Spurlockbb4a7022014-11-08 12:40:19 -050019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
21import android.animation.ValueAnimator;
Eric Laurentc34dcc12012-09-10 13:51:52 -070022import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080024import android.content.BroadcastReceiver;
John Spurlockb4782522014-08-22 14:54:46 -040025import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080027import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040028import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080030import android.content.IntentFilter;
John Spurlockb4782522014-08-22 14:54:46 -040031import android.content.pm.PackageManager;
John Spurlockb4782522014-08-22 14:54:46 -040032import android.content.pm.ServiceInfo;
John Spurlock7e6809a2014-08-06 16:03:14 -040033import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040035import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040036import android.graphics.PixelFormat;
37import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040038import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.media.AudioManager;
40import android.media.AudioService;
41import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070042import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070044import android.media.VolumeProvider;
45import android.media.session.MediaController;
RoboErikd2b8c942014-08-19 11:23:40 -070046import android.media.session.MediaController.PlaybackInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070047import android.net.Uri;
John Spurlockbb4a7022014-11-08 12:40:19 -050048import android.os.AsyncTask;
49import android.os.Bundle;
John Spurlocka0457c22014-09-26 13:22:08 -040050import android.os.Debug;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080051import android.os.Handler;
52import android.os.Message;
53import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080054import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040055import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040056import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040057import android.view.LayoutInflater;
58import android.view.MotionEvent;
59import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040060import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040061import android.view.ViewGroup;
62import android.view.Window;
63import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070064import android.view.WindowManager.LayoutParams;
Selim Cinek62ea3402014-09-08 12:11:51 +020065import android.view.accessibility.AccessibilityEvent;
Selim Cinek9f6ceb12014-08-29 16:28:39 +020066import android.view.accessibility.AccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -050067import android.view.animation.AnimationUtils;
68import android.view.animation.Interpolator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080070import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080071import android.widget.SeekBar.OnSeekBarChangeListener;
John Spurlockb4782522014-08-22 14:54:46 -040072import android.widget.TextView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080073
John Spurlock86005342014-05-23 11:58:00 -040074import com.android.internal.R;
John Spurlockbb4a7022014-11-08 12:40:19 -050075import com.android.systemui.DemoMode;
John Spurlock35134602014-07-24 18:10:48 -040076import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040077import com.android.systemui.statusbar.policy.ZenModeController;
78
John Spurlockad494bc2014-07-19 15:56:19 -040079import java.io.FileDescriptor;
80import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081
82/**
John Spurlock3346a802014-05-20 16:25:37 -040083 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070084 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 * @hide
86 */
John Spurlockbb4a7022014-11-08 12:40:19 -050087public class VolumePanel extends Handler implements DemoMode {
John Spurlockae641c92014-06-30 18:11:40 -040088 private static final String TAG = "VolumePanel";
89 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
John Spurlock3346a802014-05-20 16:25:37 -040091 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092
93 /**
94 * The delay before vibrating. This small period exists so if the user is
95 * moving to silent mode, it will not emit a short vibrate (it normally
96 * would since vibrate is between normal mode and silent mode using hardware
97 * keys).
98 */
99 public static final int VIBRATE_DELAY = 300;
100
101 private static final int VIBRATE_DURATION = 300;
102 private static final int BEEP_DURATION = 150;
103 private static final int MAX_VOLUME = 100;
104 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800105 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -0400106 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -0400107 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -0400108 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400109 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110
111 private static final int MSG_VOLUME_CHANGED = 0;
112 private static final int MSG_FREE_RESOURCES = 1;
113 private static final int MSG_PLAY_SOUND = 2;
114 private static final int MSG_STOP_SOUNDS = 3;
115 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800116 private static final int MSG_TIMEOUT = 5;
117 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800118 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700119 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
120 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
121 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700122 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400123 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400124 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400125 private static final int MSG_USER_ACTIVITY = 14;
John Spurlockb4782522014-08-22 14:54:46 -0400126 private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400128 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800129 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700130 // Pseudo stream type for remote volume
131 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400132
John Spurlock7b414672014-07-18 13:02:39 -0400133 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
134 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
135 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
136 .build();
137
John Spurlock4bebb1d2014-10-02 19:17:19 -0400138 private static final int IC_AUDIO_VOL = com.android.systemui.R.drawable.ic_audio_vol;
139 private static final int IC_AUDIO_VOL_MUTE = com.android.systemui.R.drawable.ic_audio_vol_mute;
John Spurlockbb4a7022014-11-08 12:40:19 -0500140 private static final int IC_AUDIO_BT = com.android.systemui.R.drawable.ic_audio_bt;
141 private static final int IC_AUDIO_BT_MUTE = com.android.systemui.R.drawable.ic_audio_bt_mute;
John Spurlock4bebb1d2014-10-02 19:17:19 -0400142
John Spurlock86005342014-05-23 11:58:00 -0400143 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400144 protected final Context mContext;
145 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400146 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700147 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700148 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400149 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400150 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400151 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400152 private float mDisabledAlpha;
153 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
154 private int mLastRingerProgress = 0;
John Spurlockbb4a7022014-11-08 12:40:19 -0500155 private int mDemoIcon;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800156
Christopher Tatec4b78d22012-05-22 13:57:58 -0700157 // True if we want to play tones on the system stream when the master stream is specified.
158 private final boolean mPlayMasterStreamTones;
159
John Spurlock86005342014-05-23 11:58:00 -0400160
161 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800162 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400163 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400164 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800165
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700166 /** The visible portion of the volume overlay */
167 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400168 /** Contains the slider and its touchable icons */
169 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400170 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400171 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400172
John Spurlockae641c92014-06-30 18:11:40 -0400173 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800174
175 /** Currently active stream that shows up at the top of the list of sliders */
176 private int mActiveStreamType = -1;
177 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400178 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200179 private final AccessibilityManager mAccessibilityManager;
John Spurlockbb4a7022014-11-08 12:40:19 -0500180 private final SecondaryIconTransition mSecondaryIconTransition;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800181
Amith Yamasani71def772011-10-12 12:25:24 -0700182 private enum StreamResources {
183 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
184 R.string.volume_icon_description_bluetooth,
John Spurlockbb4a7022014-11-08 12:40:19 -0500185 IC_AUDIO_BT,
186 IC_AUDIO_BT_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700187 false),
188 RingerStream(AudioManager.STREAM_RING,
189 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400190 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400191 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700192 false),
193 VoiceStream(AudioManager.STREAM_VOICE_CALL,
194 R.string.volume_icon_description_incall,
John Spurlockbb4a7022014-11-08 12:40:19 -0500195 com.android.systemui.R.drawable.ic_audio_phone,
196 com.android.systemui.R.drawable.ic_audio_phone,
Amith Yamasani71def772011-10-12 12:25:24 -0700197 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700198 AlarmStream(AudioManager.STREAM_ALARM,
199 R.string.volume_alarm,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400200 com.android.systemui.R.drawable.ic_audio_alarm,
201 com.android.systemui.R.drawable.ic_audio_alarm_mute,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700202 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700203 MediaStream(AudioManager.STREAM_MUSIC,
204 R.string.volume_icon_description_media,
John Spurlock4bebb1d2014-10-02 19:17:19 -0400205 IC_AUDIO_VOL,
206 IC_AUDIO_VOL_MUTE,
Amith Yamasani71def772011-10-12 12:25:24 -0700207 true),
208 NotificationStream(AudioManager.STREAM_NOTIFICATION,
209 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400210 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400211 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400212 true),
213 // for now, use media resources for master volume
214 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700215 R.string.volume_icon_description_media, //FIXME should have its own description
John Spurlock4bebb1d2014-10-02 19:17:19 -0400216 IC_AUDIO_VOL,
217 IC_AUDIO_VOL_MUTE,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700218 false),
RoboErik2811dd32014-08-12 09:48:13 -0700219 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700220 R.string.volume_icon_description_media, //FIXME should have its own description
221 R.drawable.ic_media_route_on_holo_dark,
222 R.drawable.ic_media_route_disabled_holo_dark,
223 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700224
225 int streamType;
226 int descRes;
227 int iconRes;
228 int iconMuteRes;
229 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
230 boolean show;
231
232 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
233 this.streamType = streamType;
234 this.descRes = descRes;
235 this.iconRes = iconRes;
236 this.iconMuteRes = iconMuteRes;
237 this.show = show;
238 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700239 }
Amith Yamasani71def772011-10-12 12:25:24 -0700240
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800241 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700242 private static final StreamResources[] STREAMS = {
243 StreamResources.BluetoothSCOStream,
244 StreamResources.RingerStream,
245 StreamResources.VoiceStream,
246 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700247 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400248 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700249 StreamResources.MasterStream,
250 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800251 };
252
253 /** Object that contains data for each slider */
254 private class StreamControl {
255 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700256 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800257 ViewGroup group;
258 ImageView icon;
259 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400260 TextView suppressorView;
John Spurlockbb4a7022014-11-08 12:40:19 -0500261 View divider;
262 ImageView secondaryIcon;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800263 int iconRes;
264 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400265 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800266 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800267
268 // Synchronize when accessing this
269 private ToneGenerator mToneGenerators[];
270 private Vibrator mVibrator;
271
John Spurlock35134602014-07-24 18:10:48 -0400272 private static AlertDialog sSafetyWarning;
273 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700274
John Spurlock35134602014-07-24 18:10:48 -0400275 private static class SafetyWarning extends SystemUIDialog
276 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800277 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800278 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400279 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700280
John Spurlock2d28d6e2014-08-01 13:10:14 -0400281 private boolean mNewVolumeUp;
282
John Spurlock35134602014-07-24 18:10:48 -0400283 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
284 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700285 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800286 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400287 mAudioManager = audioManager;
288
289 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
290 setButton(DialogInterface.BUTTON_POSITIVE,
291 mContext.getString(com.android.internal.R.string.yes), this);
292 setButton(DialogInterface.BUTTON_NEGATIVE,
293 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
294 setOnDismissListener(this);
295
Eric Laurentc34dcc12012-09-10 13:51:52 -0700296 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400297 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700298 }
299
300 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400301 public boolean onKeyDown(int keyCode, KeyEvent event) {
302 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
303 mNewVolumeUp = true;
304 }
305 return super.onKeyDown(keyCode, event);
306 }
307
308 @Override
309 public boolean onKeyUp(int keyCode, KeyEvent event) {
310 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
311 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
312 mAudioManager.disableSafeMediaVolume();
313 dismiss();
314 }
315 return super.onKeyUp(keyCode, event);
316 }
317
318 @Override
John Spurlock35134602014-07-24 18:10:48 -0400319 public void onClick(DialogInterface dialog, int which) {
320 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700321 }
322
Alan Viverette494fb7b2014-04-10 18:12:56 -0700323 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700324 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400325 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800326 cleanUp();
327 }
328
329 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400330 synchronized (sSafetyWarningLock) {
331 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700332 }
John Spurlock1dad2722014-07-11 11:07:53 -0400333 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800334 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700335 }
John Spurlock35134602014-07-24 18:10:48 -0400336
337 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
338 @Override
339 public void onReceive(Context context, Intent intent) {
340 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
341 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
342 cancel();
343 cleanUp();
344 }
345 }
346 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700347 }
348
John Spurlockeb2727b2014-07-19 23:11:36 -0400349 public VolumePanel(Context context, ZenModeController zenController) {
350 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400352 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800353 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200354 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
355 Context.ACCESSIBILITY_SERVICE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500356 mSecondaryIconTransition = new SecondaryIconTransition();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400358 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700359 final Resources res = context.getResources();
360 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400361 if (useMasterVolume) {
362 for (int i = 0; i < STREAMS.length; i++) {
363 StreamResources streamRes = STREAMS[i];
364 streamRes.show = (streamRes.streamType == STREAM_MASTER);
365 }
366 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400367 if (LOGD) Log.d(mTag, "new VolumePanel");
368
John Spurlockad494bc2014-07-19 15:56:19 -0400369 mDisabledAlpha = 0.5f;
370 if (mContext.getTheme() != null) {
371 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
372 new int[] { android.R.attr.disabledAlpha });
373 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
374 arr.recycle();
375 }
376
John Spurlockeb2727b2014-07-19 23:11:36 -0400377 mDialog = new Dialog(context) {
378 @Override
379 public boolean onTouchEvent(MotionEvent event) {
380 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400381 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400382 forceTimeout(0);
383 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700384 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400385 return false;
386 }
387 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700388
John Spurlockeb2727b2014-07-19 23:11:36 -0400389 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400390 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400391 mDialog.setCanceledOnTouchOutside(true);
392 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
393 mDialog.setOnDismissListener(new OnDismissListener() {
394 @Override
395 public void onDismiss(DialogInterface dialog) {
396 mActiveStreamType = -1;
397 mAudioManager.forceVolumeControlStream(mActiveStreamType);
398 setZenPanelVisible(false);
John Spurlockbb4a7022014-11-08 12:40:19 -0500399 mDemoIcon = 0;
400 mSecondaryIconTransition.cancel();
John Spurlockeb2727b2014-07-19 23:11:36 -0400401 }
402 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700403
John Spurlockeb2727b2014-07-19 23:11:36 -0400404 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700405
John Spurlock7e6809a2014-08-06 16:03:14 -0400406 final LayoutParams lp = window.getAttributes();
407 lp.token = null;
408 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
409 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
410 lp.format = PixelFormat.TRANSLUCENT;
411 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
412 lp.setTitle(TAG);
413 window.setAttributes(lp);
414
415 updateWidth();
416
417 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
418 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
419 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
420 | LayoutParams.FLAG_NOT_TOUCH_MODAL
421 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
422 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400423 mView = window.findViewById(R.id.content);
John Spurlocka0457c22014-09-26 13:22:08 -0400424 Interaction.register(mView, new Interaction.Callback() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400425 @Override
John Spurlocka0457c22014-09-26 13:22:08 -0400426 public void onInteraction() {
John Spurlockeb2727b2014-07-19 23:11:36 -0400427 resetTimeout();
John Spurlockeb2727b2014-07-19 23:11:36 -0400428 }
429 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700430
John Spurlock86005342014-05-23 11:58:00 -0400431 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
432 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400433 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
434 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800435
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700437 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700438 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700439
John Spurlock45601d62014-08-07 17:40:50 -0400440 if (mZenController != null && !useMasterVolume) {
441 mZenModeAvailable = mZenController.isZenAvailable();
442 mZenController.addCallback(mZenCallback);
443 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700444
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700445 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
446 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700447 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
448
John Spurlock8c79d2e2014-07-24 15:15:25 -0400449 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800450 }
451
John Spurlock7e6809a2014-08-06 16:03:14 -0400452 public void onConfigurationChanged(Configuration newConfig) {
453 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400454 if (mZenPanel != null) {
455 mZenPanel.updateLocale();
456 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400457 }
458
459 private void updateWidth() {
460 final Resources res = mContext.getResources();
461 final LayoutParams lp = mDialog.getWindow().getAttributes();
462 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
463 lp.gravity =
464 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
465 mDialog.getWindow().setAttributes(lp);
466 }
467
John Spurlockad494bc2014-07-19 15:56:19 -0400468 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
469 pw.println("VolumePanel state:");
470 pw.print(" mTag="); pw.println(mTag);
471 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
472 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock45601d62014-08-07 17:40:50 -0400473 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400474 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
475 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
476 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
477 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
478 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
479 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
480 pw.print(" isShowing()="); pw.println(isShowing());
481 pw.print(" mCallback="); pw.println(mCallback);
482 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400483 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400484 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
485 pw.print(" mStreamControls=");
486 if (mStreamControls == null) {
487 pw.println("null");
488 } else {
489 final int N = mStreamControls.size();
490 pw.print("<size "); pw.print(N); pw.println('>');
491 for (int i = 0; i < N; i++) {
492 final StreamControl sc = mStreamControls.valueAt(i);
493 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
494 if (sc.seekbarView != null) {
495 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
496 pw.print(" of "); pw.print(sc.seekbarView.getMax());
497 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
498 }
499 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
500 pw.println();
501 }
502 }
503 }
504
John Spurlockeb2727b2014-07-19 23:11:36 -0400505 private void initZenModePanel() {
506 mZenPanel.init(mZenController);
507 mZenPanel.setCallback(new ZenModePanel.Callback() {
508 @Override
509 public void onMoreSettings() {
510 if (mCallback != null) {
511 mCallback.onZenSettings();
512 }
513 }
514
515 @Override
516 public void onInteraction() {
517 resetTimeout();
518 }
519
520 @Override
521 public void onExpanded(boolean expanded) {
522 if (mZenPanelExpanded == expanded) return;
523 mZenPanelExpanded = expanded;
524 updateTimeoutDelay();
525 resetTimeout();
526 }
527 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400528 }
529
John Spurlock86005342014-05-23 11:58:00 -0400530 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800531 mPanel.setLayoutDirection(layoutDirection);
532 updateStates();
533 }
534
John Spurlock8c79d2e2014-07-24 15:15:25 -0400535 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800536 final IntentFilter filter = new IntentFilter();
537 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400538 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800539 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700540 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800541 public void onReceive(Context context, Intent intent) {
542 final String action = intent.getAction();
543
544 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
545 removeMessages(MSG_RINGER_MODE_CHANGED);
546 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
547 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400548
549 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
550 postDismiss(0);
551 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800552 }
553 }, filter);
554 }
555
556 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400557 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700558 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700559 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700560 // TODO do we need to support a distinct mute property for remote?
561 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400562 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700563 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400564 }
565 }
566
567 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400568 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700569 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700570 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700571 if (mStreamControls != null) {
572 StreamControl sc = mStreamControls.get(streamType);
573 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700574 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700575 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700576 }
577 }
578 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400579 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700580 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400581 }
582 }
583
584 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400585 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700586 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700587 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700588 if (mStreamControls != null) {
589 StreamControl sc = mStreamControls.get(streamType);
590 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700591 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700592 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700593 }
594 }
595 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400596 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700597 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400598 }
599 }
600
RoboErik19c95182014-06-23 15:38:48 -0700601 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700602 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700603 if (sc.controller != null) {
604 sc.controller.setVolumeTo(index, flags);
605 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700606 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700607 }
608 } else if (getStreamVolume(sc.streamType) != index) {
609 if (sc.streamType == STREAM_MASTER) {
610 mAudioManager.setMasterVolume(index, flags);
611 } else {
612 mAudioManager.setStreamVolume(sc.streamType, index, flags);
613 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400614 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800615 }
616
617 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700618 final Resources res = mContext.getResources();
619 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
620 Context.LAYOUT_INFLATER_SERVICE);
621
John Spurlockad494bc2014-07-19 15:56:19 -0400622 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700623
John Spurlockbb4a7022014-11-08 12:40:19 -0500624 final StreamResources notificationStream = StreamResources.NotificationStream;
Amith Yamasani71def772011-10-12 12:25:24 -0700625 for (int i = 0; i < STREAMS.length; i++) {
626 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700627
628 final int streamType = streamRes.streamType;
John Spurlockbb4a7022014-11-08 12:40:19 -0500629 final boolean isNotification = isNotificationOrRing(streamType);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700630
631 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700632 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400633 sc.group = (ViewGroup) inflater.inflate(
634 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800635 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400636 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800637 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700638 sc.icon.setContentDescription(res.getString(streamRes.descRes));
639 sc.iconRes = streamRes.iconRes;
640 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800641 sc.icon.setImageResource(sc.iconRes);
John Spurlockbb4a7022014-11-08 12:40:19 -0500642 sc.icon.setClickable(isNotification);
643 if (isNotification) {
John Spurlockab4ea762014-07-19 18:50:16 -0400644 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400645 sc.icon.setOnClickListener(new OnClickListener() {
646 @Override
647 public void onClick(View v) {
648 resetTimeout();
649 toggle(sc);
650 }
651 });
John Spurlockb4782522014-08-22 14:54:46 -0400652 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400653 }
John Spurlock86005342014-05-23 11:58:00 -0400654 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400655 sc.suppressorView =
656 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
657 sc.suppressorView.setVisibility(View.GONE);
John Spurlockbb4a7022014-11-08 12:40:19 -0500658 final boolean showSecondary = !isNotification && notificationStream.show;
659 sc.divider = sc.group.findViewById(com.android.systemui.R.id.divider);
660 sc.secondaryIcon = (ImageView) sc.group
661 .findViewById(com.android.systemui.R.id.secondary_icon);
662 sc.secondaryIcon.setImageResource(com.android.systemui.R.drawable.ic_ringer_audible);
663 sc.secondaryIcon.setContentDescription(res.getString(notificationStream.descRes));
664 sc.secondaryIcon.setClickable(showSecondary);
665 sc.divider.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
666 sc.secondaryIcon.setVisibility(showSecondary ? View.VISIBLE : View.GONE);
667 if (showSecondary) {
668 sc.secondaryIcon.setOnClickListener(new OnClickListener() {
669 @Override
670 public void onClick(View v) {
671 mSecondaryIconTransition.start(sc);
672 }
673 });
674 }
Alan Viverette494fb7b2014-04-10 18:12:56 -0700675 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700676 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400677 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700678 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800679 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700680 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800681 }
682 }
683
John Spurlock7f1df5e2014-05-31 19:11:40 -0400684 private void toggle(StreamControl sc) {
685 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
686 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
687 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
688 } else {
689 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
690 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
691 }
692 }
693
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800694 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400695 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800696
Alan Viverette494fb7b2014-04-10 18:12:56 -0700697 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800698 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400699 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800700 mActiveStreamType = -1;
701 } else {
John Spurlock86005342014-05-23 11:58:00 -0400702 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800703 mActiveStreamType = activeStreamType;
704 active.group.setVisibility(View.VISIBLE);
705 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400706 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400707 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800708 }
709 }
710
John Spurlockad494bc2014-07-19 15:56:19 -0400711 private void updateSliderProgress(StreamControl sc, int progress) {
712 final boolean isRinger = isNotificationOrRing(sc.streamType);
713 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
714 progress = mLastRingerProgress;
715 }
716 if (progress < 0) {
717 progress = getStreamVolume(sc.streamType);
718 }
719 sc.seekbarView.setProgress(progress);
720 if (isRinger) {
721 mLastRingerProgress = progress;
722 }
723 }
724
725 private void updateSliderIcon(StreamControl sc, boolean muted) {
726 if (isNotificationOrRing(sc.streamType)) {
727 int ringerMode = mAudioManager.getRingerMode();
728 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
729 ringerMode = mLastRingerMode;
730 } else {
731 mLastRingerMode = ringerMode;
732 }
733 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
734 }
John Spurlockbb4a7022014-11-08 12:40:19 -0500735 sc.icon.setImageResource(mDemoIcon != 0 ? mDemoIcon : muted ? sc.iconMuteRes : sc.iconRes);
John Spurlockad494bc2014-07-19 15:56:19 -0400736 }
737
John Spurlockb4782522014-08-22 14:54:46 -0400738 private void updateSliderSupressor(StreamControl sc) {
739 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
740 ? mZenController.getEffectsSuppressor() : null;
741 if (suppressor == null) {
742 sc.seekbarView.setVisibility(View.VISIBLE);
743 sc.suppressorView.setVisibility(View.GONE);
744 } else {
745 sc.seekbarView.setVisibility(View.GONE);
746 sc.suppressorView.setVisibility(View.VISIBLE);
John Spurlock96e7f0e2014-11-14 14:35:01 -0500747 sc.suppressorView.setText(mContext.getString(R.string.muted_by,
John Spurlockb4782522014-08-22 14:54:46 -0400748 getSuppressorCaption(suppressor)));
749 sc.icon.setImageResource(sc.iconSuppressedRes);
750 }
751 }
752
753 private String getSuppressorCaption(ComponentName suppressor) {
754 final PackageManager pm = mContext.getPackageManager();
755 try {
756 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
757 if (info != null) {
758 final CharSequence seq = info.loadLabel(pm);
759 if (seq != null) {
760 final String str = seq.toString().trim();
761 if (str.length() > 0) {
762 return str;
763 }
764 }
765 }
766 } catch (Throwable e) {
767 Log.w(TAG, "Error loading suppressor caption", e);
768 }
769 return suppressor.getPackageName();
770 }
771
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800772 /** Update the mute and progress state of a slider */
773 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400774 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800775 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800776 // Force reloading the image resource
777 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400778 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400779 updateSliderEnabled(sc, muted, false);
John Spurlockb4782522014-08-22 14:54:46 -0400780 updateSliderSupressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400781 }
782
John Spurlock5f640e42014-05-31 20:15:59 -0400783 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
784 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400785 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700786 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700787 // never disable touch interactions for remote playback, the muting is not tied to
788 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700789 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400790 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400791 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400792 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400793 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400794 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400795 } else if (fixedVolume ||
796 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
797 (sSafetyWarning != null)) {
798 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700799 } else {
800 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400801 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400802 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700803 }
John Spurlockae641c92014-06-30 18:11:40 -0400804 // show the silent hint when the disabled slider is touched in silent mode
805 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400806 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400807 sc.group.setOnTouchListener(null);
808 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400809 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400810 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400811 @Override
812 public boolean onTouch(View v, MotionEvent event) {
813 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400814 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400815 return false;
816 }
John Spurlockae641c92014-06-30 18:11:40 -0400817 };
818 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400819 }
820 }
821 }
822
John Spurlockae641c92014-06-30 18:11:40 -0400823 private void showSilentHint() {
824 if (mZenPanel != null) {
825 mZenPanel.showSilentHint();
826 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800827 }
828
John Spurlock7f1df5e2014-05-31 19:11:40 -0400829 private static boolean isNotificationOrRing(int streamType) {
830 return streamType == AudioManager.STREAM_RING
831 || streamType == AudioManager.STREAM_NOTIFICATION;
832 }
833
John Spurlockae641c92014-06-30 18:11:40 -0400834 public void setCallback(Callback callback) {
835 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400836 }
837
John Spurlock8845da72014-07-07 21:29:48 -0400838 private void updateTimeoutDelay() {
John Spurlockbb4a7022014-11-08 12:40:19 -0500839 mTimeoutDelay = mDemoIcon != 0 ? TIMEOUT_DELAY_EXPANDED
840 : sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400841 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400842 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
843 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
844 : TIMEOUT_DELAY;
845 }
846
847 private boolean isZenPanelVisible() {
848 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400849 }
850
John Spurlockae641c92014-06-30 18:11:40 -0400851 private void setZenPanelVisible(boolean visible) {
852 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400853 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400854 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400855 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400856 resetTimeout();
857 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400858 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800859 }
John Spurlockea9938c2014-07-11 18:51:32 -0400860 if (changing) {
861 updateTimeoutDelay();
862 resetTimeout();
863 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800864 }
865
Eric Laurentfde16d52012-12-03 14:42:39 -0800866 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400867 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800868 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400869 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800870 updateSlider(sc);
871 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 }
873
John Spurlock45601d62014-08-07 17:40:50 -0400874 private void updateZenPanelVisible() {
875 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400876 }
877
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 public void postVolumeChanged(int streamType, int flags) {
879 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700880 synchronized (this) {
881 if (mStreamControls == null) {
882 createSliders();
883 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800884 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800885 removeMessages(MSG_FREE_RESOURCES);
886 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
887 }
888
RoboErik19c95182014-06-23 15:38:48 -0700889 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700890 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
891 synchronized (this) {
892 if (mStreamControls == null) {
893 createSliders();
894 }
895 }
896 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700897 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700898 }
899
900 public void postRemoteSliderVisibility(boolean visible) {
901 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700902 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700903 }
904
905 /**
906 * Called by AudioService when it has received new remote playback information that
907 * would affect the VolumePanel display (mainly volumes). The difference with
908 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
909 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
910 * displayed.
911 * This special code path is due to the fact that remote volume updates arrive to AudioService
912 * asynchronously. So after AudioService has sent the volume update (which should be treated
913 * as a request to update the volume), the application will likely set a new volume. If the UI
914 * is still up, we need to refresh the display to show this new value.
915 */
916 public void postHasNewRemotePlaybackInfo() {
917 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
918 // don't create or prevent resources to be freed, if they disappear, this update came too
919 // late and shouldn't warrant the panel to be displayed longer
920 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
921 }
922
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400923 public void postMasterVolumeChanged(int flags) {
924 postVolumeChanged(STREAM_MASTER, flags);
925 }
926
Mike Lockwoodce952c82011-11-14 10:47:42 -0800927 public void postMuteChanged(int streamType, int flags) {
928 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700929 synchronized (this) {
930 if (mStreamControls == null) {
931 createSliders();
932 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800933 }
934 removeMessages(MSG_FREE_RESOURCES);
935 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
936 }
937
938 public void postMasterMuteChanged(int flags) {
939 postMuteChanged(STREAM_MASTER, flags);
940 }
941
Eric Laurentfde16d52012-12-03 14:42:39 -0800942 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700943 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800944 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700945 }
946
John Spurlock1dad2722014-07-11 11:07:53 -0400947 public void postDismiss(long delay) {
948 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400949 }
950
951 public void postLayoutDirection(int layoutDirection) {
952 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400953 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400954 }
955
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 /**
957 * Override this if you have other work to do when the volume changes (for
958 * example, vibrating, playing a sound, etc.). Make sure to call through to
959 * the superclass implementation.
960 */
961 protected void onVolumeChanged(int streamType, int flags) {
962
John Spurlock86005342014-05-23 11:58:00 -0400963 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800964
965 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700966 synchronized (this) {
967 if (mActiveStreamType != streamType) {
968 reorderSliders(streamType);
969 }
RoboErik19c95182014-06-23 15:38:48 -0700970 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800971 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800972 }
973
Marco Nelissen69f593c2009-07-28 09:55:04 -0700974 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 removeMessages(MSG_PLAY_SOUND);
976 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
977 }
978
979 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
980 removeMessages(MSG_PLAY_SOUND);
981 removeMessages(MSG_VIBRATE);
982 onStopSounds();
983 }
984
985 removeMessages(MSG_FREE_RESOURCES);
986 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800987 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 }
989
Mike Lockwoodce952c82011-11-14 10:47:42 -0800990 protected void onMuteChanged(int streamType, int flags) {
991
John Spurlock86005342014-05-23 11:58:00 -0400992 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800993
994 StreamControl sc = mStreamControls.get(streamType);
995 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400996 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800997 }
998
999 onVolumeChanged(streamType, flags);
1000 }
1001
RoboErik19c95182014-06-23 15:38:48 -07001002 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -07001003 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -08001004
Marco Nelissen69f593c2009-07-28 09:55:04 -07001005 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001006
1007 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001008 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 + ", flags: " + flags + "), index: " + index);
1010 }
1011
1012 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001013
Mike Lockwood8dc1dab2011-10-27 09:52:41 -04001014 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -07001015 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016
1017 switch (streamType) {
1018
1019 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001020// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -07001021 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1022 mContext, RingtoneManager.TYPE_RINGTONE);
1023 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001024 mRingIsSilent = true;
1025 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 break;
1027 }
1028
1029 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001030 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -08001031 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
1032 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
1033 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
1034 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
John Spurlockbb4a7022014-11-08 12:40:19 -05001035 setMusicIcon(IC_AUDIO_BT, IC_AUDIO_BT_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 } else {
John Spurlock4bebb1d2014-10-02 19:17:19 -04001037 setMusicIcon(IC_AUDIO_VOL, IC_AUDIO_VOL_MUTE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039 break;
1040 }
1041
1042 case AudioManager.STREAM_VOICE_CALL: {
1043 /*
1044 * For in-call voice call volume, there is no inaudible volume.
1045 * Rescale the UI control so the progress bar doesn't go all
1046 * the way to zero and don't show the mute icon.
1047 */
1048 index++;
1049 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 break;
1051 }
1052
1053 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 break;
1055 }
1056
1057 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001058 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1059 mContext, RingtoneManager.TYPE_NOTIFICATION);
1060 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001061 mRingIsSilent = true;
1062 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 break;
1064 }
1065
1066 case AudioManager.STREAM_BLUETOOTH_SCO: {
1067 /*
1068 * For in-call voice call volume, there is no inaudible volume.
1069 * Rescale the UI control so the progress bar doesn't go all
1070 * the way to zero and don't show the mute icon.
1071 */
1072 index++;
1073 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 break;
1075 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001076
RoboErik2811dd32014-08-12 09:48:13 -07001077 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001078 if (controller == null && sc != null) {
1079 // If we weren't passed one try using the last one set.
1080 controller = sc.controller;
1081 }
1082 if (controller == null) {
1083 // We still don't have one, ignore the command.
1084 Log.w(mTag, "sent remote volume change without a controller!");
1085 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001086 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001087 index = vi.getCurrentVolume();
1088 max = vi.getMaxVolume();
1089 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1090 // if the remote volume is fixed add the flag for the UI
1091 flags |= AudioManager.FLAG_FIXED_VOLUME;
1092 }
1093 }
John Spurlock86005342014-05-23 11:58:00 -04001094 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001095 break;
1096 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001097 }
1098
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001099 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001100 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001101 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001102 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001103 }
1104 sc.controller = controller;
1105 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001106 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001107 }
1108 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001109 if (sc.seekbarView.getMax() != max) {
1110 sc.seekbarView.setMax(max);
1111 }
John Spurlockad494bc2014-07-19 15:56:19 -04001112 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001113 updateSliderEnabled(sc, isMuted(streamType),
1114 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
John Spurlockbb4a7022014-11-08 12:40:19 -05001115 // check for secondary-icon transition completion
1116 if (isNotificationOrRing(streamType) && mSecondaryIconTransition.isRunning()) {
1117 mSecondaryIconTransition.cancel(); // safe to reset
1118 sc.seekbarView.setAlpha(0); sc.seekbarView.animate().alpha(1);
1119 mZenPanel.setAlpha(0); mZenPanel.animate().alpha(1);
1120 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 }
1122
John Spurlock86005342014-05-23 11:58:00 -04001123 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001124 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001125 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1126 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001127 mDialog.show();
1128 if (mCallback != null) {
1129 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001130 }
Selim Cinek62ea3402014-09-08 12:11:51 +02001131 announceDialogShown();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 }
1133
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001134 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001135 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001136 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001137 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001138 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001139 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1140 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001141
1142 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001143 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1144 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001145 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 }
1147
Selim Cinek62ea3402014-09-08 12:11:51 +02001148 private void announceDialogShown() {
1149 mView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1150 }
1151
John Spurlock86005342014-05-23 11:58:00 -04001152 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001153 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001154 }
1155
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001156 protected void onPlaySound(int streamType, int flags) {
1157
1158 if (hasMessages(MSG_STOP_SOUNDS)) {
1159 removeMessages(MSG_STOP_SOUNDS);
1160 // Force stop right now
1161 onStopSounds();
1162 }
1163
1164 synchronized (this) {
1165 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001166 if (toneGen != null) {
1167 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1168 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1169 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001171 }
1172
1173 protected void onStopSounds() {
1174
1175 synchronized (this) {
1176 int numStreamTypes = AudioSystem.getNumStreamTypes();
1177 for (int i = numStreamTypes - 1; i >= 0; i--) {
1178 ToneGenerator toneGen = mToneGenerators[i];
1179 if (toneGen != null) {
1180 toneGen.stopTone();
1181 }
1182 }
1183 }
1184 }
1185
1186 protected void onVibrate() {
1187
1188 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001189 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001190 return;
1191 }
1192
John Spurlock7b414672014-07-18 13:02:39 -04001193 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001194 }
1195
RoboErik19c95182014-06-23 15:38:48 -07001196 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1197 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1198 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001199
John Spurlock86005342014-05-23 11:58:00 -04001200 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001201 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001202 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1203 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001204 }
RoboErik2811dd32014-08-12 09:48:13 -07001205 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001206 }
1207 } else {
John Spurlock86005342014-05-23 11:58:00 -04001208 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001209 }
1210
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001211 removeMessages(MSG_FREE_RESOURCES);
1212 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001213 resetTimeout();
1214 }
1215
1216 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001217 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1218 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001219 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001220 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001221 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001222 }
1223 }
1224
RoboErik19c95182014-06-23 15:38:48 -07001225 /**
1226 * Clear the current remote stream controller.
1227 */
1228 private void clearRemoteStreamController() {
1229 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001230 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001231 if (sc != null) {
1232 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001233 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001234 sc.controller = null;
1235 }
1236 }
1237 }
1238 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001239
1240 /**
RoboErik2811dd32014-08-12 09:48:13 -07001241 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1242 *
1243 * @param streamType can be a valid stream type value, or
1244 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001245 * @param visible
1246 */
1247 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001248 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001249 boolean isVisible = (visible == 1);
1250 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1251 StreamResources streamRes = STREAMS[i];
1252 if (streamRes.streamType == streamType) {
1253 streamRes.show = isVisible;
1254 if (!isVisible && (mActiveStreamType == streamType)) {
1255 mActiveStreamType = -1;
1256 }
1257 break;
1258 }
1259 }
1260 }
1261
Eric Laurentfde16d52012-12-03 14:42:39 -08001262 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001263 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1264 || isShowing()) {
1265 synchronized (sSafetyWarningLock) {
1266 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001267 return;
1268 }
John Spurlock35134602014-07-24 18:10:48 -04001269 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1270 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001271 }
1272 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001273 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001274 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1275 removeMessages(MSG_TIMEOUT);
1276 } else {
1277 updateTimeoutDelay();
1278 resetTimeout();
1279 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001280 }
1281
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001282 /**
1283 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1284 */
1285 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001286 if (streamType == STREAM_MASTER) {
1287 // For devices that use the master volume setting only but still want to
1288 // play a volume-changed tone, direct the master volume pseudostream to
1289 // the system stream's tone generator.
1290 if (mPlayMasterStreamTones) {
1291 streamType = AudioManager.STREAM_SYSTEM;
1292 } else {
1293 return null;
1294 }
1295 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001296 synchronized (this) {
1297 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001298 try {
1299 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1300 } catch (RuntimeException e) {
1301 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001302 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001303 + "RuntimeException: " + e);
1304 }
1305 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001306 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001307 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001308 }
1309 }
1310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001311
1312 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001313 * Switch between icons because Bluetooth music is same as music volume, but with
1314 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001315 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001316 private void setMusicIcon(int resId, int resMuteId) {
1317 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1318 if (sc != null) {
1319 sc.iconRes = resId;
1320 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001321 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001322 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001323 }
1324
1325 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001326 synchronized (this) {
1327 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1328 if (mToneGenerators[i] != null) {
1329 mToneGenerators[i].release();
1330 }
1331 mToneGenerators[i] = null;
1332 }
1333 }
1334 }
1335
1336 @Override
1337 public void handleMessage(Message msg) {
1338 switch (msg.what) {
1339
1340 case MSG_VOLUME_CHANGED: {
1341 onVolumeChanged(msg.arg1, msg.arg2);
1342 break;
1343 }
1344
Mike Lockwoodce952c82011-11-14 10:47:42 -08001345 case MSG_MUTE_CHANGED: {
1346 onMuteChanged(msg.arg1, msg.arg2);
1347 break;
1348 }
1349
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001350 case MSG_FREE_RESOURCES: {
1351 onFreeResources();
1352 break;
1353 }
1354
1355 case MSG_STOP_SOUNDS: {
1356 onStopSounds();
1357 break;
1358 }
1359
1360 case MSG_PLAY_SOUND: {
1361 onPlaySound(msg.arg1, msg.arg2);
1362 break;
1363 }
1364
1365 case MSG_VIBRATE: {
1366 onVibrate();
1367 break;
1368 }
1369
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001370 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001371 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001372 mDialog.dismiss();
1373 clearRemoteStreamController();
1374 mActiveStreamType = -1;
1375 if (mCallback != null) {
1376 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001377 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001378 }
John Spurlock35134602014-07-24 18:10:48 -04001379 synchronized (sSafetyWarningLock) {
1380 if (sSafetyWarning != null) {
1381 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1382 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001383 }
1384 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001385 break;
1386 }
John Spurlockb4782522014-08-22 14:54:46 -04001387
1388 case MSG_RINGER_MODE_CHANGED:
1389 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001390 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001391 updateStates();
1392 }
1393 break;
1394 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001395
1396 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001397 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001398 break;
1399 }
1400
1401 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1402 onRemoteVolumeUpdateIfShown();
1403 break;
1404
1405 case MSG_SLIDER_VISIBILITY_CHANGED:
1406 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1407 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001408
1409 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001410 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001411 break;
John Spurlock86005342014-05-23 11:58:00 -04001412
1413 case MSG_LAYOUT_DIRECTION:
1414 setLayoutDirection(msg.arg1);
1415 break;
1416
John Spurlock45601d62014-08-07 17:40:50 -04001417 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1418 mZenModeAvailable = msg.arg1 != 0;
1419 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001420 break;
1421
1422 case MSG_USER_ACTIVITY:
1423 if (mCallback != null) {
1424 mCallback.onInteraction();
1425 }
John Spurlock86005342014-05-23 11:58:00 -04001426 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001427 }
1428 }
1429
John Spurlockae641c92014-06-30 18:11:40 -04001430 private void resetTimeout() {
John Spurlocka0457c22014-09-26 13:22:08 -04001431 final boolean touchExploration = mAccessibilityManager.isTouchExplorationEnabled();
John Spurlockea9938c2014-07-11 18:51:32 -04001432 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
John Spurlocka0457c22014-09-26 13:22:08 -04001433 + " delay=" + mTimeoutDelay + " touchExploration=" + touchExploration);
1434 if (sSafetyWarning == null || !touchExploration) {
Selim Cinek1cf89062014-08-29 19:21:44 +02001435 removeMessages(MSG_TIMEOUT);
1436 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1437 removeMessages(MSG_USER_ACTIVITY);
1438 sendEmptyMessage(MSG_USER_ACTIVITY);
1439 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001440 }
1441
John Spurlock1dad2722014-07-11 11:07:53 -04001442 private void forceTimeout(long delay) {
John Spurlocka0457c22014-09-26 13:22:08 -04001443 if (LOGD) Log.d(mTag, "forceTimeout delay=" + delay + " callers=" + Debug.getCallers(3));
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001444 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001445 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001446 }
1447
1448 public ZenModeController getZenController() {
1449 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001450 }
1451
John Spurlockbb4a7022014-11-08 12:40:19 -05001452 @Override
1453 public void dispatchDemoCommand(String command, Bundle args) {
1454 if (!COMMAND_VOLUME.equals(command)) return;
1455 String icon = args.getString("icon");
1456 final String iconMute = args.getString("iconmute");
1457 final boolean mute = iconMute != null;
1458 icon = mute ? iconMute : icon;
1459 icon = icon.endsWith("Stream") ? icon : (icon + "Stream");
1460 final StreamResources sr = StreamResources.valueOf(icon);
1461 mDemoIcon = mute ? sr.iconMuteRes : sr.iconRes;
1462 final int forcedStreamType = StreamResources.MediaStream.streamType;
1463 mAudioManager.forceVolumeControlStream(forcedStreamType);
1464 mAudioManager.adjustStreamVolume(forcedStreamType, AudioManager.ADJUST_SAME,
1465 AudioManager.FLAG_SHOW_UI);
1466 }
1467
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001468 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1469 @Override
1470 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1471 final Object tag = seekBar.getTag();
1472 if (fromUser && tag instanceof StreamControl) {
1473 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001474 setStreamVolume(sc, progress,
1475 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001476 }
1477 resetTimeout();
1478 }
1479
1480 @Override
1481 public void onStartTrackingTouch(SeekBar seekBar) {
1482 }
1483
1484 @Override
1485 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001486 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001487 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001488
John Spurlock86005342014-05-23 11:58:00 -04001489 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001490 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001491 public void onZenAvailableChanged(boolean available) {
1492 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001493 }
John Spurlockb4782522014-08-22 14:54:46 -04001494 @Override
1495 public void onEffectsSupressorChanged() {
1496 obtainMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED,
1497 mZenController.getEffectsSuppressor()).sendToTarget();
1498 }
John Spurlock86005342014-05-23 11:58:00 -04001499 };
RoboErik19c95182014-06-23 15:38:48 -07001500
1501 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001502 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001503 onRemoteVolumeUpdateIfShown();
1504 }
1505 };
John Spurlockae641c92014-06-30 18:11:40 -04001506
John Spurlockbb4a7022014-11-08 12:40:19 -05001507 private final class SecondaryIconTransition extends AnimatorListenerAdapter
1508 implements Runnable {
1509 private static final int ANIMATION_TIME = 400;
1510 private static final int WAIT_FOR_SWITCH_TIME = 1000;
1511
1512 private final int mAnimationTime = (int)(ANIMATION_TIME * ValueAnimator.getDurationScale());
1513 private final int mFadeOutTime = mAnimationTime / 2;
1514 private final int mDelayTime = mAnimationTime / 3;
1515
1516 private final Interpolator mIconInterpolator =
1517 AnimationUtils.loadInterpolator(mContext, android.R.interpolator.fast_out_slow_in);
1518
1519 private StreamControl mTarget;
1520
1521 public void start(StreamControl sc) {
1522 if (sc == null) throw new IllegalArgumentException();
1523 if (mTarget != null) {
1524 cancel();
1525 }
1526 mTarget = sc;
1527 mTimeoutDelay = mAnimationTime + WAIT_FOR_SWITCH_TIME;
1528 resetTimeout();
1529 mTarget.secondaryIcon.setClickable(false);
1530 final int N = mTarget.group.getChildCount();
1531 for (int i = 0; i < N; i++) {
1532 final View child = mTarget.group.getChildAt(i);
1533 if (child != mTarget.secondaryIcon) {
1534 child.animate().alpha(0).setDuration(mFadeOutTime).start();
1535 }
1536 }
1537 mTarget.secondaryIcon.animate()
1538 .translationXBy(mTarget.icon.getX() - mTarget.secondaryIcon.getX())
1539 .setInterpolator(mIconInterpolator)
1540 .setStartDelay(mDelayTime)
1541 .setDuration(mAnimationTime - mDelayTime)
1542 .setListener(this)
1543 .start();
1544 }
1545
1546 public boolean isRunning() {
1547 return mTarget != null;
1548 }
1549
1550 public void cancel() {
1551 if (mTarget == null) return;
1552 mTarget.secondaryIcon.setClickable(true);
1553 final int N = mTarget.group.getChildCount();
1554 for (int i = 0; i < N; i++) {
1555 final View child = mTarget.group.getChildAt(i);
1556 if (child != mTarget.secondaryIcon) {
1557 child.animate().cancel();
1558 child.setAlpha(1);
1559 }
1560 }
1561 mTarget.secondaryIcon.animate().cancel();
1562 mTarget.secondaryIcon.setTranslationX(0);
1563 mTarget = null;
1564 }
1565
1566 @Override
1567 public void onAnimationEnd(Animator animation) {
1568 if (mTarget == null) return;
1569 AsyncTask.execute(this);
1570 }
1571
1572 @Override
1573 public void run() {
1574 if (mTarget == null) return;
1575 mAudioManager.forceVolumeControlStream(StreamResources.NotificationStream.streamType);
1576 mAudioManager.adjustStreamVolume(StreamResources.NotificationStream.streamType,
1577 AudioManager.ADJUST_SAME, AudioManager.FLAG_SHOW_UI);
1578 }
1579 }
1580
John Spurlockae641c92014-06-30 18:11:40 -04001581 public interface Callback {
1582 void onZenSettings();
1583 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001584 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001585 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001586}