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.util.Map; 46 47import com.google.android.collect.Maps; 48 49import static org.junit.Assert.fail; 50 51@SuppressWarnings("deprecation") // For Pair 52public 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 64 public LayoutLibTestCallback(ILogger logger) { 65 mLog = logger; 66 } 67 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 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 114 public String getNamespace() { 115 return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S, 116 PACKAGE_NAME); 117 } 118 119 @Override 120 public Pair<ResourceType, String> resolveResourceId(int id) { 121 return mProjectResources.get(id); 122 } 123 124 @Override 125 public String resolveResourceId(int[] id) { 126 return mStyleableValueToNameMap.get(new IntArrayWrapper(id)); 127 } 128 129 @Override 130 public Integer getResourceId(ResourceType type, String name) { 131 return mResources.get(type).get(name); 132 } 133 134 @Override 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 141 public ILayoutPullParser getParser(ResourceValue layoutResource) { 142 return new LayoutPullParser(new File(layoutResource.getValue())); 143 } 144 145 @Override 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 154 public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, 155 Object viewObject) { 156 return null; 157 } 158 159 @Override 160 public ActionBarCallback getActionBarCallback() { 161 return mActionBarCallback; 162 } 163 164 @Override 165 public boolean supports(int ideFeature) { 166 return false; 167 } 168 169 @NonNull 170 @Override 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