• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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.car.volume;
18 
19 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_EVENTS;
20 import static android.car.media.CarAudioManager.INVALID_AUDIO_ZONE;
21 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
22 import static android.car.media.CarVolumeGroupEvent.EXTRA_INFO_SHOW_UI;
23 import static android.car.media.CarVolumeGroupEvent.EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_AUDIO_SYSTEM;
24 
25 import android.app.ActivityManager;
26 import android.car.Car;
27 import android.car.CarOccupantZoneManager;
28 import android.car.media.CarAudioManager;
29 import android.car.media.CarVolumeGroupEvent;
30 import android.car.media.CarVolumeGroupEventCallback;
31 import android.car.media.CarVolumeGroupInfo;
32 import android.content.res.Configuration;
33 import android.content.res.Resources;
34 import android.media.AudioManager;
35 import android.os.Handler;
36 import android.os.HandlerExecutor;
37 import android.util.Log;
38 
39 import com.android.systemui.CoreStartable;
40 import com.android.systemui.R;
41 import com.android.systemui.car.CarServiceProvider;
42 import com.android.systemui.dagger.SysUISingleton;
43 import com.android.systemui.dagger.qualifiers.Main;
44 import com.android.systemui.settings.UserTracker;
45 import com.android.systemui.statusbar.policy.ConfigurationController;
46 import com.android.systemui.volume.VolumeDialogComponent;
47 
48 import dagger.Lazy;
49 
50 import java.io.PrintWriter;
51 import java.util.List;
52 import java.util.concurrent.Executor;
53 
54 import javax.inject.Inject;
55 
56 /** The entry point for controlling the volume ui in cars. */
57 @SysUISingleton
58 public class VolumeUI implements CoreStartable, ConfigurationController.ConfigurationListener {
59 
60     private static final String TAG = "VolumeUI";
61     private final Resources mResources;
62     private final Handler mMainHandler;
63     private final CarServiceProvider mCarServiceProvider;
64     private final Lazy<VolumeDialogComponent> mVolumeDialogComponentLazy;
65     private final UserTracker mUserTracker;
66     private int mAudioZoneId = INVALID_AUDIO_ZONE;
67 
68     private final CarAudioManager.CarVolumeCallback mVolumeChangeCallback =
69             new CarAudioManager.CarVolumeCallback() {
70                 @Override
71                 public void onGroupVolumeChanged(int zoneId, int groupId, int flags) {
72                     handleVolumeCallback(zoneId, flags);
73                 }
74 
75                 @Override
76                 public void onMasterMuteChanged(int zoneId, int flags) {
77                     handleVolumeCallback(zoneId, flags);
78                 }
79 
80                 private void handleVolumeCallback(int zoneId, int flags) {
81                     if (mAudioZoneId != zoneId || (flags & AudioManager.FLAG_SHOW_UI) == 0) {
82                         // only initialize for current audio zone when show requested
83                         return;
84                     }
85                     initVolumeDialogComponent();
86                     unregistarCarAudioManagerCallbacks();
87                 }
88 
89             };
90 
91     private final CarVolumeGroupEventCallback mCarVolumeGroupEventCallback =
92             new CarVolumeGroupEventCallback() {
93                 @Override
94                 public void onVolumeGroupEvent(List<CarVolumeGroupEvent> volumeGroupEvents) {
95                     if (!hasEventsForZone(volumeGroupEvents)) {
96                         return;
97                     }
98                     initVolumeDialogComponent();
99                     unregistarCarAudioManagerCallbacks();
100                 }
101 
102                 private boolean hasEventsForZone(List<CarVolumeGroupEvent> events) {
103                     for (int index = 0; index < events.size(); index++) {
104                         List<CarVolumeGroupInfo> infos = events.get(index).getCarVolumeGroupInfos();
105                         if (!shouldShowUi(events.get(index).getExtraInfos())) {
106                             continue;
107                         }
108 
109                         for (int infoIndex = 0; infoIndex < infos.size(); infoIndex++) {
110                             if (infos.get(infoIndex).getZoneId() == mAudioZoneId) {
111                                 // at least one event for this zone exists that needs to show UI
112                                 return true;
113                             }
114                         }
115                     }
116                     return false;
117                 }
118 
119                 private boolean shouldShowUi(List<Integer> extraInfos) {
120                     if (extraInfos.contains(EXTRA_INFO_SHOW_UI)
121                             || extraInfos.contains(
122                             EXTRA_INFO_VOLUME_INDEX_CHANGED_BY_AUDIO_SYSTEM)) {
123                         return true;
124                     }
125                     return false;
126                 }
127             };
128 
129     private boolean mEnabled;
130     private CarAudioManager mCarAudioManager;
131     private VolumeDialogComponent mVolumeDialogComponent;
132     private final Executor mExecutor;
133 
134     @Inject
VolumeUI( @ain Resources resources, @Main Handler mainHandler, CarServiceProvider carServiceProvider, Lazy<VolumeDialogComponent> volumeDialogComponentLazy, UserTracker userTracker )135     public VolumeUI(
136             @Main Resources resources,
137             @Main Handler mainHandler,
138             CarServiceProvider carServiceProvider,
139             Lazy<VolumeDialogComponent> volumeDialogComponentLazy,
140             UserTracker userTracker
141     ) {
142         mResources = resources;
143         mMainHandler = mainHandler;
144         mCarServiceProvider = carServiceProvider;
145         mVolumeDialogComponentLazy = volumeDialogComponentLazy;
146         mUserTracker = userTracker;
147         mExecutor = new HandlerExecutor(mainHandler);
148     }
149 
150     @Override
start()151     public void start() {
152         boolean enableVolumeUi = mResources.getBoolean(R.bool.enable_volume_ui);
153         mEnabled = enableVolumeUi;
154         if (!mEnabled) return;
155 
156         mCarServiceProvider.addListener(car -> {
157             if (mCarAudioManager != null) {
158                 // already initialized
159                 return;
160             }
161 
162             CarOccupantZoneManager carOccupantZoneManager = car.getCarManager(
163                     CarOccupantZoneManager.class);
164             if (carOccupantZoneManager != null) {
165                 CarOccupantZoneManager.OccupantZoneInfo info =
166                         carOccupantZoneManager.getOccupantZoneForUser(mUserTracker.getUserHandle());
167                 if (info != null) {
168                     mAudioZoneId = carOccupantZoneManager.getAudioZoneIdForOccupant(info);
169                 }
170             }
171 
172             if (mAudioZoneId == INVALID_AUDIO_ZONE) {
173                 if (mUserTracker.getUserId() == ActivityManager.getCurrentUser()) {
174                     // Certain devices may not have occupant zones configured. As a fallback for
175                     // this situation, if the user is the foreground user, assume driver and use the
176                     // primary audio zone.
177                     mAudioZoneId = PRIMARY_AUDIO_ZONE;
178                 } else {
179                     return;
180                 }
181             }
182 
183             mCarAudioManager = (CarAudioManager) car.getCarManager(Car.AUDIO_SERVICE);
184             if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS)) {
185                 Log.d(TAG, "Registering mCarVolumeGroupEventCallback.");
186                 mCarAudioManager.registerCarVolumeGroupEventCallback(mExecutor,
187                         mCarVolumeGroupEventCallback);
188             } else {
189                 Log.d(TAG, "Registering mVolumeChangeCallback.");
190                 // This volume call back is never unregistered because CarStatusBar is
191                 // never destroyed.
192                 mCarAudioManager.registerCarVolumeCallback(mVolumeChangeCallback);
193             }
194         });
195     }
196 
197     @Override
onConfigChanged(Configuration newConfig)198     public void onConfigChanged(Configuration newConfig) {
199         if (!mEnabled) return;
200         if (mVolumeDialogComponent != null) {
201             mVolumeDialogComponent.onConfigurationChanged(newConfig);
202         }
203     }
204 
205     @Override
dump(PrintWriter pw, String[] args)206     public void dump(PrintWriter pw, String[] args) {
207         pw.print("mEnabled="); pw.println(mEnabled);
208         if (!mEnabled) return;
209         if (mVolumeDialogComponent != null) {
210             mVolumeDialogComponent.dump(pw, args);
211         }
212     }
213 
initVolumeDialogComponent()214     private void initVolumeDialogComponent() {
215         if (mVolumeDialogComponent == null) {
216             mMainHandler.post(() -> {
217                 mVolumeDialogComponent = mVolumeDialogComponentLazy.get();
218                 mVolumeDialogComponent.register();
219             });
220         }
221     }
222 
unregistarCarAudioManagerCallbacks()223     private void unregistarCarAudioManagerCallbacks() {
224         mCarAudioManager.unregisterCarVolumeCallback(mVolumeChangeCallback);
225         if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_EVENTS)) {
226             mCarAudioManager.unregisterCarVolumeGroupEventCallback(mCarVolumeGroupEventCallback);
227         }
228     }
229 }
230