1 /* 2 * Copyright (C) 2022 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.layoutlib.bridge.intensive.setup; 18 19 import com.android.ide.common.rendering.api.ILayoutPullParser; 20 import com.android.ide.common.rendering.api.ResourceNamespace; 21 22 import org.kxml2.io.KXmlParser; 23 import org.xmlpull.v1.XmlPullParserException; 24 25 import android.annotation.NonNull; 26 27 import java.io.ByteArrayInputStream; 28 import java.io.File; 29 import java.io.FileInputStream; 30 import java.io.FileNotFoundException; 31 import java.io.IOError; 32 import java.io.InputStream; 33 import java.nio.charset.StandardCharsets; 34 import java.util.HashMap; 35 import java.util.Map; 36 37 import static com.android.SdkConstants.ATTR_IGNORE; 38 import static com.android.SdkConstants.EXPANDABLE_LIST_VIEW; 39 import static com.android.SdkConstants.GRID_VIEW; 40 import static com.android.SdkConstants.LIST_VIEW; 41 import static com.android.SdkConstants.SPINNER; 42 import static com.android.SdkConstants.TOOLS_URI; 43 44 public class LayoutPullParser extends KXmlParser implements ILayoutPullParser{ 45 46 private ResourceNamespace mLayoutNamespace = ResourceNamespace.RES_AUTO; 47 48 @NonNull createFromFile(@onNull File layoutFile)49 public static LayoutPullParser createFromFile(@NonNull File layoutFile) 50 throws FileNotFoundException { 51 return new LayoutPullParser(new FileInputStream(layoutFile)); 52 } 53 54 /** 55 * @param layoutPath Must start with '/' and be relative to test resources. 56 */ 57 @NonNull createFromPath(@onNull String layoutPath)58 public static LayoutPullParser createFromPath(@NonNull String layoutPath) { 59 if (layoutPath.startsWith("/")) { 60 layoutPath = layoutPath.substring(1); 61 } 62 63 return new LayoutPullParser(LayoutPullParser.class.getClassLoader().getResourceAsStream 64 (layoutPath)); 65 } 66 67 @NonNull createFromString(@onNull String contents)68 public static LayoutPullParser createFromString(@NonNull String contents) { 69 return new LayoutPullParser(new ByteArrayInputStream( 70 contents.getBytes(StandardCharsets.UTF_8))); 71 } 72 LayoutPullParser(@onNull InputStream inputStream)73 private LayoutPullParser(@NonNull InputStream inputStream) { 74 try { 75 setFeature(FEATURE_PROCESS_NAMESPACES, true); 76 setInput(inputStream, null); 77 } catch (XmlPullParserException e) { 78 throw new IOError(e); 79 } 80 } 81 82 @Override getViewCookie()83 public Object getViewCookie() { 84 // TODO: Implement this properly. 85 String name = super.getName(); 86 if (name == null) { 87 return null; 88 } 89 90 // Store tools attributes if this looks like a layout we'll need adapter view 91 // bindings for in the LayoutlibCallback. 92 if (LIST_VIEW.equals(name) || EXPANDABLE_LIST_VIEW.equals(name) || GRID_VIEW.equals(name) || SPINNER.equals(name)) { 93 Map<String, String> map = null; 94 int count = getAttributeCount(); 95 for (int i = 0; i < count; i++) { 96 String namespace = getAttributeNamespace(i); 97 if (namespace != null && namespace.equals(TOOLS_URI)) { 98 String attribute = getAttributeName(i); 99 if (attribute.equals(ATTR_IGNORE)) { 100 continue; 101 } 102 if (map == null) { 103 map = new HashMap<>(4); 104 } 105 map.put(attribute, getAttributeValue(i)); 106 } 107 } 108 109 return map; 110 } 111 112 return null; 113 } 114 115 @Override 116 @NonNull getLayoutNamespace()117 public ResourceNamespace getLayoutNamespace() { 118 return mLayoutNamespace; 119 } 120 setLayoutNamespace(@onNull ResourceNamespace layoutNamespace)121 public void setLayoutNamespace(@NonNull ResourceNamespace layoutNamespace) { 122 mLayoutNamespace = layoutNamespace; 123 } 124 } 125