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