1 /*
2  * Copyright (C) 2021 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.car.settings.qc;
18 
19 import static android.car.media.CarAudioManager.AUDIO_FEATURE_VOLUME_GROUP_MUTING;
20 import static android.car.media.CarAudioManager.PRIMARY_AUDIO_ZONE;
21 
22 import static com.android.car.qc.QCItem.QC_ACTION_SLIDER_VALUE;
23 
24 import android.app.PendingIntent;
25 import android.car.Car;
26 import android.car.CarNotConnectedException;
27 import android.car.media.CarAudioManager;
28 import android.content.Context;
29 import android.content.Intent;
30 import android.graphics.drawable.Icon;
31 import android.os.Bundle;
32 import android.util.SparseArray;
33 
34 import androidx.annotation.VisibleForTesting;
35 import androidx.annotation.XmlRes;
36 
37 import com.android.car.qc.QCItem;
38 import com.android.car.qc.QCList;
39 import com.android.car.qc.QCRow;
40 import com.android.car.qc.QCSlider;
41 import com.android.car.settings.R;
42 import com.android.car.settings.common.Logger;
43 import com.android.car.settings.sound.VolumeItemParser;
44 
45 /**
46  * Base class for showing a volume slider quick control view.
47  * Extended classes should override {@link #getUsages} and specify the array of audio usages that
48  * should be shown as part of the quick control.
49  */
50 public abstract class BaseVolumeSlider extends SettingsQCItem {
51     static final int QC_VOLUME_SELF_CHANGE = 7919;
52     @VisibleForTesting
53     static final String EXTRA_GROUP_ID = "QC_VOLUME_EXTRA_GROUP_ID";
54     private static final Logger LOG = new Logger(BaseVolumeSlider.class);
55 
56     private final SparseArray<VolumeItemParser.VolumeItem> mVolumeItems;
57     private Car mCar;
58     private CarAudioManager mCarAudioManager;
59 
BaseVolumeSlider(Context context)60     public BaseVolumeSlider(Context context) {
61         super(context);
62         mVolumeItems = VolumeItemParser.loadAudioUsageItems(context, carVolumeItemsXml());
63     }
64 
getUsages()65     protected abstract int[] getUsages();
66 
67     @Override
getQCItem()68     QCItem getQCItem() {
69         if (!initializeCarAudioManager()) {
70             return null;
71         }
72 
73         QCList.Builder listBuilder = new QCList.Builder();
74         for (int usage : getUsages()) {
75             VolumeItemParser.VolumeItem volumeItem = mVolumeItems.get(usage);
76             int groupId = mCarAudioManager.getVolumeGroupIdForUsage(usage);
77             int min = mCarAudioManager.getGroupMinVolume(groupId);
78             int max = mCarAudioManager.getGroupMaxVolume(groupId);
79             int value = mCarAudioManager.getGroupVolume(groupId);
80             int iconResId = volumeItem.getIcon();
81             if (mCarAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING)
82                     && mCarAudioManager.isVolumeGroupMuted(PRIMARY_AUDIO_ZONE, groupId)) {
83                 iconResId = volumeItem.getMuteIcon();
84             }
85             listBuilder.addRow(new QCRow.Builder()
86                     .setTitle(getContext().getString(volumeItem.getTitle()))
87                     .setIcon(Icon.createWithResource(getContext(), iconResId))
88                     .addSlider(new QCSlider.Builder()
89                             .setMin(min)
90                             .setMax(max)
91                             .setValue(value)
92                             .setInputAction(createSliderAction(groupId))
93                             .build()
94                     )
95                     .build()
96             );
97         }
98 
99         cleanupCarAudioManager();
100         return listBuilder.build();
101     }
102 
103     @Override
onNotifyChange(Intent intent)104     void onNotifyChange(Intent intent) {
105         int value = intent.getIntExtra(QC_ACTION_SLIDER_VALUE, Integer.MIN_VALUE);
106         int groupId = intent.getIntExtra(EXTRA_GROUP_ID, Integer.MIN_VALUE);
107         if (value == Integer.MIN_VALUE || groupId == Integer.MIN_VALUE) {
108             return;
109         }
110         if (!initializeCarAudioManager()) {
111             return;
112         }
113         setGroupVolume(groupId, value);
114         cleanupCarAudioManager();
115     }
116 
117     /**
118      * The resource which lists the car volume resources associated with the various usage enums.
119      */
120     @XmlRes
carVolumeItemsXml()121     protected int carVolumeItemsXml() {
122         return R.xml.car_volume_items;
123     }
124 
125     /**
126      * Initializes the CarAudioManager instance.
127      * @return true if the CarAudioManager was successfully initialized, false otherwise.
128      */
initializeCarAudioManager()129     private boolean initializeCarAudioManager() {
130         mCar = Car.createCar(getContext());
131         mCarAudioManager = (CarAudioManager) mCar.getCarManager(Car.AUDIO_SERVICE);
132         if (mCarAudioManager == null) {
133             cleanupCarAudioManager();
134             return false;
135         }
136         return true;
137     }
138 
cleanupCarAudioManager()139     private void cleanupCarAudioManager() {
140         if (mCar != null) {
141             mCar.disconnect();
142         }
143         mCar = null;
144         mCarAudioManager = null;
145     }
146 
createSliderAction(int groupId)147     private PendingIntent createSliderAction(int groupId) {
148         Bundle extras = new Bundle();
149         extras.putInt(EXTRA_GROUP_ID, groupId);
150         return getBroadcastIntent(extras, groupId);
151     }
152 
setGroupVolume(int volumeGroupId, int newVolume)153     private void setGroupVolume(int volumeGroupId, int newVolume) {
154         try {
155             mCarAudioManager.setGroupVolume(volumeGroupId, newVolume, QC_VOLUME_SELF_CHANGE);
156         } catch (CarNotConnectedException e) {
157             LOG.w("Ignoring volume change event because the car isn't connected", e);
158         }
159     }
160 }
161