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