1 /* 2 * Copyright (C) 2014 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.SdkConstants; 20 import com.android.ide.common.rendering.api.ActionBarCallback; 21 import com.android.ide.common.rendering.api.AdapterBinding; 22 import com.android.ide.common.rendering.api.ILayoutPullParser; 23 import com.android.ide.common.rendering.api.LayoutlibCallback; 24 import com.android.ide.common.rendering.api.ParserFactory; 25 import com.android.ide.common.rendering.api.ResourceReference; 26 import com.android.ide.common.rendering.api.ResourceValue; 27 import com.android.ide.common.rendering.api.SessionParams.Key; 28 import com.android.ide.common.resources.IntArrayWrapper; 29 import com.android.layoutlib.bridge.android.RenderParamsFlags; 30 import com.android.resources.ResourceType; 31 import com.android.util.Pair; 32 import com.android.utils.ILogger; 33 34 import org.kxml2.io.KXmlParser; 35 import org.xmlpull.v1.XmlPullParser; 36 import org.xmlpull.v1.XmlPullParserException; 37 38 import android.annotation.NonNull; 39 import android.annotation.Nullable; 40 41 import java.io.File; 42 import java.lang.reflect.Constructor; 43 import java.lang.reflect.Field; 44 import java.lang.reflect.Modifier; 45 import java.util.Map; 46 47 import com.google.android.collect.Maps; 48 49 import static org.junit.Assert.fail; 50 51 @SuppressWarnings("deprecation") // For Pair 52 public class LayoutLibTestCallback extends LayoutlibCallback { 53 54 private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/"; 55 private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication"; 56 57 private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap(); 58 private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap(); 59 private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap(); 60 private final ILogger mLog; 61 private final ActionBarCallback mActionBarCallback = new ActionBarCallback(); 62 private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION); 63 LayoutLibTestCallback(ILogger logger)64 public LayoutLibTestCallback(ILogger logger) { 65 mLog = logger; 66 } 67 initResources()68 public void initResources() throws ClassNotFoundException { 69 Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R"); 70 Class<?>[] nestedClasses = rClass.getDeclaredClasses(); 71 for (Class<?> resClass : nestedClasses) { 72 final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName()); 73 74 if (resType != null) { 75 final Map<String, Integer> resName2Id = Maps.newHashMap(); 76 mResources.put(resType, resName2Id); 77 78 for (Field field : resClass.getDeclaredFields()) { 79 final int modifiers = field.getModifiers(); 80 if (Modifier.isStatic(modifiers)) { // May not be final in library projects 81 final Class<?> type = field.getType(); 82 try { 83 if (type.isArray() && type.getComponentType() == int.class) { 84 mStyleableValueToNameMap.put( 85 new IntArrayWrapper((int[]) field.get(null)), 86 field.getName()); 87 } else if (type == int.class) { 88 final Integer value = (Integer) field.get(null); 89 mProjectResources.put(value, Pair.of(resType, field.getName())); 90 resName2Id.put(field.getName(), value); 91 } else { 92 mLog.error(null, "Unknown field type in R class: %1$s", type); 93 } 94 } catch (IllegalAccessException ignored) { 95 mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R"); 96 } 97 } 98 } 99 } 100 } 101 } 102 103 104 @Override loadView(String name, Class[] constructorSignature, Object[] constructorArgs)105 public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) 106 throws Exception { 107 Class<?> viewClass = mModuleClassLoader.loadClass(name); 108 Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature); 109 viewConstructor.setAccessible(true); 110 return viewConstructor.newInstance(constructorArgs); 111 } 112 113 @Override getNamespace()114 public String getNamespace() { 115 return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S, 116 PACKAGE_NAME); 117 } 118 119 @Override resolveResourceId(int id)120 public Pair<ResourceType, String> resolveResourceId(int id) { 121 return mProjectResources.get(id); 122 } 123 124 @Override resolveResourceId(int[] id)125 public String resolveResourceId(int[] id) { 126 return mStyleableValueToNameMap.get(new IntArrayWrapper(id)); 127 } 128 129 @Override getResourceId(ResourceType type, String name)130 public Integer getResourceId(ResourceType type, String name) { 131 return mResources.get(type).get(name); 132 } 133 134 @Override getParser(String layoutName)135 public ILayoutPullParser getParser(String layoutName) { 136 fail("This method shouldn't be called by this version of LayoutLib."); 137 return null; 138 } 139 140 @Override getParser(ResourceValue layoutResource)141 public ILayoutPullParser getParser(ResourceValue layoutResource) { 142 return new LayoutPullParser(new File(layoutResource.getValue())); 143 } 144 145 @Override getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int positionPerType, int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)146 public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, 147 ResourceReference itemRef, int fullPosition, int positionPerType, 148 int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, 149 ViewAttribute viewAttribute, Object defaultValue) { 150 return null; 151 } 152 153 @Override getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, Object viewObject)154 public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, 155 Object viewObject) { 156 return null; 157 } 158 159 @Override getActionBarCallback()160 public ActionBarCallback getActionBarCallback() { 161 return mActionBarCallback; 162 } 163 164 @Override supports(int ideFeature)165 public boolean supports(int ideFeature) { 166 return false; 167 } 168 169 @NonNull 170 @Override getParserFactory()171 public ParserFactory getParserFactory() { 172 return new ParserFactory() { 173 @NonNull 174 @Override 175 public XmlPullParser createParser(@Nullable String debugName) 176 throws XmlPullParserException { 177 return new KXmlParser(); 178 } 179 }; 180 } 181 182 @Override 183 public <T> T getFlag(Key<T> key) { 184 if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) { 185 return (T) PACKAGE_NAME; 186 } 187 return null; 188 } 189 } 190