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 21 import static com.android.car.qc.QCItem.QC_ACTION_SLIDER_VALUE; 22 import static com.android.car.settings.qc.QCUtils.getActionDisabledDialogIntent; 23 import static com.android.car.settings.qc.QCUtils.getAvailabilityStatusForZoneFromXml; 24 25 import android.app.PendingIntent; 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.os.UserManager; 33 import android.util.SparseArray; 34 35 import androidx.annotation.VisibleForTesting; 36 import androidx.annotation.XmlRes; 37 38 import com.android.car.qc.QCItem; 39 import com.android.car.qc.QCList; 40 import com.android.car.qc.QCRow; 41 import com.android.car.qc.QCSlider; 42 import com.android.car.settings.CarSettingsApplication; 43 import com.android.car.settings.R; 44 import com.android.car.settings.common.Logger; 45 import com.android.car.settings.enterprise.EnterpriseUtils; 46 import com.android.car.settings.sound.VolumeItemParser; 47 48 /** 49 * Base class for showing a volume slider quick control view. 50 * Extended classes should override {@link #getUsages} and specify the array of audio usages that 51 * should be shown as part of the quick control. 52 */ 53 public abstract class BaseVolumeSlider extends SettingsQCItem { 54 static final int QC_VOLUME_SELF_CHANGE = 7918; 55 @VisibleForTesting 56 static final String EXTRA_GROUP_ID = "QC_VOLUME_EXTRA_GROUP_ID"; 57 private static final Logger LOG = new Logger(BaseVolumeSlider.class); 58 59 private final Context mContext; 60 private final SparseArray<VolumeItemParser.VolumeItem> mVolumeItems; 61 BaseVolumeSlider(Context context)62 public BaseVolumeSlider(Context context) { 63 super(context); 64 mContext = context; 65 setAvailabilityStatusForZone(getAvailabilityStatusForZoneFromXml(context, 66 R.xml.sound_settings_fragment, R.string.pk_volume_settings)); 67 mVolumeItems = VolumeItemParser.loadAudioUsageItems(context, carVolumeItemsXml()); 68 } 69 getUsages()70 protected abstract int[] getUsages(); 71 72 @Override getQCItem()73 QCItem getQCItem() { 74 if (isHiddenForZone()) { 75 return null; 76 } 77 CarAudioManager carAudioManager = getCarAudioManager(); 78 int zoneId = getMyAudioZoneId(); 79 if (carAudioManager == null || zoneId == CarAudioManager.INVALID_AUDIO_ZONE) { 80 return null; 81 } 82 83 String userRestriction = UserManager.DISALLOW_ADJUST_VOLUME; 84 boolean hasDpmRestrictions = EnterpriseUtils.hasUserRestrictionByDpm(getContext(), 85 userRestriction); 86 boolean hasUmRestrictions = EnterpriseUtils.hasUserRestrictionByUm(getContext(), 87 userRestriction); 88 89 90 boolean isReadOnlyForZone = isReadOnlyForZone(); 91 PendingIntent disabledPendingIntent = isReadOnlyForZone 92 ? QCUtils.getDisabledToastBroadcastIntent(getContext()) 93 : getActionDisabledDialogIntent(getContext(), userRestriction); 94 95 QCList.Builder listBuilder = new QCList.Builder(); 96 for (int usage : getUsages()) { 97 VolumeItemParser.VolumeItem volumeItem = mVolumeItems.get(usage); 98 int groupId = carAudioManager.getVolumeGroupIdForUsage(zoneId, usage); 99 int min = carAudioManager.getGroupMinVolume(zoneId, groupId); 100 int max = carAudioManager.getGroupMaxVolume(zoneId, groupId); 101 int value = carAudioManager.getGroupVolume(zoneId, groupId); 102 int iconResId = volumeItem.getIcon(); 103 if (carAudioManager.isAudioFeatureEnabled(AUDIO_FEATURE_VOLUME_GROUP_MUTING) 104 && carAudioManager.isVolumeGroupMuted(zoneId, groupId)) { 105 iconResId = volumeItem.getMuteIcon(); 106 } 107 listBuilder.addRow(new QCRow.Builder() 108 .setTitle(getContext().getString(volumeItem.getTitle())) 109 .setIcon(showSliderWithIcon() 110 ? Icon.createWithResource(getContext(), iconResId) : null) 111 .addSlider(new QCSlider.Builder() 112 .setMin(min) 113 .setMax(max) 114 .setValue(value) 115 .setInputAction(createSliderAction(groupId)) 116 .setEnabled(!hasUmRestrictions && !hasDpmRestrictions 117 && isWritableForZone()) 118 .setClickableWhileDisabled(hasDpmRestrictions || isReadOnlyForZone) 119 .setDisabledClickAction(disabledPendingIntent) 120 .build() 121 ) 122 .build() 123 ); 124 } 125 return listBuilder.build(); 126 } 127 128 @Override onNotifyChange(Intent intent)129 void onNotifyChange(Intent intent) { 130 int value = intent.getIntExtra(QC_ACTION_SLIDER_VALUE, Integer.MIN_VALUE); 131 int groupId = intent.getIntExtra(EXTRA_GROUP_ID, Integer.MIN_VALUE); 132 if (value == Integer.MIN_VALUE || groupId == Integer.MIN_VALUE) { 133 return; 134 } 135 setGroupVolume(groupId, value); 136 } 137 138 /** 139 * The resource which lists the car volume resources associated with the various usage enums. 140 */ 141 @XmlRes carVolumeItemsXml()142 protected int carVolumeItemsXml() { 143 return R.xml.car_volume_items; 144 } 145 createSliderAction(int groupId)146 private PendingIntent createSliderAction(int groupId) { 147 Bundle extras = new Bundle(); 148 extras.putInt(EXTRA_GROUP_ID, groupId); 149 return getBroadcastIntent(extras, groupId); 150 } 151 setGroupVolume(int volumeGroupId, int newVolume)152 private void setGroupVolume(int volumeGroupId, int newVolume) { 153 CarAudioManager carAudioManager = getCarAudioManager(); 154 int zoneId = getMyAudioZoneId(); 155 if (carAudioManager == null || zoneId == CarAudioManager.INVALID_AUDIO_ZONE) { 156 return; 157 } 158 try { 159 carAudioManager.setGroupVolume(zoneId, volumeGroupId, newVolume, 160 QC_VOLUME_SELF_CHANGE); 161 } catch (CarNotConnectedException e) { 162 LOG.w("Ignoring volume change event because the car isn't connected", e); 163 } 164 } 165 showSliderWithIcon()166 protected boolean showSliderWithIcon() { 167 return true; // by default 168 } 169 getMyAudioZoneId()170 private int getMyAudioZoneId() { 171 return ((CarSettingsApplication) mContext.getApplicationContext()) 172 .getMyAudioZoneId(); 173 } 174 getCarAudioManager()175 private CarAudioManager getCarAudioManager() { 176 return ((CarSettingsApplication) mContext.getApplicationContext()) 177 .getCarAudioManager(); 178 } 179 } 180