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.ActionBarCallback; 20 import com.android.ide.common.rendering.api.AdapterBinding; 21 import com.android.ide.common.rendering.api.ILayoutPullParser; 22 import com.android.ide.common.rendering.api.LayoutlibCallback; 23 import com.android.ide.common.rendering.api.ResourceReference; 24 import com.android.ide.common.rendering.api.ResourceValue; 25 import com.android.resources.ResourceType; 26 import com.android.utils.ILogger; 27 28 import org.kxml2.io.KXmlParser; 29 import org.xmlpull.v1.XmlPullParser; 30 import org.xmlpull.v1.XmlPullParserException; 31 32 import android.annotation.NonNull; 33 import android.annotation.Nullable; 34 35 import java.io.ByteArrayInputStream; 36 import java.io.ByteArrayOutputStream; 37 import java.io.File; 38 import java.io.FileInputStream; 39 import java.io.FileNotFoundException; 40 import java.io.IOException; 41 import java.lang.reflect.Constructor; 42 import java.lang.reflect.Field; 43 import java.lang.reflect.Modifier; 44 import java.util.HashMap; 45 import java.util.Map; 46 47 import com.google.common.io.ByteStreams; 48 49 import static com.android.ide.common.rendering.api.ResourceNamespace.RES_AUTO; 50 51 public class LayoutLibTestCallback extends LayoutlibCallback { 52 private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication"; 53 54 private final Map<Integer, ResourceReference> mProjectResources = new HashMap<>(); 55 private final Map<ResourceReference, Integer> mResources = new HashMap<>(); 56 private final ILogger mLog; 57 private final ActionBarCallback mActionBarCallback = new ActionBarCallback(); 58 private final ClassLoader mModuleClassLoader; 59 private String mAdaptiveIconMaskPath; 60 LayoutLibTestCallback(ILogger logger, ClassLoader classLoader)61 public LayoutLibTestCallback(ILogger logger, ClassLoader classLoader) { 62 mLog = logger; 63 mModuleClassLoader = classLoader; 64 } 65 initResources()66 public void initResources() throws ClassNotFoundException { 67 Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R"); 68 Class<?>[] nestedClasses = rClass.getDeclaredClasses(); 69 for (Class<?> resClass : nestedClasses) { 70 final ResourceType resType = ResourceType.fromClassName(resClass.getSimpleName()); 71 72 if (resType != null) { 73 for (Field field : resClass.getDeclaredFields()) { 74 final int modifiers = field.getModifiers(); 75 if (Modifier.isStatic(modifiers)) { // May not be final in library projects 76 final Class<?> type = field.getType(); 77 try { 78 if (type == int.class) { 79 final Integer value = (Integer) field.get(null); 80 ResourceReference reference = 81 new ResourceReference(RES_AUTO, resType, field.getName()); 82 mProjectResources.put(value, reference); 83 mResources.put(reference, value); 84 } else if (!(type.isArray() && type.getComponentType() == int.class)) { 85 mLog.error(null, "Unknown field type in R class: %1$s", type); 86 } 87 } catch (IllegalAccessException e) { 88 mLog.error(e, "Malformed R class: %1$s", PACKAGE_NAME + ".R"); 89 } 90 } 91 } 92 } 93 } 94 } 95 96 97 @Override loadView(@onNull String name, @NonNull Class[] constructorSignature, Object[] constructorArgs)98 public Object loadView(@NonNull String name, @NonNull Class[] constructorSignature, Object[] constructorArgs) 99 throws Exception { 100 Class<?> viewClass = mModuleClassLoader.loadClass(name); 101 Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature); 102 viewConstructor.setAccessible(true); 103 return viewConstructor.newInstance(constructorArgs); 104 } 105 106 @Override resolveResourceId(int id)107 public ResourceReference resolveResourceId(int id) { 108 return mProjectResources.get(id); 109 } 110 111 @Override getOrGenerateResourceId(@onNull ResourceReference resource)112 public int getOrGenerateResourceId(@NonNull ResourceReference resource) { 113 Integer id = mResources.get(resource); 114 return id != null ? id : 0; 115 } 116 117 @Override getParser(@onNull ResourceValue layoutResource)118 public ILayoutPullParser getParser(@NonNull ResourceValue layoutResource) { 119 try { 120 return LayoutPullParser.createFromFile(new File(layoutResource.getValue())); 121 } catch (FileNotFoundException e) { 122 return null; 123 } 124 } 125 126 @Override getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int positionPerType, int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)127 public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, 128 ResourceReference itemRef, int fullPosition, int positionPerType, 129 int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, 130 ViewAttribute viewAttribute, Object defaultValue) { 131 return null; 132 } 133 134 @Override getAdapterBinding(Object viewObject, Map<String, String> attributes)135 public AdapterBinding getAdapterBinding(Object viewObject, Map<String, String> attributes) { 136 return null; 137 } 138 139 @Override getActionBarCallback()140 public ActionBarCallback getActionBarCallback() { 141 return mActionBarCallback; 142 } 143 144 @Override 145 @Nullable createXmlParserForPsiFile(@onNull String fileName)146 public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { 147 return createXmlParserForFile(fileName); 148 } 149 150 @Override 151 @Nullable createXmlParserForFile(@onNull String fileName)152 public XmlPullParser createXmlParserForFile(@NonNull String fileName) { 153 try (FileInputStream fileStream = new FileInputStream(fileName)) { 154 // Read data fully to memory to be able to close the file stream. 155 ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); 156 ByteStreams.copy(fileStream, byteOutputStream); 157 KXmlParser parser = new KXmlParser(); 158 parser.setInput(new ByteArrayInputStream(byteOutputStream.toByteArray()), null); 159 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 160 return parser; 161 } catch (IOException | XmlPullParserException e) { 162 return null; 163 } 164 } 165 166 @Override 167 @NonNull createXmlParser()168 public XmlPullParser createXmlParser() { 169 return new KXmlParser(); 170 } 171 172 @Override getApplicationId()173 public String getApplicationId() { 174 return PACKAGE_NAME; 175 } 176 177 @Override getResourcePackage()178 public String getResourcePackage() { 179 return PACKAGE_NAME; 180 } 181 182 @Override findClass(String name)183 public Class<?> findClass(String name) throws ClassNotFoundException { 184 return mModuleClassLoader.loadClass(name); 185 } 186 } 187