blob: 3a63a79e8308560178f8facd8203532b10d8c9ce [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;
40import android.media.session.MediaController.VolumeInfo;
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;
110 private static final int MSG_ZEN_MODE_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;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700115 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400116
John Spurlock7b414672014-07-18 13:02:39 -0400117 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
118 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
119 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
120 .build();
121
John Spurlock86005342014-05-23 11:58:00 -0400122 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400123 protected final Context mContext;
124 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400125 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700126 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700127 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400128 private boolean mZenModeCapable;
John Spurlock8845da72014-07-07 21:29:48 -0400129 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400130 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400131 private float mDisabledAlpha;
132 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
133 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800134
Christopher Tatec4b78d22012-05-22 13:57:58 -0700135 // True if we want to play tones on the system stream when the master stream is specified.
136 private final boolean mPlayMasterStreamTones;
137
John Spurlock86005342014-05-23 11:58:00 -0400138
139 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400141 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400142 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800143
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700144 /** The visible portion of the volume overlay */
145 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400146 /** Contains the slider and its touchable icons */
147 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400148 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400149 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400150
John Spurlockae641c92014-06-30 18:11:40 -0400151 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800152
153 /** Currently active stream that shows up at the top of the list of sliders */
154 private int mActiveStreamType = -1;
155 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400156 private SparseArray<StreamControl> mStreamControls;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800157
Amith Yamasani71def772011-10-12 12:25:24 -0700158 private enum StreamResources {
159 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
160 R.string.volume_icon_description_bluetooth,
161 R.drawable.ic_audio_bt,
162 R.drawable.ic_audio_bt,
163 false),
164 RingerStream(AudioManager.STREAM_RING,
165 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400166 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400167 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700168 false),
169 VoiceStream(AudioManager.STREAM_VOICE_CALL,
170 R.string.volume_icon_description_incall,
171 R.drawable.ic_audio_phone,
172 R.drawable.ic_audio_phone,
173 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700174 AlarmStream(AudioManager.STREAM_ALARM,
175 R.string.volume_alarm,
176 R.drawable.ic_audio_alarm,
177 R.drawable.ic_audio_alarm_mute,
178 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700179 MediaStream(AudioManager.STREAM_MUSIC,
180 R.string.volume_icon_description_media,
181 R.drawable.ic_audio_vol,
182 R.drawable.ic_audio_vol_mute,
183 true),
184 NotificationStream(AudioManager.STREAM_NOTIFICATION,
185 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400186 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400187 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400188 true),
189 // for now, use media resources for master volume
190 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700191 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400192 R.drawable.ic_audio_vol,
193 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700194 false),
195 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
196 R.string.volume_icon_description_media, //FIXME should have its own description
197 R.drawable.ic_media_route_on_holo_dark,
198 R.drawable.ic_media_route_disabled_holo_dark,
199 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700200
201 int streamType;
202 int descRes;
203 int iconRes;
204 int iconMuteRes;
205 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
206 boolean show;
207
208 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
209 this.streamType = streamType;
210 this.descRes = descRes;
211 this.iconRes = iconRes;
212 this.iconMuteRes = iconMuteRes;
213 this.show = show;
214 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700215 }
Amith Yamasani71def772011-10-12 12:25:24 -0700216
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800217 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700218 private static final StreamResources[] STREAMS = {
219 StreamResources.BluetoothSCOStream,
220 StreamResources.RingerStream,
221 StreamResources.VoiceStream,
222 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700223 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400224 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700225 StreamResources.MasterStream,
226 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800227 };
228
229 /** Object that contains data for each slider */
230 private class StreamControl {
231 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700232 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800233 ViewGroup group;
234 ImageView icon;
235 SeekBar seekbarView;
236 int iconRes;
237 int iconMuteRes;
238 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800239
240 // Synchronize when accessing this
241 private ToneGenerator mToneGenerators[];
242 private Vibrator mVibrator;
243
John Spurlock35134602014-07-24 18:10:48 -0400244 private static AlertDialog sSafetyWarning;
245 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700246
John Spurlock35134602014-07-24 18:10:48 -0400247 private static class SafetyWarning extends SystemUIDialog
248 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800249 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800250 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400251 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700252
John Spurlock2d28d6e2014-08-01 13:10:14 -0400253 private boolean mNewVolumeUp;
254
John Spurlock35134602014-07-24 18:10:48 -0400255 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
256 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700257 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800258 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400259 mAudioManager = audioManager;
260
261 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
262 setButton(DialogInterface.BUTTON_POSITIVE,
263 mContext.getString(com.android.internal.R.string.yes), this);
264 setButton(DialogInterface.BUTTON_NEGATIVE,
265 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
266 setOnDismissListener(this);
267
Eric Laurentc34dcc12012-09-10 13:51:52 -0700268 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400269 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700270 }
271
272 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400273 public boolean onKeyDown(int keyCode, KeyEvent event) {
274 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
275 mNewVolumeUp = true;
276 }
277 return super.onKeyDown(keyCode, event);
278 }
279
280 @Override
281 public boolean onKeyUp(int keyCode, KeyEvent event) {
282 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
283 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
284 mAudioManager.disableSafeMediaVolume();
285 dismiss();
286 }
287 return super.onKeyUp(keyCode, event);
288 }
289
290 @Override
John Spurlock35134602014-07-24 18:10:48 -0400291 public void onClick(DialogInterface dialog, int which) {
292 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700293 }
294
Alan Viverette494fb7b2014-04-10 18:12:56 -0700295 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700296 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400297 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800298 cleanUp();
299 }
300
301 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400302 synchronized (sSafetyWarningLock) {
303 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700304 }
John Spurlock1dad2722014-07-11 11:07:53 -0400305 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800306 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700307 }
John Spurlock35134602014-07-24 18:10:48 -0400308
309 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
310 @Override
311 public void onReceive(Context context, Intent intent) {
312 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
313 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
314 cancel();
315 cleanUp();
316 }
317 }
318 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700319 }
320
John Spurlockeb2727b2014-07-19 23:11:36 -0400321 public VolumePanel(Context context, ZenModeController zenController) {
322 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800323 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400324 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800325 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800326
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400327 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700328 final Resources res = context.getResources();
329 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400330 if (useMasterVolume) {
331 for (int i = 0; i < STREAMS.length; i++) {
332 StreamResources streamRes = STREAMS[i];
333 streamRes.show = (streamRes.streamType == STREAM_MASTER);
334 }
335 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400336 if (LOGD) Log.d(mTag, "new VolumePanel");
337
John Spurlockad494bc2014-07-19 15:56:19 -0400338 mDisabledAlpha = 0.5f;
339 if (mContext.getTheme() != null) {
340 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
341 new int[] { android.R.attr.disabledAlpha });
342 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
343 arr.recycle();
344 }
345
John Spurlockeb2727b2014-07-19 23:11:36 -0400346 mDialog = new Dialog(context) {
347 @Override
348 public boolean onTouchEvent(MotionEvent event) {
349 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400350 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400351 forceTimeout(0);
352 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700353 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400354 return false;
355 }
356 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700357
John Spurlockeb2727b2014-07-19 23:11:36 -0400358 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400359 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400360 mDialog.setCanceledOnTouchOutside(true);
361 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
362 mDialog.setOnDismissListener(new OnDismissListener() {
363 @Override
364 public void onDismiss(DialogInterface dialog) {
365 mActiveStreamType = -1;
366 mAudioManager.forceVolumeControlStream(mActiveStreamType);
367 setZenPanelVisible(false);
368 }
369 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700370
John Spurlockeb2727b2014-07-19 23:11:36 -0400371 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700372
John Spurlock7e6809a2014-08-06 16:03:14 -0400373 final LayoutParams lp = window.getAttributes();
374 lp.token = null;
375 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
376 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
377 lp.format = PixelFormat.TRANSLUCENT;
378 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
379 lp.setTitle(TAG);
380 window.setAttributes(lp);
381
382 updateWidth();
383
384 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
385 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
386 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
387 | LayoutParams.FLAG_NOT_TOUCH_MODAL
388 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
389 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400390 mView = window.findViewById(R.id.content);
391 mView.setOnTouchListener(new View.OnTouchListener() {
392 @Override
393 public boolean onTouch(View v, MotionEvent event) {
394 resetTimeout();
395 return false;
396 }
397 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700398
John Spurlock86005342014-05-23 11:58:00 -0400399 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
400 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400401 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
402 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800403
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800404 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700405 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700406 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700407
John Spurlock86005342014-05-23 11:58:00 -0400408 mZenModeCapable = !useMasterVolume && mZenController != null;
John Spurlockae641c92014-06-30 18:11:40 -0400409 updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
John Spurlock86005342014-05-23 11:58:00 -0400410 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700411
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700412 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
413 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700414 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
415
John Spurlock8c79d2e2014-07-24 15:15:25 -0400416 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800417 }
418
John Spurlock7e6809a2014-08-06 16:03:14 -0400419 public void onConfigurationChanged(Configuration newConfig) {
420 updateWidth();
421 }
422
423 private void updateWidth() {
424 final Resources res = mContext.getResources();
425 final LayoutParams lp = mDialog.getWindow().getAttributes();
426 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
427 lp.gravity =
428 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
429 mDialog.getWindow().setAttributes(lp);
430 }
431
John Spurlockad494bc2014-07-19 15:56:19 -0400432 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
433 pw.println("VolumePanel state:");
434 pw.print(" mTag="); pw.println(mTag);
435 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
436 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
437 pw.print(" mZenModeCapable="); pw.println(mZenModeCapable);
438 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
439 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
440 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
441 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
442 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
443 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
444 pw.print(" isShowing()="); pw.println(isShowing());
445 pw.print(" mCallback="); pw.println(mCallback);
446 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400447 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400448 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
449 pw.print(" mStreamControls=");
450 if (mStreamControls == null) {
451 pw.println("null");
452 } else {
453 final int N = mStreamControls.size();
454 pw.print("<size "); pw.print(N); pw.println('>');
455 for (int i = 0; i < N; i++) {
456 final StreamControl sc = mStreamControls.valueAt(i);
457 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
458 if (sc.seekbarView != null) {
459 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
460 pw.print(" of "); pw.print(sc.seekbarView.getMax());
461 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
462 }
463 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
464 pw.println();
465 }
466 }
467 }
468
John Spurlockeb2727b2014-07-19 23:11:36 -0400469 private void initZenModePanel() {
470 mZenPanel.init(mZenController);
471 mZenPanel.setCallback(new ZenModePanel.Callback() {
472 @Override
473 public void onMoreSettings() {
474 if (mCallback != null) {
475 mCallback.onZenSettings();
476 }
477 }
478
479 @Override
480 public void onInteraction() {
481 resetTimeout();
482 }
483
484 @Override
485 public void onExpanded(boolean expanded) {
486 if (mZenPanelExpanded == expanded) return;
487 mZenPanelExpanded = expanded;
488 updateTimeoutDelay();
489 resetTimeout();
490 }
491 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400492 }
493
John Spurlock86005342014-05-23 11:58:00 -0400494 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800495 mPanel.setLayoutDirection(layoutDirection);
496 updateStates();
497 }
498
John Spurlock8c79d2e2014-07-24 15:15:25 -0400499 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800500 final IntentFilter filter = new IntentFilter();
501 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400502 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800503 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700504 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800505 public void onReceive(Context context, Intent intent) {
506 final String action = intent.getAction();
507
508 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
509 removeMessages(MSG_RINGER_MODE_CHANGED);
510 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
511 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400512
513 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
514 postDismiss(0);
515 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800516 }
517 }, filter);
518 }
519
520 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400521 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700522 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700523 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700524 // TODO do we need to support a distinct mute property for remote?
525 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400526 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700527 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400528 }
529 }
530
531 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400532 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700533 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700534 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700535 if (mStreamControls != null) {
536 StreamControl sc = mStreamControls.get(streamType);
537 if (sc != null && sc.controller != null) {
538 VolumeInfo vi = sc.controller.getVolumeInfo();
539 return vi.getMaxVolume();
540 }
541 }
542 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400543 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700544 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400545 }
546 }
547
548 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400549 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700550 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700551 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700552 if (mStreamControls != null) {
553 StreamControl sc = mStreamControls.get(streamType);
554 if (sc != null && sc.controller != null) {
555 VolumeInfo vi = sc.controller.getVolumeInfo();
556 return vi.getCurrentVolume();
557 }
558 }
559 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400560 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700561 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400562 }
563 }
564
RoboErik19c95182014-06-23 15:38:48 -0700565 private void setStreamVolume(StreamControl sc, int index, int flags) {
566 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
567 if (sc.controller != null) {
568 sc.controller.setVolumeTo(index, flags);
569 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700570 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700571 }
572 } else if (getStreamVolume(sc.streamType) != index) {
573 if (sc.streamType == STREAM_MASTER) {
574 mAudioManager.setMasterVolume(index, flags);
575 } else {
576 mAudioManager.setStreamVolume(sc.streamType, index, flags);
577 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400578 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800579 }
580
581 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700582 final Resources res = mContext.getResources();
583 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
584 Context.LAYOUT_INFLATER_SERVICE);
585
John Spurlockad494bc2014-07-19 15:56:19 -0400586 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700587
Amith Yamasani71def772011-10-12 12:25:24 -0700588 for (int i = 0; i < STREAMS.length; i++) {
589 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700590
591 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700592
593 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700594 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400595 sc.group = (ViewGroup) inflater.inflate(
596 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800597 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400598 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800599 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700600 sc.icon.setContentDescription(res.getString(streamRes.descRes));
601 sc.iconRes = streamRes.iconRes;
602 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800603 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400604 sc.icon.setClickable(isNotificationOrRing(streamType));
605 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400606 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400607 sc.icon.setOnClickListener(new OnClickListener() {
608 @Override
609 public void onClick(View v) {
610 resetTimeout();
611 toggle(sc);
612 }
613 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400614 }
John Spurlock86005342014-05-23 11:58:00 -0400615 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700616 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700617 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400618 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700619 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800620 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700621 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800622 }
623 }
624
John Spurlock7f1df5e2014-05-31 19:11:40 -0400625 private void toggle(StreamControl sc) {
626 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
627 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
628 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
629 } else {
630 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
631 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
632 }
633 }
634
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800635 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400636 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800637
Alan Viverette494fb7b2014-04-10 18:12:56 -0700638 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800639 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400640 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800641 mActiveStreamType = -1;
642 } else {
John Spurlock86005342014-05-23 11:58:00 -0400643 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800644 mActiveStreamType = activeStreamType;
645 active.group.setVisibility(View.VISIBLE);
646 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400647 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400648 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800649 }
650 }
651
John Spurlockad494bc2014-07-19 15:56:19 -0400652 private void updateSliderProgress(StreamControl sc, int progress) {
653 final boolean isRinger = isNotificationOrRing(sc.streamType);
654 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
655 progress = mLastRingerProgress;
656 }
657 if (progress < 0) {
658 progress = getStreamVolume(sc.streamType);
659 }
660 sc.seekbarView.setProgress(progress);
661 if (isRinger) {
662 mLastRingerProgress = progress;
663 }
664 }
665
666 private void updateSliderIcon(StreamControl sc, boolean muted) {
667 if (isNotificationOrRing(sc.streamType)) {
668 int ringerMode = mAudioManager.getRingerMode();
669 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
670 ringerMode = mLastRingerMode;
671 } else {
672 mLastRingerMode = ringerMode;
673 }
674 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
675 }
676 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
677 }
678
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800679 /** Update the mute and progress state of a slider */
680 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400681 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800682 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800683 // Force reloading the image resource
684 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400685 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400686 updateSliderEnabled(sc, muted, false);
687 }
688
John Spurlock5f640e42014-05-31 20:15:59 -0400689 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
690 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400691 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700692 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
693 // never disable touch interactions for remote playback, the muting is not tied to
694 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700695 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400696 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400697 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400698 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400699 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400700 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400701 } else if (fixedVolume ||
702 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
703 (sSafetyWarning != null)) {
704 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700705 } else {
706 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400707 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400708 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700709 }
John Spurlockae641c92014-06-30 18:11:40 -0400710 // show the silent hint when the disabled slider is touched in silent mode
711 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400712 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400713 sc.group.setOnTouchListener(null);
714 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400715 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400716 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400717 @Override
718 public boolean onTouch(View v, MotionEvent event) {
719 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400720 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400721 return false;
722 }
John Spurlockae641c92014-06-30 18:11:40 -0400723 };
724 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400725 }
726 }
727 }
728
John Spurlockae641c92014-06-30 18:11:40 -0400729 private void showSilentHint() {
730 if (mZenPanel != null) {
731 mZenPanel.showSilentHint();
732 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800733 }
734
John Spurlock7f1df5e2014-05-31 19:11:40 -0400735 private static boolean isNotificationOrRing(int streamType) {
736 return streamType == AudioManager.STREAM_RING
737 || streamType == AudioManager.STREAM_NOTIFICATION;
738 }
739
John Spurlockae641c92014-06-30 18:11:40 -0400740 public void setCallback(Callback callback) {
741 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400742 }
743
John Spurlock8845da72014-07-07 21:29:48 -0400744 private void updateTimeoutDelay() {
John Spurlock35134602014-07-24 18:10:48 -0400745 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
746 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400747 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
748 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
749 : TIMEOUT_DELAY;
750 }
751
752 private boolean isZenPanelVisible() {
753 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400754 }
755
John Spurlockae641c92014-06-30 18:11:40 -0400756 private void setZenPanelVisible(boolean visible) {
757 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400758 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400759 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400760 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400761 resetTimeout();
762 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400763 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800764 }
John Spurlockea9938c2014-07-11 18:51:32 -0400765 if (changing) {
766 updateTimeoutDelay();
767 resetTimeout();
768 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800769 }
770
Eric Laurentfde16d52012-12-03 14:42:39 -0800771 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400772 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800773 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400774 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800775 updateSlider(sc);
776 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778
John Spurlockae641c92014-06-30 18:11:40 -0400779 private void updateZenMode(int zen) {
780 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
781 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400782 }
783
John Spurlockae641c92014-06-30 18:11:40 -0400784 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400785 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400786 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400787 }
788
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 public void postVolumeChanged(int streamType, int flags) {
790 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700791 synchronized (this) {
792 if (mStreamControls == null) {
793 createSliders();
794 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800795 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 removeMessages(MSG_FREE_RESOURCES);
797 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
798 }
799
RoboErik19c95182014-06-23 15:38:48 -0700800 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700801 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
802 synchronized (this) {
803 if (mStreamControls == null) {
804 createSliders();
805 }
806 }
807 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700808 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700809 }
810
811 public void postRemoteSliderVisibility(boolean visible) {
812 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
813 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
814 }
815
816 /**
817 * Called by AudioService when it has received new remote playback information that
818 * would affect the VolumePanel display (mainly volumes). The difference with
819 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
820 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
821 * displayed.
822 * This special code path is due to the fact that remote volume updates arrive to AudioService
823 * asynchronously. So after AudioService has sent the volume update (which should be treated
824 * as a request to update the volume), the application will likely set a new volume. If the UI
825 * is still up, we need to refresh the display to show this new value.
826 */
827 public void postHasNewRemotePlaybackInfo() {
828 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
829 // don't create or prevent resources to be freed, if they disappear, this update came too
830 // late and shouldn't warrant the panel to be displayed longer
831 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
832 }
833
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400834 public void postMasterVolumeChanged(int flags) {
835 postVolumeChanged(STREAM_MASTER, flags);
836 }
837
Mike Lockwoodce952c82011-11-14 10:47:42 -0800838 public void postMuteChanged(int streamType, int flags) {
839 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700840 synchronized (this) {
841 if (mStreamControls == null) {
842 createSliders();
843 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800844 }
845 removeMessages(MSG_FREE_RESOURCES);
846 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
847 }
848
849 public void postMasterMuteChanged(int flags) {
850 postMuteChanged(STREAM_MASTER, flags);
851 }
852
Eric Laurentfde16d52012-12-03 14:42:39 -0800853 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700854 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800855 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700856 }
857
John Spurlock1dad2722014-07-11 11:07:53 -0400858 public void postDismiss(long delay) {
859 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400860 }
861
862 public void postLayoutDirection(int layoutDirection) {
863 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400864 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400865 }
866
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 /**
868 * Override this if you have other work to do when the volume changes (for
869 * example, vibrating, playing a sound, etc.). Make sure to call through to
870 * the superclass implementation.
871 */
872 protected void onVolumeChanged(int streamType, int flags) {
873
John Spurlock86005342014-05-23 11:58:00 -0400874 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800875
876 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700877 synchronized (this) {
878 if (mActiveStreamType != streamType) {
879 reorderSliders(streamType);
880 }
RoboErik19c95182014-06-23 15:38:48 -0700881 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800882 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800883 }
884
Marco Nelissen69f593c2009-07-28 09:55:04 -0700885 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 removeMessages(MSG_PLAY_SOUND);
887 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
888 }
889
890 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
891 removeMessages(MSG_PLAY_SOUND);
892 removeMessages(MSG_VIBRATE);
893 onStopSounds();
894 }
895
896 removeMessages(MSG_FREE_RESOURCES);
897 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800898 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 }
900
Mike Lockwoodce952c82011-11-14 10:47:42 -0800901 protected void onMuteChanged(int streamType, int flags) {
902
John Spurlock86005342014-05-23 11:58:00 -0400903 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800904
905 StreamControl sc = mStreamControls.get(streamType);
906 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400907 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800908 }
909
910 onVolumeChanged(streamType, flags);
911 }
912
RoboErik19c95182014-06-23 15:38:48 -0700913 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700914 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800915
Marco Nelissen69f593c2009-07-28 09:55:04 -0700916 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917
918 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400919 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800920 + ", flags: " + flags + "), index: " + index);
921 }
922
923 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800924
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400925 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700926 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800927
928 switch (streamType) {
929
930 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800931// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700932 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
933 mContext, RingtoneManager.TYPE_RINGTONE);
934 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700935 mRingIsSilent = true;
936 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800937 break;
938 }
939
940 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800941 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800942 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
943 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
944 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
945 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800946 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800948 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800949 }
950 break;
951 }
952
953 case AudioManager.STREAM_VOICE_CALL: {
954 /*
955 * For in-call voice call volume, there is no inaudible volume.
956 * Rescale the UI control so the progress bar doesn't go all
957 * the way to zero and don't show the mute icon.
958 */
959 index++;
960 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800961 break;
962 }
963
964 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965 break;
966 }
967
968 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700969 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
970 mContext, RingtoneManager.TYPE_NOTIFICATION);
971 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700972 mRingIsSilent = true;
973 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800974 break;
975 }
976
977 case AudioManager.STREAM_BLUETOOTH_SCO: {
978 /*
979 * For in-call voice call volume, there is no inaudible volume.
980 * Rescale the UI control so the progress bar doesn't go all
981 * the way to zero and don't show the mute icon.
982 */
983 index++;
984 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 break;
986 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700987
988 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700989 if (controller == null && sc != null) {
990 // If we weren't passed one try using the last one set.
991 controller = sc.controller;
992 }
993 if (controller == null) {
994 // We still don't have one, ignore the command.
995 Log.w(mTag, "sent remote volume change without a controller!");
996 } else {
997 VolumeInfo vi = controller.getVolumeInfo();
998 index = vi.getCurrentVolume();
999 max = vi.getMaxVolume();
1000 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1001 // if the remote volume is fixed add the flag for the UI
1002 flags |= AudioManager.FLAG_FIXED_VOLUME;
1003 }
1004 }
John Spurlock86005342014-05-23 11:58:00 -04001005 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001006 break;
1007 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001008 }
1009
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001010 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -07001011 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
1012 if (sc.controller != null) {
1013 sc.controller.removeCallback(mMediaControllerCb);
1014 }
1015 sc.controller = controller;
1016 if (controller != null) {
1017 sc.controller.addCallback(mMediaControllerCb);
1018 }
1019 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001020 if (sc.seekbarView.getMax() != max) {
1021 sc.seekbarView.setMax(max);
1022 }
John Spurlockad494bc2014-07-19 15:56:19 -04001023 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001024 updateSliderEnabled(sc, isMuted(streamType),
1025 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001026 }
1027
John Spurlock86005342014-05-23 11:58:00 -04001028 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001029 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
1030 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1031 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001032 mDialog.show();
1033 if (mCallback != null) {
1034 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001035 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001036 }
1037
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001038 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001039 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
1040 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001041 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001042 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1044 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001045
1046 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001047 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1048 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001049 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
1051
John Spurlock86005342014-05-23 11:58:00 -04001052 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001053 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001054 }
1055
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 protected void onPlaySound(int streamType, int flags) {
1057
1058 if (hasMessages(MSG_STOP_SOUNDS)) {
1059 removeMessages(MSG_STOP_SOUNDS);
1060 // Force stop right now
1061 onStopSounds();
1062 }
1063
1064 synchronized (this) {
1065 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001066 if (toneGen != null) {
1067 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1068 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1069 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001071 }
1072
1073 protected void onStopSounds() {
1074
1075 synchronized (this) {
1076 int numStreamTypes = AudioSystem.getNumStreamTypes();
1077 for (int i = numStreamTypes - 1; i >= 0; i--) {
1078 ToneGenerator toneGen = mToneGenerators[i];
1079 if (toneGen != null) {
1080 toneGen.stopTone();
1081 }
1082 }
1083 }
1084 }
1085
1086 protected void onVibrate() {
1087
1088 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001089 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001090 return;
1091 }
1092
John Spurlock7b414672014-07-18 13:02:39 -04001093 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001094 }
1095
RoboErik19c95182014-06-23 15:38:48 -07001096 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1097 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1098 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001099
John Spurlock86005342014-05-23 11:58:00 -04001100 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001101 synchronized (this) {
1102 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
1103 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
1104 }
RoboErik19c95182014-06-23 15:38:48 -07001105 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001106 }
1107 } else {
John Spurlock86005342014-05-23 11:58:00 -04001108 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001109 }
1110
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001111 removeMessages(MSG_FREE_RESOURCES);
1112 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001113 resetTimeout();
1114 }
1115
1116 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001117 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1118 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001119 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1120 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001121 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001122 }
1123 }
1124
RoboErik19c95182014-06-23 15:38:48 -07001125 /**
1126 * Clear the current remote stream controller.
1127 */
1128 private void clearRemoteStreamController() {
1129 if (mStreamControls != null) {
1130 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1131 if (sc != null) {
1132 if (sc.controller != null) {
1133 sc.controller.removeCallback(mMediaControllerCb);
1134 sc.controller = null;
1135 }
1136 }
1137 }
1138 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001139
1140 /**
1141 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1142 * Hide or show a slider
1143 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1144 * or AudioService.STREAM_REMOTE_MUSIC
1145 * @param visible
1146 */
1147 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001148 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001149 boolean isVisible = (visible == 1);
1150 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1151 StreamResources streamRes = STREAMS[i];
1152 if (streamRes.streamType == streamType) {
1153 streamRes.show = isVisible;
1154 if (!isVisible && (mActiveStreamType == streamType)) {
1155 mActiveStreamType = -1;
1156 }
1157 break;
1158 }
1159 }
1160 }
1161
Eric Laurentfde16d52012-12-03 14:42:39 -08001162 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001163 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1164 || isShowing()) {
1165 synchronized (sSafetyWarningLock) {
1166 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001167 return;
1168 }
John Spurlock35134602014-07-24 18:10:48 -04001169 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1170 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001171 }
1172 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001173 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001174 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001175 }
1176
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001177 /**
1178 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1179 */
1180 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001181 if (streamType == STREAM_MASTER) {
1182 // For devices that use the master volume setting only but still want to
1183 // play a volume-changed tone, direct the master volume pseudostream to
1184 // the system stream's tone generator.
1185 if (mPlayMasterStreamTones) {
1186 streamType = AudioManager.STREAM_SYSTEM;
1187 } else {
1188 return null;
1189 }
1190 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001191 synchronized (this) {
1192 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001193 try {
1194 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1195 } catch (RuntimeException e) {
1196 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001197 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001198 + "RuntimeException: " + e);
1199 }
1200 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001201 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001202 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001203 }
1204 }
1205
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001206
1207 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001208 * Switch between icons because Bluetooth music is same as music volume, but with
1209 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001210 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001211 private void setMusicIcon(int resId, int resMuteId) {
1212 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1213 if (sc != null) {
1214 sc.iconRes = resId;
1215 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001216 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001217 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001218 }
1219
1220 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 synchronized (this) {
1222 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1223 if (mToneGenerators[i] != null) {
1224 mToneGenerators[i].release();
1225 }
1226 mToneGenerators[i] = null;
1227 }
1228 }
1229 }
1230
1231 @Override
1232 public void handleMessage(Message msg) {
1233 switch (msg.what) {
1234
1235 case MSG_VOLUME_CHANGED: {
1236 onVolumeChanged(msg.arg1, msg.arg2);
1237 break;
1238 }
1239
Mike Lockwoodce952c82011-11-14 10:47:42 -08001240 case MSG_MUTE_CHANGED: {
1241 onMuteChanged(msg.arg1, msg.arg2);
1242 break;
1243 }
1244
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001245 case MSG_FREE_RESOURCES: {
1246 onFreeResources();
1247 break;
1248 }
1249
1250 case MSG_STOP_SOUNDS: {
1251 onStopSounds();
1252 break;
1253 }
1254
1255 case MSG_PLAY_SOUND: {
1256 onPlaySound(msg.arg1, msg.arg2);
1257 break;
1258 }
1259
1260 case MSG_VIBRATE: {
1261 onVibrate();
1262 break;
1263 }
1264
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001265 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001266 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001267 mDialog.dismiss();
1268 clearRemoteStreamController();
1269 mActiveStreamType = -1;
1270 if (mCallback != null) {
1271 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001272 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001273 }
John Spurlock35134602014-07-24 18:10:48 -04001274 synchronized (sSafetyWarningLock) {
1275 if (sSafetyWarning != null) {
1276 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1277 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001278 }
1279 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001280 break;
1281 }
1282 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001283 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001284 updateStates();
1285 }
1286 break;
1287 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001288
1289 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001290 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001291 break;
1292 }
1293
1294 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1295 onRemoteVolumeUpdateIfShown();
1296 break;
1297
1298 case MSG_SLIDER_VISIBILITY_CHANGED:
1299 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1300 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001301
1302 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001303 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001304 break;
John Spurlock86005342014-05-23 11:58:00 -04001305
1306 case MSG_LAYOUT_DIRECTION:
1307 setLayoutDirection(msg.arg1);
1308 break;
1309
1310 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001311 updateZenMode(msg.arg1);
1312 break;
1313
1314 case MSG_USER_ACTIVITY:
1315 if (mCallback != null) {
1316 mCallback.onInteraction();
1317 }
John Spurlock86005342014-05-23 11:58:00 -04001318 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001319 }
1320 }
1321
John Spurlockae641c92014-06-30 18:11:40 -04001322 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001323 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1324 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001325 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001326 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001327 removeMessages(MSG_USER_ACTIVITY);
1328 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001329 }
1330
John Spurlock1dad2722014-07-11 11:07:53 -04001331 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001332 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001333 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001334 }
1335
1336 public ZenModeController getZenController() {
1337 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001338 }
1339
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001340 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1341 @Override
1342 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1343 final Object tag = seekBar.getTag();
1344 if (fromUser && tag instanceof StreamControl) {
1345 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001346 setStreamVolume(sc, progress,
1347 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001348 }
1349 resetTimeout();
1350 }
1351
1352 @Override
1353 public void onStartTrackingTouch(SeekBar seekBar) {
1354 }
1355
1356 @Override
1357 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001358 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001359 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001360
John Spurlock86005342014-05-23 11:58:00 -04001361 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001362 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001363 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001364 }
1365 };
RoboErik19c95182014-06-23 15:38:48 -07001366
1367 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1368 public void onVolumeInfoChanged(VolumeInfo info) {
1369 onRemoteVolumeUpdateIfShown();
1370 }
1371 };
John Spurlockae641c92014-06-30 18:11:40 -04001372
1373 public interface Callback {
1374 void onZenSettings();
1375 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001376 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001377 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001378}