blob: 4ada5ec6e6d3bc6143f611103e8fd8b5cf3f7136 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
Eric Laurentc34dcc12012-09-10 13:51:52 -070019import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080020import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080021import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080022import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040024import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080026import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.res.Resources;
John Spurlock2078caf2014-05-29 22:20:14 -040028import android.graphics.PixelFormat;
29import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040030import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.media.AudioManager;
32import android.media.AudioService;
33import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070034import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070036import android.media.VolumeProvider;
37import android.media.session.MediaController;
38import android.media.session.MediaController.VolumeInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070039import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.os.Handler;
41import android.os.Message;
42import android.os.Vibrator;
John Spurlockae641c92014-06-30 18:11:40 -040043import android.provider.Settings.Global;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040045import android.view.Gravity;
46import android.view.LayoutInflater;
47import android.view.MotionEvent;
48import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040049import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040050import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040051import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040052import android.view.Window;
53import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070054import android.view.WindowManager.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080056import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080057import android.widget.SeekBar.OnSeekBarChangeListener;
58
John Spurlock86005342014-05-23 11:58:00 -040059import com.android.internal.R;
60import com.android.systemui.statusbar.policy.ZenModeController;
61
Amith Yamasani2bbdd772011-02-02 18:54:13 -080062import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080063
64/**
John Spurlock3346a802014-05-20 16:25:37 -040065 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070066 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080067 * @hide
68 */
John Spurlock3346a802014-05-20 16:25:37 -040069public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040070 private static final String TAG = "VolumePanel";
71 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072
John Spurlock3346a802014-05-20 16:25:37 -040073 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074
75 /**
76 * The delay before vibrating. This small period exists so if the user is
77 * moving to silent mode, it will not emit a short vibrate (it normally
78 * would since vibrate is between normal mode and silent mode using hardware
79 * keys).
80 */
81 public static final int VIBRATE_DELAY = 300;
82
83 private static final int VIBRATE_DURATION = 300;
84 private static final int BEEP_DURATION = 150;
85 private static final int MAX_VOLUME = 100;
86 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080087 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040088 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040089 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock3bd4fee2014-05-29 20:51:09 -040090 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091
92 private static final int MSG_VOLUME_CHANGED = 0;
93 private static final int MSG_FREE_RESOURCES = 1;
94 private static final int MSG_PLAY_SOUND = 2;
95 private static final int MSG_STOP_SOUNDS = 3;
96 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080097 private static final int MSG_TIMEOUT = 5;
98 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080099 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700100 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
101 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
102 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700103 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400104 private static final int MSG_LAYOUT_DIRECTION = 12;
105 private static final int MSG_ZEN_MODE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400106 private static final int MSG_USER_ACTIVITY = 14;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800107
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400108 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800109 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700110 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400111
John Spurlock7b414672014-07-18 13:02:39 -0400112 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
113 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
114 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
115 .build();
116
John Spurlock86005342014-05-23 11:58:00 -0400117 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400118 protected final Context mContext;
119 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400120 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700121 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700122 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400123 private boolean mZenModeCapable;
John Spurlock8845da72014-07-07 21:29:48 -0400124 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400125 private int mTimeoutDelay = TIMEOUT_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800126
Christopher Tatec4b78d22012-05-22 13:57:58 -0700127 // True if we want to play tones on the system stream when the master stream is specified.
128 private final boolean mPlayMasterStreamTones;
129
John Spurlock86005342014-05-23 11:58:00 -0400130
131 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800132 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400133 /** Dialog hosting the panel, if not embedded */
134 private final Dialog mDialog;
135 /** Parent view hosting the panel, if embedded */
136 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800137
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700138 /** The visible portion of the volume overlay */
139 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400140 /** Contains the slider and its touchable icons */
141 private final ViewGroup mSliderPanel;
John Spurlock86005342014-05-23 11:58:00 -0400142 /** The zen mode configuration panel view stub */
143 private final ViewStub mZenPanelStub;
144 /** The zen mode configuration panel view, once inflated */
145 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400146
John Spurlockae641c92014-06-30 18:11:40 -0400147 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800148
149 /** Currently active stream that shows up at the top of the list of sliders */
150 private int mActiveStreamType = -1;
151 /** All the slider controls mapped by stream type */
152 private HashMap<Integer,StreamControl> mStreamControls;
153
Amith Yamasani71def772011-10-12 12:25:24 -0700154 private enum StreamResources {
155 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
156 R.string.volume_icon_description_bluetooth,
157 R.drawable.ic_audio_bt,
158 R.drawable.ic_audio_bt,
159 false),
160 RingerStream(AudioManager.STREAM_RING,
161 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400162 com.android.systemui.R.drawable.ic_ringer_audible,
163 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700164 false),
165 VoiceStream(AudioManager.STREAM_VOICE_CALL,
166 R.string.volume_icon_description_incall,
167 R.drawable.ic_audio_phone,
168 R.drawable.ic_audio_phone,
169 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700170 AlarmStream(AudioManager.STREAM_ALARM,
171 R.string.volume_alarm,
172 R.drawable.ic_audio_alarm,
173 R.drawable.ic_audio_alarm_mute,
174 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700175 MediaStream(AudioManager.STREAM_MUSIC,
176 R.string.volume_icon_description_media,
177 R.drawable.ic_audio_vol,
178 R.drawable.ic_audio_vol_mute,
179 true),
180 NotificationStream(AudioManager.STREAM_NOTIFICATION,
181 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400182 com.android.systemui.R.drawable.ic_ringer_audible,
183 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400184 true),
185 // for now, use media resources for master volume
186 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700187 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400188 R.drawable.ic_audio_vol,
189 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700190 false),
191 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
192 R.string.volume_icon_description_media, //FIXME should have its own description
193 R.drawable.ic_media_route_on_holo_dark,
194 R.drawable.ic_media_route_disabled_holo_dark,
195 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700196
197 int streamType;
198 int descRes;
199 int iconRes;
200 int iconMuteRes;
201 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
202 boolean show;
203
204 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
205 this.streamType = streamType;
206 this.descRes = descRes;
207 this.iconRes = iconRes;
208 this.iconMuteRes = iconMuteRes;
209 this.show = show;
210 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700211 }
Amith Yamasani71def772011-10-12 12:25:24 -0700212
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800213 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700214 private static final StreamResources[] STREAMS = {
215 StreamResources.BluetoothSCOStream,
216 StreamResources.RingerStream,
217 StreamResources.VoiceStream,
218 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700219 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400220 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700221 StreamResources.MasterStream,
222 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800223 };
224
225 /** Object that contains data for each slider */
226 private class StreamControl {
227 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700228 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800229 ViewGroup group;
230 ImageView icon;
231 SeekBar seekbarView;
232 int iconRes;
233 int iconMuteRes;
234 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800235
236 // Synchronize when accessing this
237 private ToneGenerator mToneGenerators[];
238 private Vibrator mVibrator;
239
Eric Laurentc34dcc12012-09-10 13:51:52 -0700240 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700241 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700242
243 private static class WarningDialogReceiver extends BroadcastReceiver
244 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800245 private final Context mContext;
246 private final Dialog mDialog;
247 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700248
Eric Laurentfde16d52012-12-03 14:42:39 -0800249 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700250 mContext = context;
251 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800252 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700253 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
254 context.registerReceiver(this, filter);
255 }
256
257 @Override
258 public void onReceive(Context context, Intent intent) {
259 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800260 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700261 }
262
Alan Viverette494fb7b2014-04-10 18:12:56 -0700263 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700264 public void onDismiss(DialogInterface unused) {
265 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800266 cleanUp();
267 }
268
269 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700270 synchronized (sConfirmSafeVolumeLock) {
271 sConfirmSafeVolumeDialog = null;
272 }
John Spurlock1dad2722014-07-11 11:07:53 -0400273 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800274 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700275 }
276 }
277
278
John Spurlock86005342014-05-23 11:58:00 -0400279 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
John Spurlockae641c92014-06-30 18:11:40 -0400280 mTag = String.format("%s.%s.%08x", TAG, parent == null ? "Dialog" : "Embed", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800281 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400282 mParent = parent;
283 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800284 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800285
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400286 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700287 final Resources res = context.getResources();
288 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400289 if (useMasterVolume) {
290 for (int i = 0; i < STREAMS.length; i++) {
291 StreamResources streamRes = STREAMS[i];
292 streamRes.show = (streamRes.streamType == STREAM_MASTER);
293 }
294 }
John Spurlock86005342014-05-23 11:58:00 -0400295 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
John Spurlock86005342014-05-23 11:58:00 -0400296 if (parent == null) {
297 // dialog mode
298 mDialog = new Dialog(context) {
299 @Override
300 public boolean onTouchEvent(MotionEvent event) {
301 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
302 sConfirmSafeVolumeDialog == null) {
John Spurlock1dad2722014-07-11 11:07:53 -0400303 forceTimeout(0);
John Spurlock86005342014-05-23 11:58:00 -0400304 return true;
305 }
306 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700307 }
John Spurlock86005342014-05-23 11:58:00 -0400308 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700309
John Spurlock86005342014-05-23 11:58:00 -0400310 // Change some window properties
311 final Window window = mDialog.getWindow();
312 final LayoutParams lp = window.getAttributes();
313 lp.token = null;
314 // Offset from the top
315 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
John Spurlockae641c92014-06-30 18:11:40 -0400316 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
John Spurlock2078caf2014-05-29 22:20:14 -0400317 lp.format = PixelFormat.TRANSLUCENT;
John Spurlockae641c92014-06-30 18:11:40 -0400318 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
319 lp.gravity = Gravity.TOP;
John Spurlock86005342014-05-23 11:58:00 -0400320 window.setAttributes(lp);
John Spurlock86005342014-05-23 11:58:00 -0400321 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
322 window.requestFeature(Window.FEATURE_NO_TITLE);
323 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
324 | LayoutParams.FLAG_NOT_TOUCH_MODAL
John Spurlock2078caf2014-05-29 22:20:14 -0400325 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
326 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlock86005342014-05-23 11:58:00 -0400327 mDialog.setCanceledOnTouchOutside(true);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400328 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
John Spurlock86005342014-05-23 11:58:00 -0400329 mDialog.setOnDismissListener(new OnDismissListener() {
330 @Override
331 public void onDismiss(DialogInterface dialog) {
332 mActiveStreamType = -1;
333 mAudioManager.forceVolumeControlStream(mActiveStreamType);
John Spurlockae641c92014-06-30 18:11:40 -0400334 setZenPanelVisible(false);
John Spurlock86005342014-05-23 11:58:00 -0400335 }
336 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700337
John Spurlock86005342014-05-23 11:58:00 -0400338 mDialog.create();
John Spurlock7f1df5e2014-05-31 19:11:40 -0400339 // temporary workaround, until we support window-level shadows
John Spurlock2078caf2014-05-29 22:20:14 -0400340 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700341
John Spurlock86005342014-05-23 11:58:00 -0400342 mView = window.findViewById(R.id.content);
343 mView.setOnTouchListener(new View.OnTouchListener() {
344 @Override
345 public boolean onTouch(View v, MotionEvent event) {
346 resetTimeout();
347 return false;
348 }
349 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700350
John Spurlock86005342014-05-23 11:58:00 -0400351 } else {
352 // embedded mode
353 mDialog = null;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400354 mView = LayoutInflater.from(mContext).inflate(
John Spurlock7f8f22a2014-07-02 18:54:17 -0400355 com.android.systemui.R.layout.volume_panel, parent, false);
John Spurlock86005342014-05-23 11:58:00 -0400356 }
357 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
358 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlock86005342014-05-23 11:58:00 -0400359 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800360
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800361 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700362 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700363 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700364
John Spurlock86005342014-05-23 11:58:00 -0400365 mZenModeCapable = !useMasterVolume && mZenController != null;
John Spurlockae641c92014-06-30 18:11:40 -0400366 updateZenMode(mZenController != null ? mZenController.getZen() : Global.ZEN_MODE_OFF);
John Spurlock86005342014-05-23 11:58:00 -0400367 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700368
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700369 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
370 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700371 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
372
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800373 listenToRingerMode();
374 }
375
John Spurlock7f8f22a2014-07-02 18:54:17 -0400376 public View getContentView() {
377 return mView;
378 }
379
John Spurlock86005342014-05-23 11:58:00 -0400380 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800381 mPanel.setLayoutDirection(layoutDirection);
382 updateStates();
383 }
384
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800385 private void listenToRingerMode() {
386 final IntentFilter filter = new IntentFilter();
387 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
388 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700389 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800390 public void onReceive(Context context, Intent intent) {
391 final String action = intent.getAction();
392
393 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
394 removeMessages(MSG_RINGER_MODE_CHANGED);
395 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
396 }
397 }
398 }, filter);
399 }
400
401 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400402 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700403 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700404 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700405 // TODO do we need to support a distinct mute property for remote?
406 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400407 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700408 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400409 }
410 }
411
412 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400413 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700414 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700415 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700416 if (mStreamControls != null) {
417 StreamControl sc = mStreamControls.get(streamType);
418 if (sc != null && sc.controller != null) {
419 VolumeInfo vi = sc.controller.getVolumeInfo();
420 return vi.getMaxVolume();
421 }
422 }
423 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400424 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700425 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400426 }
427 }
428
429 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400430 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700431 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700432 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700433 if (mStreamControls != null) {
434 StreamControl sc = mStreamControls.get(streamType);
435 if (sc != null && sc.controller != null) {
436 VolumeInfo vi = sc.controller.getVolumeInfo();
437 return vi.getCurrentVolume();
438 }
439 }
440 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400441 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700442 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400443 }
444 }
445
RoboErik19c95182014-06-23 15:38:48 -0700446 private void setStreamVolume(StreamControl sc, int index, int flags) {
447 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
448 if (sc.controller != null) {
449 sc.controller.setVolumeTo(index, flags);
450 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700451 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700452 }
453 } else if (getStreamVolume(sc.streamType) != index) {
454 if (sc.streamType == STREAM_MASTER) {
455 mAudioManager.setMasterVolume(index, flags);
456 } else {
457 mAudioManager.setStreamVolume(sc.streamType, index, flags);
458 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400459 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800460 }
461
462 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700463 final Resources res = mContext.getResources();
464 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
465 Context.LAYOUT_INFLATER_SERVICE);
466
Amith Yamasani71def772011-10-12 12:25:24 -0700467 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700468
Amith Yamasani71def772011-10-12 12:25:24 -0700469 for (int i = 0; i < STREAMS.length; i++) {
470 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700471
472 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700473
474 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700475 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400476 sc.group = (ViewGroup) inflater.inflate(
477 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800478 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400479 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800480 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700481 sc.icon.setContentDescription(res.getString(streamRes.descRes));
482 sc.iconRes = streamRes.iconRes;
483 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800484 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400485 sc.icon.setClickable(isNotificationOrRing(streamType));
486 if (sc.icon.isClickable()) {
487 sc.icon.setOnClickListener(new OnClickListener() {
488 @Override
489 public void onClick(View v) {
490 resetTimeout();
491 toggle(sc);
492 }
493 });
John Spurlock7f1df5e2014-05-31 19:11:40 -0400494 }
John Spurlock86005342014-05-23 11:58:00 -0400495 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700496 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700497 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400498 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700499 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800500 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700501 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800502 }
503 }
504
John Spurlock7f1df5e2014-05-31 19:11:40 -0400505 private void toggle(StreamControl sc) {
506 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
507 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
508 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
509 } else {
510 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
511 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
512 }
513 }
514
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800515 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400516 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800517
Alan Viverette494fb7b2014-04-10 18:12:56 -0700518 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800519 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400520 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800521 mActiveStreamType = -1;
522 } else {
John Spurlock86005342014-05-23 11:58:00 -0400523 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800524 mActiveStreamType = activeStreamType;
525 active.group.setVisibility(View.VISIBLE);
526 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400527 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400528 setZenPanelVisible(isNotificationOrRing(mActiveStreamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800529 }
530 }
531
532 /** Update the mute and progress state of a slider */
533 private void updateSlider(StreamControl sc) {
Eric Laurent8c787522012-05-14 14:09:43 -0700534 sc.seekbarView.setProgress(getStreamVolume(sc.streamType));
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800535 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800536 // Force reloading the image resource
537 sc.icon.setImageDrawable(null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800538 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400539 if (isNotificationOrRing(sc.streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700540 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
John Spurlock86005342014-05-23 11:58:00 -0400541 sc.icon.setImageResource(com.android.systemui.R.drawable.ic_ringer_vibrate);
Amith Yamasanic696a532011-10-28 17:02:37 -0700542 }
John Spurlock7f1df5e2014-05-31 19:11:40 -0400543 updateSliderEnabled(sc, muted, false);
544 }
545
John Spurlock5f640e42014-05-31 20:15:59 -0400546 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
547 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400548 final boolean isRinger = isNotificationOrRing(sc.streamType);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700549 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
550 // never disable touch interactions for remote playback, the muting is not tied to
551 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700552 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400553 } else if (fixedVolume ||
554 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800555 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700556 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400557 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400558 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400559 sc.icon.setEnabled(false);
560 sc.icon.setClickable(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700561 } else {
562 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400563 sc.icon.setEnabled(true);
Eric Laurent8c787522012-05-14 14:09:43 -0700564 }
John Spurlockae641c92014-06-30 18:11:40 -0400565 // show the silent hint when the disabled slider is touched in silent mode
566 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400567 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400568 sc.group.setOnTouchListener(null);
569 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400570 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400571 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400572 @Override
573 public boolean onTouch(View v, MotionEvent event) {
574 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400575 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400576 return false;
577 }
John Spurlockae641c92014-06-30 18:11:40 -0400578 };
579 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400580 }
581 }
582 }
583
John Spurlockae641c92014-06-30 18:11:40 -0400584 private void showSilentHint() {
585 if (mZenPanel != null) {
586 mZenPanel.showSilentHint();
587 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800588 }
589
John Spurlock7f1df5e2014-05-31 19:11:40 -0400590 private static boolean isNotificationOrRing(int streamType) {
591 return streamType == AudioManager.STREAM_RING
592 || streamType == AudioManager.STREAM_NOTIFICATION;
593 }
594
John Spurlockae641c92014-06-30 18:11:40 -0400595 public void setCallback(Callback callback) {
596 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400597 }
598
John Spurlock8845da72014-07-07 21:29:48 -0400599 private void updateTimeoutDelay() {
600 mTimeoutDelay = mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400601 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
602 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
603 : TIMEOUT_DELAY;
604 }
605
606 private boolean isZenPanelVisible() {
607 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400608 }
609
John Spurlockae641c92014-06-30 18:11:40 -0400610 private void setZenPanelVisible(boolean visible) {
611 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400612 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400613 if (visible) {
614 if (mZenPanel == null) {
615 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
616 mZenPanel.init(mZenController, mDialog != null ? 'D' : 'E');
617 mZenPanel.setCallback(new ZenModePanel.Callback() {
618 @Override
619 public void onMoreSettings() {
620 if (mCallback != null) {
621 mCallback.onZenSettings();
622 }
John Spurlock86005342014-05-23 11:58:00 -0400623 }
John Spurlock86005342014-05-23 11:58:00 -0400624
John Spurlockae641c92014-06-30 18:11:40 -0400625 @Override
626 public void onInteraction() {
627 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400628 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800629
John Spurlockae641c92014-06-30 18:11:40 -0400630 @Override
631 public void onExpanded(boolean expanded) {
John Spurlock8845da72014-07-07 21:29:48 -0400632 if (mZenPanelExpanded == expanded) return;
633 mZenPanelExpanded = expanded;
634 updateTimeoutDelay();
John Spurlockae641c92014-06-30 18:11:40 -0400635 resetTimeout();
636 }
637 });
638 }
639 mZenPanel.setVisibility(View.VISIBLE);
640 resetTimeout();
641 } else {
642 if (mZenPanel != null) {
643 mZenPanel.setVisibility(View.GONE);
644 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800645 }
John Spurlockea9938c2014-07-11 18:51:32 -0400646 if (changing) {
647 updateTimeoutDelay();
648 resetTimeout();
649 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800650 }
651
Eric Laurentfde16d52012-12-03 14:42:39 -0800652 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400653 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800654 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400655 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800656 updateSlider(sc);
657 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 }
659
John Spurlockae641c92014-06-30 18:11:40 -0400660 private void updateZenMode(int zen) {
661 final boolean show = mZenModeCapable && isNotificationOrRing(mActiveStreamType);
662 setZenPanelVisible(show);
John Spurlock86005342014-05-23 11:58:00 -0400663 }
664
John Spurlockae641c92014-06-30 18:11:40 -0400665 public void postZenModeChanged(int zen) {
John Spurlock86005342014-05-23 11:58:00 -0400666 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlockae641c92014-06-30 18:11:40 -0400667 obtainMessage(MSG_ZEN_MODE_CHANGED, zen).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400668 }
669
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800670 public void postVolumeChanged(int streamType, int flags) {
671 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700672 synchronized (this) {
673 if (mStreamControls == null) {
674 createSliders();
675 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800676 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 removeMessages(MSG_FREE_RESOURCES);
678 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
679 }
680
RoboErik19c95182014-06-23 15:38:48 -0700681 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700682 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
683 synchronized (this) {
684 if (mStreamControls == null) {
685 createSliders();
686 }
687 }
688 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700689 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700690 }
691
692 public void postRemoteSliderVisibility(boolean visible) {
693 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
694 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
695 }
696
697 /**
698 * Called by AudioService when it has received new remote playback information that
699 * would affect the VolumePanel display (mainly volumes). The difference with
700 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
701 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
702 * displayed.
703 * This special code path is due to the fact that remote volume updates arrive to AudioService
704 * asynchronously. So after AudioService has sent the volume update (which should be treated
705 * as a request to update the volume), the application will likely set a new volume. If the UI
706 * is still up, we need to refresh the display to show this new value.
707 */
708 public void postHasNewRemotePlaybackInfo() {
709 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
710 // don't create or prevent resources to be freed, if they disappear, this update came too
711 // late and shouldn't warrant the panel to be displayed longer
712 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
713 }
714
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400715 public void postMasterVolumeChanged(int flags) {
716 postVolumeChanged(STREAM_MASTER, flags);
717 }
718
Mike Lockwoodce952c82011-11-14 10:47:42 -0800719 public void postMuteChanged(int streamType, int flags) {
720 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700721 synchronized (this) {
722 if (mStreamControls == null) {
723 createSliders();
724 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800725 }
726 removeMessages(MSG_FREE_RESOURCES);
727 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
728 }
729
730 public void postMasterMuteChanged(int flags) {
731 postMuteChanged(STREAM_MASTER, flags);
732 }
733
Eric Laurentfde16d52012-12-03 14:42:39 -0800734 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700735 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800736 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700737 }
738
John Spurlock1dad2722014-07-11 11:07:53 -0400739 public void postDismiss(long delay) {
740 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400741 }
742
743 public void postLayoutDirection(int layoutDirection) {
744 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400745 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400746 }
747
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800748 /**
749 * Override this if you have other work to do when the volume changes (for
750 * example, vibrating, playing a sound, etc.). Make sure to call through to
751 * the superclass implementation.
752 */
753 protected void onVolumeChanged(int streamType, int flags) {
754
John Spurlock86005342014-05-23 11:58:00 -0400755 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756
757 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700758 synchronized (this) {
759 if (mActiveStreamType != streamType) {
760 reorderSliders(streamType);
761 }
RoboErik19c95182014-06-23 15:38:48 -0700762 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800763 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800764 }
765
Marco Nelissen69f593c2009-07-28 09:55:04 -0700766 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800767 removeMessages(MSG_PLAY_SOUND);
768 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
769 }
770
771 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
772 removeMessages(MSG_PLAY_SOUND);
773 removeMessages(MSG_VIBRATE);
774 onStopSounds();
775 }
776
777 removeMessages(MSG_FREE_RESOURCES);
778 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800779 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800780 }
781
Mike Lockwoodce952c82011-11-14 10:47:42 -0800782 protected void onMuteChanged(int streamType, int flags) {
783
John Spurlock86005342014-05-23 11:58:00 -0400784 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800785
786 StreamControl sc = mStreamControls.get(streamType);
787 if (sc != null) {
788 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
789 }
790
791 onVolumeChanged(streamType, flags);
792 }
793
RoboErik19c95182014-06-23 15:38:48 -0700794 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700795 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800796
Marco Nelissen69f593c2009-07-28 09:55:04 -0700797 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800798
799 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400800 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 + ", flags: " + flags + "), index: " + index);
802 }
803
804 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800805
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400806 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700807 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800808
809 switch (streamType) {
810
811 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800812// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700813 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
814 mContext, RingtoneManager.TYPE_RINGTONE);
815 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700816 mRingIsSilent = true;
817 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 break;
819 }
820
821 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800822 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800823 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
824 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
825 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
826 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800827 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800828 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800829 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 }
831 break;
832 }
833
834 case AudioManager.STREAM_VOICE_CALL: {
835 /*
836 * For in-call voice call volume, there is no inaudible volume.
837 * Rescale the UI control so the progress bar doesn't go all
838 * the way to zero and don't show the mute icon.
839 */
840 index++;
841 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 break;
843 }
844
845 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800846 break;
847 }
848
849 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700850 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
851 mContext, RingtoneManager.TYPE_NOTIFICATION);
852 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700853 mRingIsSilent = true;
854 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 break;
856 }
857
858 case AudioManager.STREAM_BLUETOOTH_SCO: {
859 /*
860 * For in-call voice call volume, there is no inaudible volume.
861 * Rescale the UI control so the progress bar doesn't go all
862 * the way to zero and don't show the mute icon.
863 */
864 index++;
865 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800866 break;
867 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700868
869 case AudioService.STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -0700870 if (controller == null && sc != null) {
871 // If we weren't passed one try using the last one set.
872 controller = sc.controller;
873 }
874 if (controller == null) {
875 // We still don't have one, ignore the command.
876 Log.w(mTag, "sent remote volume change without a controller!");
877 } else {
878 VolumeInfo vi = controller.getVolumeInfo();
879 index = vi.getCurrentVolume();
880 max = vi.getMaxVolume();
881 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
882 // if the remote volume is fixed add the flag for the UI
883 flags |= AudioManager.FLAG_FIXED_VOLUME;
884 }
885 }
John Spurlock86005342014-05-23 11:58:00 -0400886 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700887 break;
888 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
890
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800891 if (sc != null) {
RoboErik19c95182014-06-23 15:38:48 -0700892 if (streamType == AudioService.STREAM_REMOTE_MUSIC && controller != sc.controller) {
893 if (sc.controller != null) {
894 sc.controller.removeCallback(mMediaControllerCb);
895 }
896 sc.controller = controller;
897 if (controller != null) {
898 sc.controller.addCallback(mMediaControllerCb);
899 }
900 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700901 if (sc.seekbarView.getMax() != max) {
902 sc.seekbarView.setMax(max);
903 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700904
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800905 sc.seekbarView.setProgress(index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400906 updateSliderEnabled(sc, isMuted(streamType),
907 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 }
909
John Spurlock86005342014-05-23 11:58:00 -0400910 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700911 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
912 // when the stream is for remote playback, use -1 to reset the stream type evaluation
913 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700914
John Spurlock86005342014-05-23 11:58:00 -0400915 if (mDialog != null) {
916 mDialog.show();
John Spurlock33f4e042014-07-11 13:10:58 -0400917 if (mCallback != null) {
918 mCallback.onVisible(true);
919 }
John Spurlock86005342014-05-23 11:58:00 -0400920 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
922
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700924 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
925 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400926 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700927 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800928 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
929 }
John Spurlocka11b4af2014-06-01 11:52:23 -0400930
931 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -0400932 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
933 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -0400934 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800935 }
936
John Spurlock86005342014-05-23 11:58:00 -0400937 private boolean isShowing() {
938 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
939 }
940
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 protected void onPlaySound(int streamType, int flags) {
942
943 if (hasMessages(MSG_STOP_SOUNDS)) {
944 removeMessages(MSG_STOP_SOUNDS);
945 // Force stop right now
946 onStopSounds();
947 }
948
949 synchronized (this) {
950 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800951 if (toneGen != null) {
952 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
953 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
954 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800955 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800956 }
957
958 protected void onStopSounds() {
959
960 synchronized (this) {
961 int numStreamTypes = AudioSystem.getNumStreamTypes();
962 for (int i = numStreamTypes - 1; i >= 0; i--) {
963 ToneGenerator toneGen = mToneGenerators[i];
964 if (toneGen != null) {
965 toneGen.stopTone();
966 }
967 }
968 }
969 }
970
971 protected void onVibrate() {
972
973 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700974 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975 return;
976 }
977
John Spurlock7b414672014-07-18 13:02:39 -0400978 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 }
980
RoboErik19c95182014-06-23 15:38:48 -0700981 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
982 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
983 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700984
John Spurlock86005342014-05-23 11:58:00 -0400985 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700986 synchronized (this) {
987 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
988 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
989 }
RoboErik19c95182014-06-23 15:38:48 -0700990 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700991 }
992 } else {
John Spurlock86005342014-05-23 11:58:00 -0400993 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700994 }
995
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700996 removeMessages(MSG_FREE_RESOURCES);
997 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700998 resetTimeout();
999 }
1000
1001 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001002 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1003 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001004 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
1005 && (mStreamControls != null)) {
RoboErik19c95182014-06-23 15:38:48 -07001006 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001007 }
1008 }
1009
RoboErik19c95182014-06-23 15:38:48 -07001010 /**
1011 * Clear the current remote stream controller.
1012 */
1013 private void clearRemoteStreamController() {
1014 if (mStreamControls != null) {
1015 StreamControl sc = mStreamControls.get(AudioService.STREAM_REMOTE_MUSIC);
1016 if (sc != null) {
1017 if (sc.controller != null) {
1018 sc.controller.removeCallback(mMediaControllerCb);
1019 sc.controller = null;
1020 }
1021 }
1022 }
1023 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001024
1025 /**
1026 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
1027 * Hide or show a slider
1028 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
1029 * or AudioService.STREAM_REMOTE_MUSIC
1030 * @param visible
1031 */
1032 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001033 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001034 boolean isVisible = (visible == 1);
1035 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1036 StreamResources streamRes = STREAMS[i];
1037 if (streamRes.streamType == streamType) {
1038 streamRes.show = isVisible;
1039 if (!isVisible && (mActiveStreamType == streamType)) {
1040 mActiveStreamType = -1;
1041 }
1042 break;
1043 }
1044 }
1045 }
1046
Eric Laurentfde16d52012-12-03 14:42:39 -08001047 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001048 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001049 synchronized (sConfirmSafeVolumeLock) {
1050 if (sConfirmSafeVolumeDialog != null) {
1051 return;
1052 }
1053 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1054 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1055 .setPositiveButton(com.android.internal.R.string.yes,
1056 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001057 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001058 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001059 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001060 }
1061 })
1062 .setNegativeButton(com.android.internal.R.string.no, null)
1063 .setIconAttribute(android.R.attr.alertDialogIcon)
1064 .create();
1065 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1066 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001067
Eric Laurentfde16d52012-12-03 14:42:39 -08001068 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1069 sConfirmSafeVolumeDialog.getWindow().setType(
1070 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1071 sConfirmSafeVolumeDialog.show();
1072 }
1073 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001074 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001075 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001076 }
1077
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001078 /**
1079 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1080 */
1081 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001082 if (streamType == STREAM_MASTER) {
1083 // For devices that use the master volume setting only but still want to
1084 // play a volume-changed tone, direct the master volume pseudostream to
1085 // the system stream's tone generator.
1086 if (mPlayMasterStreamTones) {
1087 streamType = AudioManager.STREAM_SYSTEM;
1088 } else {
1089 return null;
1090 }
1091 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001092 synchronized (this) {
1093 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001094 try {
1095 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1096 } catch (RuntimeException e) {
1097 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001098 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001099 + "RuntimeException: " + e);
1100 }
1101 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001103 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 }
1105 }
1106
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001107
1108 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001109 * Switch between icons because Bluetooth music is same as music volume, but with
1110 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001112 private void setMusicIcon(int resId, int resMuteId) {
1113 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1114 if (sc != null) {
1115 sc.iconRes = resId;
1116 sc.iconMuteRes = resMuteId;
1117 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001118 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 }
1120
1121 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001122 synchronized (this) {
1123 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1124 if (mToneGenerators[i] != null) {
1125 mToneGenerators[i].release();
1126 }
1127 mToneGenerators[i] = null;
1128 }
1129 }
1130 }
1131
1132 @Override
1133 public void handleMessage(Message msg) {
1134 switch (msg.what) {
1135
1136 case MSG_VOLUME_CHANGED: {
1137 onVolumeChanged(msg.arg1, msg.arg2);
1138 break;
1139 }
1140
Mike Lockwoodce952c82011-11-14 10:47:42 -08001141 case MSG_MUTE_CHANGED: {
1142 onMuteChanged(msg.arg1, msg.arg2);
1143 break;
1144 }
1145
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001146 case MSG_FREE_RESOURCES: {
1147 onFreeResources();
1148 break;
1149 }
1150
1151 case MSG_STOP_SOUNDS: {
1152 onStopSounds();
1153 break;
1154 }
1155
1156 case MSG_PLAY_SOUND: {
1157 onPlaySound(msg.arg1, msg.arg2);
1158 break;
1159 }
1160
1161 case MSG_VIBRATE: {
1162 onVibrate();
1163 break;
1164 }
1165
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001166 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001167 if (isShowing()) {
1168 if (mDialog != null) {
1169 mDialog.dismiss();
RoboErik19c95182014-06-23 15:38:48 -07001170 clearRemoteStreamController();
John Spurlockf71205c2014-05-29 10:17:51 -04001171 mActiveStreamType = -1;
John Spurlock33f4e042014-07-11 13:10:58 -04001172 if (mCallback != null) {
1173 mCallback.onVisible(false);
1174 }
John Spurlock86005342014-05-23 11:58:00 -04001175 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001176 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001177 synchronized (sConfirmSafeVolumeLock) {
1178 if (sConfirmSafeVolumeDialog != null) {
1179 sConfirmSafeVolumeDialog.dismiss();
1180 }
1181 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001182 break;
1183 }
1184 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001185 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001186 updateStates();
1187 }
1188 break;
1189 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001190
1191 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001192 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001193 break;
1194 }
1195
1196 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1197 onRemoteVolumeUpdateIfShown();
1198 break;
1199
1200 case MSG_SLIDER_VISIBILITY_CHANGED:
1201 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1202 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001203
1204 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001205 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001206 break;
John Spurlock86005342014-05-23 11:58:00 -04001207
1208 case MSG_LAYOUT_DIRECTION:
1209 setLayoutDirection(msg.arg1);
1210 break;
1211
1212 case MSG_ZEN_MODE_CHANGED:
John Spurlockae641c92014-06-30 18:11:40 -04001213 updateZenMode(msg.arg1);
1214 break;
1215
1216 case MSG_USER_ACTIVITY:
1217 if (mCallback != null) {
1218 mCallback.onInteraction();
1219 }
John Spurlock86005342014-05-23 11:58:00 -04001220 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001221 }
1222 }
1223
John Spurlockae641c92014-06-30 18:11:40 -04001224 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001225 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1226 + " delay=" + mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001227 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001228 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
John Spurlockae641c92014-06-30 18:11:40 -04001229 removeMessages(MSG_USER_ACTIVITY);
1230 sendEmptyMessage(MSG_USER_ACTIVITY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001231 }
1232
John Spurlock1dad2722014-07-11 11:07:53 -04001233 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001234 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001235 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001236 }
1237
1238 public ZenModeController getZenController() {
1239 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001240 }
1241
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001242 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1243 @Override
1244 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1245 final Object tag = seekBar.getTag();
1246 if (fromUser && tag instanceof StreamControl) {
1247 StreamControl sc = (StreamControl) tag;
RoboErik19c95182014-06-23 15:38:48 -07001248 setStreamVolume(sc, progress, 0);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001249 }
1250 resetTimeout();
1251 }
1252
1253 @Override
1254 public void onStartTrackingTouch(SeekBar seekBar) {
1255 }
1256
1257 @Override
1258 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001259 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001260 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001261
John Spurlock86005342014-05-23 11:58:00 -04001262 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockae641c92014-06-30 18:11:40 -04001263 public void onZenChanged(int zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001264 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001265 }
1266 };
RoboErik19c95182014-06-23 15:38:48 -07001267
1268 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
1269 public void onVolumeInfoChanged(VolumeInfo info) {
1270 onRemoteVolumeUpdateIfShown();
1271 }
1272 };
John Spurlockae641c92014-06-30 18:11:40 -04001273
1274 public interface Callback {
1275 void onZenSettings();
1276 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001277 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001278 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001279}