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