/* * Copyright (C) 2018 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.car.settings.sound; import android.content.Context; import android.content.res.TypedArray; import android.content.res.XmlResourceParser; import android.media.AudioAttributes; import android.util.AttributeSet; import android.util.SparseArray; import android.util.Xml; import androidx.annotation.DrawableRes; import androidx.annotation.StringRes; import androidx.annotation.XmlRes; import com.android.car.settings.R; import com.android.car.settings.common.Logger; import org.xmlpull.v1.XmlPullParserException; import java.io.IOException; /** * Parses the xml file which specifies which Audio usages should be considered by sound settings. */ public class VolumeItemParser { private static final Logger LOG = new Logger(VolumeItemParser.class); private static final String XML_TAG_VOLUME_ITEMS = "carVolumeItems"; private static final String XML_TAG_VOLUME_ITEM = "item"; /** * Parses the volume items listed in the xml resource provided. This is returned as a sparse * array which is keyed by the rank (the order in which the volume item appears in the xml * resrouce). */ public static SparseArray loadAudioUsageItems(Context context, @XmlRes int volumeItemsXml) { SparseArray volumeItems = new SparseArray<>(); try (XmlResourceParser parser = context.getResources().getXml(volumeItemsXml)) { AttributeSet attrs = Xml.asAttributeSet(parser); int type; // Traverse to the first start tag. while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT && type != XmlResourceParser.START_TAG) { continue; } if (!XML_TAG_VOLUME_ITEMS.equals(parser.getName())) { throw new RuntimeException("Meta-data does not start with carVolumeItems tag"); } int outerDepth = parser.getDepth(); int rank = 0; while ((type = parser.next()) != XmlResourceParser.END_DOCUMENT && (type != XmlResourceParser.END_TAG || parser.getDepth() > outerDepth)) { if (type == XmlResourceParser.END_TAG) { continue; } if (XML_TAG_VOLUME_ITEM.equals(parser.getName())) { TypedArray item = context.getResources().obtainAttributes( attrs, R.styleable.carVolumeItems_item); int usage = item.getInt(R.styleable.carVolumeItems_item_usage, -1); if (usage >= 0) { volumeItems.put(usage, new VolumeItemParser.VolumeItem( usage, rank, item.getResourceId(R.styleable.carVolumeItems_item_titleText, 0), item.getResourceId(R.styleable.carVolumeItems_item_icon, 0), item.getResourceId(R.styleable.carVolumeItems_item_mute_icon, 0))); rank++; } item.recycle(); } } } catch (XmlPullParserException | IOException e) { LOG.e("Error parsing volume groups configuration", e); } return volumeItems; } /** * Wrapper class which contains information to render volume item on UI. */ public static class VolumeItem { @AudioAttributes.AttributeUsage private final int mUsage; private final int mRank; @StringRes private final int mTitle; @DrawableRes private final int mIcon; @DrawableRes private final int mMuteIcon; /** Constructs the VolumeItem container with the given values. */ public VolumeItem(@AudioAttributes.AttributeUsage int usage, int rank, @StringRes int title, @DrawableRes int icon, @DrawableRes int muteIcon) { mUsage = usage; mRank = rank; mTitle = title; mIcon = icon; mMuteIcon = muteIcon; } /** * Usage is used to represent what purpose the sound is used for. The values should be * defined within AudioAttributes.USAGE_*. */ public int getUsage() { return mUsage; } /** * Rank represents the order in which the usage appears in * {@link R.xml#car_volume_items}. This order is used to determine which title and icon * should be used for each audio group. The lowest rank has the highest precedence. */ public int getRank() { return mRank; } /** Title which should be used for the seek bar preference. */ public int getTitle() { return mTitle; } /** Icon which should be used for the seek bar preference. */ public int getIcon() { return mIcon; } /** Icon which should be used for the seek bar preference when muted. */ public int getMuteIcon() { return mMuteIcon; } } }