blob: 149d09a0e4009cd7cd294949d96e9d8d87996463 [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
Eric Laurentc34dcc12012-09-10 13:51:52 -070019import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080020import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080021import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040024import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080026import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040028import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040029import android.graphics.PixelFormat;
30import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040031import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.media.AudioManager;
33import android.media.AudioService;
34import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070035import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070037import android.media.VolumeProvider;
38import android.media.session.MediaController;
39import android.media.session.MediaController.VolumeInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070040import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080041import android.os.Handler;
42import android.os.Message;
43import android.os.Vibrator;
John Spurlockae641c92014-06-30 18:11:40 -040044import android.provider.Settings.Global;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040046import android.util.SparseArray;
John Spurlock3346a802014-05-20 16:25:37 -040047import android.view.Gravity;
48import android.view.LayoutInflater;
49import android.view.MotionEvent;
50import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040051import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040052import android.view.ViewGroup;
53import android.view.Window;
54import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070055import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080057import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080058import android.widget.SeekBar.OnSeekBarChangeListener;
59
John Spurlock86005342014-05-23 11:58:00 -040060import com.android.internal.R;
John Spurlock35134602014-07-24 18:10:48 -040061import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040062import com.android.systemui.statusbar.policy.ZenModeController;
63
John Spurlockad494bc2014-07-19 15:56:19 -040064import java.io.FileDescriptor;
65import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67/**
John Spurlock3346a802014-05-20 16:25:37 -040068 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070069 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 * @hide
71 */
John Spurlock3346a802014-05-20 16:25:37 -040072public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040073 private static final String TAG = "VolumePanel";
74 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075
John Spurlock3346a802014-05-20 16:25:37 -040076 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077
78 /**
79 * The delay before vibrating. This small period exists so if the user is
80 * moving to silent mode, it will not emit a short vibrate (it normally
81 * would since vibrate is between normal mode and silent mode using hardware
82 * keys).
83 */
84 public static final int VIBRATE_DELAY = 300;
85
86 private static final int VIBRATE_DURATION = 300;
87 private static final int BEEP_DURATION = 150;
88 private static final int MAX_VOLUME = 100;
89 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080090 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040091 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040092 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -040093 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -040094 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095
96 private static final int MSG_VOLUME_CHANGED = 0;
97 private static final int MSG_FREE_RESOURCES = 1;
98 private static final int MSG_PLAY_SOUND = 2;
99 private static final int MSG_STOP_SOUNDS = 3;
100 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800101 private static final int MSG_TIMEOUT = 5;
102 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800103 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700104 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
105 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
106 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700107 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400108 private static final int MSG_LAYOUT_DIRECTION = 12;
109 private static final int MSG_ZEN_MODE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400110 private static final int MSG_USER_ACTIVITY = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800111
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400112 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800113 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700114 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400115
John Spurlock7b414672014-07-18 13:02:39 -0400116 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
117 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
118 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
119 .build();
120
John Spurlock86005342014-05-23 11:58:00 -0400121 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400122 protected final Context mContext;
123 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400124 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700125 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700126 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400127 private boolean mZenModeCapable;
John Spurlock8845da72014-07-07 21:29:48 -0400128 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400129 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400130 private float mDisabledAlpha;
131 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
132 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133
Christopher Tatec4b78d22012-05-22 13:57:58 -0700134 // True if we want to play tones on the system stream when the master stream is specified.
135 private final boolean mPlayMasterStreamTones;
136
John Spurlock86005342014-05-23 11:58:00 -0400137
138 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400140 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400141 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800142
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700143 /** The visible portion of the volume overlay */
144 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400145 /** Contains the slider and its touchable icons */
146 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400147 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400148 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400149
John Spurlockae641c92014-06-30 18:11:40 -0400150 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800151
152 /** Currently active stream that shows up at the top of the list of sliders */
153 private int mActiveStreamType = -1;
154 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400155 private SparseArray<StreamControl> mStreamControls;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800156
Amith Yamasani71def772011-10-12 12:25:24 -0700157 private enum StreamResources {
158 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
159 R.string.volume_icon_description_bluetooth,
160 R.drawable.ic_audio_bt,
161 R.drawable.ic_audio_bt,
162 false),
163 RingerStream(AudioManager.STREAM_RING,
164 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400165 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400166 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700167 false),
168 VoiceStream(AudioManager.STREAM_VOICE_CALL,
169 R.string.volume_icon_description_incall,
170 R.drawable.ic_audio_phone,
171 R.drawable.ic_audio_phone,
172 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700173 AlarmStream(AudioManager.STREAM_ALARM,
174 R.string.volume_alarm,
175 R.drawable.ic_audio_alarm,
176 R.drawable.ic_audio_alarm_mute,
177 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700178 MediaStream(AudioManager.STREAM_MUSIC,
179 R.string.volume_icon_description_media,
180 R.drawable.ic_audio_vol,
181 R.drawable.ic_audio_vol_mute,
182 true),
183 NotificationStream(AudioManager.STREAM_NOTIFICATION,
184 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400185 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400186 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400187 true),
188 // for now, use media resources for master volume
189 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700190 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400191 R.drawable.ic_audio_vol,
192 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700193 false),
194 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
195 R.string.volume_icon_description_media, //FIXME should have its own description
196 R.drawable.ic_media_route_on_holo_dark,
197 R.drawable.ic_media_route_disabled_holo_dark,
198 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700199
200 int streamType;
201 int descRes;
202 int iconRes;
203 int iconMuteRes;
204 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
205 boolean show;
206
207 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
208 this.streamType = streamType;
209 this.descRes = descRes;
210 this.iconRes = iconRes;
211 this.iconMuteRes = iconMuteRes;
212 this.show = show;
213 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700214 }
Amith Yamasani71def772011-10-12 12:25:24 -0700215
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800216 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700217 private static final StreamResources[] STREAMS = {
218 StreamResources.BluetoothSCOStream,
219 StreamResources.RingerStream,
220 StreamResources.VoiceStream,
221 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700222 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400223 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700224 StreamResources.MasterStream,
225 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800226 };
227
228 /** Object that contains data for each slider */
229 private class StreamControl {
230 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700231 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800232 ViewGroup group;
233 ImageView icon;
234 SeekBar seekbarView;
235 int iconRes;
236 int iconMuteRes;
237 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238
239 // Synchronize when accessing this
240 private ToneGenerator mToneGenerators[];
241 private Vibrator mVibrator;
242
John Spurlock35134602014-07-24 18:10:48 -0400243 private static AlertDialog sSafetyWarning;
244 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700245
John Spurlock35134602014-07-24 18:10:48 -0400246 private static class SafetyWarning extends SystemUIDialog
247 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800248 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800249 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400250 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700251
John Spurlock35134602014-07-24 18:10:48 -0400252 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
253 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700254 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800255 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400256 mAudioManager = audioManager;
257
258 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
259 setButton(DialogInterface.BUTTON_POSITIVE,
260 mContext.getString(com.android.internal.R.string.yes), this);
261 setButton(DialogInterface.BUTTON_NEGATIVE,
262 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
263 setOnDismissListener(this);
264
Eric Laurentc34dcc12012-09-10 13:51:52 -0700265 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400266 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700267 }
268
269 @Override
John Spurlock35134602014-07-24 18:10:48 -0400270 public void onClick(DialogInterface dialog, int which) {
271 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700272 }
273
Alan Viverette494fb7b2014-04-10 18:12:56 -0700274 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700275 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400276 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800277 cleanUp();
278 }
279
280 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400281 synchronized (sSafetyWarningLock) {
282 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700283 }
John Spurlock1dad2722014-07-11 11:07:53 -0400284 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800285 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700286 }
John Spurlock35134602014-07-24 18:10:48 -0400287
288 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
289 @Override
290 public void onReceive(Context context, Intent intent) {
291 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
292 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
293 cancel();
294 cleanUp();
295 }
296 }
297 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700298 }
299
John Spurlockeb2727b2014-07-19 23:11:36 -0400300 public VolumePanel(Context context, ZenModeController zenController) {
301 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800302 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400303 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800304 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800305
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400306 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700307 final Resources res = context.getResources();
308 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400309 if (useMasterVolume) {
310 for (int i = 0; i < STREAMS.length; i++) {
311 StreamResources streamRes = STREAMS[i];
312 streamRes.show = (streamRes.streamType == STREAM_MASTER);
313 }
314 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400315 if (LOGD) Log.d(mTag, "new VolumePanel");
316
John Spurlockad494bc2014-07-19 15:56:19 -0400317 mDisabledAlpha = 0.5f;
318 if (mContext.getTheme() != null) {
319 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
320 new int[] { android.R.attr.disabledAlpha });
321 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
322 arr.recycle();
323 }
324
John Spurlockeb2727b2014-07-19 23:11:36 -0400325 mDialog = new Dialog(context) {
326 @Override
327 public boolean onTouchEvent(MotionEvent event) {
328 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400329 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400330 forceTimeout(0);
331 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700332 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400333 return false;
334 }
335 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700336
John Spurlockeb2727b2014-07-19 23:11:36 -0400337 // Change some window properties
338 final Window window = mDialog.getWindow();
339 final LayoutParams lp = window.getAttributes();
340 lp.token = null;
341 // Offset from the top
342 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
343 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
344 lp.format = PixelFormat.TRANSLUCENT;
345 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
346 lp.gravity = Gravity.TOP;
347 window.setAttributes(lp);
348 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
349 window.requestFeature(Window.FEATURE_NO_TITLE);
350 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
351 | LayoutParams.FLAG_NOT_TOUCH_MODAL
352 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
353 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
354 mDialog.setCanceledOnTouchOutside(true);
355 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
356 mDialog.setOnDismissListener(new OnDismissListener() {
357 @Override
358 public void onDismiss(DialogInterface dialog) {
359 mActiveStreamType = -1;
360 mAudioManager.forceVolumeControlStream(mActiveStreamType);
361 setZenPanelVisible(false);
362 }
363 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700364
John Spurlockeb2727b2014-07-19 23:11:36 -0400365 mDialog.create();
366 // temporary workaround, until we support window-level shadows
367 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700368
John Spurlockeb2727b2014-07-19 23:11:36 -0400369 mView = window.findViewById(R.id.content);
370 mView.setOnTouchListener(new View.OnTouchListener() {
371 @Override
372 public boolean onTouch(View v, MotionEvent event) {
373 resetTimeout();
374 return false;
375 }
376 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700377
John Spurlock86005342014-05-23 11:58:00 -0400378 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
379 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400380 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
381 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700384 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700385 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700386
John Spurlock86005342014-05-23 11:58:00 -0400387 mZenModeCapable = !useMasterVolume && mZenController != null;
John Spurlockae641c92014-06-30 18:11:40 -0400388 updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
John Spurlock86005342014-05-23 11:58:00 -0400389 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700390
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700391 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
392 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700393 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
394
John Spurlock8c79d2e2014-07-24 15:15:25 -0400395 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800396 }
397
John Spurlockad494bc2014-07-19 15:56:19 -0400398 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
399 pw.println("VolumePanel state:");
400 pw.print(" mTag="); pw.println(mTag);
401 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
402 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
403 pw.print(" mZenModeCapable="); pw.println(mZenModeCapable);
404 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
405 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
406 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
407 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
408 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
409 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
410 pw.print(" isShowing()="); pw.println(isShowing());
411 pw.print(" mCallback="); pw.println(mCallback);
412 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400413 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400414 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
415 pw.print(" mStreamControls=");
416 if (mStreamControls == null) {
417 pw.println("null");
418 } else {
419 final int N = mStreamControls.size();
420 pw.print("<size "); pw.print(N); pw.println('>');
421 for (int i = 0; i < N; i++) {
422 final StreamControl sc = mStreamControls.valueAt(i);
423 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
424 if (sc.seekbarView != null) {
425 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
426 pw.print(" of "); pw.print(sc.seekbarView.getMax());
427 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
428 }
429 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
430 pw.println();
431 }
432 }
433 }
434
John Spurlockeb2727b2014-07-19 23:11:36 -0400435 private void initZenModePanel() {
436 mZenPanel.init(mZenController);
437 mZenPanel.setCallback(new ZenModePanel.Callback() {
438 @Override
439 public void onMoreSettings() {
440 if (mCallback != null) {
441 mCallback.onZenSettings();
442 }
443 }
444
445 @Override
446 public void onInteraction() {
447 resetTimeout();
448 }
449
450 @Override
451 public void onExpanded(boolean expanded) {
452 if (mZenPanelExpanded == expanded) return;
453 mZenPanelExpanded = expanded;
454 updateTimeoutDelay();
455 resetTimeout();
456 }
457 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400458 }
459
John Spurlock86005342014-05-23 11:58:00 -0400460 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800461 mPanel.setLayoutDirection(layoutDirection);
462 updateStates();
463 }
464
John Spurlock8c79d2e2014-07-24 15:15:25 -0400465 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800466 final IntentFilter filter = new IntentFilter();
467 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400468 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800469 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700470 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800471 public void onReceive(Context context, Intent intent) {
472 final String action = intent.getAction();
473
474 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
475 removeMessages(MSG_RINGER_MODE_CHANGED);
476 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
477 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400478
479 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
480 postDismiss(0);
481 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800482 }
483 }, filter);
484 }
485
486 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400487 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700488 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700489 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700490 // TODO do we need to support a distinct mute property for remote?
491 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400492 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700493 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400494 }
495 }
496
497 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400498 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700499 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700500 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700501 if (mStreamControls != null) {
502 StreamControl sc = mStreamControls.get(streamType);
503 if (sc != null && sc.controller != null) {
504 VolumeInfo vi = sc.controller.getVolumeInfo();
505 return vi.getMaxVolume();
506 }
507 }
508 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400509 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700510 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400511 }
512 }
513
514 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400515 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700516 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700517 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700518 if (mStreamControls != null) {
519 StreamControl sc = mStreamControls.get(streamType);
520 if (sc != null && sc.controller != null) {
521 VolumeInfo vi = sc.controller.getVolumeInfo();
522 return vi.getCurrentVolume();
523 }
524 }
525 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400526 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700527 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400528 }
529 }
530
RoboErik19c95182014-06-23 15:38:48 -0700531 private void setStreamVolume(StreamControl sc, int index, int flags) {
532 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
533 if (sc.controller != null) {
534 sc.controller.setVolumeTo(index, flags);
535 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700536 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700537 }
538 } else if (getStreamVolume(sc.streamType) != index) {
539 if (sc.streamType == STREAM_MASTER) {
540 mAudioManager.setMasterVolume(index, flags);
541 } else {
542 mAudioManager.setStreamVolume(sc.streamType, index, flags);
543 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400544 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800545 }
546
547 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700548 final Resources res = mContext.getResources();
549 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
550 Context.LAYOUT_INFLATER_SERVICE);
551
John Spurlockad494bc2014-07-19 15:56:19 -0400552 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700553
Amith Yamasani71def772011-10-12 12:25:24 -0700554 for (int i = 0; i < STREAMS.length; i++) {
555 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700556
557 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700558
559 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700560 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400561 sc.group = (ViewGroup) inflater.inflate(
562 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800563 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400564 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800565 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700566 sc.icon.setContentDescription(res.getString(streamRes.descRes));
567 sc.iconRes = streamRes.iconRes;
568 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800569 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400570 sc.icon.setClickable(isNotificationOrRing(streamType));
571 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400572 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400573 sc.icon.setOnClickListener(new OnClickListener() {
574 @Override
575 public void onClick(View v) {
576 resetTimeout();
577 toggle(sc);
578 }
579 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400580 }
John Spurlock86005342014-05-23 11:58:00 -0400581 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700582 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700583 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400584 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700585 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800586 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700587 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800588 }
589 }
590
John Spurlock7f1df5e2014-05-31 19:11:40 -0400591 private void toggle(StreamControl sc) {
592 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
593 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
594 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
595 } else {
596 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
597 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
598 }
599 }
600
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800601 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400602 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800603
Alan Viverette494fb7b2014-04-10 18:12:56 -0700604 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800605 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400606 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800607 mActiveStreamType = -1;
608 } else {
John Spurlock86005342014-05-23 11:58:00 -0400609 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800610 mActiveStreamType = activeStreamType;
611 active.group.setVisibility(View.VISIBLE);
612 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400613 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400614 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800615 }
616 }
617
John Spurlockad494bc2014-07-19 15:56:19 -0400618 private void updateSliderProgress(StreamControl sc, int progress) {
619 final boolean isRinger = isNotificationOrRing(sc.streamType);
620 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
621 progress = mLastRingerProgress;
622 }
623 if (progress < 0) {
624 progress = getStreamVolume(sc.streamType);
625 }
626 sc.seekbarView.setProgress(progress);
627 if (isRinger) {
628 mLastRingerProgress = progress;
629 }
630 }
631
632 private void updateSliderIcon(StreamControl sc, boolean muted) {
633 if (isNotificationOrRing(sc.streamType)) {
634 int ringerMode = mAudioManager.getRingerMode();
635 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
636 ringerMode = mLastRingerMode;
637 } else {
638 mLastRingerMode = ringerMode;
639 }
640 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
641 }
642 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
643 }
644
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800645 /** Update the mute and progress state of a slider */
646 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400647 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800648 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800649 // Force reloading the image resource
650 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400651 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400652 updateSliderEnabled(sc, muted, false);
653 }
654
John Spurlock5f640e42014-05-31 20:15:59 -0400655 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
656 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400657 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700658 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
659 // never disable touch interactions for remote playback, the muting is not tied to
660 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700661 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400662 } else if (fixedVolume ||
663 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
John Spurlock35134602014-07-24 18:10:48 -0400664 (sSafetyWarning != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700665 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400666 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400667 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400668 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400669 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400670 sc.icon.setClickable(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700671 } else {
672 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400673 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400674 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700675 }
John Spurlockae641c92014-06-30 18:11:40 -0400676 // show the silent hint when the disabled slider is touched in silent mode
677 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400678 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400679 sc.group.setOnTouchListener(null);
680 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400681 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400682 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400683 @Override
684 public boolean onTouch(View v, MotionEvent event) {
685 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400686 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400687 return false;
688 }
John Spurlockae641c92014-06-30 18:11:40 -0400689 };
690 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400691 }
692 }
693 }
694
John Spurlockae641c92014-06-30 18:11:40 -0400695 private void showSilentHint() {
696 if (mZenPanel != null) {
697 mZenPanel.showSilentHint();
698 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800699 }
700
John Spurlock7f1df5e2014-05-31 19:11:40 -0400701 private static boolean isNotificationOrRing(int streamType) {
702 return streamType == AudioManager.STREAM_RING
703 || streamType == AudioManager.STREAM_NOTIFICATION;
704 }
705
John Spurlockae641c92014-06-30 18:11:40 -0400706 public void setCallback(Callback callback) {
707 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400708 }
709
John Spurlock8845da72014-07-07 21:29:48 -0400710 private void updateTimeoutDelay() {
John Spurlock35134602014-07-24 18:10:48 -0400711 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
712 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400713 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
714 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
715 : TIMEOUT_DELAY;
716 }
717
718 private boolean isZenPanelVisible() {
719 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400720 }
721
John Spurlockae641c92014-06-30 18:11:40 -0400722 private void setZenPanelVisible(boolean visible) {
723 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400724 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400725 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400726 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400727 resetTimeout();
728 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400729 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800730 }
John Spurlockea9938c2014-07-11 18:51:32 -0400731 if (changing) {
732 updateTimeoutDelay();
733 resetTimeout();
734 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800735 }
736
Eric Laurentfde16d52012-12-03 14:42:39 -0800737 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400738 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800739 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400740 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800741 updateSlider(sc);
742 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 }
744
John Spurlockae641c92014-06-30 18:11:40 -0400745 private void updateZenMode(int zen) {
746 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
747 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400748 }
749
John Spurlockae641c92014-06-30 18:11:40 -0400750 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400751 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400752 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400753 }
754
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800755 public void postVolumeChanged(int streamType, int flags) {
756 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700757 synchronized (this) {
758 if (mStreamControls == null) {
759 createSliders();
760 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 removeMessages(MSG_FREE_RESOURCES);
763 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
764 }
765
RoboErik19c95182014-06-23 15:38:48 -0700766 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700767 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
768 synchronized (this) {
769 if (mStreamControls == null) {
770 createSliders();
771 }
772 }
773 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700774 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700775 }
776
777 public void postRemoteSliderVisibility(boolean visible) {
778 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
779 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
780 }
781
782 /**
783 * Called by AudioService when it has received new remote playback information that
784 * would affect the VolumePanel display (mainly volumes). The difference with
785 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
786 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
787 * displayed.
788 * This special code path is due to the fact that remote volume updates arrive to AudioService
789 * asynchronously. So after AudioService has sent the volume update (which should be treated
790 * as a request to update the volume), the application will likely set a new volume. If the UI
791 * is still up, we need to refresh the display to show this new value.
792 */
793 public void postHasNewRemotePlaybackInfo() {
794 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
795 // don't create or prevent resources to be freed, if they disappear, this update came too
796 // late and shouldn't warrant the panel to be displayed longer
797 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
798 }
799
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400800 public void postMasterVolumeChanged(int flags) {
801 postVolumeChanged(STREAM_MASTER, flags);
802 }
803
Mike Lockwoodce952c82011-11-14 10:47:42 -0800804 public void postMuteChanged(int streamType, int flags) {
805 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700806 synchronized (this) {
807 if (mStreamControls == null) {
808 createSliders();
809 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800810 }
811 removeMessages(MSG_FREE_RESOURCES);
812 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
813 }
814
815 public void postMasterMuteChanged(int flags) {
816 postMuteChanged(STREAM_MASTER, flags);
817 }
818
Eric Laurentfde16d52012-12-03 14:42:39 -0800819 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700820 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800821 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700822 }
823
John Spurlock1dad2722014-07-11 11:07:53 -0400824 public void postDismiss(long delay) {
825 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400826 }
827
828 public void postLayoutDirection(int layoutDirection) {
829 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400830 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400831 }
832
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800833 /**
834 * Override this if you have other work to do when the volume changes (for
835 * example, vibrating, playing a sound, etc.). Make sure to call through to
836 * the superclass implementation.
837 */
838 protected void onVolumeChanged(int streamType, int flags) {
839
John Spurlock86005342014-05-23 11:58:00 -0400840 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800841
842 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700843 synchronized (this) {
844 if (mActiveStreamType != streamType) {
845 reorderSliders(streamType);
846 }
RoboErik19c95182014-06-23 15:38:48 -0700847 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800848 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 }
850
Marco Nelissen69f593c2009-07-28 09:55:04 -0700851 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 removeMessages(MSG_PLAY_SOUND);
853 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
854 }
855
856 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
857 removeMessages(MSG_PLAY_SOUND);
858 removeMessages(MSG_VIBRATE);
859 onStopSounds();
860 }
861
862 removeMessages(MSG_FREE_RESOURCES);
863 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800864 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 }
866
Mike Lockwoodce952c82011-11-14 10:47:42 -0800867 protected void onMuteChanged(int streamType, int flags) {
868
John Spurlock86005342014-05-23 11:58:00 -0400869 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800870
871 StreamControl sc = mStreamControls.get(streamType);
872 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400873 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800874 }
875
876 onVolumeChanged(streamType, flags);
877 }
878
RoboErik19c95182014-06-23 15:38:48 -0700879 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700880 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800881
Marco Nelissen69f593c2009-07-28 09:55:04 -0700882 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883
884 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400885 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 + ", flags: " + flags + "), index: " + index);
887 }
888
889 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800890
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400891 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700892 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800893
894 switch (streamType) {
895
896 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800897// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700898 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
899 mContext, RingtoneManager.TYPE_RINGTONE);
900 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700901 mRingIsSilent = true;
902 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 break;
904 }
905
906 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800907 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800908 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
909 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
910 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
911 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800912 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800914 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 }
916 break;
917 }
918
919 case AudioManager.STREAM_VOICE_CALL: {
920 /*
921 * For in-call voice call volume, there is no inaudible volume.
922 * Rescale the UI control so the progress bar doesn't go all
923 * the way to zero and don't show the mute icon.
924 */
925 index++;
926 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927 break;
928 }
929
930 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 break;
932 }
933
934 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700935 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
936 mContext, RingtoneManager.TYPE_NOTIFICATION);
937 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700938 mRingIsSilent = true;
939 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800940 break;
941 }
942
943 case AudioManager.STREAM_BLUETOOTH_SCO: {
944 /*
945 * For in-call voice call volume, there is no inaudible volume.
946 * Rescale the UI control so the progress bar doesn't go all
947 * the way to zero and don't show the mute icon.
948 */
949 index++;
950 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 break;
952 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700953
954 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700955 if (controller == null && sc != null) {
956 // If we weren't passed one try using the last one set.
957 controller = sc.controller;
958 }
959 if (controller == null) {
960 // We still don't have one, ignore the command.
961 Log.w(mTag, "sent remote volume change without a controller!");
962 } else {
963 VolumeInfo vi = controller.getVolumeInfo();
964 index = vi.getCurrentVolume();
965 max = vi.getMaxVolume();
966 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
967 // if the remote volume is fixed add the flag for the UI
968 flags |= AudioManager.FLAG_FIXED_VOLUME;
969 }
970 }
John Spurlock86005342014-05-23 11:58:00 -0400971 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700972 break;
973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 }
975
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800976 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700977 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
978 if (sc.controller != null) {
979 sc.controller.removeCallback(mMediaControllerCb);
980 }
981 sc.controller = controller;
982 if (controller != null) {
983 sc.controller.addCallback(mMediaControllerCb);
984 }
985 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700986 if (sc.seekbarView.getMax() != max) {
987 sc.seekbarView.setMax(max);
988 }
John Spurlockad494bc2014-07-19 15:56:19 -0400989 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400990 updateSliderEnabled(sc, isMuted(streamType),
991 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800992 }
993
John Spurlock86005342014-05-23 11:58:00 -0400994 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700995 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
996 // when the stream is for remote playback, use -1 to reset the stream type evaluation
997 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700998
John Spurlockeb2727b2014-07-19 23:11:36 -0400999 mDialog.show();
1000 if (mCallback != null) {
1001 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001002 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001003 }
1004
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001005 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001006 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
1007 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001008 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001009 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001010 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1011 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001012
1013 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001014 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1015 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001016 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
1018
John Spurlock86005342014-05-23 11:58:00 -04001019 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001020 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001021 }
1022
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001023 protected void onPlaySound(int streamType, int flags) {
1024
1025 if (hasMessages(MSG_STOP_SOUNDS)) {
1026 removeMessages(MSG_STOP_SOUNDS);
1027 // Force stop right now
1028 onStopSounds();
1029 }
1030
1031 synchronized (this) {
1032 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001033 if (toneGen != null) {
1034 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1035 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1036 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001037 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 }
1039
1040 protected void onStopSounds() {
1041
1042 synchronized (this) {
1043 int numStreamTypes = AudioSystem.getNumStreamTypes();
1044 for (int i = numStreamTypes - 1; i >= 0; i--) {
1045 ToneGenerator toneGen = mToneGenerators[i];
1046 if (toneGen != null) {
1047 toneGen.stopTone();
1048 }
1049 }
1050 }
1051 }
1052
1053 protected void onVibrate() {
1054
1055 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001056 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 return;
1058 }
1059
John Spurlock7b414672014-07-18 13:02:39 -04001060 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 }
1062
RoboErik19c95182014-06-23 15:38:48 -07001063 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1064 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1065 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001066
John Spurlock86005342014-05-23 11:58:00 -04001067 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001068 synchronized (this) {
1069 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1070 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1071 }
RoboErik19c95182014-06-23 15:38:48 -07001072 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001073 }
1074 } else {
John Spurlock86005342014-05-23 11:58:00 -04001075 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001076 }
1077
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001078 removeMessages(MSG_FREE_RESOURCES);
1079 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001080 resetTimeout();
1081 }
1082
1083 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001084 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1085 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001086 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1087 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001088 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001089 }
1090 }
1091
RoboErik19c95182014-06-23 15:38:48 -07001092 /**
1093 * Clear the current remote stream controller.
1094 */
1095 private void clearRemoteStreamController() {
1096 if (mStreamControls != null) {
1097 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1098 if (sc != null) {
1099 if (sc.controller != null) {
1100 sc.controller.removeCallback(mMediaControllerCb);
1101 sc.controller = null;
1102 }
1103 }
1104 }
1105 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001106
1107 /**
1108 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1109 * Hide or show a slider
1110 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1111 * or AudioService.STREAM_REMOTE_MUSIC
1112 * @param visible
1113 */
1114 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001115 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001116 boolean isVisible = (visible == 1);
1117 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1118 StreamResources streamRes = STREAMS[i];
1119 if (streamRes.streamType == streamType) {
1120 streamRes.show = isVisible;
1121 if (!isVisible && (mActiveStreamType == streamType)) {
1122 mActiveStreamType = -1;
1123 }
1124 break;
1125 }
1126 }
1127 }
1128
Eric Laurentfde16d52012-12-03 14:42:39 -08001129 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001130 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1131 || isShowing()) {
1132 synchronized (sSafetyWarningLock) {
1133 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001134 return;
1135 }
John Spurlock35134602014-07-24 18:10:48 -04001136 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1137 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001138 }
1139 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001140 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001141 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001142 }
1143
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001144 /**
1145 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1146 */
1147 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001148 if (streamType == STREAM_MASTER) {
1149 // For devices that use the master volume setting only but still want to
1150 // play a volume-changed tone, direct the master volume pseudostream to
1151 // the system stream's tone generator.
1152 if (mPlayMasterStreamTones) {
1153 streamType = AudioManager.STREAM_SYSTEM;
1154 } else {
1155 return null;
1156 }
1157 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001158 synchronized (this) {
1159 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001160 try {
1161 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1162 } catch (RuntimeException e) {
1163 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001164 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001165 + "RuntimeException: " + e);
1166 }
1167 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001168 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001169 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001170 }
1171 }
1172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173
1174 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001175 * Switch between icons because Bluetooth music is same as music volume, but with
1176 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001178 private void setMusicIcon(int resId, int resMuteId) {
1179 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1180 if (sc != null) {
1181 sc.iconRes = resId;
1182 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001183 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001184 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001185 }
1186
1187 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001188 synchronized (this) {
1189 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1190 if (mToneGenerators[i] != null) {
1191 mToneGenerators[i].release();
1192 }
1193 mToneGenerators[i] = null;
1194 }
1195 }
1196 }
1197
1198 @Override
1199 public void handleMessage(Message msg) {
1200 switch (msg.what) {
1201
1202 case MSG_VOLUME_CHANGED: {
1203 onVolumeChanged(msg.arg1, msg.arg2);
1204 break;
1205 }
1206
Mike Lockwoodce952c82011-11-14 10:47:42 -08001207 case MSG_MUTE_CHANGED: {
1208 onMuteChanged(msg.arg1, msg.arg2);
1209 break;
1210 }
1211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001212 case MSG_FREE_RESOURCES: {
1213 onFreeResources();
1214 break;
1215 }
1216
1217 case MSG_STOP_SOUNDS: {
1218 onStopSounds();
1219 break;
1220 }
1221
1222 case MSG_PLAY_SOUND: {
1223 onPlaySound(msg.arg1, msg.arg2);
1224 break;
1225 }
1226
1227 case MSG_VIBRATE: {
1228 onVibrate();
1229 break;
1230 }
1231
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001232 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001233 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001234 mDialog.dismiss();
1235 clearRemoteStreamController();
1236 mActiveStreamType = -1;
1237 if (mCallback != null) {
1238 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001239 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001240 }
John Spurlock35134602014-07-24 18:10:48 -04001241 synchronized (sSafetyWarningLock) {
1242 if (sSafetyWarning != null) {
1243 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1244 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001245 }
1246 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001247 break;
1248 }
1249 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001250 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001251 updateStates();
1252 }
1253 break;
1254 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001255
1256 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001257 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001258 break;
1259 }
1260
1261 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1262 onRemoteVolumeUpdateIfShown();
1263 break;
1264
1265 case MSG_SLIDER_VISIBILITY_CHANGED:
1266 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1267 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001268
1269 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001270 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001271 break;
John Spurlock86005342014-05-23 11:58:00 -04001272
1273 case MSG_LAYOUT_DIRECTION:
1274 setLayoutDirection(msg.arg1);
1275 break;
1276
1277 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001278 updateZenMode(msg.arg1);
1279 break;
1280
1281 case MSG_USER_ACTIVITY:
1282 if (mCallback != null) {
1283 mCallback.onInteraction();
1284 }
John Spurlock86005342014-05-23 11:58:00 -04001285 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001286 }
1287 }
1288
John Spurlockae641c92014-06-30 18:11:40 -04001289 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001290 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1291 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001292 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001293 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001294 removeMessages(MSG_USER_ACTIVITY);
1295 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001296 }
1297
John Spurlock1dad2722014-07-11 11:07:53 -04001298 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001299 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001300 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001301 }
1302
1303 public ZenModeController getZenController() {
1304 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001305 }
1306
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001307 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1308 @Override
1309 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1310 final Object tag = seekBar.getTag();
1311 if (fromUser && tag instanceof StreamControl) {
1312 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001313 setStreamVolume(sc, progress,
1314 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001315 }
1316 resetTimeout();
1317 }
1318
1319 @Override
1320 public void onStartTrackingTouch(SeekBar seekBar) {
1321 }
1322
1323 @Override
1324 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001325 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001326 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001327
John Spurlock86005342014-05-23 11:58:00 -04001328 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001329 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001330 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001331 }
1332 };
RoboErik19c95182014-06-23 15:38:48 -07001333
1334 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1335 public void onVolumeInfoChanged(VolumeInfo info) {
1336 onRemoteVolumeUpdateIfShown();
1337 }
1338 };
John Spurlockae641c92014-06-30 18:11:40 -04001339
1340 public interface Callback {
1341 void onZenSettings();
1342 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001343 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001344 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001345}