• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ParserFactory;
25 import com.android.ide.common.rendering.api.ResourceReference;
26 import com.android.ide.common.rendering.api.ResourceValue;
27 import com.android.ide.common.rendering.api.SessionParams.Key;
28 import com.android.ide.common.resources.IntArrayWrapper;
29 import com.android.layoutlib.bridge.android.RenderParamsFlags;
30 import com.android.resources.ResourceType;
31 import com.android.util.Pair;
32 import com.android.utils.ILogger;
33 
34 import org.kxml2.io.KXmlParser;
35 import org.xmlpull.v1.XmlPullParser;
36 import org.xmlpull.v1.XmlPullParserException;
37 
38 import android.annotation.NonNull;
39 import android.annotation.Nullable;
40 
41 import java.io.File;
42 import java.io.FileNotFoundException;
43 import java.lang.reflect.Constructor;
44 import java.lang.reflect.Field;
45 import java.lang.reflect.Modifier;
46 import java.util.Map;
47 
48 import com.google.android.collect.Maps;
49 
50 import static org.junit.Assert.fail;
51 
52 @SuppressWarnings("deprecation") // For Pair
53 public class LayoutLibTestCallback extends LayoutlibCallback {
54 
55     private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
56     private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
57 
58     private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
59     private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
60     private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
61     private final ILogger mLog;
62     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
63     private final ClassLoader mModuleClassLoader;
64     private String mAdaptiveIconMaskPath;
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.getEnum(resClass.getSimpleName());
76 
77             if (resType != null) {
78                 final Map<String, Integer> resName2Id = Maps.newHashMap();
79                 mResources.put(resType, resName2Id);
80 
81                 for (Field field : resClass.getDeclaredFields()) {
82                     final int modifiers = field.getModifiers();
83                     if (Modifier.isStatic(modifiers)) { // May not be final in library projects
84                         final Class<?> type = field.getType();
85                         try {
86                             if (type.isArray() && type.getComponentType() == int.class) {
87                                 mStyleableValueToNameMap.put(
88                                         new IntArrayWrapper((int[]) field.get(null)),
89                                         field.getName());
90                             } else if (type == int.class) {
91                                 final Integer value = (Integer) field.get(null);
92                                 mProjectResources.put(value, Pair.of(resType, field.getName()));
93                                 resName2Id.put(field.getName(), value);
94                             } else {
95                                 mLog.error(null, "Unknown field type in R class: %1$s", type);
96                             }
97                         } catch (IllegalAccessException ignored) {
98                             mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
99                         }
100                     }
101                 }
102             }
103         }
104     }
105 
106 
107     @Override
loadView(String name, Class[] constructorSignature, Object[] constructorArgs)108     public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
109             throws Exception {
110         Class<?> viewClass = mModuleClassLoader.loadClass(name);
111         Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
112         viewConstructor.setAccessible(true);
113         return viewConstructor.newInstance(constructorArgs);
114     }
115 
116     @Override
getNamespace()117     public String getNamespace() {
118         return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
119                 PACKAGE_NAME);
120     }
121 
122     @Override
resolveResourceId(int id)123     public Pair<ResourceType, String> resolveResourceId(int id) {
124         return mProjectResources.get(id);
125     }
126 
127     @Override
resolveResourceId(int[] id)128     public String resolveResourceId(int[] id) {
129         return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
130     }
131 
132     @Override
getResourceId(ResourceType type, String name)133     public Integer getResourceId(ResourceType type, String name) {
134         Map<String, Integer> resName2Id = mResources.get(type);
135         if (resName2Id == null) {
136             return null;
137         }
138         return resName2Id.get(name);
139     }
140 
141     @Override
getParser(String layoutName)142     public ILayoutPullParser getParser(String layoutName) {
143         fail("This method shouldn't be called by this version of LayoutLib.");
144         return null;
145     }
146 
147     @Override
getParser(ResourceValue layoutResource)148     public ILayoutPullParser getParser(ResourceValue layoutResource) {
149         try {
150             return LayoutPullParser.createFromFile(new File(layoutResource.getValue()));
151         } catch (FileNotFoundException e) {
152             return null;
153         }
154     }
155 
156     @Override
getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int positionPerType, int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)157     public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
158             ResourceReference itemRef, int fullPosition, int positionPerType,
159             int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
160             ViewAttribute viewAttribute, Object defaultValue) {
161         return null;
162     }
163 
164     @Override
getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, Object viewObject)165     public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
166             Object viewObject) {
167         return null;
168     }
169 
170     @Override
getActionBarCallback()171     public ActionBarCallback getActionBarCallback() {
172         return mActionBarCallback;
173     }
174 
175     @Override
supports(int ideFeature)176     public boolean supports(int ideFeature) {
177         return false;
178     }
179 
180     @NonNull
181     @Override
getParserFactory()182     public ParserFactory getParserFactory() {
183         return new ParserFactory() {
184             @NonNull
185             @Override
186             public XmlPullParser createParser(@Nullable String debugName)
187                     throws XmlPullParserException {
188                 return new KXmlParser();
189             }
190         };
191     }
192 
193     @Override
194     public <T> T getFlag(Key<T> key) {
195         if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
196             return (T) PACKAGE_NAME;
197         }
198         if (key.equals(RenderParamsFlags.FLAG_KEY_ADAPTIVE_ICON_MASK_PATH)) {
199             return (T) mAdaptiveIconMaskPath;
200         }
201         return null;
202     }
203 
204     public void setAdaptiveIconMaskPath(String adaptiveIconMaskPath) {
205         mAdaptiveIconMaskPath = adaptiveIconMaskPath;
206     }
207 }
208