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