blob: 84cc1cdc142fd320e16f7ebcc6d86052565eb965 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2007 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
John Spurlock3346a802014-05-20 16:25:37 -040017package com.android.systemui.volume;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080018
John Spurlock5f640e42014-05-31 20:15:59 -040019import android.animation.Animator;
20import android.animation.AnimatorListenerAdapter;
Eric Laurentc34dcc12012-09-10 13:51:52 -070021import android.app.AlertDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080022import android.app.Dialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080023import android.content.BroadcastReceiver;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080025import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040026import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080027import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080028import android.content.IntentFilter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080029import android.content.res.Resources;
John Spurlock2078caf2014-05-29 22:20:14 -040030import android.graphics.PixelFormat;
31import android.graphics.drawable.ColorDrawable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080032import android.media.AudioManager;
33import android.media.AudioService;
34import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070035import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.media.ToneGenerator;
Marco Nelissen69f593c2009-07-28 09:55:04 -070037import android.net.Uri;
John Spurlock3bd4fee2014-05-29 20:51:09 -040038import android.os.AsyncTask;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080039import android.os.Handler;
40import android.os.Message;
41import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.util.Log;
John Spurlock3346a802014-05-20 16:25:37 -040043import android.view.Gravity;
44import android.view.LayoutInflater;
45import android.view.MotionEvent;
46import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040047import android.view.View.OnClickListener;
48import android.view.View.OnLongClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040049import android.view.ViewGroup;
John Spurlock86005342014-05-23 11:58:00 -040050import android.view.ViewStub;
John Spurlock3346a802014-05-20 16:25:37 -040051import android.view.Window;
52import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070053import android.view.WindowManager.LayoutParams;
John Spurlock5f640e42014-05-31 20:15:59 -040054import android.view.animation.AnimationUtils;
55import android.view.animation.Interpolator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080057import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080058import android.widget.SeekBar.OnSeekBarChangeListener;
59
John Spurlock86005342014-05-23 11:58:00 -040060import com.android.internal.R;
61import com.android.systemui.statusbar.policy.ZenModeController;
62
Amith Yamasani2bbdd772011-02-02 18:54:13 -080063import java.util.HashMap;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080064
65/**
John Spurlock3346a802014-05-20 16:25:37 -040066 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070067 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080068 * @hide
69 */
John Spurlock3346a802014-05-20 16:25:37 -040070public class VolumePanel extends Handler {
Marco Nelissen69f593c2009-07-28 09:55:04 -070071 private static boolean LOGD = false;
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 Spurlock3bd4fee2014-05-29 20:51:09 -040088 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
John Spurlock5f640e42014-05-31 20:15:59 -040089 private static final float ICON_PULSE_SCALE = 1.3f;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090
91 private static final int MSG_VOLUME_CHANGED = 0;
92 private static final int MSG_FREE_RESOURCES = 1;
93 private static final int MSG_PLAY_SOUND = 2;
94 private static final int MSG_STOP_SOUNDS = 3;
95 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080096 private static final int MSG_TIMEOUT = 5;
97 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -080098 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -070099 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
100 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
101 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700102 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400103 private static final int MSG_LAYOUT_DIRECTION = 12;
104 private static final int MSG_ZEN_MODE_CHANGED = 13;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800105
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400106 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800107 private static final int STREAM_MASTER = -100;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700108 // Pseudo stream type for remote volume is defined in AudioService.STREAM_REMOTE_MUSIC
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400109
John Spurlock86005342014-05-23 11:58:00 -0400110 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400111 protected final Context mContext;
112 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400113 private final ZenModeController mZenController;
John Spurlock5f640e42014-05-31 20:15:59 -0400114 private final Interpolator mFastOutSlowInInterpolator;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700115 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700116 private boolean mVoiceCapable;
John Spurlock86005342014-05-23 11:58:00 -0400117 private boolean mZenModeCapable;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400118 private int mTimeoutDelay = TIMEOUT_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800119
Christopher Tatec4b78d22012-05-22 13:57:58 -0700120 // True if we want to play tones on the system stream when the master stream is specified.
121 private final boolean mPlayMasterStreamTones;
122
John Spurlock86005342014-05-23 11:58:00 -0400123
124 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 private final View mView;
John Spurlock86005342014-05-23 11:58:00 -0400126 /** Dialog hosting the panel, if not embedded */
127 private final Dialog mDialog;
128 /** Parent view hosting the panel, if embedded */
129 private final ViewGroup mParent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800130
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700131 /** The visible portion of the volume overlay */
132 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400133 /** Contains the slider and its touchable icons */
134 private final ViewGroup mSliderPanel;
135 /** The button that expands the dialog to show the zen panel */
136 private final ImageView mExpandButton;
137 /** Dummy divider icon that needs to vanish with the expand button */
138 private final View mExpandDivider;
139 /** The zen mode configuration panel view stub */
140 private final ViewStub mZenPanelStub;
141 /** The zen mode configuration panel view, once inflated */
142 private ZenModePanel mZenPanel;
143 /** Dummy divider icon that needs to vanish with the zen panel */
144 private final View mZenPanelDivider;
145
146 private ZenModePanel.Callback mZenPanelCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800147
148 /** Currently active stream that shows up at the top of the list of sliders */
149 private int mActiveStreamType = -1;
150 /** All the slider controls mapped by stream type */
151 private HashMap<Integer,StreamControl> mStreamControls;
152
Amith Yamasani71def772011-10-12 12:25:24 -0700153 private enum StreamResources {
154 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
155 R.string.volume_icon_description_bluetooth,
156 R.drawable.ic_audio_bt,
157 R.drawable.ic_audio_bt,
158 false),
159 RingerStream(AudioManager.STREAM_RING,
160 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400161 com.android.systemui.R.drawable.ic_ringer_audible,
162 com.android.systemui.R.drawable.ic_ringer_silent,
Amith Yamasani71def772011-10-12 12:25:24 -0700163 false),
164 VoiceStream(AudioManager.STREAM_VOICE_CALL,
165 R.string.volume_icon_description_incall,
166 R.drawable.ic_audio_phone,
167 R.drawable.ic_audio_phone,
168 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700169 AlarmStream(AudioManager.STREAM_ALARM,
170 R.string.volume_alarm,
171 R.drawable.ic_audio_alarm,
172 R.drawable.ic_audio_alarm_mute,
173 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700174 MediaStream(AudioManager.STREAM_MUSIC,
175 R.string.volume_icon_description_media,
176 R.drawable.ic_audio_vol,
177 R.drawable.ic_audio_vol_mute,
178 true),
179 NotificationStream(AudioManager.STREAM_NOTIFICATION,
180 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400181 com.android.systemui.R.drawable.ic_ringer_audible,
182 com.android.systemui.R.drawable.ic_ringer_silent,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400183 true),
184 // for now, use media resources for master volume
185 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700186 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400187 R.drawable.ic_audio_vol,
188 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700189 false),
190 RemoteStream(AudioService.STREAM_REMOTE_MUSIC,
191 R.string.volume_icon_description_media, //FIXME should have its own description
192 R.drawable.ic_media_route_on_holo_dark,
193 R.drawable.ic_media_route_disabled_holo_dark,
194 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700195
196 int streamType;
197 int descRes;
198 int iconRes;
199 int iconMuteRes;
200 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
201 boolean show;
202
203 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
204 this.streamType = streamType;
205 this.descRes = descRes;
206 this.iconRes = iconRes;
207 this.iconMuteRes = iconMuteRes;
208 this.show = show;
209 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700210 }
Amith Yamasani71def772011-10-12 12:25:24 -0700211
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800212 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700213 private static final StreamResources[] STREAMS = {
214 StreamResources.BluetoothSCOStream,
215 StreamResources.RingerStream,
216 StreamResources.VoiceStream,
217 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700218 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400219 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700220 StreamResources.MasterStream,
221 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800222 };
223
224 /** Object that contains data for each slider */
225 private class StreamControl {
226 int streamType;
227 ViewGroup group;
228 ImageView icon;
229 SeekBar seekbarView;
John Spurlock5f640e42014-05-31 20:15:59 -0400230 View seekbarContainer;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800231 int iconRes;
232 int iconMuteRes;
233 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234
235 // Synchronize when accessing this
236 private ToneGenerator mToneGenerators[];
237 private Vibrator mVibrator;
238
Eric Laurentc34dcc12012-09-10 13:51:52 -0700239 private static AlertDialog sConfirmSafeVolumeDialog;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700240 private static Object sConfirmSafeVolumeLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700241
242 private static class WarningDialogReceiver extends BroadcastReceiver
243 implements DialogInterface.OnDismissListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800244 private final Context mContext;
245 private final Dialog mDialog;
246 private final VolumePanel mVolumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700247
Eric Laurentfde16d52012-12-03 14:42:39 -0800248 WarningDialogReceiver(Context context, Dialog dialog, VolumePanel volumePanel) {
Eric Laurentc34dcc12012-09-10 13:51:52 -0700249 mContext = context;
250 mDialog = dialog;
Eric Laurentfde16d52012-12-03 14:42:39 -0800251 mVolumePanel = volumePanel;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700252 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
253 context.registerReceiver(this, filter);
254 }
255
256 @Override
257 public void onReceive(Context context, Intent intent) {
258 mDialog.cancel();
Eric Laurentfde16d52012-12-03 14:42:39 -0800259 cleanUp();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700260 }
261
Alan Viverette494fb7b2014-04-10 18:12:56 -0700262 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700263 public void onDismiss(DialogInterface unused) {
264 mContext.unregisterReceiver(this);
Eric Laurentfde16d52012-12-03 14:42:39 -0800265 cleanUp();
266 }
267
268 private void cleanUp() {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700269 synchronized (sConfirmSafeVolumeLock) {
270 sConfirmSafeVolumeDialog = null;
271 }
Eric Laurentfde16d52012-12-03 14:42:39 -0800272 mVolumePanel.forceTimeout();
273 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700274 }
275 }
276
277
John Spurlock86005342014-05-23 11:58:00 -0400278 public VolumePanel(Context context, ViewGroup parent, ZenModeController zenController) {
279 mTag = String.format("VolumePanel%s.%08x", parent == null ? "Dialog" : "", hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800280 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400281 mParent = parent;
282 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800283 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
John Spurlock5f640e42014-05-31 20:15:59 -0400284 mFastOutSlowInInterpolator = AnimationUtils.loadInterpolator(mContext,
285 android.R.interpolator.fast_out_slow_in);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400287 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700288 final Resources res = context.getResources();
289 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400290 if (useMasterVolume) {
291 for (int i = 0; i < STREAMS.length; i++) {
292 StreamResources streamRes = STREAMS[i];
293 streamRes.show = (streamRes.streamType == STREAM_MASTER);
294 }
295 }
John Spurlock86005342014-05-23 11:58:00 -0400296 if (LOGD) Log.d(mTag, String.format("new VolumePanel hasParent=%s", parent != null));
John Spurlock86005342014-05-23 11:58:00 -0400297 if (parent == null) {
298 // dialog mode
299 mDialog = new Dialog(context) {
300 @Override
301 public boolean onTouchEvent(MotionEvent event) {
302 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
303 sConfirmSafeVolumeDialog == null) {
304 forceTimeout();
305 return true;
306 }
307 return false;
Amith Yamasani284e6302011-09-16 18:24:47 -0700308 }
John Spurlock86005342014-05-23 11:58:00 -0400309 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700310
John Spurlock86005342014-05-23 11:58:00 -0400311 // Change some window properties
312 final Window window = mDialog.getWindow();
313 final LayoutParams lp = window.getAttributes();
314 lp.token = null;
315 // Offset from the top
316 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
317 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.volume_panel_width);
318 lp.type = LayoutParams.TYPE_VOLUME_OVERLAY;
John Spurlock2078caf2014-05-29 22:20:14 -0400319 lp.format = PixelFormat.TRANSLUCENT;
John Spurlock86005342014-05-23 11:58:00 -0400320 lp.windowAnimations = R.style.Animation_VolumePanel;
John Spurlock86005342014-05-23 11:58:00 -0400321 window.setAttributes(lp);
322 window.setGravity(Gravity.TOP);
323 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
324 window.requestFeature(Window.FEATURE_NO_TITLE);
325 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
326 | LayoutParams.FLAG_NOT_TOUCH_MODAL
John Spurlock2078caf2014-05-29 22:20:14 -0400327 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
328 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlock86005342014-05-23 11:58:00 -0400329 mDialog.setCanceledOnTouchOutside(true);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400330 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
John Spurlock86005342014-05-23 11:58:00 -0400331 mDialog.setOnDismissListener(new OnDismissListener() {
332 @Override
333 public void onDismiss(DialogInterface dialog) {
334 mActiveStreamType = -1;
335 mAudioManager.forceVolumeControlStream(mActiveStreamType);
336 }
337 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700338
John Spurlock86005342014-05-23 11:58:00 -0400339 mDialog.create();
John Spurlock7f1df5e2014-05-31 19:11:40 -0400340 // temporary workaround, until we support window-level shadows
John Spurlock2078caf2014-05-29 22:20:14 -0400341 mDialog.getWindow().setBackgroundDrawable(new ColorDrawable(0x00000000));
Alan Viverette494fb7b2014-04-10 18:12:56 -0700342
John Spurlock86005342014-05-23 11:58:00 -0400343 mView = window.findViewById(R.id.content);
344 mView.setOnTouchListener(new View.OnTouchListener() {
345 @Override
346 public boolean onTouch(View v, MotionEvent event) {
347 resetTimeout();
348 return false;
349 }
350 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700351
John Spurlock86005342014-05-23 11:58:00 -0400352 } else {
353 // embedded mode
354 mDialog = null;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400355 mView = LayoutInflater.from(mContext).inflate(
356 com.android.systemui.R.layout.volume_panel, parent, true);
John Spurlock86005342014-05-23 11:58:00 -0400357 }
358 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
359 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
360 mExpandButton = (ImageView) mView.findViewById(com.android.systemui.R.id.expand_button);
361 mExpandDivider = mView.findViewById(com.android.systemui.R.id.expand_button_divider);
362 mZenPanelStub = (ViewStub)mView.findViewById(com.android.systemui.R.id.zen_panel_stub);
363 mZenPanelDivider = mView.findViewById(com.android.systemui.R.id.zen_panel_divider);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800364
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700366 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700367 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700368
John Spurlock86005342014-05-23 11:58:00 -0400369 mZenModeCapable = !useMasterVolume && mZenController != null;
370 mZenPanelDivider.setVisibility(View.GONE);
371 mExpandButton.setOnClickListener(mClickListener);
372 updateZenMode(mZenController == null ? false : mZenController.isZen());
373 mZenController.addCallback(mZenCallback);
Amith Yamasani42722bf2011-07-22 10:34:27 -0700374
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700375 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
376 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700377 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
378
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800379 listenToRingerMode();
380 }
381
John Spurlock86005342014-05-23 11:58:00 -0400382 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800383 mPanel.setLayoutDirection(layoutDirection);
384 updateStates();
385 }
386
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800387 private void listenToRingerMode() {
388 final IntentFilter filter = new IntentFilter();
389 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
390 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700391 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800392 public void onReceive(Context context, Intent intent) {
393 final String action = intent.getAction();
394
395 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
396 removeMessages(MSG_RINGER_MODE_CHANGED);
397 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
398 }
399 }
400 }, filter);
401 }
402
403 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400404 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700405 return mAudioManager.isMasterMute();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700406 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400407 return (mAudioManager.getRemoteStreamVolume() <= 0);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400408 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700409 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400410 }
411 }
412
413 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400414 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700415 return mAudioManager.getMasterMaxVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700416 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400417 return mAudioManager.getRemoteStreamMaxVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400418 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700419 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400420 }
421 }
422
423 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400424 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700425 return mAudioManager.getMasterVolume();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700426 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400427 return mAudioManager.getRemoteStreamVolume();
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400428 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700429 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400430 }
431 }
432
433 private void setStreamVolume(int streamType, int index, int flags) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400434 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700435 mAudioManager.setMasterVolume(index, flags);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700436 } else if (streamType == AudioService.STREAM_REMOTE_MUSIC) {
John Spurlock3346a802014-05-20 16:25:37 -0400437 mAudioManager.setRemoteStreamVolume(index);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400438 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700439 mAudioManager.setStreamVolume(streamType, index, flags);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400440 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800441 }
442
443 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700444 final Resources res = mContext.getResources();
445 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
446 Context.LAYOUT_INFLATER_SERVICE);
447
Amith Yamasani71def772011-10-12 12:25:24 -0700448 mStreamControls = new HashMap<Integer, StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700449
Amith Yamasani71def772011-10-12 12:25:24 -0700450 for (int i = 0; i < STREAMS.length; i++) {
451 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700452
453 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700454
455 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700456 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400457 sc.group = (ViewGroup) inflater.inflate(
458 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800459 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400460 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800461 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700462 sc.icon.setContentDescription(res.getString(streamRes.descRes));
463 sc.iconRes = streamRes.iconRes;
464 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800465 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400466 sc.icon.setClickable(isNotificationOrRing(streamType));
467 if (sc.icon.isClickable()) {
468 sc.icon.setOnClickListener(new OnClickListener() {
469 @Override
470 public void onClick(View v) {
471 resetTimeout();
472 toggle(sc);
473 }
474 });
475 sc.icon.setOnLongClickListener(new OnLongClickListener() {
476 @Override
477 public boolean onLongClick(View v) {
478 resetTimeout();
479 longToggle(sc);
480 return true;
481 }
482 });
483 }
John Spurlock5f640e42014-05-31 20:15:59 -0400484 sc.seekbarContainer =
485 sc.group.findViewById(com.android.systemui.R.id.seekbar_container);
John Spurlock86005342014-05-23 11:58:00 -0400486 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700487 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700488 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400489 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700490 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800491 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700492 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800493 }
494 }
495
John Spurlock7f1df5e2014-05-31 19:11:40 -0400496 private void toggle(StreamControl sc) {
497 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
498 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
499 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
500 } else {
501 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
502 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
503 }
504 }
505
506 private void longToggle(StreamControl sc) {
507 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
508 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
509 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
510 } else {
511 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_SILENT);
512 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI); // disable the slider
513 }
514 }
515
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800516 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400517 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800518
Alan Viverette494fb7b2014-04-10 18:12:56 -0700519 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800520 if (active == null) {
521 Log.e("VolumePanel", "Missing stream type! - " + activeStreamType);
522 mActiveStreamType = -1;
523 } else {
John Spurlock86005342014-05-23 11:58:00 -0400524 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800525 mActiveStreamType = activeStreamType;
526 active.group.setVisibility(View.VISIBLE);
527 updateSlider(active);
John Spurlock86005342014-05-23 11:58:00 -0400528 updateZenMode(mZenController == null ? false : mZenController.isZen());
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();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700548 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
549 // never disable touch interactions for remote playback, the muting is not tied to
550 // the state of the phone.
551 sc.seekbarView.setEnabled(true);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400552 } else if (fixedVolume ||
553 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
Eric Laurentfde16d52012-12-03 14:42:39 -0800554 (sConfirmSafeVolumeDialog != null)) {
Eric Laurent8c787522012-05-14 14:09:43 -0700555 sc.seekbarView.setEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400556 } else if (isNotificationOrRing(sc.streamType)
557 && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
558 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700559 } else {
560 sc.seekbarView.setEnabled(true);
561 }
John Spurlock5f640e42014-05-31 20:15:59 -0400562 // pulse the ringer icon when the disabled slider is touched in silent mode
563 if (sc.icon.isClickable() && wasEnabled != sc.seekbarView.isEnabled()) {
564 if (sc.seekbarView.isEnabled()) {
565 sc.seekbarContainer.setOnTouchListener(null);
566 } else {
567 sc.seekbarContainer.setOnTouchListener(new View.OnTouchListener() {
568 @Override
569 public boolean onTouch(View v, MotionEvent event) {
570 resetTimeout();
571 pulseIcon(sc.icon);
572 return false;
573 }
574 });
575 }
576 }
577 }
578
579 private void pulseIcon(final ImageView icon) {
580 if (icon.getScaleX() != 1) return; // already running
581 icon.animate().cancel();
582 icon.animate().scaleX(ICON_PULSE_SCALE).scaleY(ICON_PULSE_SCALE)
583 .setInterpolator(mFastOutSlowInInterpolator)
584 .setListener(new AnimatorListenerAdapter() {
585 @Override
586 public void onAnimationEnd(Animator animation) {
587 icon.animate().scaleX(1).scaleY(1).setListener(null);
588 }
589 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800590 }
591
John Spurlock7f1df5e2014-05-31 19:11:40 -0400592 private static boolean isNotificationOrRing(int streamType) {
593 return streamType == AudioManager.STREAM_RING
594 || streamType == AudioManager.STREAM_NOTIFICATION;
595 }
596
John Spurlock86005342014-05-23 11:58:00 -0400597 public void setZenModePanelCallback(ZenModePanel.Callback callback) {
598 mZenPanelCallback = callback;
599 }
600
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800601 private void expand() {
John Spurlock86005342014-05-23 11:58:00 -0400602 if (LOGD) Log.d(mTag, "expand mZenPanel=" + mZenPanel);
603 if (mZenPanel == null) {
604 mZenPanel = (ZenModePanel) mZenPanelStub.inflate();
605 mZenPanel.init(mZenController);
606 mZenPanel.setCallback(new ZenModePanel.Callback() {
607 @Override
608 public void onMoreSettings() {
609 if (mZenPanelCallback != null) {
610 mZenPanelCallback.onMoreSettings();
611 }
612 }
613
614 @Override
615 public void onInteraction() {
John Spurlock3bd4fee2014-05-29 20:51:09 -0400616 resetTimeout();
John Spurlock86005342014-05-23 11:58:00 -0400617 if (mZenPanelCallback != null) {
618 mZenPanelCallback.onInteraction();
619 }
620 }
621 });
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800622 }
John Spurlock86005342014-05-23 11:58:00 -0400623 mZenPanel.setVisibility(View.VISIBLE);
624 mZenPanelDivider.setVisibility(View.VISIBLE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400625 mTimeoutDelay = TIMEOUT_DELAY_EXPANDED;
626 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800627 }
628
629 private void collapse() {
John Spurlock86005342014-05-23 11:58:00 -0400630 if (LOGD) Log.d(mTag, "collapse mZenPanel=" + mZenPanel);
631 if (mZenPanel != null) {
632 mZenPanel.setVisibility(View.GONE);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800633 }
John Spurlock86005342014-05-23 11:58:00 -0400634 mZenPanelDivider.setVisibility(View.GONE);
John Spurlock3bd4fee2014-05-29 20:51:09 -0400635 mTimeoutDelay = TIMEOUT_DELAY;
636 resetTimeout();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800637 }
638
Eric Laurentfde16d52012-12-03 14:42:39 -0800639 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400640 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800641 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400642 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800643 updateSlider(sc);
644 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 }
646
John Spurlock86005342014-05-23 11:58:00 -0400647 private void updateZenMode(boolean zen) {
648 if (mZenModeCapable) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400649 final boolean show = isNotificationOrRing(mActiveStreamType);
John Spurlock86005342014-05-23 11:58:00 -0400650 mExpandButton.setVisibility(show ? View.VISIBLE : View.GONE);
651 mExpandDivider.setVisibility(show ? View.VISIBLE : View.GONE);
652 mExpandButton.setImageResource(zen ? com.android.systemui.R.drawable.ic_vol_zen_on
653 : com.android.systemui.R.drawable.ic_vol_zen_off);
654 } else {
655 mExpandButton.setVisibility(View.GONE);
656 mExpandDivider.setVisibility(View.GONE);
657 }
658 }
659
660 public void postZenModeChanged(boolean zen) {
661 removeMessages(MSG_ZEN_MODE_CHANGED);
John Spurlock84da84c2014-05-31 22:21:52 -0400662 obtainMessage(MSG_ZEN_MODE_CHANGED, zen ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -0400663 }
664
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 public void postVolumeChanged(int streamType, int flags) {
666 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700667 synchronized (this) {
668 if (mStreamControls == null) {
669 createSliders();
670 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800671 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 removeMessages(MSG_FREE_RESOURCES);
673 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
674 }
675
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700676 public void postRemoteVolumeChanged(int streamType, int flags) {
677 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
678 synchronized (this) {
679 if (mStreamControls == null) {
680 createSliders();
681 }
682 }
683 removeMessages(MSG_FREE_RESOURCES);
684 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, streamType, flags).sendToTarget();
685 }
686
687 public void postRemoteSliderVisibility(boolean visible) {
688 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
689 AudioService.STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
690 }
691
692 /**
693 * Called by AudioService when it has received new remote playback information that
694 * would affect the VolumePanel display (mainly volumes). The difference with
695 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
696 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
697 * displayed.
698 * This special code path is due to the fact that remote volume updates arrive to AudioService
699 * asynchronously. So after AudioService has sent the volume update (which should be treated
700 * as a request to update the volume), the application will likely set a new volume. If the UI
701 * is still up, we need to refresh the display to show this new value.
702 */
703 public void postHasNewRemotePlaybackInfo() {
704 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
705 // don't create or prevent resources to be freed, if they disappear, this update came too
706 // late and shouldn't warrant the panel to be displayed longer
707 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
708 }
709
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400710 public void postMasterVolumeChanged(int flags) {
711 postVolumeChanged(STREAM_MASTER, flags);
712 }
713
Mike Lockwoodce952c82011-11-14 10:47:42 -0800714 public void postMuteChanged(int streamType, int flags) {
715 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700716 synchronized (this) {
717 if (mStreamControls == null) {
718 createSliders();
719 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800720 }
721 removeMessages(MSG_FREE_RESOURCES);
722 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
723 }
724
725 public void postMasterMuteChanged(int flags) {
726 postMuteChanged(STREAM_MASTER, flags);
727 }
728
Eric Laurentfde16d52012-12-03 14:42:39 -0800729 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700730 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800731 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700732 }
733
John Spurlock3346a802014-05-20 16:25:37 -0400734 public void postDismiss() {
John Spurlock86005342014-05-23 11:58:00 -0400735 forceTimeout();
736 }
737
738 public void postLayoutDirection(int layoutDirection) {
739 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400740 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400741 }
742
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800743 /**
744 * Override this if you have other work to do when the volume changes (for
745 * example, vibrating, playing a sound, etc.). Make sure to call through to
746 * the superclass implementation.
747 */
748 protected void onVolumeChanged(int streamType, int flags) {
749
John Spurlock86005342014-05-23 11:58:00 -0400750 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800751
752 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700753 synchronized (this) {
754 if (mActiveStreamType != streamType) {
755 reorderSliders(streamType);
756 }
757 onShowVolumeChanged(streamType, flags);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800758 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800759 }
760
Marco Nelissen69f593c2009-07-28 09:55:04 -0700761 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 removeMessages(MSG_PLAY_SOUND);
763 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
764 }
765
766 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
767 removeMessages(MSG_PLAY_SOUND);
768 removeMessages(MSG_VIBRATE);
769 onStopSounds();
770 }
771
772 removeMessages(MSG_FREE_RESOURCES);
773 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800774 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 }
776
Mike Lockwoodce952c82011-11-14 10:47:42 -0800777 protected void onMuteChanged(int streamType, int flags) {
778
John Spurlock86005342014-05-23 11:58:00 -0400779 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800780
781 StreamControl sc = mStreamControls.get(streamType);
782 if (sc != null) {
783 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
784 }
785
786 onVolumeChanged(streamType, flags);
787 }
788
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800789 protected void onShowVolumeChanged(int streamType, int flags) {
Eric Laurent8c787522012-05-14 14:09:43 -0700790 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800791
Marco Nelissen69f593c2009-07-28 09:55:04 -0700792 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800793
794 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400795 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800796 + ", flags: " + flags + "), index: " + index);
797 }
798
799 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800800
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400801 int max = getStreamMaxVolume(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802
803 switch (streamType) {
804
805 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800806// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700807 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
808 mContext, RingtoneManager.TYPE_RINGTONE);
809 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700810 mRingIsSilent = true;
811 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800812 break;
813 }
814
815 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800816 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800817 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
818 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
819 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
820 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800821 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800823 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800824 }
825 break;
826 }
827
828 case AudioManager.STREAM_VOICE_CALL: {
829 /*
830 * For in-call voice call volume, there is no inaudible volume.
831 * Rescale the UI control so the progress bar doesn't go all
832 * the way to zero and don't show the mute icon.
833 */
834 index++;
835 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800836 break;
837 }
838
839 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800840 break;
841 }
842
843 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700844 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
845 mContext, RingtoneManager.TYPE_NOTIFICATION);
846 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700847 mRingIsSilent = true;
848 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800849 break;
850 }
851
852 case AudioManager.STREAM_BLUETOOTH_SCO: {
853 /*
854 * For in-call voice call volume, there is no inaudible volume.
855 * Rescale the UI control so the progress bar doesn't go all
856 * the way to zero and don't show the mute icon.
857 */
858 index++;
859 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800860 break;
861 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700862
863 case AudioService.STREAM_REMOTE_MUSIC: {
John Spurlock86005342014-05-23 11:58:00 -0400864 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700865 break;
866 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800867 }
868
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800869 StreamControl sc = mStreamControls.get(streamType);
870 if (sc != null) {
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700871 if (sc.seekbarView.getMax() != max) {
872 sc.seekbarView.setMax(max);
873 }
Eric Laurent4bbcc652012-09-24 14:26:30 -0700874
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800875 sc.seekbarView.setProgress(index);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400876 updateSliderEnabled(sc, isMuted(streamType),
877 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800878 }
879
John Spurlock86005342014-05-23 11:58:00 -0400880 if (!isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700881 int stream = (streamType == AudioService.STREAM_REMOTE_MUSIC) ? -1 : streamType;
882 // when the stream is for remote playback, use -1 to reset the stream type evaluation
883 mAudioManager.forceVolumeControlStream(stream);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700884
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800885 // Showing dialog - use collapsed state
John Spurlock86005342014-05-23 11:58:00 -0400886 if (mZenModeCapable) {
Amith Yamasani42722bf2011-07-22 10:34:27 -0700887 collapse();
888 }
John Spurlock86005342014-05-23 11:58:00 -0400889 if (mDialog != null) {
890 mDialog.show();
891 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800892 }
893
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 // Do a little vibrate if applicable (only when going into vibrate mode)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700895 if ((streamType != AudioService.STREAM_REMOTE_MUSIC) &&
896 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -0400897 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -0700898 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800899 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
900 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800901 }
902
John Spurlock86005342014-05-23 11:58:00 -0400903 private boolean isShowing() {
904 return mDialog != null ? mDialog.isShowing() : mParent.isAttachedToWindow();
905 }
906
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800907 protected void onPlaySound(int streamType, int flags) {
908
909 if (hasMessages(MSG_STOP_SOUNDS)) {
910 removeMessages(MSG_STOP_SOUNDS);
911 // Force stop right now
912 onStopSounds();
913 }
914
915 synchronized (this) {
916 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -0800917 if (toneGen != null) {
918 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
919 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
920 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800921 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 }
923
924 protected void onStopSounds() {
925
926 synchronized (this) {
927 int numStreamTypes = AudioSystem.getNumStreamTypes();
928 for (int i = numStreamTypes - 1; i >= 0; i--) {
929 ToneGenerator toneGen = mToneGenerators[i];
930 if (toneGen != null) {
931 toneGen.stopTone();
932 }
933 }
934 }
935 }
936
937 protected void onVibrate() {
938
939 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -0700940 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 return;
942 }
943
John Spurlockf9e1a0b2014-03-19 22:09:06 -0400944 mVibrator.vibrate(VIBRATE_DURATION, AudioManager.STREAM_SYSTEM);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 }
946
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700947 protected void onRemoteVolumeChanged(int streamType, int flags) {
948 // streamType is the real stream type being affected, but for the UI sliders, we
949 // refer to AudioService.STREAM_REMOTE_MUSIC. We still play the beeps on the real
950 // stream type.
John Spurlock86005342014-05-23 11:58:00 -0400951 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(stream:"+streamType+", flags: " + flags + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700952
John Spurlock86005342014-05-23 11:58:00 -0400953 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700954 synchronized (this) {
955 if (mActiveStreamType != AudioService.STREAM_REMOTE_MUSIC) {
956 reorderSliders(AudioService.STREAM_REMOTE_MUSIC);
957 }
958 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, flags);
959 }
960 } else {
John Spurlock86005342014-05-23 11:58:00 -0400961 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700962 }
963
964 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
965 removeMessages(MSG_PLAY_SOUND);
966 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
967 }
968
969 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
970 removeMessages(MSG_PLAY_SOUND);
971 removeMessages(MSG_VIBRATE);
972 onStopSounds();
973 }
974
975 removeMessages(MSG_FREE_RESOURCES);
976 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700977 resetTimeout();
978 }
979
980 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -0400981 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
982 if (isShowing()
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700983 && (mActiveStreamType == AudioService.STREAM_REMOTE_MUSIC)
984 && (mStreamControls != null)) {
985 onShowVolumeChanged(AudioService.STREAM_REMOTE_MUSIC, 0);
986 }
987 }
988
989
990 /**
991 * Handler for MSG_SLIDER_VISIBILITY_CHANGED
992 * Hide or show a slider
993 * @param streamType can be a valid stream type value, or VolumePanel.STREAM_MASTER,
994 * or AudioService.STREAM_REMOTE_MUSIC
995 * @param visible
996 */
997 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -0400998 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700999 boolean isVisible = (visible == 1);
1000 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1001 StreamResources streamRes = STREAMS[i];
1002 if (streamRes.streamType == streamType) {
1003 streamRes.show = isVisible;
1004 if (!isVisible && (mActiveStreamType == streamType)) {
1005 mActiveStreamType = -1;
1006 }
1007 break;
1008 }
1009 }
1010 }
1011
Eric Laurentfde16d52012-12-03 14:42:39 -08001012 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock86005342014-05-23 11:58:00 -04001013 if ((flags & AudioManager.FLAG_SHOW_UI) != 0 || isShowing()) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001014 synchronized (sConfirmSafeVolumeLock) {
1015 if (sConfirmSafeVolumeDialog != null) {
1016 return;
1017 }
1018 sConfirmSafeVolumeDialog = new AlertDialog.Builder(mContext)
1019 .setMessage(com.android.internal.R.string.safe_media_volume_warning)
1020 .setPositiveButton(com.android.internal.R.string.yes,
1021 new DialogInterface.OnClickListener() {
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001022 @Override
Eric Laurentfde16d52012-12-03 14:42:39 -08001023 public void onClick(DialogInterface dialog, int which) {
John Spurlock3346a802014-05-20 16:25:37 -04001024 mAudioManager.disableSafeMediaVolume();
Eric Laurentfde16d52012-12-03 14:42:39 -08001025 }
1026 })
1027 .setNegativeButton(com.android.internal.R.string.no, null)
1028 .setIconAttribute(android.R.attr.alertDialogIcon)
1029 .create();
1030 final WarningDialogReceiver warning = new WarningDialogReceiver(mContext,
1031 sConfirmSafeVolumeDialog, this);
Eric Laurent0516a9e2012-09-19 11:53:03 -07001032
Eric Laurentfde16d52012-12-03 14:42:39 -08001033 sConfirmSafeVolumeDialog.setOnDismissListener(warning);
1034 sConfirmSafeVolumeDialog.getWindow().setType(
1035 WindowManager.LayoutParams.TYPE_KEYGUARD_DIALOG);
1036 sConfirmSafeVolumeDialog.show();
1037 }
1038 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001039 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001040 resetTimeout();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001041 }
1042
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001043 /**
1044 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1045 */
1046 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001047 if (streamType == STREAM_MASTER) {
1048 // For devices that use the master volume setting only but still want to
1049 // play a volume-changed tone, direct the master volume pseudostream to
1050 // the system stream's tone generator.
1051 if (mPlayMasterStreamTones) {
1052 streamType = AudioManager.STREAM_SYSTEM;
1053 } else {
1054 return null;
1055 }
1056 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 synchronized (this) {
1058 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001059 try {
1060 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1061 } catch (RuntimeException e) {
1062 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001063 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001064 + "RuntimeException: " + e);
1065 }
1066 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001067 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001068 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001069 }
1070 }
1071
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001072
1073 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001074 * Switch between icons because Bluetooth music is same as music volume, but with
1075 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001076 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001077 private void setMusicIcon(int resId, int resMuteId) {
1078 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1079 if (sc != null) {
1080 sc.iconRes = resId;
1081 sc.iconMuteRes = resMuteId;
1082 sc.icon.setImageResource(isMuted(sc.streamType) ? sc.iconMuteRes : sc.iconRes);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001083 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085
1086 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 synchronized (this) {
1088 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1089 if (mToneGenerators[i] != null) {
1090 mToneGenerators[i].release();
1091 }
1092 mToneGenerators[i] = null;
1093 }
1094 }
1095 }
1096
1097 @Override
1098 public void handleMessage(Message msg) {
1099 switch (msg.what) {
1100
1101 case MSG_VOLUME_CHANGED: {
1102 onVolumeChanged(msg.arg1, msg.arg2);
1103 break;
1104 }
1105
Mike Lockwoodce952c82011-11-14 10:47:42 -08001106 case MSG_MUTE_CHANGED: {
1107 onMuteChanged(msg.arg1, msg.arg2);
1108 break;
1109 }
1110
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001111 case MSG_FREE_RESOURCES: {
1112 onFreeResources();
1113 break;
1114 }
1115
1116 case MSG_STOP_SOUNDS: {
1117 onStopSounds();
1118 break;
1119 }
1120
1121 case MSG_PLAY_SOUND: {
1122 onPlaySound(msg.arg1, msg.arg2);
1123 break;
1124 }
1125
1126 case MSG_VIBRATE: {
1127 onVibrate();
1128 break;
1129 }
1130
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001131 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001132 if (isShowing()) {
1133 if (mDialog != null) {
1134 mDialog.dismiss();
John Spurlockf71205c2014-05-29 10:17:51 -04001135 mActiveStreamType = -1;
John Spurlock86005342014-05-23 11:58:00 -04001136 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001137 }
Eric Laurentfde16d52012-12-03 14:42:39 -08001138 synchronized (sConfirmSafeVolumeLock) {
1139 if (sConfirmSafeVolumeDialog != null) {
1140 sConfirmSafeVolumeDialog.dismiss();
1141 }
1142 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001143 break;
1144 }
1145 case MSG_RINGER_MODE_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001146 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001147 updateStates();
1148 }
1149 break;
1150 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001151
1152 case MSG_REMOTE_VOLUME_CHANGED: {
1153 onRemoteVolumeChanged(msg.arg1, msg.arg2);
1154 break;
1155 }
1156
1157 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1158 onRemoteVolumeUpdateIfShown();
1159 break;
1160
1161 case MSG_SLIDER_VISIBILITY_CHANGED:
1162 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1163 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001164
1165 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001166 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001167 break;
John Spurlock86005342014-05-23 11:58:00 -04001168
1169 case MSG_LAYOUT_DIRECTION:
1170 setLayoutDirection(msg.arg1);
1171 break;
1172
1173 case MSG_ZEN_MODE_CHANGED:
1174 updateZenMode(msg.arg1 != 0);
1175 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001176 }
1177 }
1178
John Spurlock86005342014-05-23 11:58:00 -04001179 public void resetTimeout() {
1180 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis());
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001181 removeMessages(MSG_TIMEOUT);
John Spurlock3bd4fee2014-05-29 20:51:09 -04001182 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001183 }
1184
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001185 private void forceTimeout() {
1186 removeMessages(MSG_TIMEOUT);
John Spurlock86005342014-05-23 11:58:00 -04001187 sendEmptyMessage(MSG_TIMEOUT);
1188 }
1189
1190 public ZenModeController getZenController() {
1191 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001192 }
1193
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001194 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1195 @Override
1196 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1197 final Object tag = seekBar.getTag();
1198 if (fromUser && tag instanceof StreamControl) {
1199 StreamControl sc = (StreamControl) tag;
1200 if (getStreamVolume(sc.streamType) != progress) {
1201 setStreamVolume(sc.streamType, progress, 0);
1202 }
1203 }
1204 resetTimeout();
1205 }
1206
1207 @Override
1208 public void onStartTrackingTouch(SeekBar seekBar) {
1209 }
1210
1211 @Override
1212 public void onStopTrackingTouch(SeekBar seekBar) {
1213 final Object tag = seekBar.getTag();
1214 if (tag instanceof StreamControl) {
1215 StreamControl sc = (StreamControl) tag;
1216 // Because remote volume updates are asynchronous, AudioService
1217 // might have received a new remote volume value since the
1218 // finger adjusted the slider. So when the progress of the
1219 // slider isn't being tracked anymore, adjust the slider to the
1220 // last "published" remote volume value, so the UI reflects the
1221 // actual volume.
1222 if (sc.streamType == AudioService.STREAM_REMOTE_MUSIC) {
1223 seekBar.setProgress(getStreamVolume(AudioService.STREAM_REMOTE_MUSIC));
1224 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001225 }
1226 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001227 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001228
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001229 private final View.OnClickListener mClickListener = new View.OnClickListener() {
1230 @Override
1231 public void onClick(View v) {
John Spurlock86005342014-05-23 11:58:00 -04001232 if (v == mExpandButton && mZenController != null) {
1233 final boolean newZen = !mZenController.isZen();
John Spurlock3bd4fee2014-05-29 20:51:09 -04001234 AsyncTask.execute(new Runnable() {
1235 @Override
1236 public void run() {
1237 mZenController.setZen(newZen);
1238 }
1239 });
John Spurlock86005342014-05-23 11:58:00 -04001240 if (newZen) {
1241 expand();
1242 } else {
1243 collapse();
1244 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001245 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001246 resetTimeout();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001247 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001248 };
John Spurlock86005342014-05-23 11:58:00 -04001249
1250 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
1251 public void onZenChanged(boolean zen) {
John Spurlock7f1df5e2014-05-31 19:11:40 -04001252 postZenModeChanged(zen);
John Spurlock86005342014-05-23 11:58:00 -04001253 }
1254 };
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001255}