blob: 5e3ec3fe3657e7f4e4ce51789aacacbcd1d4ad79 [file] [log] [blame]
John Spurlock3346a802014-05-20 16:25:37 -04001/*
2 * Copyright (C) 2014 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 Spurlockcdb57ae2015-02-11 19:04:11 -050017package com.android.systemui.volume;
18
19import android.app.Notification;
20import android.app.NotificationManager;
21import android.app.PendingIntent;
22import android.content.BroadcastReceiver;
23import android.content.ComponentName;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.DialogInterface.OnClickListener;
27import android.content.Intent;
28import android.content.IntentFilter;
29import android.content.pm.ApplicationInfo;
30import android.content.res.Configuration;
31import android.media.AudioManager;
32import android.media.IRemoteVolumeController;
33import android.media.IVolumeController;
34import android.media.session.ISessionController;
35import android.media.session.MediaController;
36import android.media.session.MediaSessionManager;
37import android.os.Bundle;
38import android.os.Handler;
39import android.os.RemoteException;
40import android.provider.Settings;
41import android.text.TextUtils;
42import android.util.Log;
43
44import com.android.systemui.R;
45import com.android.systemui.SystemUI;
46import com.android.systemui.keyguard.KeyguardViewMediator;
John Spurlock3c4076a2015-02-24 12:12:25 -050047import com.android.systemui.qs.tiles.DndTile;
John Spurlockcdb57ae2015-02-11 19:04:11 -050048import com.android.systemui.statusbar.ServiceMonitor;
49import com.android.systemui.statusbar.phone.PhoneStatusBar;
50import com.android.systemui.statusbar.phone.SystemUIDialog;
51import com.android.systemui.statusbar.policy.ZenModeController;
52import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
53
54import java.io.FileDescriptor;
55import java.io.PrintWriter;
56
John Spurlock3346a802014-05-20 16:25:37 -040057public class VolumeUI extends SystemUI {
58 private static final String TAG = "VolumeUI";
John Spurlockcdb57ae2015-02-11 19:04:11 -050059 private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
John Spurlock3346a802014-05-20 16:25:37 -040060
John Spurlock86005342014-05-23 11:58:00 -040061 private final Handler mHandler = new Handler();
John Spurlockcdb57ae2015-02-11 19:04:11 -050062 private final Receiver mReceiver = new Receiver();
63 private final RestorationNotification mRestorationNotification = new RestorationNotification();
John Spurlock1dad2722014-07-11 11:07:53 -040064
John Spurlockf2565a82014-10-23 20:16:22 -040065 private boolean mEnabled;
John Spurlock3346a802014-05-20 16:25:37 -040066 private AudioManager mAudioManager;
John Spurlockcdb57ae2015-02-11 19:04:11 -050067 private NotificationManager mNotificationManager;
RoboErik19c95182014-06-23 15:38:48 -070068 private MediaSessionManager mMediaSessionManager;
John Spurlock3346a802014-05-20 16:25:37 -040069 private VolumeController mVolumeController;
RoboErik19c95182014-06-23 15:38:48 -070070 private RemoteVolumeController mRemoteVolumeController;
John Spurlockcdb57ae2015-02-11 19:04:11 -050071 private ServiceMonitor mVolumeControllerService;
RoboErik19c95182014-06-23 15:38:48 -070072
RoboErik19c95182014-06-23 15:38:48 -070073 private VolumePanel mPanel;
John Spurlock1dad2722014-07-11 11:07:53 -040074 private int mDismissDelay;
John Spurlock3346a802014-05-20 16:25:37 -040075
76 @Override
77 public void start() {
John Spurlockf2565a82014-10-23 20:16:22 -040078 mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
79 if (!mEnabled) return;
John Spurlock3346a802014-05-20 16:25:37 -040080 mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
John Spurlockcdb57ae2015-02-11 19:04:11 -050081 mNotificationManager =
82 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
RoboErik19c95182014-06-23 15:38:48 -070083 mMediaSessionManager = (MediaSessionManager) mContext
84 .getSystemService(Context.MEDIA_SESSION_SERVICE);
85 initPanel();
86 mVolumeController = new VolumeController();
87 mRemoteVolumeController = new RemoteVolumeController();
John Spurlock86005342014-05-23 11:58:00 -040088 putComponent(VolumeComponent.class, mVolumeController);
John Spurlockcdb57ae2015-02-11 19:04:11 -050089 mReceiver.start();
90 mVolumeControllerService = new ServiceMonitor(TAG, LOGD,
91 mContext, Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT,
92 new ServiceMonitorCallbacks());
93 mVolumeControllerService.start();
John Spurlock3346a802014-05-20 16:25:37 -040094 }
95
John Spurlockad494bc2014-07-19 15:56:19 -040096 @Override
John Spurlock7e6809a2014-08-06 16:03:14 -040097 protected void onConfigurationChanged(Configuration newConfig) {
98 super.onConfigurationChanged(newConfig);
99 if (mPanel != null) {
100 mPanel.onConfigurationChanged(newConfig);
101 }
102 }
103
104 @Override
John Spurlockad494bc2014-07-19 15:56:19 -0400105 public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
John Spurlockf2565a82014-10-23 20:16:22 -0400106 pw.print("mEnabled="); pw.println(mEnabled);
John Spurlockcdb57ae2015-02-11 19:04:11 -0500107 pw.print("mVolumeControllerService="); pw.println(mVolumeControllerService.getComponent());
John Spurlockad494bc2014-07-19 15:56:19 -0400108 if (mPanel != null) {
109 mPanel.dump(fd, pw, args);
110 }
111 }
112
John Spurlockcdb57ae2015-02-11 19:04:11 -0500113 private void setVolumeController(boolean register) {
114 if (register) {
115 if (LOGD) Log.d(TAG, "Registering volume controller");
John Spurlock3346a802014-05-20 16:25:37 -0400116 mAudioManager.setVolumeController(mVolumeController);
RoboErik19c95182014-06-23 15:38:48 -0700117 mMediaSessionManager.setRemoteVolumeController(mRemoteVolumeController);
John Spurlock3c4076a2015-02-24 12:12:25 -0500118 DndTile.setVisible(mContext, false);
John Spurlock3346a802014-05-20 16:25:37 -0400119 } else {
John Spurlockcdb57ae2015-02-11 19:04:11 -0500120 if (LOGD) Log.d(TAG, "Unregistering volume controller");
John Spurlock3346a802014-05-20 16:25:37 -0400121 mAudioManager.setVolumeController(null);
RoboErik19c95182014-06-23 15:38:48 -0700122 mMediaSessionManager.setRemoteVolumeController(null);
John Spurlock3346a802014-05-20 16:25:37 -0400123 }
124 }
125
RoboErik19c95182014-06-23 15:38:48 -0700126 private void initPanel() {
John Spurlock1dad2722014-07-11 11:07:53 -0400127 mDismissDelay = mContext.getResources().getInteger(R.integer.volume_panel_dismiss_delay);
John Spurlockeb2727b2014-07-19 23:11:36 -0400128 mPanel = new VolumePanel(mContext, new ZenModeControllerImpl(mContext, mHandler));
John Spurlockae641c92014-06-30 18:11:40 -0400129 mPanel.setCallback(new VolumePanel.Callback() {
RoboErik19c95182014-06-23 15:38:48 -0700130 @Override
John Spurlockae641c92014-06-30 18:11:40 -0400131 public void onZenSettings() {
RoboErik19c95182014-06-23 15:38:48 -0700132 mHandler.removeCallbacks(mStartZenSettings);
John Spurlockae641c92014-06-30 18:11:40 -0400133 mHandler.post(mStartZenSettings);
RoboErik19c95182014-06-23 15:38:48 -0700134 }
135
136 @Override
137 public void onInteraction() {
John Spurlockae641c92014-06-30 18:11:40 -0400138 final KeyguardViewMediator kvm = getComponent(KeyguardViewMediator.class);
139 if (kvm != null) {
140 kvm.userActivity();
141 }
RoboErik19c95182014-06-23 15:38:48 -0700142 }
John Spurlock33f4e042014-07-11 13:10:58 -0400143
144 @Override
145 public void onVisible(boolean visible) {
146 if (mAudioManager != null && mVolumeController != null) {
147 mAudioManager.notifyVolumeControllerVisible(mVolumeController, visible);
148 }
149 }
RoboErik19c95182014-06-23 15:38:48 -0700150 });
RoboErik19c95182014-06-23 15:38:48 -0700151 }
152
John Spurlockcdb57ae2015-02-11 19:04:11 -0500153 private String getAppLabel(ComponentName component) {
154 final String pkg = component.getPackageName();
155 try {
156 final ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(pkg, 0);
157 final String rt = mContext.getPackageManager().getApplicationLabel(ai).toString();
158 if (!TextUtils.isEmpty(rt)) {
159 return rt;
John Spurlock3346a802014-05-20 16:25:37 -0400160 }
John Spurlockcdb57ae2015-02-11 19:04:11 -0500161 } catch (Exception e) {
162 Log.w(TAG, "Error loading app label", e);
John Spurlock3346a802014-05-20 16:25:37 -0400163 }
John Spurlockcdb57ae2015-02-11 19:04:11 -0500164 return pkg;
165 }
166
167 private void showServiceActivationDialog(final ComponentName component) {
168 final SystemUIDialog d = new SystemUIDialog(mContext);
169 d.setMessage(mContext.getString(R.string.volumeui_prompt_message, getAppLabel(component)));
170 d.setPositiveButton(R.string.volumeui_prompt_allow, new OnClickListener() {
171 @Override
172 public void onClick(DialogInterface dialog, int which) {
173 mVolumeControllerService.setComponent(component);
174 }
175 });
176 d.setNegativeButton(R.string.volumeui_prompt_deny, null);
177 d.show();
178 }
John Spurlock3346a802014-05-20 16:25:37 -0400179
RoboErik19c95182014-06-23 15:38:48 -0700180 private final Runnable mStartZenSettings = new Runnable() {
181 @Override
182 public void run() {
Jorim Jaggi8de4311c2014-08-11 22:36:20 +0200183 getComponent(PhoneStatusBar.class).startActivityDismissingKeyguard(
Jorim Jaggi85dc23c2014-09-08 14:42:29 +0200184 ZenModePanel.ZEN_SETTINGS, true /* onlyProvisioned */, true /* dismissShade */);
John Spurlockad494bc2014-07-19 15:56:19 -0400185 mPanel.postDismiss(mDismissDelay);
RoboErik19c95182014-06-23 15:38:48 -0700186 }
187 };
188
John Spurlock3346a802014-05-20 16:25:37 -0400189 /** For now, simply host an unmodified base volume panel in this process. */
John Spurlock86005342014-05-23 11:58:00 -0400190 private final class VolumeController extends IVolumeController.Stub implements VolumeComponent {
John Spurlock3346a802014-05-20 16:25:37 -0400191
192 @Override
193 public void displaySafeVolumeWarning(int flags) throws RemoteException {
194 mPanel.postDisplaySafeVolumeWarning(flags);
195 }
196
197 @Override
198 public void volumeChanged(int streamType, int flags)
199 throws RemoteException {
200 mPanel.postVolumeChanged(streamType, flags);
201 }
202
203 @Override
204 public void masterVolumeChanged(int flags) throws RemoteException {
205 mPanel.postMasterVolumeChanged(flags);
206 }
207
208 @Override
209 public void masterMuteChanged(int flags) throws RemoteException {
210 mPanel.postMasterMuteChanged(flags);
211 }
212
213 @Override
214 public void setLayoutDirection(int layoutDirection)
215 throws RemoteException {
John Spurlock86005342014-05-23 11:58:00 -0400216 mPanel.postLayoutDirection(layoutDirection);
John Spurlock3346a802014-05-20 16:25:37 -0400217 }
218
219 @Override
220 public void dismiss() throws RemoteException {
John Spurlock7fbf5732014-11-18 11:40:22 -0500221 dismissNow();
John Spurlock3346a802014-05-20 16:25:37 -0400222 }
John Spurlock86005342014-05-23 11:58:00 -0400223
224 @Override
225 public ZenModeController getZenController() {
John Spurlockad494bc2014-07-19 15:56:19 -0400226 return mPanel.getZenController();
John Spurlock86005342014-05-23 11:58:00 -0400227 }
John Spurlockbb4a7022014-11-08 12:40:19 -0500228
229 @Override
230 public void dispatchDemoCommand(String command, Bundle args) {
231 mPanel.dispatchDemoCommand(command, args);
232 }
John Spurlock7fbf5732014-11-18 11:40:22 -0500233
234 @Override
235 public void dismissNow() {
236 mPanel.postDismiss(0);
237 }
John Spurlock3346a802014-05-20 16:25:37 -0400238 }
RoboErik19c95182014-06-23 15:38:48 -0700239
240 private final class RemoteVolumeController extends IRemoteVolumeController.Stub {
241
242 @Override
243 public void remoteVolumeChanged(ISessionController binder, int flags)
244 throws RemoteException {
RoboErik031149c2014-07-25 16:00:52 -0700245 MediaController controller = new MediaController(mContext, binder);
RoboErik19c95182014-06-23 15:38:48 -0700246 mPanel.postRemoteVolumeChanged(controller, flags);
247 }
248
249 @Override
250 public void updateRemoteController(ISessionController session) throws RemoteException {
251 mPanel.postRemoteSliderVisibility(session != null);
252 // TODO stash default session in case the slider can be opened other
253 // than by remoteVolumeChanged.
254 }
255 }
John Spurlockcdb57ae2015-02-11 19:04:11 -0500256
257 private final class ServiceMonitorCallbacks implements ServiceMonitor.Callbacks {
258 @Override
259 public void onNoService() {
260 if (LOGD) Log.d(TAG, "onNoService");
261 setVolumeController(true);
262 mRestorationNotification.hide();
263 }
264
265 @Override
266 public long onServiceStartAttempt() {
267 if (LOGD) Log.d(TAG, "onServiceStartAttempt");
268 setVolumeController(false);
269 mVolumeController.dismissNow();
270 mRestorationNotification.show();
271 return 0;
272 }
273 }
274
275 private final class Receiver extends BroadcastReceiver {
276 private static final String ENABLE = "com.android.systemui.vui.ENABLE";
277 private static final String DISABLE = "com.android.systemui.vui.DISABLE";
278 private static final String EXTRA_COMPONENT = "component";
279
280 public void start() {
281 final IntentFilter filter = new IntentFilter();
282 filter.addAction(ENABLE);
283 filter.addAction(DISABLE);
284 mContext.registerReceiver(this, filter, null, mHandler);
285 }
286
287 @Override
288 public void onReceive(Context context, Intent intent) {
289 final String action = intent.getAction();
290 final ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT);
291 final boolean current = component.equals(mVolumeControllerService.getComponent());
292 if (ENABLE.equals(action) && component != null) {
293 if (!current) {
294 showServiceActivationDialog(component);
295 }
296 }
297 if (DISABLE.equals(action) && component != null) {
298 if (current) {
299 mVolumeControllerService.setComponent(null);
300 }
301 }
302 }
303 }
304
305 private final class RestorationNotification {
306 public void hide() {
307 mNotificationManager.cancel(R.id.notification_volumeui);
308 }
309
310 public void show() {
311 final ComponentName component = mVolumeControllerService.getComponent();
312 if (component == null) {
313 Log.w(TAG, "Not showing restoration notification, component not active");
314 return;
315 }
316 final Intent intent = new Intent(Receiver.DISABLE)
317 .putExtra(Receiver.EXTRA_COMPONENT, component);
318 mNotificationManager.notify(R.id.notification_volumeui,
319 new Notification.Builder(mContext)
320 .setSmallIcon(R.drawable.ic_ringer_audible)
321 .setWhen(0)
322 .setShowWhen(false)
323 .setOngoing(true)
324 .setContentTitle(mContext.getString(
325 R.string.volumeui_notification_title, getAppLabel(component)))
326 .setContentText(mContext.getString(R.string.volumeui_notification_text))
327 .setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent, 0))
328 .setPriority(Notification.PRIORITY_MIN)
329 .setVisibility(Notification.VISIBILITY_PUBLIC)
330 .setColor(mContext.getResources().getColor(
331 com.android.internal.R.color.system_notification_accent_color))
332 .build());
333 }
334 }
John Spurlock3346a802014-05-20 16:25:37 -0400335}