• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.systemui.volume;
18 
19 import android.app.Notification;
20 import android.app.NotificationManager;
21 import android.app.PendingIntent;
22 import android.content.BroadcastReceiver;
23 import android.content.ComponentName;
24 import android.content.Context;
25 import android.content.DialogInterface;
26 import android.content.DialogInterface.OnClickListener;
27 import android.content.Intent;
28 import android.content.IntentFilter;
29 import android.content.pm.ApplicationInfo;
30 import android.content.res.Configuration;
31 import android.media.AudioManager;
32 import android.media.session.MediaSessionManager;
33 import android.os.Handler;
34 import android.provider.Settings;
35 import android.text.TextUtils;
36 import android.util.Log;
37 
38 import com.android.systemui.Prefs;
39 import com.android.systemui.R;
40 import com.android.systemui.SystemUI;
41 import com.android.systemui.qs.tiles.DndTile;
42 import com.android.systemui.statusbar.ServiceMonitor;
43 import com.android.systemui.statusbar.phone.SystemUIDialog;
44 import com.android.systemui.statusbar.policy.ZenModeController;
45 import com.android.systemui.statusbar.policy.ZenModeControllerImpl;
46 
47 import java.io.FileDescriptor;
48 import java.io.PrintWriter;
49 
50 public class VolumeUI extends SystemUI {
51     private static final String TAG = "VolumeUI";
52     private static boolean LOGD = Log.isLoggable(TAG, Log.DEBUG);
53 
54     private final Handler mHandler = new Handler();
55     private final Receiver mReceiver = new Receiver();
56     private final RestorationNotification mRestorationNotification = new RestorationNotification();
57 
58     private boolean mEnabled;
59     private AudioManager mAudioManager;
60     private NotificationManager mNotificationManager;
61     private MediaSessionManager mMediaSessionManager;
62     private ServiceMonitor mVolumeControllerService;
63 
64     private VolumeDialogComponent mVolumeComponent;
65 
66     @Override
start()67     public void start() {
68         mEnabled = mContext.getResources().getBoolean(R.bool.enable_volume_ui);
69         if (!mEnabled) return;
70         mAudioManager = (AudioManager) mContext.getSystemService(Context.AUDIO_SERVICE);
71         mNotificationManager =
72                 (NotificationManager) mContext.getSystemService(Context.NOTIFICATION_SERVICE);
73         mMediaSessionManager = (MediaSessionManager) mContext
74                 .getSystemService(Context.MEDIA_SESSION_SERVICE);
75         final ZenModeController zenController = new ZenModeControllerImpl(mContext, mHandler);
76         mVolumeComponent = new VolumeDialogComponent(this, mContext, null, zenController);
77         putComponent(VolumeComponent.class, getVolumeComponent());
78         mReceiver.start();
79         mVolumeControllerService = new ServiceMonitor(TAG, LOGD,
80                 mContext, Settings.Secure.VOLUME_CONTROLLER_SERVICE_COMPONENT,
81                 new ServiceMonitorCallbacks());
82         mVolumeControllerService.start();
83     }
84 
getVolumeComponent()85     private VolumeComponent getVolumeComponent() {
86         return mVolumeComponent;
87     }
88 
89     @Override
onConfigurationChanged(Configuration newConfig)90     protected void onConfigurationChanged(Configuration newConfig) {
91         super.onConfigurationChanged(newConfig);
92         if (!mEnabled) return;
93         getVolumeComponent().onConfigurationChanged(newConfig);
94     }
95 
96     @Override
dump(FileDescriptor fd, PrintWriter pw, String[] args)97     public void dump(FileDescriptor fd, PrintWriter pw, String[] args) {
98         pw.print("mEnabled="); pw.println(mEnabled);
99         if (!mEnabled) return;
100         pw.print("mVolumeControllerService="); pw.println(mVolumeControllerService.getComponent());
101         getVolumeComponent().dump(fd, pw, args);
102     }
103 
setDefaultVolumeController(boolean register)104     private void setDefaultVolumeController(boolean register) {
105         if (register) {
106             DndTile.setVisible(mContext, true);
107             if (LOGD) Log.d(TAG, "Registering default volume controller");
108             getVolumeComponent().register();
109         } else {
110             if (LOGD) Log.d(TAG, "Unregistering default volume controller");
111             mAudioManager.setVolumeController(null);
112             mMediaSessionManager.setRemoteVolumeController(null);
113         }
114     }
115 
getAppLabel(ComponentName component)116     private String getAppLabel(ComponentName component) {
117         final String pkg = component.getPackageName();
118         try {
119             final ApplicationInfo ai = mContext.getPackageManager().getApplicationInfo(pkg, 0);
120             final String rt = mContext.getPackageManager().getApplicationLabel(ai).toString();
121             if (!TextUtils.isEmpty(rt)) {
122                 return rt;
123             }
124         } catch (Exception e) {
125             Log.w(TAG, "Error loading app label", e);
126         }
127         return pkg;
128     }
129 
showServiceActivationDialog(final ComponentName component)130     private void showServiceActivationDialog(final ComponentName component) {
131         final SystemUIDialog d = new SystemUIDialog(mContext);
132         d.setMessage(mContext.getString(R.string.volumeui_prompt_message, getAppLabel(component)));
133         d.setPositiveButton(R.string.volumeui_prompt_allow, new OnClickListener() {
134             @Override
135             public void onClick(DialogInterface dialog, int which) {
136                 mVolumeControllerService.setComponent(component);
137             }
138         });
139         d.setNegativeButton(R.string.volumeui_prompt_deny, null);
140         d.show();
141     }
142 
143     private final class ServiceMonitorCallbacks implements ServiceMonitor.Callbacks {
144         @Override
onNoService()145         public void onNoService() {
146             if (LOGD) Log.d(TAG, "onNoService");
147             setDefaultVolumeController(true);
148             mRestorationNotification.hide();
149             if (!mVolumeControllerService.isPackageAvailable()) {
150                 mVolumeControllerService.setComponent(null);
151             }
152         }
153 
154         @Override
onServiceStartAttempt()155         public long onServiceStartAttempt() {
156             if (LOGD) Log.d(TAG, "onServiceStartAttempt");
157             // poke the setting to update the uid
158             mVolumeControllerService.setComponent(mVolumeControllerService.getComponent());
159             setDefaultVolumeController(false);
160             getVolumeComponent().dismissNow();
161             mRestorationNotification.show();
162             return 0;
163         }
164     }
165 
166     private final class Receiver extends BroadcastReceiver {
167         private static final String ENABLE = "com.android.systemui.vui.ENABLE";
168         private static final String DISABLE = "com.android.systemui.vui.DISABLE";
169         private static final String EXTRA_COMPONENT = "component";
170 
171         private static final String PREF = "com.android.systemui.PREF";
172         private static final String EXTRA_KEY = "key";
173         private static final String EXTRA_VALUE = "value";
174 
start()175         public void start() {
176             final IntentFilter filter = new IntentFilter();
177             filter.addAction(ENABLE);
178             filter.addAction(DISABLE);
179             filter.addAction(PREF);
180             mContext.registerReceiver(this, filter, null, mHandler);
181         }
182 
183         @Override
onReceive(Context context, Intent intent)184         public void onReceive(Context context, Intent intent) {
185             final String action = intent.getAction();
186             if (PREF.equals(action)) {
187                 final String key = intent.getStringExtra(EXTRA_KEY);
188                 if (key != null && intent.getExtras() != null) {
189                     final Object value = intent.getExtras().get(EXTRA_VALUE);
190                     if (value == null) {
191                         Prefs.remove(mContext, key);
192                     } else if (value instanceof Boolean) {
193                         Prefs.putBoolean(mContext, key, (Boolean) value);
194                     } else if (value instanceof Integer) {
195                         Prefs.putInt(mContext, key, (Integer) value);
196                     } else if (value instanceof Long) {
197                         Prefs.putLong(mContext, key, (Long) value);
198                     }
199                 }
200                 return;
201             }
202             final ComponentName component = intent.getParcelableExtra(EXTRA_COMPONENT);
203             final boolean current = component != null
204                     && component.equals(mVolumeControllerService.getComponent());
205             if (ENABLE.equals(action) && component != null) {
206                 if (!current) {
207                     showServiceActivationDialog(component);
208                 }
209             }
210             if (DISABLE.equals(action) && component != null) {
211                 if (current) {
212                     mVolumeControllerService.setComponent(null);
213                 }
214             }
215         }
216     }
217 
218     private final class RestorationNotification {
hide()219         public void hide() {
220             mNotificationManager.cancel(R.id.notification_volumeui);
221         }
222 
show()223         public void show() {
224             final ComponentName component = mVolumeControllerService.getComponent();
225             if (component == null) {
226                 Log.w(TAG, "Not showing restoration notification, component not active");
227                 return;
228             }
229             final Intent intent =  new Intent(Receiver.DISABLE)
230                     .putExtra(Receiver.EXTRA_COMPONENT, component);
231             Notification.Builder builder = new Notification.Builder(mContext)
232                     .setSmallIcon(R.drawable.ic_volume_media)
233                     .setWhen(0)
234                     .setShowWhen(false)
235                     .setOngoing(true)
236                     .setContentTitle(mContext.getString(
237                             R.string.volumeui_notification_title, getAppLabel(component)))
238                     .setContentText(mContext.getString(R.string.volumeui_notification_text))
239                     .setContentIntent(PendingIntent.getBroadcast(mContext, 0, intent,
240                             PendingIntent.FLAG_UPDATE_CURRENT))
241                     .setPriority(Notification.PRIORITY_MIN)
242                     .setVisibility(Notification.VISIBILITY_PUBLIC)
243                     .setColor(mContext.getColor(
244                             com.android.internal.R.color.system_notification_accent_color));
245             overrideNotificationAppName(mContext, builder);
246             mNotificationManager.notify(R.id.notification_volumeui,
247                     builder.build());
248         }
249     }
250 }
251