• 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.lang.reflect.Constructor;
43 import java.lang.reflect.Field;
44 import java.lang.reflect.Modifier;
45 import java.util.Map;
46 
47 import com.google.android.collect.Maps;
48 
49 import static org.junit.Assert.fail;
50 
51 @SuppressWarnings("deprecation") // For Pair
52 public class LayoutLibTestCallback extends LayoutlibCallback {
53 
54     private static final String PROJECT_CLASSES_LOCATION = "/testApp/MyApplication/build/intermediates/classes/debug/";
55     private static final String PACKAGE_NAME = "com.android.layoutlib.test.myapplication";
56 
57     private final Map<Integer, Pair<ResourceType, String>> mProjectResources = Maps.newHashMap();
58     private final Map<IntArrayWrapper, String> mStyleableValueToNameMap = Maps.newHashMap();
59     private final Map<ResourceType, Map<String, Integer>> mResources = Maps.newHashMap();
60     private final ILogger mLog;
61     private final ActionBarCallback mActionBarCallback = new ActionBarCallback();
62     private final ClassLoader mModuleClassLoader = new ModuleClassLoader(PROJECT_CLASSES_LOCATION);
63 
LayoutLibTestCallback(ILogger logger)64     public LayoutLibTestCallback(ILogger logger) {
65         mLog = logger;
66     }
67 
initResources()68     public void initResources() throws ClassNotFoundException {
69         Class<?> rClass = mModuleClassLoader.loadClass(PACKAGE_NAME + ".R");
70         Class<?>[] nestedClasses = rClass.getDeclaredClasses();
71         for (Class<?> resClass : nestedClasses) {
72             final ResourceType resType = ResourceType.getEnum(resClass.getSimpleName());
73 
74             if (resType != null) {
75                 final Map<String, Integer> resName2Id = Maps.newHashMap();
76                 mResources.put(resType, resName2Id);
77 
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.isArray() && type.getComponentType() == int.class) {
84                                 mStyleableValueToNameMap.put(
85                                         new IntArrayWrapper((int[]) field.get(null)),
86                                         field.getName());
87                             } else if (type == int.class) {
88                                 final Integer value = (Integer) field.get(null);
89                                 mProjectResources.put(value, Pair.of(resType, field.getName()));
90                                 resName2Id.put(field.getName(), value);
91                             } else {
92                                 mLog.error(null, "Unknown field type in R class: %1$s", type);
93                             }
94                         } catch (IllegalAccessException ignored) {
95                             mLog.error(ignored, "Malformed R class: %1$s", PACKAGE_NAME + ".R");
96                         }
97                     }
98                 }
99             }
100         }
101     }
102 
103 
104     @Override
loadView(String name, Class[] constructorSignature, Object[] constructorArgs)105     public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
106             throws Exception {
107         Class<?> viewClass = mModuleClassLoader.loadClass(name);
108         Constructor<?> viewConstructor = viewClass.getConstructor(constructorSignature);
109         viewConstructor.setAccessible(true);
110         return viewConstructor.newInstance(constructorArgs);
111     }
112 
113     @Override
getNamespace()114     public String getNamespace() {
115         return String.format(SdkConstants.NS_CUSTOM_RESOURCES_S,
116                 PACKAGE_NAME);
117     }
118 
119     @Override
resolveResourceId(int id)120     public Pair<ResourceType, String> resolveResourceId(int id) {
121         return mProjectResources.get(id);
122     }
123 
124     @Override
resolveResourceId(int[] id)125     public String resolveResourceId(int[] id) {
126         return mStyleableValueToNameMap.get(new IntArrayWrapper(id));
127     }
128 
129     @Override
getResourceId(ResourceType type, String name)130     public Integer getResourceId(ResourceType type, String name) {
131         return mResources.get(type).get(name);
132     }
133 
134     @Override
getParser(String layoutName)135     public ILayoutPullParser getParser(String layoutName) {
136         fail("This method shouldn't be called by this version of LayoutLib.");
137         return null;
138     }
139 
140     @Override
getParser(ResourceValue layoutResource)141     public ILayoutPullParser getParser(ResourceValue layoutResource) {
142         return new LayoutPullParser(new File(layoutResource.getValue()));
143     }
144 
145     @Override
getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int positionPerType, int fullParentPosition, int parentPositionPerType, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)146     public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
147             ResourceReference itemRef, int fullPosition, int positionPerType,
148             int fullParentPosition, int parentPositionPerType, ResourceReference viewRef,
149             ViewAttribute viewAttribute, Object defaultValue) {
150         return null;
151     }
152 
153     @Override
getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie, Object viewObject)154     public AdapterBinding getAdapterBinding(ResourceReference adapterViewRef, Object adapterCookie,
155             Object viewObject) {
156         return null;
157     }
158 
159     @Override
getActionBarCallback()160     public ActionBarCallback getActionBarCallback() {
161         return mActionBarCallback;
162     }
163 
164     @Override
supports(int ideFeature)165     public boolean supports(int ideFeature) {
166         return false;
167     }
168 
169     @NonNull
170     @Override
getParserFactory()171     public ParserFactory getParserFactory() {
172         return new ParserFactory() {
173             @NonNull
174             @Override
175             public XmlPullParser createParser(@Nullable String debugName)
176                     throws XmlPullParserException {
177                 return new KXmlParser();
178             }
179         };
180     }
181 
182     @Override
183     public <T> T getFlag(Key<T> key) {
184         if (key.equals(RenderParamsFlags.FLAG_KEY_APPLICATION_PACKAGE)) {
185             return (T) PACKAGE_NAME;
186         }
187         return null;
188     }
189 }
190