1 /* 2 * Copyright (C) 2020 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 package com.android.car.uxr; 17 18 import android.content.Context; 19 import android.content.res.TypedArray; 20 import android.content.res.XmlResourceParser; 21 import android.util.AttributeSet; 22 import android.util.Log; 23 import android.util.Xml; 24 import android.view.View; 25 26 import androidx.annotation.XmlRes; 27 28 import com.android.car.uxr.CarUxRestrictionsAppConfig.ListConfig; 29 30 import org.xmlpull.v1.XmlPullParser; 31 import org.xmlpull.v1.XmlPullParserException; 32 33 import java.io.IOException; 34 import java.util.HashMap; 35 import java.util.Map; 36 37 /** 38 * A parser that can read an XML resource file and construct the corresponding 39 * {@link CarUxRestrictionsAppConfig} object. 40 * 41 * See car-uxr-client-lib/res/values/attrs.xml for the definition of the relevant XML tags. 42 */ 43 public class CarUxRestrictionsAppConfigParser { 44 private static final String TAG = "UxrAppConfigParser"; 45 parseConfig(Context context, @XmlRes int xmlRes)46 static CarUxRestrictionsAppConfig parseConfig(Context context, @XmlRes int xmlRes) { 47 try (XmlResourceParser parser = context.getResources().getXml(xmlRes)) { 48 AttributeSet attrs = Xml.asAttributeSet(parser); 49 Map<Integer, ListConfig> mapping = new HashMap<>(); 50 51 // Skip over the xml version tag 52 parser.next(); 53 // Skip over the copyright comment block 54 parser.next(); 55 parser.require(XmlPullParser.START_TAG, null, "Mapping"); 56 while (parser.next() != XmlPullParser.END_TAG) { 57 ListConfig listConfig = parseListConfigItem(context, parser, attrs); 58 mapping.put(listConfig.getId(), listConfig); 59 } 60 61 return new CarUxRestrictionsAppConfig(mapping); 62 } catch (XmlPullParserException | IOException e) { 63 throw new RuntimeException("Unable to parse CarUxRestrictionsAppConfig", e); 64 } 65 } 66 parseListConfigItem( Context context, XmlResourceParser parser, AttributeSet attrs)67 private static ListConfig parseListConfigItem( 68 Context context, XmlResourceParser parser, AttributeSet attrs) 69 throws XmlPullParserException, IOException { 70 71 parser.require(XmlPullParser.START_TAG, null, "ListConfig"); 72 73 TypedArray a = context.obtainStyledAttributes( 74 attrs, R.styleable.CarUxRestrictionsAppConfig_ListConfig); 75 76 try { 77 int id = a.getResourceId(R.styleable.CarUxRestrictionsAppConfig_ListConfig_id, 78 View.NO_ID); 79 if (id == View.NO_ID) { 80 throw new IllegalStateException("Id field is required"); 81 } 82 83 boolean messageExists = a.hasValue( 84 R.styleable.CarUxRestrictionsAppConfig_ListConfig_message); 85 int messageResId = a.getResourceId( 86 R.styleable.CarUxRestrictionsAppConfig_ListConfig_message, View.NO_ID); 87 if (Log.isLoggable(TAG, Log.DEBUG)) { 88 Log.d(TAG, messageExists 89 ? "message field is set to " + messageResId 90 : "message field not specified"); 91 } 92 93 boolean maxLengthExists = a.hasValue( 94 R.styleable.CarUxRestrictionsAppConfig_ListConfig_maxLength); 95 int maxLengthInt = a.getInt( 96 R.styleable.CarUxRestrictionsAppConfig_ListConfig_maxLength, 0); 97 if (Log.isLoggable(TAG, Log.DEBUG)) { 98 Log.d(TAG, maxLengthExists 99 ? "maxLength field is set to " + maxLengthInt 100 : "maxLength field not specified"); 101 } 102 103 parser.next(); 104 parser.require(XmlPullParser.END_TAG, null, "ListConfig"); 105 106 ListConfig.Builder builder = ListConfig.builder(id); 107 if (maxLengthExists) { 108 builder.setContentLimit(maxLengthInt); 109 } 110 if (messageExists) { 111 builder.setScrollingLimitedMessageResId(messageResId); 112 } 113 return builder.build(); 114 } finally { 115 a.recycle(); 116 } 117 } 118 } 119