blob: 51876b85778f02f8e83f23eea0836a9483a6b402 [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;
John Spurlock7e6809a2014-08-06 16:03:14 -040027import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040029import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040030import android.graphics.PixelFormat;
31import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040032import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080033import android.media.AudioManager;
34import android.media.AudioService;
35import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070036import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080037import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070038import android.media.VolumeProvider;
39import android.media.session.MediaController;
RoboErika66c40b2014-08-15 15:21:41 -070040import android.media.session.MediaController.AudioInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070041import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.os.Handler;
43import android.os.Message;
44import android.os.Vibrator;
John Spurlockae641c92014-06-30 18:11:40 -040045import android.provider.Settings.Global;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040047import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040048import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040049import android.view.LayoutInflater;
50import android.view.MotionEvent;
51import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040052import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040053import android.view.ViewGroup;
54import android.view.Window;
55import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070056import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080057import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080058import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080059import android.widget.SeekBar.OnSeekBarChangeListener;
60
John Spurlock86005342014-05-23 11:58:00 -040061import com.android.internal.R;
John Spurlock35134602014-07-24 18:10:48 -040062import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040063import com.android.systemui.statusbar.policy.ZenModeController;
64
John Spurlockad494bc2014-07-19 15:56:19 -040065import java.io.FileDescriptor;
66import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067
68/**
John Spurlock3346a802014-05-20 16:25:37 -040069 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070070 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 * @hide
72 */
John Spurlock3346a802014-05-20 16:25:37 -040073public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040074 private static final String TAG = "VolumePanel";
75 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076
John Spurlock3346a802014-05-20 16:25:37 -040077 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078
79 /**
80 * The delay before vibrating. This small period exists so if the user is
81 * moving to silent mode, it will not emit a short vibrate (it normally
82 * would since vibrate is between normal mode and silent mode using hardware
83 * keys).
84 */
85 public static final int VIBRATE_DELAY = 300;
86
87 private static final int VIBRATE_DURATION = 300;
88 private static final int BEEP_DURATION = 150;
89 private static final int MAX_VOLUME = 100;
90 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080091 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040092 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040093 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -040094 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -040095 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080096
97 private static final int MSG_VOLUME_CHANGED = 0;
98 private static final int MSG_FREE_RESOURCES = 1;
99 private static final int MSG_PLAY_SOUND = 2;
100 private static final int MSG_STOP_SOUNDS = 3;
101 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800102 private static final int MSG_TIMEOUT = 5;
103 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800104 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700105 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
106 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
107 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700108 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400109 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400110 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400111 private static final int MSG_USER_ACTIVITY = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800112
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400113 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800114 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700115 // Pseudo stream type for remote volume
116 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400117
John Spurlock7b414672014-07-18 13:02:39 -0400118 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
119 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
120 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
121 .build();
122
John Spurlock86005342014-05-23 11:58:00 -0400123 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400124 protected final Context mContext;
125 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400126 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700127 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700128 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400129 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400130 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400131 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400132 private float mDisabledAlpha;
133 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
134 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135
Christopher Tatec4b78d22012-05-22 13:57:58 -0700136 // True if we want to play tones on the system stream when the master stream is specified.
137 private final boolean mPlayMasterStreamTones;
138
John Spurlock86005342014-05-23 11:58:00 -0400139
140 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800141 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400142 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400143 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800144
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700145 /** The visible portion of the volume overlay */
146 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400147 /** Contains the slider and its touchable icons */
148 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400149 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400150 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400151
John Spurlockae641c92014-06-30 18:11:40 -0400152 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800153
154 /** Currently active stream that shows up at the top of the list of sliders */
155 private int mActiveStreamType = -1;
156 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400157 private SparseArray<StreamControl> mStreamControls;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800158
Amith Yamasani71def772011-10-12 12:25:24 -0700159 private enum StreamResources {
160 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
161 R.string.volume_icon_description_bluetooth,
162 R.drawable.ic_audio_bt,
163 R.drawable.ic_audio_bt,
164 false),
165 RingerStream(AudioManager.STREAM_RING,
166 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400167 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400168 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700169 false),
170 VoiceStream(AudioManager.STREAM_VOICE_CALL,
171 R.string.volume_icon_description_incall,
172 R.drawable.ic_audio_phone,
173 R.drawable.ic_audio_phone,
174 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700175 AlarmStream(AudioManager.STREAM_ALARM,
176 R.string.volume_alarm,
177 R.drawable.ic_audio_alarm,
178 R.drawable.ic_audio_alarm_mute,
179 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700180 MediaStream(AudioManager.STREAM_MUSIC,
181 R.string.volume_icon_description_media,
182 R.drawable.ic_audio_vol,
183 R.drawable.ic_audio_vol_mute,
184 true),
185 NotificationStream(AudioManager.STREAM_NOTIFICATION,
186 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400187 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400188 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400189 true),
190 // for now, use media resources for master volume
191 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700192 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400193 R.drawable.ic_audio_vol,
194 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700195 false),
RoboErik2811dd32014-08-12 09:48:13 -0700196 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700197 R.string.volume_icon_description_media, //FIXME should have its own description
198 R.drawable.ic_media_route_on_holo_dark,
199 R.drawable.ic_media_route_disabled_holo_dark,
200 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700201
202 int streamType;
203 int descRes;
204 int iconRes;
205 int iconMuteRes;
206 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
207 boolean show;
208
209 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
210 this.streamType = streamType;
211 this.descRes = descRes;
212 this.iconRes = iconRes;
213 this.iconMuteRes = iconMuteRes;
214 this.show = show;
215 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700216 }
Amith Yamasani71def772011-10-12 12:25:24 -0700217
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800218 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700219 private static final StreamResources[] STREAMS = {
220 StreamResources.BluetoothSCOStream,
221 StreamResources.RingerStream,
222 StreamResources.VoiceStream,
223 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700224 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400225 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700226 StreamResources.MasterStream,
227 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800228 };
229
230 /** Object that contains data for each slider */
231 private class StreamControl {
232 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700233 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800234 ViewGroup group;
235 ImageView icon;
236 SeekBar seekbarView;
237 int iconRes;
238 int iconMuteRes;
239 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800240
241 // Synchronize when accessing this
242 private ToneGenerator mToneGenerators[];
243 private Vibrator mVibrator;
244
John Spurlock35134602014-07-24 18:10:48 -0400245 private static AlertDialog sSafetyWarning;
246 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700247
John Spurlock35134602014-07-24 18:10:48 -0400248 private static class SafetyWarning extends SystemUIDialog
249 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800250 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800251 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400252 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700253
John Spurlock2d28d6e2014-08-01 13:10:14 -0400254 private boolean mNewVolumeUp;
255
John Spurlock35134602014-07-24 18:10:48 -0400256 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
257 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700258 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800259 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400260 mAudioManager = audioManager;
261
262 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
263 setButton(DialogInterface.BUTTON_POSITIVE,
264 mContext.getString(com.android.internal.R.string.yes), this);
265 setButton(DialogInterface.BUTTON_NEGATIVE,
266 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
267 setOnDismissListener(this);
268
Eric Laurentc34dcc12012-09-10 13:51:52 -0700269 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400270 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700271 }
272
273 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400274 public boolean onKeyDown(int keyCode, KeyEvent event) {
275 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
276 mNewVolumeUp = true;
277 }
278 return super.onKeyDown(keyCode, event);
279 }
280
281 @Override
282 public boolean onKeyUp(int keyCode, KeyEvent event) {
283 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
284 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
285 mAudioManager.disableSafeMediaVolume();
286 dismiss();
287 }
288 return super.onKeyUp(keyCode, event);
289 }
290
291 @Override
John Spurlock35134602014-07-24 18:10:48 -0400292 public void onClick(DialogInterface dialog, int which) {
293 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700294 }
295
Alan Viverette494fb7b2014-04-10 18:12:56 -0700296 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700297 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400298 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800299 cleanUp();
300 }
301
302 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400303 synchronized (sSafetyWarningLock) {
304 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700305 }
John Spurlock1dad2722014-07-11 11:07:53 -0400306 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800307 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700308 }
John Spurlock35134602014-07-24 18:10:48 -0400309
310 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
311 @Override
312 public void onReceive(Context context, Intent intent) {
313 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
314 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
315 cancel();
316 cleanUp();
317 }
318 }
319 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700320 }
321
John Spurlockeb2727b2014-07-19 23:11:36 -0400322 public VolumePanel(Context context, ZenModeController zenController) {
323 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800324 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400325 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800327
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400328 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700329 final Resources res = context.getResources();
330 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400331 if (useMasterVolume) {
332 for (int i = 0; i < STREAMS.length; i++) {
333 StreamResources streamRes = STREAMS[i];
334 streamRes.show = (streamRes.streamType == STREAM_MASTER);
335 }
336 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400337 if (LOGD) Log.d(mTag, "new VolumePanel");
338
John Spurlockad494bc2014-07-19 15:56:19 -0400339 mDisabledAlpha = 0.5f;
340 if (mContext.getTheme() != null) {
341 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
342 new int[] { android.R.attr.disabledAlpha });
343 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
344 arr.recycle();
345 }
346
John Spurlockeb2727b2014-07-19 23:11:36 -0400347 mDialog = new Dialog(context) {
348 @Override
349 public boolean onTouchEvent(MotionEvent event) {
350 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400351 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400352 forceTimeout(0);
353 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700354 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400355 return false;
356 }
357 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700358
John Spurlockeb2727b2014-07-19 23:11:36 -0400359 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400360 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400361 mDialog.setCanceledOnTouchOutside(true);
362 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
363 mDialog.setOnDismissListener(new OnDismissListener() {
364 @Override
365 public void onDismiss(DialogInterface dialog) {
366 mActiveStreamType = -1;
367 mAudioManager.forceVolumeControlStream(mActiveStreamType);
368 setZenPanelVisible(false);
369 }
370 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700371
John Spurlockeb2727b2014-07-19 23:11:36 -0400372 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700373
John Spurlock7e6809a2014-08-06 16:03:14 -0400374 final LayoutParams lp = window.getAttributes();
375 lp.token = null;
376 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
377 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
378 lp.format = PixelFormat.TRANSLUCENT;
379 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
380 lp.setTitle(TAG);
381 window.setAttributes(lp);
382
383 updateWidth();
384
385 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
386 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
387 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
388 | LayoutParams.FLAG_NOT_TOUCH_MODAL
389 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
390 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400391 mView = window.findViewById(R.id.content);
392 mView.setOnTouchListener(new View.OnTouchListener() {
393 @Override
394 public boolean onTouch(View v, MotionEvent event) {
395 resetTimeout();
396 return false;
397 }
398 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700399
John Spurlock86005342014-05-23 11:58:00 -0400400 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
401 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400402 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
403 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800404
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800405 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700406 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700407 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700408
John Spurlock45601d62014-08-07 17:40:50 -0400409 if (mZenController != null && !useMasterVolume) {
410 mZenModeAvailable = mZenController.isZenAvailable();
411 mZenController.addCallback(mZenCallback);
412 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700413
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700414 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
415 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700416 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
417
John Spurlock8c79d2e2014-07-24 15:15:25 -0400418 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800419 }
420
John Spurlock7e6809a2014-08-06 16:03:14 -0400421 public void onConfigurationChanged(Configuration newConfig) {
422 updateWidth();
423 }
424
425 private void updateWidth() {
426 final Resources res = mContext.getResources();
427 final LayoutParams lp = mDialog.getWindow().getAttributes();
428 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
429 lp.gravity =
430 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
431 mDialog.getWindow().setAttributes(lp);
432 }
433
John Spurlockad494bc2014-07-19 15:56:19 -0400434 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
435 pw.println("VolumePanel state:");
436 pw.print(" mTag="); pw.println(mTag);
437 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
438 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock45601d62014-08-07 17:40:50 -0400439 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400440 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
441 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
442 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
443 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
444 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
445 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
446 pw.print(" isShowing()="); pw.println(isShowing());
447 pw.print(" mCallback="); pw.println(mCallback);
448 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400449 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400450 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
451 pw.print(" mStreamControls=");
452 if (mStreamControls == null) {
453 pw.println("null");
454 } else {
455 final int N = mStreamControls.size();
456 pw.print("<size "); pw.print(N); pw.println('>');
457 for (int i = 0; i < N; i++) {
458 final StreamControl sc = mStreamControls.valueAt(i);
459 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
460 if (sc.seekbarView != null) {
461 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
462 pw.print(" of "); pw.print(sc.seekbarView.getMax());
463 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
464 }
465 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
466 pw.println();
467 }
468 }
469 }
470
John Spurlockeb2727b2014-07-19 23:11:36 -0400471 private void initZenModePanel() {
472 mZenPanel.init(mZenController);
473 mZenPanel.setCallback(new ZenModePanel.Callback() {
474 @Override
475 public void onMoreSettings() {
476 if (mCallback != null) {
477 mCallback.onZenSettings();
478 }
479 }
480
481 @Override
482 public void onInteraction() {
483 resetTimeout();
484 }
485
486 @Override
487 public void onExpanded(boolean expanded) {
488 if (mZenPanelExpanded == expanded) return;
489 mZenPanelExpanded = expanded;
490 updateTimeoutDelay();
491 resetTimeout();
492 }
493 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400494 }
495
John Spurlock86005342014-05-23 11:58:00 -0400496 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800497 mPanel.setLayoutDirection(layoutDirection);
498 updateStates();
499 }
500
John Spurlock8c79d2e2014-07-24 15:15:25 -0400501 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800502 final IntentFilter filter = new IntentFilter();
503 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400504 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800505 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700506 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800507 public void onReceive(Context context, Intent intent) {
508 final String action = intent.getAction();
509
510 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
511 removeMessages(MSG_RINGER_MODE_CHANGED);
512 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
513 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400514
515 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
516 postDismiss(0);
517 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800518 }
519 }, filter);
520 }
521
522 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400523 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700524 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700525 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700526 // TODO do we need to support a distinct mute property for remote?
527 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400528 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700529 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400530 }
531 }
532
533 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400534 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700535 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700536 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700537 if (mStreamControls != null) {
538 StreamControl sc = mStreamControls.get(streamType);
539 if (sc != null && sc.controller != null) {
RoboErika66c40b2014-08-15 15:21:41 -0700540 AudioInfo ai = sc.controller.getAudioInfo();
541 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700542 }
543 }
544 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400545 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700546 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400547 }
548 }
549
550 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400551 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700552 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700553 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700554 if (mStreamControls != null) {
555 StreamControl sc = mStreamControls.get(streamType);
556 if (sc != null && sc.controller != null) {
RoboErika66c40b2014-08-15 15:21:41 -0700557 AudioInfo ai = sc.controller.getAudioInfo();
558 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700559 }
560 }
561 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400562 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700563 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400564 }
565 }
566
RoboErik19c95182014-06-23 15:38:48 -0700567 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700568 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700569 if (sc.controller != null) {
570 sc.controller.setVolumeTo(index, flags);
571 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700572 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700573 }
574 } else if (getStreamVolume(sc.streamType) != index) {
575 if (sc.streamType == STREAM_MASTER) {
576 mAudioManager.setMasterVolume(index, flags);
577 } else {
578 mAudioManager.setStreamVolume(sc.streamType, index, flags);
579 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400580 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800581 }
582
583 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700584 final Resources res = mContext.getResources();
585 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
586 Context.LAYOUT_INFLATER_SERVICE);
587
John Spurlockad494bc2014-07-19 15:56:19 -0400588 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700589
Amith Yamasani71def772011-10-12 12:25:24 -0700590 for (int i = 0; i < STREAMS.length; i++) {
591 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700592
593 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700594
595 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700596 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400597 sc.group = (ViewGroup) inflater.inflate(
598 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800599 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400600 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800601 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700602 sc.icon.setContentDescription(res.getString(streamRes.descRes));
603 sc.iconRes = streamRes.iconRes;
604 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800605 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400606 sc.icon.setClickable(isNotificationOrRing(streamType));
607 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400608 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400609 sc.icon.setOnClickListener(new OnClickListener() {
610 @Override
611 public void onClick(View v) {
612 resetTimeout();
613 toggle(sc);
614 }
615 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400616 }
John Spurlock86005342014-05-23 11:58:00 -0400617 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700618 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700619 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400620 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700621 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800622 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700623 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800624 }
625 }
626
John Spurlock7f1df5e2014-05-31 19:11:40 -0400627 private void toggle(StreamControl sc) {
628 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
629 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
630 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
631 } else {
632 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
633 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
634 }
635 }
636
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800637 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400638 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800639
Alan Viverette494fb7b2014-04-10 18:12:56 -0700640 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800641 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400642 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800643 mActiveStreamType = -1;
644 } else {
John Spurlock86005342014-05-23 11:58:00 -0400645 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800646 mActiveStreamType = activeStreamType;
647 active.group.setVisibility(View.VISIBLE);
648 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400649 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400650 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800651 }
652 }
653
John Spurlockad494bc2014-07-19 15:56:19 -0400654 private void updateSliderProgress(StreamControl sc, int progress) {
655 final boolean isRinger = isNotificationOrRing(sc.streamType);
656 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
657 progress = mLastRingerProgress;
658 }
659 if (progress < 0) {
660 progress = getStreamVolume(sc.streamType);
661 }
662 sc.seekbarView.setProgress(progress);
663 if (isRinger) {
664 mLastRingerProgress = progress;
665 }
666 }
667
668 private void updateSliderIcon(StreamControl sc, boolean muted) {
669 if (isNotificationOrRing(sc.streamType)) {
670 int ringerMode = mAudioManager.getRingerMode();
671 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
672 ringerMode = mLastRingerMode;
673 } else {
674 mLastRingerMode = ringerMode;
675 }
676 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
677 }
678 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
679 }
680
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800681 /** Update the mute and progress state of a slider */
682 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400683 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800684 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800685 // Force reloading the image resource
686 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400687 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400688 updateSliderEnabled(sc, muted, false);
689 }
690
John Spurlock5f640e42014-05-31 20:15:59 -0400691 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
692 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400693 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700694 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700695 // never disable touch interactions for remote playback, the muting is not tied to
696 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700697 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400698 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400699 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400700 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400701 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400702 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400703 } else if (fixedVolume ||
704 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
705 (sSafetyWarning != null)) {
706 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700707 } else {
708 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400709 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400710 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700711 }
John Spurlockae641c92014-06-30 18:11:40 -0400712 // show the silent hint when the disabled slider is touched in silent mode
713 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400714 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400715 sc.group.setOnTouchListener(null);
716 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400717 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400718 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400719 @Override
720 public boolean onTouch(View v, MotionEvent event) {
721 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400722 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400723 return false;
724 }
John Spurlockae641c92014-06-30 18:11:40 -0400725 };
726 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400727 }
728 }
729 }
730
John Spurlockae641c92014-06-30 18:11:40 -0400731 private void showSilentHint() {
732 if (mZenPanel != null) {
733 mZenPanel.showSilentHint();
734 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800735 }
736
John Spurlock7f1df5e2014-05-31 19:11:40 -0400737 private static boolean isNotificationOrRing(int streamType) {
738 return streamType == AudioManager.STREAM_RING
739 || streamType == AudioManager.STREAM_NOTIFICATION;
740 }
741
John Spurlockae641c92014-06-30 18:11:40 -0400742 public void setCallback(Callback callback) {
743 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400744 }
745
John Spurlock8845da72014-07-07 21:29:48 -0400746 private void updateTimeoutDelay() {
John Spurlock35134602014-07-24 18:10:48 -0400747 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
748 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400749 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
750 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
751 : TIMEOUT_DELAY;
752 }
753
754 private boolean isZenPanelVisible() {
755 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400756 }
757
John Spurlockae641c92014-06-30 18:11:40 -0400758 private void setZenPanelVisible(boolean visible) {
759 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400760 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400761 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400762 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400763 resetTimeout();
764 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400765 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800766 }
John Spurlockea9938c2014-07-11 18:51:32 -0400767 if (changing) {
768 updateTimeoutDelay();
769 resetTimeout();
770 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800771 }
772
Eric Laurentfde16d52012-12-03 14:42:39 -0800773 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400774 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800775 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400776 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800777 updateSlider(sc);
778 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800779 }
780
John Spurlock45601d62014-08-07 17:40:50 -0400781 private void updateZenPanelVisible() {
782 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400783 }
784
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 public void postVolumeChanged(int streamType, int flags) {
786 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700787 synchronized (this) {
788 if (mStreamControls == null) {
789 createSliders();
790 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800791 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 removeMessages(MSG_FREE_RESOURCES);
793 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
794 }
795
RoboErik19c95182014-06-23 15:38:48 -0700796 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700797 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
798 synchronized (this) {
799 if (mStreamControls == null) {
800 createSliders();
801 }
802 }
803 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700804 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700805 }
806
807 public void postRemoteSliderVisibility(boolean visible) {
808 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700809 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700810 }
811
812 /**
813 * Called by AudioService when it has received new remote playback information that
814 * would affect the VolumePanel display (mainly volumes). The difference with
815 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
816 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
817 * displayed.
818 * This special code path is due to the fact that remote volume updates arrive to AudioService
819 * asynchronously. So after AudioService has sent the volume update (which should be treated
820 * as a request to update the volume), the application will likely set a new volume. If the UI
821 * is still up, we need to refresh the display to show this new value.
822 */
823 public void postHasNewRemotePlaybackInfo() {
824 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
825 // don't create or prevent resources to be freed, if they disappear, this update came too
826 // late and shouldn't warrant the panel to be displayed longer
827 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
828 }
829
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400830 public void postMasterVolumeChanged(int flags) {
831 postVolumeChanged(STREAM_MASTER, flags);
832 }
833
Mike Lockwoodce952c82011-11-14 10:47:42 -0800834 public void postMuteChanged(int streamType, int flags) {
835 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700836 synchronized (this) {
837 if (mStreamControls == null) {
838 createSliders();
839 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800840 }
841 removeMessages(MSG_FREE_RESOURCES);
842 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
843 }
844
845 public void postMasterMuteChanged(int flags) {
846 postMuteChanged(STREAM_MASTER, flags);
847 }
848
Eric Laurentfde16d52012-12-03 14:42:39 -0800849 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700850 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800851 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700852 }
853
John Spurlock1dad2722014-07-11 11:07:53 -0400854 public void postDismiss(long delay) {
855 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400856 }
857
858 public void postLayoutDirection(int layoutDirection) {
859 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400860 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400861 }
862
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800863 /**
864 * Override this if you have other work to do when the volume changes (for
865 * example, vibrating, playing a sound, etc.). Make sure to call through to
866 * the superclass implementation.
867 */
868 protected void onVolumeChanged(int streamType, int flags) {
869
John Spurlock86005342014-05-23 11:58:00 -0400870 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800871
872 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700873 synchronized (this) {
874 if (mActiveStreamType != streamType) {
875 reorderSliders(streamType);
876 }
RoboErik19c95182014-06-23 15:38:48 -0700877 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800878 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800879 }
880
Marco Nelissen69f593c2009-07-28 09:55:04 -0700881 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800882 removeMessages(MSG_PLAY_SOUND);
883 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
884 }
885
886 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
887 removeMessages(MSG_PLAY_SOUND);
888 removeMessages(MSG_VIBRATE);
889 onStopSounds();
890 }
891
892 removeMessages(MSG_FREE_RESOURCES);
893 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800894 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800895 }
896
Mike Lockwoodce952c82011-11-14 10:47:42 -0800897 protected void onMuteChanged(int streamType, int flags) {
898
John Spurlock86005342014-05-23 11:58:00 -0400899 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800900
901 StreamControl sc = mStreamControls.get(streamType);
902 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400903 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800904 }
905
906 onVolumeChanged(streamType, flags);
907 }
908
RoboErik19c95182014-06-23 15:38:48 -0700909 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700910 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800911
Marco Nelissen69f593c2009-07-28 09:55:04 -0700912 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913
914 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400915 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800916 + ", flags: " + flags + "), index: " + index);
917 }
918
919 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800920
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400921 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700922 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923
924 switch (streamType) {
925
926 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800927// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700928 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
929 mContext, RingtoneManager.TYPE_RINGTONE);
930 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700931 mRingIsSilent = true;
932 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800933 break;
934 }
935
936 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800937 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800938 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
939 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
940 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
941 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800942 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800944 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
946 break;
947 }
948
949 case AudioManager.STREAM_VOICE_CALL: {
950 /*
951 * For in-call voice call volume, there is no inaudible volume.
952 * Rescale the UI control so the progress bar doesn't go all
953 * the way to zero and don't show the mute icon.
954 */
955 index++;
956 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800957 break;
958 }
959
960 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 break;
962 }
963
964 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700965 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
966 mContext, RingtoneManager.TYPE_NOTIFICATION);
967 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700968 mRingIsSilent = true;
969 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800970 break;
971 }
972
973 case AudioManager.STREAM_BLUETOOTH_SCO: {
974 /*
975 * For in-call voice call volume, there is no inaudible volume.
976 * Rescale the UI control so the progress bar doesn't go all
977 * the way to zero and don't show the mute icon.
978 */
979 index++;
980 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800981 break;
982 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700983
RoboErik2811dd32014-08-12 09:48:13 -0700984 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700985 if (controller == null && sc != null) {
986 // If we weren't passed one try using the last one set.
987 controller = sc.controller;
988 }
989 if (controller == null) {
990 // We still don't have one, ignore the command.
991 Log.w(mTag, "sent remote volume change without a controller!");
992 } else {
RoboErika66c40b2014-08-15 15:21:41 -0700993 AudioInfo vi = controller.getAudioInfo();
RoboErik19c95182014-06-23 15:38:48 -0700994 index = vi.getCurrentVolume();
995 max = vi.getMaxVolume();
996 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
997 // if the remote volume is fixed add the flag for the UI
998 flags |= AudioManager.FLAG_FIXED_VOLUME;
999 }
1000 }
John Spurlock86005342014-05-23 11:58:00 -04001001 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001002 break;
1003 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001004 }
1005
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001006 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001007 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001008 if (sc.controller != null) {
1009 sc.controller.removeCallback(mMediaControllerCb);
1010 }
1011 sc.controller = controller;
1012 if (controller != null) {
1013 sc.controller.addCallback(mMediaControllerCb);
1014 }
1015 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001016 if (sc.seekbarView.getMax() != max) {
1017 sc.seekbarView.setMax(max);
1018 }
John Spurlockad494bc2014-07-19 15:56:19 -04001019 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001020 updateSliderEnabled(sc, isMuted(streamType),
1021 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 }
1023
John Spurlock86005342014-05-23 11:58:00 -04001024 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001025 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001026 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1027 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001028 mDialog.show();
1029 if (mCallback != null) {
1030 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001031 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001032 }
1033
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001034 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001035 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001036 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001037 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001038 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1040 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001041
1042 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001043 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1044 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001045 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001046 }
1047
John Spurlock86005342014-05-23 11:58:00 -04001048 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001049 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001050 }
1051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 protected void onPlaySound(int streamType, int flags) {
1053
1054 if (hasMessages(MSG_STOP_SOUNDS)) {
1055 removeMessages(MSG_STOP_SOUNDS);
1056 // Force stop right now
1057 onStopSounds();
1058 }
1059
1060 synchronized (this) {
1061 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001062 if (toneGen != null) {
1063 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1064 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1065 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 }
1068
1069 protected void onStopSounds() {
1070
1071 synchronized (this) {
1072 int numStreamTypes = AudioSystem.getNumStreamTypes();
1073 for (int i = numStreamTypes - 1; i >= 0; i--) {
1074 ToneGenerator toneGen = mToneGenerators[i];
1075 if (toneGen != null) {
1076 toneGen.stopTone();
1077 }
1078 }
1079 }
1080 }
1081
1082 protected void onVibrate() {
1083
1084 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001085 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 return;
1087 }
1088
John Spurlock7b414672014-07-18 13:02:39 -04001089 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 }
1091
RoboErik19c95182014-06-23 15:38:48 -07001092 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1093 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1094 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001095
John Spurlock86005342014-05-23 11:58:00 -04001096 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001097 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001098 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1099 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001100 }
RoboErik2811dd32014-08-12 09:48:13 -07001101 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001102 }
1103 } else {
John Spurlock86005342014-05-23 11:58:00 -04001104 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001105 }
1106
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001107 removeMessages(MSG_FREE_RESOURCES);
1108 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001109 resetTimeout();
1110 }
1111
1112 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001113 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1114 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001115 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001116 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001117 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001118 }
1119 }
1120
RoboErik19c95182014-06-23 15:38:48 -07001121 /**
1122 * Clear the current remote stream controller.
1123 */
1124 private void clearRemoteStreamController() {
1125 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001126 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001127 if (sc != null) {
1128 if (sc.controller != null) {
1129 sc.controller.removeCallback(mMediaControllerCb);
1130 sc.controller = null;
1131 }
1132 }
1133 }
1134 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001135
1136 /**
RoboErik2811dd32014-08-12 09:48:13 -07001137 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1138 *
1139 * @param streamType can be a valid stream type value, or
1140 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001141 * @param visible
1142 */
1143 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001144 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001145 boolean isVisible = (visible == 1);
1146 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1147 StreamResources streamRes = STREAMS[i];
1148 if (streamRes.streamType == streamType) {
1149 streamRes.show = isVisible;
1150 if (!isVisible && (mActiveStreamType == streamType)) {
1151 mActiveStreamType = -1;
1152 }
1153 break;
1154 }
1155 }
1156 }
1157
Eric Laurentfde16d52012-12-03 14:42:39 -08001158 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001159 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1160 || isShowing()) {
1161 synchronized (sSafetyWarningLock) {
1162 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001163 return;
1164 }
John Spurlock35134602014-07-24 18:10:48 -04001165 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1166 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001167 }
1168 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001169 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001170 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001171 }
1172
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001173 /**
1174 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1175 */
1176 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001177 if (streamType == STREAM_MASTER) {
1178 // For devices that use the master volume setting only but still want to
1179 // play a volume-changed tone, direct the master volume pseudostream to
1180 // the system stream's tone generator.
1181 if (mPlayMasterStreamTones) {
1182 streamType = AudioManager.STREAM_SYSTEM;
1183 } else {
1184 return null;
1185 }
1186 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001187 synchronized (this) {
1188 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001189 try {
1190 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1191 } catch (RuntimeException e) {
1192 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001193 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001194 + "RuntimeException: " + e);
1195 }
1196 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001197 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001198 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001199 }
1200 }
1201
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001202
1203 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001204 * Switch between icons because Bluetooth music is same as music volume, but with
1205 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001207 private void setMusicIcon(int resId, int resMuteId) {
1208 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1209 if (sc != null) {
1210 sc.iconRes = resId;
1211 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001212 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001213 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001214 }
1215
1216 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 synchronized (this) {
1218 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1219 if (mToneGenerators[i] != null) {
1220 mToneGenerators[i].release();
1221 }
1222 mToneGenerators[i] = null;
1223 }
1224 }
1225 }
1226
1227 @Override
1228 public void handleMessage(Message msg) {
1229 switch (msg.what) {
1230
1231 case MSG_VOLUME_CHANGED: {
1232 onVolumeChanged(msg.arg1, msg.arg2);
1233 break;
1234 }
1235
Mike Lockwoodce952c82011-11-14 10:47:42 -08001236 case MSG_MUTE_CHANGED: {
1237 onMuteChanged(msg.arg1, msg.arg2);
1238 break;
1239 }
1240
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001241 case MSG_FREE_RESOURCES: {
1242 onFreeResources();
1243 break;
1244 }
1245
1246 case MSG_STOP_SOUNDS: {
1247 onStopSounds();
1248 break;
1249 }
1250
1251 case MSG_PLAY_SOUND: {
1252 onPlaySound(msg.arg1, msg.arg2);
1253 break;
1254 }
1255
1256 case MSG_VIBRATE: {
1257 onVibrate();
1258 break;
1259 }
1260
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001261 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001262 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001263 mDialog.dismiss();
1264 clearRemoteStreamController();
1265 mActiveStreamType = -1;
1266 if (mCallback != null) {
1267 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001268 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001269 }
John Spurlock35134602014-07-24 18:10:48 -04001270 synchronized (sSafetyWarningLock) {
1271 if (sSafetyWarning != null) {
1272 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1273 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001274 }
1275 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001276 break;
1277 }
1278 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001279 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001280 updateStates();
1281 }
1282 break;
1283 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001284
1285 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001286 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001287 break;
1288 }
1289
1290 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1291 onRemoteVolumeUpdateIfShown();
1292 break;
1293
1294 case MSG_SLIDER_VISIBILITY_CHANGED:
1295 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1296 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001297
1298 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001299 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001300 break;
John Spurlock86005342014-05-23 11:58:00 -04001301
1302 case MSG_LAYOUT_DIRECTION:
1303 setLayoutDirection(msg.arg1);
1304 break;
1305
John Spurlock45601d62014-08-07 17:40:50 -04001306 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1307 mZenModeAvailable = msg.arg1 != 0;
1308 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001309 break;
1310
1311 case MSG_USER_ACTIVITY:
1312 if (mCallback != null) {
1313 mCallback.onInteraction();
1314 }
John Spurlock86005342014-05-23 11:58:00 -04001315 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001316 }
1317 }
1318
John Spurlockae641c92014-06-30 18:11:40 -04001319 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001320 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1321 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001322 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001323 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001324 removeMessages(MSG_USER_ACTIVITY);
1325 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001326 }
1327
John Spurlock1dad2722014-07-11 11:07:53 -04001328 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001329 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001330 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001331 }
1332
1333 public ZenModeController getZenController() {
1334 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001335 }
1336
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001337 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1338 @Override
1339 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1340 final Object tag = seekBar.getTag();
1341 if (fromUser && tag instanceof StreamControl) {
1342 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001343 setStreamVolume(sc, progress,
1344 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001345 }
1346 resetTimeout();
1347 }
1348
1349 @Override
1350 public void onStartTrackingTouch(SeekBar seekBar) {
1351 }
1352
1353 @Override
1354 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001355 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001356 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001357
John Spurlock86005342014-05-23 11:58:00 -04001358 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlock45601d62014-08-07 17:40:50 -04001359 public void onZenAvailableChanged(boolean available) {
1360 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001361 }
1362 };
RoboErik19c95182014-06-23 15:38:48 -07001363
1364 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErika66c40b2014-08-15 15:21:41 -07001365 public void onAudioInfoChanged(AudioInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001366 onRemoteVolumeUpdateIfShown();
1367 }
1368 };
John Spurlockae641c92014-06-30 18:11:40 -04001369
1370 public interface Callback {
1371 void onZenSettings();
1372 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001373 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001374 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375}