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