1 /* 2 * Copyright (C) 2018 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.sound; 18 19 import android.content.Context; 20 import android.content.res.TypedArray; 21 import android.content.res.XmlResourceParser; 22 import android.media.AudioAttributes; 23 import android.util.AttributeSet; 24 import android.util.SparseArray; 25 import android.util.Xml; 26 27 import androidx.annotation.DrawableRes; 28 import androidx.annotation.StringRes; 29 import androidx.annotation.XmlRes; 30 31 import com.android.car.settings.R; 32 import com.android.car.settings.common.Logger; 33 34 import org.xmlpull.v1.XmlPullParserException; 35 36 import java.io.IOException; 37 38 /** 39 * Parses the xml file which specifies which Audio usages should be considered by sound settings. 40 */ 41 public class VolumeItemParser { 42 private static final Logger LOG = new Logger(VolumeItemParser.class); 43 44 private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; 45 private static final String XML_TAG_VOLUME_ITEM = "item"; 46 47 /** 48 * Parses the volume items listed in the xml resource provided. This is returned as a sparse 49 * array which is keyed by the rank (the order in which the volume item appears in the xml 50 * resrouce). 51 */ loadAudioUsageItems(Context context, @XmlRes int volumeItemsXml)52 public static SparseArray<VolumeItem> loadAudioUsageItems(Context context, 53 @XmlRes int volumeItemsXml) { 54 SparseArray<VolumeItem> volumeItems = new SparseArray<>(); 55 try (XmlResourceParser parser = context.getResources().getXml(volumeItemsXml)) { 56 AttributeSet attrs = Xml.asAttributeSet(parser); 57 int type; 58 // Traverse to the first start tag. 59 while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT 60 && type != XmlResourceParser.START_TAG) { 61 continue; 62 } 63 64 if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) { 65 throw new RuntimeException("Meta-data does not start with carVolumeItems tag"); 66 } 67 int outerDepth = parser.getDepth(); 68 int rank = 0; 69 while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT 70 && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) { 71 if (type == XmlResourceParser.END_TAG) { 72 continue; 73 } 74 if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { 75 TypedArray item = context.getResources().obtainAttributes( 76 attrs, R.styleable.carVolumeItems_item); 77 int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); 78 if (usage >= 0) { 79 volumeItems.put(usage, new VolumeItemParser.VolumeItem( 80 usage, rank, 81 item.getResourceId(R.styleable.carVolumeItems_item_titleText, 0), 82 item.getResourceId(R.styleable.carVolumeItems_item_icon, 0), 83 item.getResourceId(R.styleable.carVolumeItems_item_mute_icon, 0))); 84 rank++; 85 } 86 item.recycle(); 87 } 88 } 89 } catch (XmlPullParserException | IOException e) { 90 LOG.e("Error parsing volume groups configuration", e); 91 } 92 return volumeItems; 93 } 94 95 /** 96 * Wrapper class which contains information to render volume item on UI. 97 */ 98 public static class VolumeItem { 99 @AudioAttributes.AttributeUsage 100 private final int mUsage; 101 private final int mRank; 102 @StringRes 103 private final int mTitle; 104 @DrawableRes 105 private final int mIcon; 106 @DrawableRes 107 private final int mMuteIcon; 108 109 /** Constructs the VolumeItem container with the given values. */ VolumeItem(@udioAttributes.AttributeUsage int usage, int rank, @StringRes int title, @DrawableRes int icon, @DrawableRes int muteIcon)110 public VolumeItem(@AudioAttributes.AttributeUsage int usage, int rank, 111 @StringRes int title, @DrawableRes int icon, @DrawableRes int muteIcon) { 112 mUsage = usage; 113 mRank = rank; 114 mTitle = title; 115 mIcon = icon; 116 mMuteIcon = muteIcon; 117 } 118 119 /** 120 * Usage is used to represent what purpose the sound is used for. The values should be 121 * defined within AudioAttributes.USAGE_*. 122 */ getUsage()123 public int getUsage() { 124 return mUsage; 125 } 126 127 /** 128 * Rank represents the order in which the usage appears in 129 * {@link R.xml#car_volume_items}. This order is used to determine which title and icon 130 * should be used for each audio group. The lowest rank has the highest precedence. 131 */ getRank()132 public int getRank() { 133 return mRank; 134 } 135 136 /** Title which should be used for the seek bar preference. */ getTitle()137 public int getTitle() { 138 return mTitle; 139 } 140 141 /** Icon which should be used for the seek bar preference. */ getIcon()142 public int getIcon() { 143 return mIcon; 144 } 145 146 /** Icon which should be used for the seek bar preference when muted. */ getMuteIcon()147 public int getMuteIcon() { 148 return mMuteIcon; 149 } 150 } 151 } 152