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 final 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 getAdapterBinding(Object viewObject, Map<String, String> attributes)128 public AdapterBinding getAdapterBinding(Object viewObject, Map<String, String> attributes) { 129 return null; 130 } 131 132 @Override getActionBarCallback()133 public ActionBarCallback getActionBarCallback() { 134 return mActionBarCallback; 135 } 136 137 @Override 138 @Nullable createXmlParserForPsiFile(@onNull String fileName)139 public XmlPullParser createXmlParserForPsiFile(@NonNull String fileName) { 140 return createXmlParserForFile(fileName); 141 } 142 143 @Override 144 @Nullable createXmlParserForFile(@onNull String fileName)145 public XmlPullParser createXmlParserForFile(@NonNull String fileName) { 146 try (FileInputStream fileStream = new FileInputStream(fileName)) { 147 // Read data fully to memory to be able to close the file stream. 148 ByteArrayOutputStream byteOutputStream = new ByteArrayOutputStream(); 149 ByteStreams.copy(fileStream, byteOutputStream); 150 KXmlParser parser = new KXmlParser(); 151 parser.setInput(new ByteArrayInputStream(byteOutputStream.toByteArray()), null); 152 parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); 153 return parser; 154 } catch (IOException | XmlPullParserException e) { 155 return null; 156 } 157 } 158 159 @Override 160 @NonNull createXmlParser()161 public XmlPullParser createXmlParser() { 162 return new KXmlParser(); 163 } 164 165 @Override getApplicationId()166 public String getApplicationId() { 167 return mPackageName; 168 } 169 170 @Override getResourcePackage()171 public String getResourcePackage() { 172 return mPackageName; 173 } 174 175 @Override findClass(String name)176 public Class<?> findClass(String name) throws ClassNotFoundException { 177 return mModuleClassLoader.loadClass(name); 178 } 179 } 180