blob: 367c31a10f8bd9fdba01953fff7f3afa9cce4826 [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;
John Spurlockb4782522014-08-22 14:54:46 -040022import android.content.ComponentName;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.content.Context;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080024import android.content.DialogInterface;
John Spurlock86005342014-05-23 11:58:00 -040025import android.content.DialogInterface.OnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080026import android.content.Intent;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080027import android.content.IntentFilter;
John Spurlockb4782522014-08-22 14:54:46 -040028import android.content.pm.PackageManager;
John Spurlockb4782522014-08-22 14:54:46 -040029import android.content.pm.ServiceInfo;
John Spurlock7e6809a2014-08-06 16:03:14 -040030import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080031import android.content.res.Resources;
John Spurlockad494bc2014-07-19 15:56:19 -040032import android.content.res.TypedArray;
John Spurlock2078caf2014-05-29 22:20:14 -040033import android.graphics.PixelFormat;
34import android.graphics.drawable.ColorDrawable;
John Spurlock7b414672014-07-18 13:02:39 -040035import android.media.AudioAttributes;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.media.AudioManager;
37import android.media.AudioService;
38import android.media.AudioSystem;
Marco Nelissen69f593c2009-07-28 09:55:04 -070039import android.media.RingtoneManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.media.ToneGenerator;
RoboErik19c95182014-06-23 15:38:48 -070041import android.media.VolumeProvider;
42import android.media.session.MediaController;
RoboErikd2b8c942014-08-19 11:23:40 -070043import android.media.session.MediaController.PlaybackInfo;
Marco Nelissen69f593c2009-07-28 09:55:04 -070044import android.net.Uri;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.os.Handler;
46import android.os.Message;
47import android.os.Vibrator;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.util.Log;
John Spurlockad494bc2014-07-19 15:56:19 -040049import android.util.SparseArray;
John Spurlock2d28d6e2014-08-01 13:10:14 -040050import android.view.KeyEvent;
John Spurlock3346a802014-05-20 16:25:37 -040051import android.view.LayoutInflater;
52import android.view.MotionEvent;
53import android.view.View;
John Spurlock7f1df5e2014-05-31 19:11:40 -040054import android.view.View.OnClickListener;
John Spurlock3346a802014-05-20 16:25:37 -040055import android.view.ViewGroup;
56import android.view.Window;
57import android.view.WindowManager;
Amith Yamasani284e6302011-09-16 18:24:47 -070058import android.view.WindowManager.LayoutParams;
Selim Cinek9f6ceb12014-08-29 16:28:39 +020059import android.view.accessibility.AccessibilityManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060import android.widget.ImageView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080061import android.widget.SeekBar;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080062import android.widget.SeekBar.OnSeekBarChangeListener;
John Spurlockb4782522014-08-22 14:54:46 -040063import android.widget.TextView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080064
John Spurlock86005342014-05-23 11:58:00 -040065import com.android.internal.R;
John Spurlock35134602014-07-24 18:10:48 -040066import com.android.systemui.statusbar.phone.SystemUIDialog;
John Spurlock86005342014-05-23 11:58:00 -040067import com.android.systemui.statusbar.policy.ZenModeController;
68
John Spurlockad494bc2014-07-19 15:56:19 -040069import java.io.FileDescriptor;
70import java.io.PrintWriter;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071
72/**
John Spurlock3346a802014-05-20 16:25:37 -040073 * Handles the user interface for the volume keys.
Dianne Hackborne8ecde12011-08-03 18:55:19 -070074 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 * @hide
76 */
John Spurlock3346a802014-05-20 16:25:37 -040077public class VolumePanel extends Handler {
John Spurlockae641c92014-06-30 18:11:40 -040078 private static final String TAG = "VolumePanel";
79 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080
John Spurlock3346a802014-05-20 16:25:37 -040081 private static final int PLAY_SOUND_DELAY = AudioService.PLAY_SOUND_DELAY;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082
83 /**
84 * The delay before vibrating. This small period exists so if the user is
85 * moving to silent mode, it will not emit a short vibrate (it normally
86 * would since vibrate is between normal mode and silent mode using hardware
87 * keys).
88 */
89 public static final int VIBRATE_DELAY = 300;
90
91 private static final int VIBRATE_DURATION = 300;
92 private static final int BEEP_DURATION = 150;
93 private static final int MAX_VOLUME = 100;
94 private static final int FREE_DELAY = 10000;
Amith Yamasani2bbdd772011-02-02 18:54:13 -080095 private static final int TIMEOUT_DELAY = 3000;
John Spurlock8845da72014-07-07 21:29:48 -040096 private static final int TIMEOUT_DELAY_SHORT = 1500;
John Spurlockea9938c2014-07-11 18:51:32 -040097 private static final int TIMEOUT_DELAY_COLLAPSED = 4500;
John Spurlock35134602014-07-24 18:10:48 -040098 private static final int TIMEOUT_DELAY_SAFETY_WARNING = 5000;
John Spurlock3bd4fee2014-05-29 20:51:09 -040099 private static final int TIMEOUT_DELAY_EXPANDED = 10000;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100
101 private static final int MSG_VOLUME_CHANGED = 0;
102 private static final int MSG_FREE_RESOURCES = 1;
103 private static final int MSG_PLAY_SOUND = 2;
104 private static final int MSG_STOP_SOUNDS = 3;
105 private static final int MSG_VIBRATE = 4;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800106 private static final int MSG_TIMEOUT = 5;
107 private static final int MSG_RINGER_MODE_CHANGED = 6;
Mike Lockwoodce952c82011-11-14 10:47:42 -0800108 private static final int MSG_MUTE_CHANGED = 7;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700109 private static final int MSG_REMOTE_VOLUME_CHANGED = 8;
110 private static final int MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN = 9;
111 private static final int MSG_SLIDER_VISIBILITY_CHANGED = 10;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700112 private static final int MSG_DISPLAY_SAFE_VOLUME_WARNING = 11;
John Spurlock86005342014-05-23 11:58:00 -0400113 private static final int MSG_LAYOUT_DIRECTION = 12;
John Spurlock45601d62014-08-07 17:40:50 -0400114 private static final int MSG_ZEN_MODE_AVAILABLE_CHANGED = 13;
John Spurlockae641c92014-06-30 18:11:40 -0400115 private static final int MSG_USER_ACTIVITY = 14;
John Spurlockb4782522014-08-22 14:54:46 -0400116 private static final int MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED = 15;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800117
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400118 // Pseudo stream type for master volume
Mike Lockwood47676902011-11-08 10:31:21 -0800119 private static final int STREAM_MASTER = -100;
RoboErik2811dd32014-08-12 09:48:13 -0700120 // Pseudo stream type for remote volume
121 private static final int STREAM_REMOTE_MUSIC = -200;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400122
John Spurlock7b414672014-07-18 13:02:39 -0400123 private static final AudioAttributes VIBRATION_ATTRIBUTES = new AudioAttributes.Builder()
124 .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
125 .setUsage(AudioAttributes.USAGE_ASSISTANCE_SONIFICATION)
126 .build();
127
John Spurlock86005342014-05-23 11:58:00 -0400128 private final String mTag;
John Spurlock3346a802014-05-20 16:25:37 -0400129 protected final Context mContext;
130 private final AudioManager mAudioManager;
John Spurlock86005342014-05-23 11:58:00 -0400131 private final ZenModeController mZenController;
Marco Nelissen69f593c2009-07-28 09:55:04 -0700132 private boolean mRingIsSilent;
Amith Yamasani71def772011-10-12 12:25:24 -0700133 private boolean mVoiceCapable;
John Spurlock45601d62014-08-07 17:40:50 -0400134 private boolean mZenModeAvailable;
John Spurlock8845da72014-07-07 21:29:48 -0400135 private boolean mZenPanelExpanded;
John Spurlock3bd4fee2014-05-29 20:51:09 -0400136 private int mTimeoutDelay = TIMEOUT_DELAY;
John Spurlockad494bc2014-07-19 15:56:19 -0400137 private float mDisabledAlpha;
138 private int mLastRingerMode = AudioManager.RINGER_MODE_NORMAL;
139 private int mLastRingerProgress = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800140
Christopher Tatec4b78d22012-05-22 13:57:58 -0700141 // True if we want to play tones on the system stream when the master stream is specified.
142 private final boolean mPlayMasterStreamTones;
143
John Spurlock86005342014-05-23 11:58:00 -0400144
145 /** Volume panel content view */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800146 private final View mView;
John Spurlockeb2727b2014-07-19 23:11:36 -0400147 /** Dialog hosting the panel */
John Spurlock86005342014-05-23 11:58:00 -0400148 private final Dialog mDialog;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800149
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -0700150 /** The visible portion of the volume overlay */
151 private final ViewGroup mPanel;
John Spurlock86005342014-05-23 11:58:00 -0400152 /** Contains the slider and its touchable icons */
153 private final ViewGroup mSliderPanel;
John Spurlockeb2727b2014-07-19 23:11:36 -0400154 /** The zen mode configuration panel view */
John Spurlock86005342014-05-23 11:58:00 -0400155 private ZenModePanel mZenPanel;
John Spurlock86005342014-05-23 11:58:00 -0400156
John Spurlockae641c92014-06-30 18:11:40 -0400157 private Callback mCallback;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800158
159 /** Currently active stream that shows up at the top of the list of sliders */
160 private int mActiveStreamType = -1;
161 /** All the slider controls mapped by stream type */
John Spurlockad494bc2014-07-19 15:56:19 -0400162 private SparseArray<StreamControl> mStreamControls;
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200163 private final AccessibilityManager mAccessibilityManager;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800164
Amith Yamasani71def772011-10-12 12:25:24 -0700165 private enum StreamResources {
166 BluetoothSCOStream(AudioManager.STREAM_BLUETOOTH_SCO,
167 R.string.volume_icon_description_bluetooth,
168 R.drawable.ic_audio_bt,
169 R.drawable.ic_audio_bt,
170 false),
171 RingerStream(AudioManager.STREAM_RING,
172 R.string.volume_icon_description_ringer,
John Spurlock86005342014-05-23 11:58:00 -0400173 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400174 com.android.systemui.R.drawable.ic_ringer_vibrate,
Amith Yamasani71def772011-10-12 12:25:24 -0700175 false),
176 VoiceStream(AudioManager.STREAM_VOICE_CALL,
177 R.string.volume_icon_description_incall,
178 R.drawable.ic_audio_phone,
179 R.drawable.ic_audio_phone,
180 false),
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700181 AlarmStream(AudioManager.STREAM_ALARM,
182 R.string.volume_alarm,
183 R.drawable.ic_audio_alarm,
184 R.drawable.ic_audio_alarm_mute,
185 false),
Amith Yamasani71def772011-10-12 12:25:24 -0700186 MediaStream(AudioManager.STREAM_MUSIC,
187 R.string.volume_icon_description_media,
188 R.drawable.ic_audio_vol,
189 R.drawable.ic_audio_vol_mute,
190 true),
191 NotificationStream(AudioManager.STREAM_NOTIFICATION,
192 R.string.volume_icon_description_notification,
John Spurlock86005342014-05-23 11:58:00 -0400193 com.android.systemui.R.drawable.ic_ringer_audible,
John Spurlockad494bc2014-07-19 15:56:19 -0400194 com.android.systemui.R.drawable.ic_ringer_vibrate,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400195 true),
196 // for now, use media resources for master volume
197 MasterStream(STREAM_MASTER,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700198 R.string.volume_icon_description_media, //FIXME should have its own description
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400199 R.drawable.ic_audio_vol,
200 R.drawable.ic_audio_vol_mute,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700201 false),
RoboErik2811dd32014-08-12 09:48:13 -0700202 RemoteStream(STREAM_REMOTE_MUSIC,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700203 R.string.volume_icon_description_media, //FIXME should have its own description
204 R.drawable.ic_media_route_on_holo_dark,
205 R.drawable.ic_media_route_disabled_holo_dark,
206 false);// will be dynamically updated
Amith Yamasani71def772011-10-12 12:25:24 -0700207
208 int streamType;
209 int descRes;
210 int iconRes;
211 int iconMuteRes;
212 // RING, VOICE_CALL & BLUETOOTH_SCO are hidden unless explicitly requested
213 boolean show;
214
215 StreamResources(int streamType, int descRes, int iconRes, int iconMuteRes, boolean show) {
216 this.streamType = streamType;
217 this.descRes = descRes;
218 this.iconRes = iconRes;
219 this.iconMuteRes = iconMuteRes;
220 this.show = show;
221 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700222 }
Amith Yamasani71def772011-10-12 12:25:24 -0700223
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800224 // List of stream types and their order
Amith Yamasani71def772011-10-12 12:25:24 -0700225 private static final StreamResources[] STREAMS = {
226 StreamResources.BluetoothSCOStream,
227 StreamResources.RingerStream,
228 StreamResources.VoiceStream,
229 StreamResources.MediaStream,
Amith Yamasani92e1b2d2011-10-14 17:24:47 -0700230 StreamResources.NotificationStream,
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400231 StreamResources.AlarmStream,
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700232 StreamResources.MasterStream,
233 StreamResources.RemoteStream
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800234 };
235
236 /** Object that contains data for each slider */
237 private class StreamControl {
238 int streamType;
RoboErik19c95182014-06-23 15:38:48 -0700239 MediaController controller;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800240 ViewGroup group;
241 ImageView icon;
242 SeekBar seekbarView;
John Spurlockb4782522014-08-22 14:54:46 -0400243 TextView suppressorView;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800244 int iconRes;
245 int iconMuteRes;
John Spurlockb4782522014-08-22 14:54:46 -0400246 int iconSuppressedRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800247 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800248
249 // Synchronize when accessing this
250 private ToneGenerator mToneGenerators[];
251 private Vibrator mVibrator;
252
John Spurlock35134602014-07-24 18:10:48 -0400253 private static AlertDialog sSafetyWarning;
254 private static Object sSafetyWarningLock = new Object();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700255
John Spurlock35134602014-07-24 18:10:48 -0400256 private static class SafetyWarning extends SystemUIDialog
257 implements DialogInterface.OnDismissListener, DialogInterface.OnClickListener {
Eric Laurentfde16d52012-12-03 14:42:39 -0800258 private final Context mContext;
Eric Laurentfde16d52012-12-03 14:42:39 -0800259 private final VolumePanel mVolumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400260 private final AudioManager mAudioManager;
Eric Laurentc34dcc12012-09-10 13:51:52 -0700261
John Spurlock2d28d6e2014-08-01 13:10:14 -0400262 private boolean mNewVolumeUp;
263
John Spurlock35134602014-07-24 18:10:48 -0400264 SafetyWarning(Context context, VolumePanel volumePanel, AudioManager audioManager) {
265 super(context);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700266 mContext = context;
Eric Laurentfde16d52012-12-03 14:42:39 -0800267 mVolumePanel = volumePanel;
John Spurlock35134602014-07-24 18:10:48 -0400268 mAudioManager = audioManager;
269
270 setMessage(mContext.getString(com.android.internal.R.string.safe_media_volume_warning));
271 setButton(DialogInterface.BUTTON_POSITIVE,
272 mContext.getString(com.android.internal.R.string.yes), this);
273 setButton(DialogInterface.BUTTON_NEGATIVE,
274 mContext.getString(com.android.internal.R.string.no), (OnClickListener) null);
275 setOnDismissListener(this);
276
Eric Laurentc34dcc12012-09-10 13:51:52 -0700277 IntentFilter filter = new IntentFilter(Intent.ACTION_CLOSE_SYSTEM_DIALOGS);
John Spurlock35134602014-07-24 18:10:48 -0400278 context.registerReceiver(mReceiver, filter);
Eric Laurentc34dcc12012-09-10 13:51:52 -0700279 }
280
281 @Override
John Spurlock2d28d6e2014-08-01 13:10:14 -0400282 public boolean onKeyDown(int keyCode, KeyEvent event) {
283 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && event.getRepeatCount() == 0) {
284 mNewVolumeUp = true;
285 }
286 return super.onKeyDown(keyCode, event);
287 }
288
289 @Override
290 public boolean onKeyUp(int keyCode, KeyEvent event) {
291 if (keyCode == KeyEvent.KEYCODE_VOLUME_UP && mNewVolumeUp) {
292 if (LOGD) Log.d(TAG, "Confirmed warning via VOLUME_UP");
293 mAudioManager.disableSafeMediaVolume();
294 dismiss();
295 }
296 return super.onKeyUp(keyCode, event);
297 }
298
299 @Override
John Spurlock35134602014-07-24 18:10:48 -0400300 public void onClick(DialogInterface dialog, int which) {
301 mAudioManager.disableSafeMediaVolume();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700302 }
303
Alan Viverette494fb7b2014-04-10 18:12:56 -0700304 @Override
Eric Laurentc34dcc12012-09-10 13:51:52 -0700305 public void onDismiss(DialogInterface unused) {
John Spurlock35134602014-07-24 18:10:48 -0400306 mContext.unregisterReceiver(mReceiver);
Eric Laurentfde16d52012-12-03 14:42:39 -0800307 cleanUp();
308 }
309
310 private void cleanUp() {
John Spurlock35134602014-07-24 18:10:48 -0400311 synchronized (sSafetyWarningLock) {
312 sSafetyWarning = null;
Eric Laurent0516a9e2012-09-19 11:53:03 -0700313 }
John Spurlock1dad2722014-07-11 11:07:53 -0400314 mVolumePanel.forceTimeout(0);
Eric Laurentfde16d52012-12-03 14:42:39 -0800315 mVolumePanel.updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700316 }
John Spurlock35134602014-07-24 18:10:48 -0400317
318 private final BroadcastReceiver mReceiver = new BroadcastReceiver() {
319 @Override
320 public void onReceive(Context context, Intent intent) {
321 if (Intent.ACTION_CLOSE_SYSTEM_DIALOGS.equals(intent.getAction())) {
322 if (LOGD) Log.d(TAG, "Received ACTION_CLOSE_SYSTEM_DIALOGS");
323 cancel();
324 cleanUp();
325 }
326 }
327 };
Eric Laurentc34dcc12012-09-10 13:51:52 -0700328 }
329
John Spurlockeb2727b2014-07-19 23:11:36 -0400330 public VolumePanel(Context context, ZenModeController zenController) {
331 mTag = String.format("%s.%08x", TAG, hashCode());
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 mContext = context;
John Spurlock86005342014-05-23 11:58:00 -0400333 mZenController = zenController;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800334 mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
Selim Cinek9f6ceb12014-08-29 16:28:39 +0200335 mAccessibilityManager = (AccessibilityManager) context.getSystemService(
336 Context.ACCESSIBILITY_SERVICE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400338 // For now, only show master volume if master volume is supported
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700339 final Resources res = context.getResources();
340 final boolean useMasterVolume = res.getBoolean(R.bool.config_useMasterVolume);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400341 if (useMasterVolume) {
342 for (int i = 0; i < STREAMS.length; i++) {
343 StreamResources streamRes = STREAMS[i];
344 streamRes.show = (streamRes.streamType == STREAM_MASTER);
345 }
346 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400347 if (LOGD) Log.d(mTag, "new VolumePanel");
348
John Spurlockad494bc2014-07-19 15:56:19 -0400349 mDisabledAlpha = 0.5f;
350 if (mContext.getTheme() != null) {
351 final TypedArray arr = mContext.getTheme().obtainStyledAttributes(
352 new int[] { android.R.attr.disabledAlpha });
353 mDisabledAlpha = arr.getFloat(0, mDisabledAlpha);
354 arr.recycle();
355 }
356
John Spurlockeb2727b2014-07-19 23:11:36 -0400357 mDialog = new Dialog(context) {
358 @Override
359 public boolean onTouchEvent(MotionEvent event) {
360 if (isShowing() && event.getAction() == MotionEvent.ACTION_OUTSIDE &&
John Spurlock35134602014-07-24 18:10:48 -0400361 sSafetyWarning == null) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400362 forceTimeout(0);
363 return true;
Amith Yamasani284e6302011-09-16 18:24:47 -0700364 }
John Spurlockeb2727b2014-07-19 23:11:36 -0400365 return false;
366 }
367 };
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700368
John Spurlockeb2727b2014-07-19 23:11:36 -0400369 final Window window = mDialog.getWindow();
John Spurlockeb2727b2014-07-19 23:11:36 -0400370 window.requestFeature(Window.FEATURE_NO_TITLE);
John Spurlockeb2727b2014-07-19 23:11:36 -0400371 mDialog.setCanceledOnTouchOutside(true);
372 mDialog.setContentView(com.android.systemui.R.layout.volume_dialog);
373 mDialog.setOnDismissListener(new OnDismissListener() {
374 @Override
375 public void onDismiss(DialogInterface dialog) {
376 mActiveStreamType = -1;
377 mAudioManager.forceVolumeControlStream(mActiveStreamType);
378 setZenPanelVisible(false);
379 }
380 });
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700381
John Spurlockeb2727b2014-07-19 23:11:36 -0400382 mDialog.create();
Alan Viverette494fb7b2014-04-10 18:12:56 -0700383
John Spurlock7e6809a2014-08-06 16:03:14 -0400384 final LayoutParams lp = window.getAttributes();
385 lp.token = null;
386 lp.y = res.getDimensionPixelOffset(com.android.systemui.R.dimen.volume_panel_top);
387 lp.type = LayoutParams.TYPE_STATUS_BAR_PANEL;
388 lp.format = PixelFormat.TRANSLUCENT;
389 lp.windowAnimations = com.android.systemui.R.style.VolumePanelAnimation;
390 lp.setTitle(TAG);
391 window.setAttributes(lp);
392
393 updateWidth();
394
395 window.setBackgroundDrawable(new ColorDrawable(0x00000000));
396 window.clearFlags(WindowManager.LayoutParams.FLAG_DIM_BEHIND);
397 window.addFlags(LayoutParams.FLAG_NOT_FOCUSABLE
398 | LayoutParams.FLAG_NOT_TOUCH_MODAL
399 | LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH
400 | LayoutParams.FLAG_HARDWARE_ACCELERATED);
John Spurlockeb2727b2014-07-19 23:11:36 -0400401 mView = window.findViewById(R.id.content);
402 mView.setOnTouchListener(new View.OnTouchListener() {
403 @Override
404 public boolean onTouch(View v, MotionEvent event) {
405 resetTimeout();
406 return false;
407 }
408 });
Alan Viverette494fb7b2014-04-10 18:12:56 -0700409
John Spurlock86005342014-05-23 11:58:00 -0400410 mPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.visible_panel);
411 mSliderPanel = (ViewGroup) mView.findViewById(com.android.systemui.R.id.slider_panel);
John Spurlockeb2727b2014-07-19 23:11:36 -0400412 mZenPanel = (ZenModePanel) mView.findViewById(com.android.systemui.R.id.zen_mode_panel);
413 initZenModePanel();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800414
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700416 mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
Amith Yamasani71def772011-10-12 12:25:24 -0700417 mVoiceCapable = context.getResources().getBoolean(R.bool.config_voice_capable);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700418
John Spurlock45601d62014-08-07 17:40:50 -0400419 if (mZenController != null && !useMasterVolume) {
420 mZenModeAvailable = mZenController.isZenAvailable();
421 mZenController.addCallback(mZenCallback);
422 }
Amith Yamasani42722bf2011-07-22 10:34:27 -0700423
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700424 final boolean masterVolumeOnly = res.getBoolean(R.bool.config_useMasterVolume);
425 final boolean masterVolumeKeySounds = res.getBoolean(R.bool.config_useVolumeKeySounds);
Christopher Tatec4b78d22012-05-22 13:57:58 -0700426 mPlayMasterStreamTones = masterVolumeOnly && masterVolumeKeySounds;
427
John Spurlock8c79d2e2014-07-24 15:15:25 -0400428 registerReceiver();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800429 }
430
John Spurlock7e6809a2014-08-06 16:03:14 -0400431 public void onConfigurationChanged(Configuration newConfig) {
432 updateWidth();
Jason Monke2f47712014-09-09 09:35:55 -0400433 if (mZenPanel != null) {
434 mZenPanel.updateLocale();
435 }
John Spurlock7e6809a2014-08-06 16:03:14 -0400436 }
437
438 private void updateWidth() {
439 final Resources res = mContext.getResources();
440 final LayoutParams lp = mDialog.getWindow().getAttributes();
441 lp.width = res.getDimensionPixelSize(com.android.systemui.R.dimen.notification_panel_width);
442 lp.gravity =
443 res.getInteger(com.android.systemui.R.integer.notification_panel_layout_gravity);
444 mDialog.getWindow().setAttributes(lp);
445 }
446
John Spurlockad494bc2014-07-19 15:56:19 -0400447 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
448 pw.println("VolumePanel state:");
449 pw.print(" mTag="); pw.println(mTag);
450 pw.print(" mRingIsSilent="); pw.println(mRingIsSilent);
451 pw.print(" mVoiceCapable="); pw.println(mVoiceCapable);
John Spurlock45601d62014-08-07 17:40:50 -0400452 pw.print(" mZenModeAvailable="); pw.println(mZenModeAvailable);
John Spurlockad494bc2014-07-19 15:56:19 -0400453 pw.print(" mZenPanelExpanded="); pw.println(mZenPanelExpanded);
454 pw.print(" mTimeoutDelay="); pw.println(mTimeoutDelay);
455 pw.print(" mDisabledAlpha="); pw.println(mDisabledAlpha);
456 pw.print(" mLastRingerMode="); pw.println(mLastRingerMode);
457 pw.print(" mLastRingerProgress="); pw.println(mLastRingerProgress);
458 pw.print(" mPlayMasterStreamTones="); pw.println(mPlayMasterStreamTones);
459 pw.print(" isShowing()="); pw.println(isShowing());
460 pw.print(" mCallback="); pw.println(mCallback);
461 pw.print(" sConfirmSafeVolumeDialog=");
John Spurlock35134602014-07-24 18:10:48 -0400462 pw.println(sSafetyWarning != null ? "<not null>" : null);
John Spurlockad494bc2014-07-19 15:56:19 -0400463 pw.print(" mActiveStreamType="); pw.println(mActiveStreamType);
464 pw.print(" mStreamControls=");
465 if (mStreamControls == null) {
466 pw.println("null");
467 } else {
468 final int N = mStreamControls.size();
469 pw.print("<size "); pw.print(N); pw.println('>');
470 for (int i = 0; i < N; i++) {
471 final StreamControl sc = mStreamControls.valueAt(i);
472 pw.print(" stream "); pw.print(sc.streamType); pw.print(":");
473 if (sc.seekbarView != null) {
474 pw.print(" progress="); pw.print(sc.seekbarView.getProgress());
475 pw.print(" of "); pw.print(sc.seekbarView.getMax());
476 if (!sc.seekbarView.isEnabled()) pw.print(" (disabled)");
477 }
478 if (sc.icon != null && sc.icon.isClickable()) pw.print(" (clickable)");
479 pw.println();
480 }
481 }
482 }
483
John Spurlockeb2727b2014-07-19 23:11:36 -0400484 private void initZenModePanel() {
485 mZenPanel.init(mZenController);
486 mZenPanel.setCallback(new ZenModePanel.Callback() {
487 @Override
488 public void onMoreSettings() {
489 if (mCallback != null) {
490 mCallback.onZenSettings();
491 }
492 }
493
494 @Override
495 public void onInteraction() {
496 resetTimeout();
497 }
498
499 @Override
500 public void onExpanded(boolean expanded) {
501 if (mZenPanelExpanded == expanded) return;
502 mZenPanelExpanded = expanded;
503 updateTimeoutDelay();
504 resetTimeout();
505 }
506 });
John Spurlock7f8f22a2014-07-02 18:54:17 -0400507 }
508
John Spurlock86005342014-05-23 11:58:00 -0400509 private void setLayoutDirection(int layoutDirection) {
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800510 mPanel.setLayoutDirection(layoutDirection);
511 updateStates();
512 }
513
John Spurlock8c79d2e2014-07-24 15:15:25 -0400514 private void registerReceiver() {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800515 final IntentFilter filter = new IntentFilter();
516 filter.addAction(AudioManager.RINGER_MODE_CHANGED_ACTION);
John Spurlock8c79d2e2014-07-24 15:15:25 -0400517 filter.addAction(Intent.ACTION_SCREEN_OFF);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800518 mContext.registerReceiver(new BroadcastReceiver() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700519 @Override
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800520 public void onReceive(Context context, Intent intent) {
521 final String action = intent.getAction();
522
523 if (AudioManager.RINGER_MODE_CHANGED_ACTION.equals(action)) {
524 removeMessages(MSG_RINGER_MODE_CHANGED);
525 sendMessage(obtainMessage(MSG_RINGER_MODE_CHANGED));
526 }
John Spurlock8c79d2e2014-07-24 15:15:25 -0400527
528 if (Intent.ACTION_SCREEN_OFF.equals(action)) {
529 postDismiss(0);
530 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800531 }
532 }, filter);
533 }
534
535 private boolean isMuted(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400536 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700537 return mAudioManager.isMasterMute();
RoboErik2811dd32014-08-12 09:48:13 -0700538 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700539 // TODO do we need to support a distinct mute property for remote?
540 return false;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400541 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700542 return mAudioManager.isStreamMute(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400543 }
544 }
545
546 private int getStreamMaxVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400547 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700548 return mAudioManager.getMasterMaxVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700549 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700550 if (mStreamControls != null) {
551 StreamControl sc = mStreamControls.get(streamType);
552 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700553 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700554 return ai.getMaxVolume();
RoboErik19c95182014-06-23 15:38:48 -0700555 }
556 }
557 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400558 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700559 return mAudioManager.getStreamMaxVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400560 }
561 }
562
563 private int getStreamVolume(int streamType) {
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400564 if (streamType == STREAM_MASTER) {
Eric Laurent8c787522012-05-14 14:09:43 -0700565 return mAudioManager.getMasterVolume();
RoboErik2811dd32014-08-12 09:48:13 -0700566 } else if (streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700567 if (mStreamControls != null) {
568 StreamControl sc = mStreamControls.get(streamType);
569 if (sc != null && sc.controller != null) {
RoboErikd2b8c942014-08-19 11:23:40 -0700570 PlaybackInfo ai = sc.controller.getPlaybackInfo();
RoboErika66c40b2014-08-15 15:21:41 -0700571 return ai.getCurrentVolume();
RoboErik19c95182014-06-23 15:38:48 -0700572 }
573 }
574 return -1;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400575 } else {
Eric Laurent8c787522012-05-14 14:09:43 -0700576 return mAudioManager.getStreamVolume(streamType);
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400577 }
578 }
579
RoboErik19c95182014-06-23 15:38:48 -0700580 private void setStreamVolume(StreamControl sc, int index, int flags) {
RoboErik2811dd32014-08-12 09:48:13 -0700581 if (sc.streamType == STREAM_REMOTE_MUSIC) {
RoboErik19c95182014-06-23 15:38:48 -0700582 if (sc.controller != null) {
583 sc.controller.setVolumeTo(index, flags);
584 } else {
Jean-Michel Trivi65820412014-06-30 12:10:44 -0700585 Log.w(mTag, "Adjusting remote volume without a controller!");
RoboErik19c95182014-06-23 15:38:48 -0700586 }
587 } else if (getStreamVolume(sc.streamType) != index) {
588 if (sc.streamType == STREAM_MASTER) {
589 mAudioManager.setMasterVolume(index, flags);
590 } else {
591 mAudioManager.setStreamVolume(sc.streamType, index, flags);
592 }
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400593 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800594 }
595
596 private void createSliders() {
Alan Viverette494fb7b2014-04-10 18:12:56 -0700597 final Resources res = mContext.getResources();
598 final LayoutInflater inflater = (LayoutInflater) mContext.getSystemService(
599 Context.LAYOUT_INFLATER_SERVICE);
600
John Spurlockad494bc2014-07-19 15:56:19 -0400601 mStreamControls = new SparseArray<StreamControl>(STREAMS.length);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700602
Amith Yamasani71def772011-10-12 12:25:24 -0700603 for (int i = 0; i < STREAMS.length; i++) {
604 StreamResources streamRes = STREAMS[i];
Alan Viverette494fb7b2014-04-10 18:12:56 -0700605
606 final int streamType = streamRes.streamType;
Alan Viverette494fb7b2014-04-10 18:12:56 -0700607
608 final StreamControl sc = new StreamControl();
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700609 sc.streamType = streamType;
John Spurlock86005342014-05-23 11:58:00 -0400610 sc.group = (ViewGroup) inflater.inflate(
611 com.android.systemui.R.layout.volume_panel_item, null);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800612 sc.group.setTag(sc);
John Spurlock86005342014-05-23 11:58:00 -0400613 sc.icon = (ImageView) sc.group.findViewById(com.android.systemui.R.id.stream_icon);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800614 sc.icon.setTag(sc);
Amith Yamasani71def772011-10-12 12:25:24 -0700615 sc.icon.setContentDescription(res.getString(streamRes.descRes));
616 sc.iconRes = streamRes.iconRes;
617 sc.iconMuteRes = streamRes.iconMuteRes;
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800618 sc.icon.setImageResource(sc.iconRes);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400619 sc.icon.setClickable(isNotificationOrRing(streamType));
620 if (sc.icon.isClickable()) {
John Spurlockab4ea762014-07-19 18:50:16 -0400621 sc.icon.setSoundEffectsEnabled(false);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400622 sc.icon.setOnClickListener(new OnClickListener() {
623 @Override
624 public void onClick(View v) {
625 resetTimeout();
626 toggle(sc);
627 }
628 });
John Spurlockb4782522014-08-22 14:54:46 -0400629 sc.iconSuppressedRes = com.android.systemui.R.drawable.ic_ringer_mute;
John Spurlock7f1df5e2014-05-31 19:11:40 -0400630 }
John Spurlock86005342014-05-23 11:58:00 -0400631 sc.seekbarView = (SeekBar) sc.group.findViewById(com.android.systemui.R.id.seekbar);
John Spurlockb4782522014-08-22 14:54:46 -0400632 sc.suppressorView =
633 (TextView) sc.group.findViewById(com.android.systemui.R.id.suppressor);
634 sc.suppressorView.setVisibility(View.GONE);
Alan Viverette494fb7b2014-04-10 18:12:56 -0700635 final int plusOne = (streamType == AudioSystem.STREAM_BLUETOOTH_SCO ||
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700636 streamType == AudioSystem.STREAM_VOICE_CALL) ? 1 : 0;
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400637 sc.seekbarView.setMax(getStreamMaxVolume(streamType) + plusOne);
Alan Viverettee8ebaf32014-04-11 15:44:15 -0700638 sc.seekbarView.setOnSeekBarChangeListener(mSeekListener);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800639 sc.seekbarView.setTag(sc);
Amith Yamasanid47a3aee2011-08-23 11:11:35 -0700640 mStreamControls.put(streamType, sc);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800641 }
642 }
643
John Spurlock7f1df5e2014-05-31 19:11:40 -0400644 private void toggle(StreamControl sc) {
645 if (mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_NORMAL) {
646 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_VIBRATE);
647 postVolumeChanged(sc.streamType, AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
648 } else {
649 mAudioManager.setRingerMode(AudioManager.RINGER_MODE_NORMAL);
650 postVolumeChanged(sc.streamType, AudioManager.FLAG_PLAY_SOUND);
651 }
652 }
653
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800654 private void reorderSliders(int activeStreamType) {
John Spurlock86005342014-05-23 11:58:00 -0400655 mSliderPanel.removeAllViews();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800656
Alan Viverette494fb7b2014-04-10 18:12:56 -0700657 final StreamControl active = mStreamControls.get(activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800658 if (active == null) {
John Spurlockae641c92014-06-30 18:11:40 -0400659 Log.e(TAG, "Missing stream type! - " + activeStreamType);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800660 mActiveStreamType = -1;
661 } else {
John Spurlock86005342014-05-23 11:58:00 -0400662 mSliderPanel.addView(active.group);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800663 mActiveStreamType = activeStreamType;
664 active.group.setVisibility(View.VISIBLE);
665 updateSlider(active);
John Spurlock8845da72014-07-07 21:29:48 -0400666 updateTimeoutDelay();
John Spurlock45601d62014-08-07 17:40:50 -0400667 updateZenPanelVisible();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800668 }
669 }
670
John Spurlockad494bc2014-07-19 15:56:19 -0400671 private void updateSliderProgress(StreamControl sc, int progress) {
672 final boolean isRinger = isNotificationOrRing(sc.streamType);
673 if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
674 progress = mLastRingerProgress;
675 }
676 if (progress < 0) {
677 progress = getStreamVolume(sc.streamType);
678 }
679 sc.seekbarView.setProgress(progress);
680 if (isRinger) {
681 mLastRingerProgress = progress;
682 }
683 }
684
685 private void updateSliderIcon(StreamControl sc, boolean muted) {
686 if (isNotificationOrRing(sc.streamType)) {
687 int ringerMode = mAudioManager.getRingerMode();
688 if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
689 ringerMode = mLastRingerMode;
690 } else {
691 mLastRingerMode = ringerMode;
692 }
693 muted = ringerMode == AudioManager.RINGER_MODE_VIBRATE;
694 }
695 sc.icon.setImageResource(muted ? sc.iconMuteRes : sc.iconRes);
696 }
697
John Spurlockb4782522014-08-22 14:54:46 -0400698 private void updateSliderSupressor(StreamControl sc) {
699 final ComponentName suppressor = isNotificationOrRing(sc.streamType)
700 ? mZenController.getEffectsSuppressor() : null;
701 if (suppressor == null) {
702 sc.seekbarView.setVisibility(View.VISIBLE);
703 sc.suppressorView.setVisibility(View.GONE);
704 } else {
705 sc.seekbarView.setVisibility(View.GONE);
706 sc.suppressorView.setVisibility(View.VISIBLE);
707 sc.suppressorView.setText(mContext.getString(com.android.systemui.R.string.muted_by,
708 getSuppressorCaption(suppressor)));
709 sc.icon.setImageResource(sc.iconSuppressedRes);
710 }
711 }
712
713 private String getSuppressorCaption(ComponentName suppressor) {
714 final PackageManager pm = mContext.getPackageManager();
715 try {
716 final ServiceInfo info = pm.getServiceInfo(suppressor, 0);
717 if (info != null) {
718 final CharSequence seq = info.loadLabel(pm);
719 if (seq != null) {
720 final String str = seq.toString().trim();
721 if (str.length() > 0) {
722 return str;
723 }
724 }
725 }
726 } catch (Throwable e) {
727 Log.w(TAG, "Error loading suppressor caption", e);
728 }
729 return suppressor.getPackageName();
730 }
731
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800732 /** Update the mute and progress state of a slider */
733 private void updateSlider(StreamControl sc) {
John Spurlockad494bc2014-07-19 15:56:19 -0400734 updateSliderProgress(sc, -1);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800735 final boolean muted = isMuted(sc.streamType);
Fabrice Di Meglio8c028842013-01-09 18:20:38 -0800736 // Force reloading the image resource
737 sc.icon.setImageDrawable(null);
John Spurlockad494bc2014-07-19 15:56:19 -0400738 updateSliderIcon(sc, muted);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400739 updateSliderEnabled(sc, muted, false);
John Spurlockb4782522014-08-22 14:54:46 -0400740 updateSliderSupressor(sc);
John Spurlock7f1df5e2014-05-31 19:11:40 -0400741 }
742
John Spurlock5f640e42014-05-31 20:15:59 -0400743 private void updateSliderEnabled(final StreamControl sc, boolean muted, boolean fixedVolume) {
744 final boolean wasEnabled = sc.seekbarView.isEnabled();
John Spurlockae641c92014-06-30 18:11:40 -0400745 final boolean isRinger = isNotificationOrRing(sc.streamType);
RoboErik2811dd32014-08-12 09:48:13 -0700746 if (sc.streamType == STREAM_REMOTE_MUSIC) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700747 // never disable touch interactions for remote playback, the muting is not tied to
748 // the state of the phone.
RoboErik19c95182014-06-23 15:38:48 -0700749 sc.seekbarView.setEnabled(!fixedVolume);
John Spurlockae641c92014-06-30 18:11:40 -0400750 } else if (isRinger && mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_SILENT) {
John Spurlock7f1df5e2014-05-31 19:11:40 -0400751 sc.seekbarView.setEnabled(false);
John Spurlockae641c92014-06-30 18:11:40 -0400752 sc.icon.setEnabled(false);
John Spurlockad494bc2014-07-19 15:56:19 -0400753 sc.icon.setAlpha(mDisabledAlpha);
John Spurlockae641c92014-06-30 18:11:40 -0400754 sc.icon.setClickable(false);
John Spurlock4f0f1202014-08-05 13:28:33 -0400755 } else if (fixedVolume ||
756 (sc.streamType != mAudioManager.getMasterStreamType() && muted) ||
757 (sSafetyWarning != null)) {
758 sc.seekbarView.setEnabled(false);
Eric Laurent8c787522012-05-14 14:09:43 -0700759 } else {
760 sc.seekbarView.setEnabled(true);
John Spurlockae641c92014-06-30 18:11:40 -0400761 sc.icon.setEnabled(true);
John Spurlockad494bc2014-07-19 15:56:19 -0400762 sc.icon.setAlpha(1f);
Eric Laurent8c787522012-05-14 14:09:43 -0700763 }
John Spurlockae641c92014-06-30 18:11:40 -0400764 // show the silent hint when the disabled slider is touched in silent mode
765 if (isRinger && wasEnabled != sc.seekbarView.isEnabled()) {
John Spurlock5f640e42014-05-31 20:15:59 -0400766 if (sc.seekbarView.isEnabled()) {
John Spurlockae641c92014-06-30 18:11:40 -0400767 sc.group.setOnTouchListener(null);
768 sc.icon.setClickable(true);
John Spurlock5f640e42014-05-31 20:15:59 -0400769 } else {
John Spurlockae641c92014-06-30 18:11:40 -0400770 final View.OnTouchListener showHintOnTouch = new View.OnTouchListener() {
John Spurlock5f640e42014-05-31 20:15:59 -0400771 @Override
772 public boolean onTouch(View v, MotionEvent event) {
773 resetTimeout();
John Spurlockae641c92014-06-30 18:11:40 -0400774 showSilentHint();
John Spurlock5f640e42014-05-31 20:15:59 -0400775 return false;
776 }
John Spurlockae641c92014-06-30 18:11:40 -0400777 };
778 sc.group.setOnTouchListener(showHintOnTouch);
John Spurlock5f640e42014-05-31 20:15:59 -0400779 }
780 }
781 }
782
John Spurlockae641c92014-06-30 18:11:40 -0400783 private void showSilentHint() {
784 if (mZenPanel != null) {
785 mZenPanel.showSilentHint();
786 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800787 }
788
John Spurlock7f1df5e2014-05-31 19:11:40 -0400789 private static boolean isNotificationOrRing(int streamType) {
790 return streamType == AudioManager.STREAM_RING
791 || streamType == AudioManager.STREAM_NOTIFICATION;
792 }
793
John Spurlockae641c92014-06-30 18:11:40 -0400794 public void setCallback(Callback callback) {
795 mCallback = callback;
John Spurlock86005342014-05-23 11:58:00 -0400796 }
797
John Spurlock8845da72014-07-07 21:29:48 -0400798 private void updateTimeoutDelay() {
Selim Cinek1cf89062014-08-29 19:21:44 +0200799 mTimeoutDelay = sSafetyWarning != null ? TIMEOUT_DELAY_SAFETY_WARNING
John Spurlock35134602014-07-24 18:10:48 -0400800 : mActiveStreamType == AudioManager.STREAM_MUSIC ? TIMEOUT_DELAY_SHORT
John Spurlockea9938c2014-07-11 18:51:32 -0400801 : mZenPanelExpanded ? TIMEOUT_DELAY_EXPANDED
802 : isZenPanelVisible() ? TIMEOUT_DELAY_COLLAPSED
803 : TIMEOUT_DELAY;
804 }
805
806 private boolean isZenPanelVisible() {
807 return mZenPanel != null && mZenPanel.getVisibility() == View.VISIBLE;
John Spurlock8845da72014-07-07 21:29:48 -0400808 }
809
John Spurlockae641c92014-06-30 18:11:40 -0400810 private void setZenPanelVisible(boolean visible) {
811 if (LOGD) Log.d(mTag, "setZenPanelVisible " + visible + " mZenPanel=" + mZenPanel);
John Spurlockea9938c2014-07-11 18:51:32 -0400812 final boolean changing = visible != isZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -0400813 if (visible) {
John Spurlockeb2727b2014-07-19 23:11:36 -0400814 mZenPanel.setHidden(false);
John Spurlockae641c92014-06-30 18:11:40 -0400815 resetTimeout();
816 } else {
John Spurlockeb2727b2014-07-19 23:11:36 -0400817 mZenPanel.setHidden(true);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800818 }
John Spurlockea9938c2014-07-11 18:51:32 -0400819 if (changing) {
820 updateTimeoutDelay();
821 resetTimeout();
822 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800823 }
824
Eric Laurentfde16d52012-12-03 14:42:39 -0800825 public void updateStates() {
John Spurlock86005342014-05-23 11:58:00 -0400826 final int count = mSliderPanel.getChildCount();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800827 for (int i = 0; i < count; i++) {
John Spurlock86005342014-05-23 11:58:00 -0400828 StreamControl sc = (StreamControl) mSliderPanel.getChildAt(i).getTag();
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800829 updateSlider(sc);
830 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 }
832
John Spurlock45601d62014-08-07 17:40:50 -0400833 private void updateZenPanelVisible() {
834 setZenPanelVisible(mZenModeAvailable && isNotificationOrRing(mActiveStreamType));
John Spurlock86005342014-05-23 11:58:00 -0400835 }
836
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800837 public void postVolumeChanged(int streamType, int flags) {
838 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700839 synchronized (this) {
840 if (mStreamControls == null) {
841 createSliders();
842 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800843 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800844 removeMessages(MSG_FREE_RESOURCES);
845 obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
846 }
847
RoboErik19c95182014-06-23 15:38:48 -0700848 public void postRemoteVolumeChanged(MediaController controller, int flags) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700849 if (hasMessages(MSG_REMOTE_VOLUME_CHANGED)) return;
850 synchronized (this) {
851 if (mStreamControls == null) {
852 createSliders();
853 }
854 }
855 removeMessages(MSG_FREE_RESOURCES);
RoboErik19c95182014-06-23 15:38:48 -0700856 obtainMessage(MSG_REMOTE_VOLUME_CHANGED, flags, 0, controller).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700857 }
858
859 public void postRemoteSliderVisibility(boolean visible) {
860 obtainMessage(MSG_SLIDER_VISIBILITY_CHANGED,
RoboErik2811dd32014-08-12 09:48:13 -0700861 STREAM_REMOTE_MUSIC, visible ? 1 : 0).sendToTarget();
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -0700862 }
863
864 /**
865 * Called by AudioService when it has received new remote playback information that
866 * would affect the VolumePanel display (mainly volumes). The difference with
867 * {@link #postRemoteVolumeChanged(int, int)} is that the handling of the posted message
868 * (MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN) will only update the volume slider if it is being
869 * displayed.
870 * This special code path is due to the fact that remote volume updates arrive to AudioService
871 * asynchronously. So after AudioService has sent the volume update (which should be treated
872 * as a request to update the volume), the application will likely set a new volume. If the UI
873 * is still up, we need to refresh the display to show this new value.
874 */
875 public void postHasNewRemotePlaybackInfo() {
876 if (hasMessages(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN)) return;
877 // don't create or prevent resources to be freed, if they disappear, this update came too
878 // late and shouldn't warrant the panel to be displayed longer
879 obtainMessage(MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN).sendToTarget();
880 }
881
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400882 public void postMasterVolumeChanged(int flags) {
883 postVolumeChanged(STREAM_MASTER, flags);
884 }
885
Mike Lockwoodce952c82011-11-14 10:47:42 -0800886 public void postMuteChanged(int streamType, int flags) {
887 if (hasMessages(MSG_VOLUME_CHANGED)) return;
Amith Yamasania6549862012-05-30 17:29:28 -0700888 synchronized (this) {
889 if (mStreamControls == null) {
890 createSliders();
891 }
Mike Lockwoodce952c82011-11-14 10:47:42 -0800892 }
893 removeMessages(MSG_FREE_RESOURCES);
894 obtainMessage(MSG_MUTE_CHANGED, streamType, flags).sendToTarget();
895 }
896
897 public void postMasterMuteChanged(int flags) {
898 postMuteChanged(STREAM_MASTER, flags);
899 }
900
Eric Laurentfde16d52012-12-03 14:42:39 -0800901 public void postDisplaySafeVolumeWarning(int flags) {
Eric Laurent0516a9e2012-09-19 11:53:03 -0700902 if (hasMessages(MSG_DISPLAY_SAFE_VOLUME_WARNING)) return;
Eric Laurentfde16d52012-12-03 14:42:39 -0800903 obtainMessage(MSG_DISPLAY_SAFE_VOLUME_WARNING, flags, 0).sendToTarget();
Eric Laurentc34dcc12012-09-10 13:51:52 -0700904 }
905
John Spurlock1dad2722014-07-11 11:07:53 -0400906 public void postDismiss(long delay) {
907 forceTimeout(delay);
John Spurlock86005342014-05-23 11:58:00 -0400908 }
909
910 public void postLayoutDirection(int layoutDirection) {
911 removeMessages(MSG_LAYOUT_DIRECTION);
John Spurlock84da84c2014-05-31 22:21:52 -0400912 obtainMessage(MSG_LAYOUT_DIRECTION, layoutDirection, 0).sendToTarget();
John Spurlock3346a802014-05-20 16:25:37 -0400913 }
914
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800915 /**
916 * Override this if you have other work to do when the volume changes (for
917 * example, vibrating, playing a sound, etc.). Make sure to call through to
918 * the superclass implementation.
919 */
920 protected void onVolumeChanged(int streamType, int flags) {
921
John Spurlock86005342014-05-23 11:58:00 -0400922 if (LOGD) Log.d(mTag, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800923
924 if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
Amith Yamasania6549862012-05-30 17:29:28 -0700925 synchronized (this) {
926 if (mActiveStreamType != streamType) {
927 reorderSliders(streamType);
928 }
RoboErik19c95182014-06-23 15:38:48 -0700929 onShowVolumeChanged(streamType, flags, null);
Amith Yamasanie3361b82011-02-10 18:20:50 -0800930 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800931 }
932
Marco Nelissen69f593c2009-07-28 09:55:04 -0700933 if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0 && ! mRingIsSilent) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800934 removeMessages(MSG_PLAY_SOUND);
935 sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
936 }
937
938 if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
939 removeMessages(MSG_PLAY_SOUND);
940 removeMessages(MSG_VIBRATE);
941 onStopSounds();
942 }
943
944 removeMessages(MSG_FREE_RESOURCES);
945 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800946 resetTimeout();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800947 }
948
Mike Lockwoodce952c82011-11-14 10:47:42 -0800949 protected void onMuteChanged(int streamType, int flags) {
950
John Spurlock86005342014-05-23 11:58:00 -0400951 if (LOGD) Log.d(mTag, "onMuteChanged(streamType: " + streamType + ", flags: " + flags + ")");
Mike Lockwoodce952c82011-11-14 10:47:42 -0800952
953 StreamControl sc = mStreamControls.get(streamType);
954 if (sc != null) {
John Spurlockad494bc2014-07-19 15:56:19 -0400955 updateSliderIcon(sc, isMuted(sc.streamType));
Mike Lockwoodce952c82011-11-14 10:47:42 -0800956 }
957
958 onVolumeChanged(streamType, flags);
959 }
960
RoboErik19c95182014-06-23 15:38:48 -0700961 protected void onShowVolumeChanged(int streamType, int flags, MediaController controller) {
Eric Laurent8c787522012-05-14 14:09:43 -0700962 int index = getStreamVolume(streamType);
Eric Laurentd72d51c2011-02-03 18:47:47 -0800963
Marco Nelissen69f593c2009-07-28 09:55:04 -0700964 mRingIsSilent = false;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800965
966 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -0400967 Log.d(mTag, "onShowVolumeChanged(streamType: " + streamType
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800968 + ", flags: " + flags + "), index: " + index);
969 }
970
971 // get max volume for progress bar
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800972
Mike Lockwood8dc1dab2011-10-27 09:52:41 -0400973 int max = getStreamMaxVolume(streamType);
RoboErik19c95182014-06-23 15:38:48 -0700974 StreamControl sc = mStreamControls.get(streamType);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800975
976 switch (streamType) {
977
978 case AudioManager.STREAM_RING: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800979// setRingerIcon();
Marco Nelissen69f593c2009-07-28 09:55:04 -0700980 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
981 mContext, RingtoneManager.TYPE_RINGTONE);
982 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -0700983 mRingIsSilent = true;
984 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800985 break;
986 }
987
988 case AudioManager.STREAM_MUSIC: {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800989 // Special case for when Bluetooth is active for music
Glenn Kasten8b4b97a2011-02-04 13:54:26 -0800990 if ((mAudioManager.getDevicesForStream(AudioManager.STREAM_MUSIC) &
991 (AudioManager.DEVICE_OUT_BLUETOOTH_A2DP |
992 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_HEADPHONES |
993 AudioManager.DEVICE_OUT_BLUETOOTH_A2DP_SPEAKER)) != 0) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800994 setMusicIcon(R.drawable.ic_audio_bt, R.drawable.ic_audio_bt_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 } else {
Amith Yamasani2bbdd772011-02-02 18:54:13 -0800996 setMusicIcon(R.drawable.ic_audio_vol, R.drawable.ic_audio_vol_mute);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800997 }
998 break;
999 }
1000
1001 case AudioManager.STREAM_VOICE_CALL: {
1002 /*
1003 * For in-call voice call volume, there is no inaudible volume.
1004 * Rescale the UI control so the progress bar doesn't go all
1005 * the way to zero and don't show the mute icon.
1006 */
1007 index++;
1008 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001009 break;
1010 }
1011
1012 case AudioManager.STREAM_ALARM: {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001013 break;
1014 }
1015
1016 case AudioManager.STREAM_NOTIFICATION: {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001017 Uri ringuri = RingtoneManager.getActualDefaultRingtoneUri(
1018 mContext, RingtoneManager.TYPE_NOTIFICATION);
1019 if (ringuri == null) {
Marco Nelissen69f593c2009-07-28 09:55:04 -07001020 mRingIsSilent = true;
1021 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001022 break;
1023 }
1024
1025 case AudioManager.STREAM_BLUETOOTH_SCO: {
1026 /*
1027 * For in-call voice call volume, there is no inaudible volume.
1028 * Rescale the UI control so the progress bar doesn't go all
1029 * the way to zero and don't show the mute icon.
1030 */
1031 index++;
1032 max++;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001033 break;
1034 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001035
RoboErik2811dd32014-08-12 09:48:13 -07001036 case STREAM_REMOTE_MUSIC: {
RoboErik19c95182014-06-23 15:38:48 -07001037 if (controller == null && sc != null) {
1038 // If we weren't passed one try using the last one set.
1039 controller = sc.controller;
1040 }
1041 if (controller == null) {
1042 // We still don't have one, ignore the command.
1043 Log.w(mTag, "sent remote volume change without a controller!");
1044 } else {
RoboErikd2b8c942014-08-19 11:23:40 -07001045 PlaybackInfo vi = controller.getPlaybackInfo();
RoboErik19c95182014-06-23 15:38:48 -07001046 index = vi.getCurrentVolume();
1047 max = vi.getMaxVolume();
1048 if ((vi.getVolumeControl() & VolumeProvider.VOLUME_CONTROL_FIXED) != 0) {
1049 // if the remote volume is fixed add the flag for the UI
1050 flags |= AudioManager.FLAG_FIXED_VOLUME;
1051 }
1052 }
John Spurlock86005342014-05-23 11:58:00 -04001053 if (LOGD) { Log.d(mTag, "showing remote volume "+index+" over "+ max); }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001054 break;
1055 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001056 }
1057
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001058 if (sc != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001059 if (streamType == STREAM_REMOTE_MUSIC && controller != sc.controller) {
RoboErik19c95182014-06-23 15:38:48 -07001060 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001061 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001062 }
1063 sc.controller = controller;
1064 if (controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001065 sc.controller.registerCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001066 }
1067 }
Amith Yamasanid47a3aee2011-08-23 11:11:35 -07001068 if (sc.seekbarView.getMax() != max) {
1069 sc.seekbarView.setMax(max);
1070 }
John Spurlockad494bc2014-07-19 15:56:19 -04001071 updateSliderProgress(sc, index);
John Spurlock7f1df5e2014-05-31 19:11:40 -04001072 updateSliderEnabled(sc, isMuted(streamType),
1073 (flags & AudioManager.FLAG_FIXED_VOLUME) != 0);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
1075
John Spurlock86005342014-05-23 11:58:00 -04001076 if (!isShowing()) {
RoboErik2811dd32014-08-12 09:48:13 -07001077 int stream = (streamType == STREAM_REMOTE_MUSIC) ? -1 : streamType;
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001078 // when the stream is for remote playback, use -1 to reset the stream type evaluation
1079 mAudioManager.forceVolumeControlStream(stream);
John Spurlockeb2727b2014-07-19 23:11:36 -04001080 mDialog.show();
1081 if (mCallback != null) {
1082 mCallback.onVisible(true);
John Spurlock86005342014-05-23 11:58:00 -04001083 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001084 }
1085
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001086 // Do a little vibrate if applicable (only when going into vibrate mode)
RoboErik2811dd32014-08-12 09:48:13 -07001087 if ((streamType != STREAM_REMOTE_MUSIC) &&
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001088 ((flags & AudioManager.FLAG_VIBRATE) != 0) &&
John Spurlock3346a802014-05-20 16:25:37 -04001089 mAudioManager.isStreamAffectedByRingerMode(streamType) &&
Eric Laurent8c787522012-05-14 14:09:43 -07001090 mAudioManager.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
1092 }
John Spurlocka11b4af2014-06-01 11:52:23 -04001093
1094 // Pulse the slider icon if an adjustment was suppressed due to silent mode.
John Spurlockae641c92014-06-30 18:11:40 -04001095 if ((flags & AudioManager.FLAG_SHOW_SILENT_HINT) != 0) {
1096 showSilentHint();
John Spurlocka11b4af2014-06-01 11:52:23 -04001097 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001098 }
1099
John Spurlock86005342014-05-23 11:58:00 -04001100 private boolean isShowing() {
John Spurlockeb2727b2014-07-19 23:11:36 -04001101 return mDialog.isShowing();
John Spurlock86005342014-05-23 11:58:00 -04001102 }
1103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001104 protected void onPlaySound(int streamType, int flags) {
1105
1106 if (hasMessages(MSG_STOP_SOUNDS)) {
1107 removeMessages(MSG_STOP_SOUNDS);
1108 // Force stop right now
1109 onStopSounds();
1110 }
1111
1112 synchronized (this) {
1113 ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
Eric Laurent733a42b2011-01-19 10:41:57 -08001114 if (toneGen != null) {
1115 toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
1116 sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
1117 }
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 onStopSounds() {
1122
1123 synchronized (this) {
1124 int numStreamTypes = AudioSystem.getNumStreamTypes();
1125 for (int i = numStreamTypes - 1; i >= 0; i--) {
1126 ToneGenerator toneGen = mToneGenerators[i];
1127 if (toneGen != null) {
1128 toneGen.stopTone();
1129 }
1130 }
1131 }
1132 }
1133
1134 protected void onVibrate() {
1135
1136 // Make sure we ended up in vibrate ringer mode
Eric Laurent8c787522012-05-14 14:09:43 -07001137 if (mAudioManager.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 return;
1139 }
1140
John Spurlock7b414672014-07-18 13:02:39 -04001141 mVibrator.vibrate(VIBRATE_DURATION, VIBRATION_ATTRIBUTES);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 }
1143
RoboErik19c95182014-06-23 15:38:48 -07001144 protected void onRemoteVolumeChanged(MediaController controller, int flags) {
1145 if (LOGD) Log.d(mTag, "onRemoteVolumeChanged(controller:" + controller + ", flags: " + flags
1146 + ")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001147
John Spurlock86005342014-05-23 11:58:00 -04001148 if (((flags & AudioManager.FLAG_SHOW_UI) != 0) || isShowing()) {
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001149 synchronized (this) {
RoboErik2811dd32014-08-12 09:48:13 -07001150 if (mActiveStreamType != STREAM_REMOTE_MUSIC) {
1151 reorderSliders(STREAM_REMOTE_MUSIC);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001152 }
RoboErik2811dd32014-08-12 09:48:13 -07001153 onShowVolumeChanged(STREAM_REMOTE_MUSIC, flags, controller);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001154 }
1155 } else {
John Spurlock86005342014-05-23 11:58:00 -04001156 if (LOGD) Log.d(mTag, "not calling onShowVolumeChanged(), no FLAG_SHOW_UI or no UI");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001157 }
1158
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001159 removeMessages(MSG_FREE_RESOURCES);
1160 sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001161 resetTimeout();
1162 }
1163
1164 protected void onRemoteVolumeUpdateIfShown() {
John Spurlock86005342014-05-23 11:58:00 -04001165 if (LOGD) Log.d(mTag, "onRemoteVolumeUpdateIfShown()");
1166 if (isShowing()
RoboErik2811dd32014-08-12 09:48:13 -07001167 && (mActiveStreamType == STREAM_REMOTE_MUSIC)
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001168 && (mStreamControls != null)) {
RoboErik2811dd32014-08-12 09:48:13 -07001169 onShowVolumeChanged(STREAM_REMOTE_MUSIC, 0, null);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001170 }
1171 }
1172
RoboErik19c95182014-06-23 15:38:48 -07001173 /**
1174 * Clear the current remote stream controller.
1175 */
1176 private void clearRemoteStreamController() {
1177 if (mStreamControls != null) {
RoboErik2811dd32014-08-12 09:48:13 -07001178 StreamControl sc = mStreamControls.get(STREAM_REMOTE_MUSIC);
RoboErik19c95182014-06-23 15:38:48 -07001179 if (sc != null) {
1180 if (sc.controller != null) {
RoboErik14f717a2014-09-04 16:08:00 -07001181 sc.controller.unregisterCallback(mMediaControllerCb);
RoboErik19c95182014-06-23 15:38:48 -07001182 sc.controller = null;
1183 }
1184 }
1185 }
1186 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001187
1188 /**
RoboErik2811dd32014-08-12 09:48:13 -07001189 * Handler for MSG_SLIDER_VISIBILITY_CHANGED Hide or show a slider
1190 *
1191 * @param streamType can be a valid stream type value, or
1192 * VolumePanel.STREAM_MASTER, or VolumePanel.STREAM_REMOTE_MUSIC
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001193 * @param visible
1194 */
1195 synchronized protected void onSliderVisibilityChanged(int streamType, int visible) {
John Spurlock86005342014-05-23 11:58:00 -04001196 if (LOGD) Log.d(mTag, "onSliderVisibilityChanged(stream="+streamType+", visi="+visible+")");
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001197 boolean isVisible = (visible == 1);
1198 for (int i = STREAMS.length - 1 ; i >= 0 ; i--) {
1199 StreamResources streamRes = STREAMS[i];
1200 if (streamRes.streamType == streamType) {
1201 streamRes.show = isVisible;
1202 if (!isVisible && (mActiveStreamType == streamType)) {
1203 mActiveStreamType = -1;
1204 }
1205 break;
1206 }
1207 }
1208 }
1209
Eric Laurentfde16d52012-12-03 14:42:39 -08001210 protected void onDisplaySafeVolumeWarning(int flags) {
John Spurlock35134602014-07-24 18:10:48 -04001211 if ((flags & (AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_SHOW_UI_WARNINGS)) != 0
1212 || isShowing()) {
1213 synchronized (sSafetyWarningLock) {
1214 if (sSafetyWarning != null) {
Eric Laurentfde16d52012-12-03 14:42:39 -08001215 return;
1216 }
John Spurlock35134602014-07-24 18:10:48 -04001217 sSafetyWarning = new SafetyWarning(mContext, this, mAudioManager);
1218 sSafetyWarning.show();
Eric Laurentfde16d52012-12-03 14:42:39 -08001219 }
1220 updateStates();
Eric Laurentc34dcc12012-09-10 13:51:52 -07001221 }
Selim Cinek1cf89062014-08-29 19:21:44 +02001222 if (mAccessibilityManager.isTouchExplorationEnabled()) {
1223 removeMessages(MSG_TIMEOUT);
1224 } else {
1225 updateTimeoutDelay();
1226 resetTimeout();
1227 }
Eric Laurentc34dcc12012-09-10 13:51:52 -07001228 }
1229
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001230 /**
1231 * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
1232 */
1233 private ToneGenerator getOrCreateToneGenerator(int streamType) {
Christopher Tatec4b78d22012-05-22 13:57:58 -07001234 if (streamType == STREAM_MASTER) {
1235 // For devices that use the master volume setting only but still want to
1236 // play a volume-changed tone, direct the master volume pseudostream to
1237 // the system stream's tone generator.
1238 if (mPlayMasterStreamTones) {
1239 streamType = AudioManager.STREAM_SYSTEM;
1240 } else {
1241 return null;
1242 }
1243 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001244 synchronized (this) {
1245 if (mToneGenerators[streamType] == null) {
Eric Laurent733a42b2011-01-19 10:41:57 -08001246 try {
1247 mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
1248 } catch (RuntimeException e) {
1249 if (LOGD) {
John Spurlock86005342014-05-23 11:58:00 -04001250 Log.d(mTag, "ToneGenerator constructor failed with "
Eric Laurent733a42b2011-01-19 10:41:57 -08001251 + "RuntimeException: " + e);
1252 }
1253 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001254 }
Eric Laurent733a42b2011-01-19 10:41:57 -08001255 return mToneGenerators[streamType];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001256 }
1257 }
1258
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001259
1260 /**
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001261 * Switch between icons because Bluetooth music is same as music volume, but with
1262 * different icons.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001263 */
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001264 private void setMusicIcon(int resId, int resMuteId) {
1265 StreamControl sc = mStreamControls.get(AudioManager.STREAM_MUSIC);
1266 if (sc != null) {
1267 sc.iconRes = resId;
1268 sc.iconMuteRes = resMuteId;
John Spurlockad494bc2014-07-19 15:56:19 -04001269 updateSliderIcon(sc, isMuted(sc.streamType));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001270 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001271 }
1272
1273 protected void onFreeResources() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001274 synchronized (this) {
1275 for (int i = mToneGenerators.length - 1; i >= 0; i--) {
1276 if (mToneGenerators[i] != null) {
1277 mToneGenerators[i].release();
1278 }
1279 mToneGenerators[i] = null;
1280 }
1281 }
1282 }
1283
1284 @Override
1285 public void handleMessage(Message msg) {
1286 switch (msg.what) {
1287
1288 case MSG_VOLUME_CHANGED: {
1289 onVolumeChanged(msg.arg1, msg.arg2);
1290 break;
1291 }
1292
Mike Lockwoodce952c82011-11-14 10:47:42 -08001293 case MSG_MUTE_CHANGED: {
1294 onMuteChanged(msg.arg1, msg.arg2);
1295 break;
1296 }
1297
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001298 case MSG_FREE_RESOURCES: {
1299 onFreeResources();
1300 break;
1301 }
1302
1303 case MSG_STOP_SOUNDS: {
1304 onStopSounds();
1305 break;
1306 }
1307
1308 case MSG_PLAY_SOUND: {
1309 onPlaySound(msg.arg1, msg.arg2);
1310 break;
1311 }
1312
1313 case MSG_VIBRATE: {
1314 onVibrate();
1315 break;
1316 }
1317
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001318 case MSG_TIMEOUT: {
John Spurlock86005342014-05-23 11:58:00 -04001319 if (isShowing()) {
John Spurlockeb2727b2014-07-19 23:11:36 -04001320 mDialog.dismiss();
1321 clearRemoteStreamController();
1322 mActiveStreamType = -1;
1323 if (mCallback != null) {
1324 mCallback.onVisible(false);
John Spurlock86005342014-05-23 11:58:00 -04001325 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001326 }
John Spurlock35134602014-07-24 18:10:48 -04001327 synchronized (sSafetyWarningLock) {
1328 if (sSafetyWarning != null) {
1329 if (LOGD) Log.d(mTag, "SafetyWarning timeout");
1330 sSafetyWarning.dismiss();
Eric Laurentfde16d52012-12-03 14:42:39 -08001331 }
1332 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001333 break;
1334 }
John Spurlockb4782522014-08-22 14:54:46 -04001335
1336 case MSG_RINGER_MODE_CHANGED:
1337 case MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED: {
John Spurlock86005342014-05-23 11:58:00 -04001338 if (isShowing()) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001339 updateStates();
1340 }
1341 break;
1342 }
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001343
1344 case MSG_REMOTE_VOLUME_CHANGED: {
RoboErik19c95182014-06-23 15:38:48 -07001345 onRemoteVolumeChanged((MediaController) msg.obj, msg.arg1);
Jean-Michel Trivi3114ce32012-06-11 15:03:52 -07001346 break;
1347 }
1348
1349 case MSG_REMOTE_VOLUME_UPDATE_IF_SHOWN:
1350 onRemoteVolumeUpdateIfShown();
1351 break;
1352
1353 case MSG_SLIDER_VISIBILITY_CHANGED:
1354 onSliderVisibilityChanged(msg.arg1, msg.arg2);
1355 break;
Eric Laurentc34dcc12012-09-10 13:51:52 -07001356
1357 case MSG_DISPLAY_SAFE_VOLUME_WARNING:
Eric Laurentfde16d52012-12-03 14:42:39 -08001358 onDisplaySafeVolumeWarning(msg.arg1);
Eric Laurentc34dcc12012-09-10 13:51:52 -07001359 break;
John Spurlock86005342014-05-23 11:58:00 -04001360
1361 case MSG_LAYOUT_DIRECTION:
1362 setLayoutDirection(msg.arg1);
1363 break;
1364
John Spurlock45601d62014-08-07 17:40:50 -04001365 case MSG_ZEN_MODE_AVAILABLE_CHANGED:
1366 mZenModeAvailable = msg.arg1 != 0;
1367 updateZenPanelVisible();
John Spurlockae641c92014-06-30 18:11:40 -04001368 break;
1369
1370 case MSG_USER_ACTIVITY:
1371 if (mCallback != null) {
1372 mCallback.onInteraction();
1373 }
John Spurlock86005342014-05-23 11:58:00 -04001374 break;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001375 }
1376 }
1377
John Spurlockae641c92014-06-30 18:11:40 -04001378 private void resetTimeout() {
John Spurlockea9938c2014-07-11 18:51:32 -04001379 if (LOGD) Log.d(mTag, "resetTimeout at " + System.currentTimeMillis()
1380 + " delay=" + mTimeoutDelay);
Selim Cinek1cf89062014-08-29 19:21:44 +02001381 if (sSafetyWarning == null || !mAccessibilityManager.isTouchExplorationEnabled()) {
1382 removeMessages(MSG_TIMEOUT);
1383 sendEmptyMessageDelayed(MSG_TIMEOUT, mTimeoutDelay);
1384 removeMessages(MSG_USER_ACTIVITY);
1385 sendEmptyMessage(MSG_USER_ACTIVITY);
1386 }
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001387 }
1388
John Spurlock1dad2722014-07-11 11:07:53 -04001389 private void forceTimeout(long delay) {
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001390 removeMessages(MSG_TIMEOUT);
John Spurlock1dad2722014-07-11 11:07:53 -04001391 sendEmptyMessageDelayed(MSG_TIMEOUT, delay);
John Spurlock86005342014-05-23 11:58:00 -04001392 }
1393
1394 public ZenModeController getZenController() {
1395 return mZenController;
Amith Yamasanibaf6dbf2011-08-18 17:40:29 -07001396 }
1397
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001398 private final OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
1399 @Override
1400 public void onProgressChanged(SeekBar seekBar, int progress, boolean fromUser) {
1401 final Object tag = seekBar.getTag();
1402 if (fromUser && tag instanceof StreamControl) {
1403 StreamControl sc = (StreamControl) tag;
John Spurlockac9ab5b2014-07-20 13:09:44 -04001404 setStreamVolume(sc, progress,
1405 AudioManager.FLAG_SHOW_UI | AudioManager.FLAG_VIBRATE);
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001406 }
1407 resetTimeout();
1408 }
1409
1410 @Override
1411 public void onStartTrackingTouch(SeekBar seekBar) {
1412 }
1413
1414 @Override
1415 public void onStopTrackingTouch(SeekBar seekBar) {
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001416 }
Alan Viverettee8ebaf32014-04-11 15:44:15 -07001417 };
Amith Yamasani2bbdd772011-02-02 18:54:13 -08001418
John Spurlock86005342014-05-23 11:58:00 -04001419 private final ZenModeController.Callback mZenCallback = new ZenModeController.Callback() {
John Spurlockb4782522014-08-22 14:54:46 -04001420 @Override
John Spurlock45601d62014-08-07 17:40:50 -04001421 public void onZenAvailableChanged(boolean available) {
1422 obtainMessage(MSG_ZEN_MODE_AVAILABLE_CHANGED, available ? 1 : 0, 0).sendToTarget();
John Spurlock86005342014-05-23 11:58:00 -04001423 }
John Spurlockb4782522014-08-22 14:54:46 -04001424 @Override
1425 public void onEffectsSupressorChanged() {
1426 obtainMessage(MSG_NOTIFICATION_EFFECTS_SUPPRESSOR_CHANGED,
1427 mZenController.getEffectsSuppressor()).sendToTarget();
1428 }
John Spurlock86005342014-05-23 11:58:00 -04001429 };
RoboErik19c95182014-06-23 15:38:48 -07001430
1431 private final MediaController.Callback mMediaControllerCb = new MediaController.Callback() {
RoboErikd2b8c942014-08-19 11:23:40 -07001432 public void onAudioInfoChanged(PlaybackInfo info) {
RoboErik19c95182014-06-23 15:38:48 -07001433 onRemoteVolumeUpdateIfShown();
1434 }
1435 };
John Spurlockae641c92014-06-30 18:11:40 -04001436
1437 public interface Callback {
1438 void onZenSettings();
1439 void onInteraction();
John Spurlock33f4e042014-07-11 13:10:58 -04001440 void onVisible(boolean visible);
John Spurlockae641c92014-06-30 18:11:40 -04001441 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001442}