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