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