Home | History | Annotate | Download | only in layoutRendering
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.0 (the "License"); you
      5  * may not use this file except in compliance with the License. You may obtain a
      6  * copy of the License at
      7  *
      8  *      http://www.eclipse.org/org/documents/epl-v10.php
      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, WITHOUT
     12  * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
     13  * License for the specific language governing permissions and limitations under
     14  * the License.
     15  */
     16 
     17 package com.android.ide.eclipse.tests.functests.layoutRendering;
     18 
     19 import com.android.SdkConstants;
     20 import com.android.ide.common.rendering.LayoutLibrary;
     21 import com.android.ide.common.rendering.api.ActionBarCallback;
     22 import com.android.ide.common.rendering.api.AdapterBinding;
     23 import com.android.ide.common.rendering.api.HardwareConfig;
     24 import com.android.ide.common.rendering.api.ILayoutPullParser;
     25 import com.android.ide.common.rendering.api.IProjectCallback;
     26 import com.android.ide.common.rendering.api.RenderSession;
     27 import com.android.ide.common.rendering.api.ResourceReference;
     28 import com.android.ide.common.rendering.api.ResourceValue;
     29 import com.android.ide.common.rendering.api.SessionParams;
     30 import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
     31 import com.android.ide.common.resources.ResourceItem;
     32 import com.android.ide.common.resources.ResourceRepository;
     33 import com.android.ide.common.resources.ResourceResolver;
     34 import com.android.ide.common.resources.configuration.DensityQualifier;
     35 import com.android.ide.common.resources.configuration.FolderConfiguration;
     36 import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
     37 import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
     38 import com.android.ide.common.resources.configuration.NavigationStateQualifier;
     39 import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
     40 import com.android.ide.common.resources.configuration.ScreenHeightQualifier;
     41 import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
     42 import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
     43 import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
     44 import com.android.ide.common.resources.configuration.ScreenWidthQualifier;
     45 import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier;
     46 import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
     47 import com.android.ide.common.resources.configuration.TouchScreenQualifier;
     48 import com.android.ide.common.sdk.LoadStatus;
     49 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
     50 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
     51 import com.android.ide.eclipse.tests.SdkLoadingTestCase;
     52 import com.android.io.FolderWrapper;
     53 import com.android.resources.Density;
     54 import com.android.resources.Keyboard;
     55 import com.android.resources.KeyboardState;
     56 import com.android.resources.Navigation;
     57 import com.android.resources.NavigationState;
     58 import com.android.resources.ResourceType;
     59 import com.android.resources.ScreenOrientation;
     60 import com.android.resources.ScreenRatio;
     61 import com.android.resources.ScreenSize;
     62 import com.android.resources.TouchScreen;
     63 import com.android.sdklib.IAndroidTarget;
     64 import com.android.util.Pair;
     65 
     66 import org.kxml2.io.KXmlParser;
     67 import org.xmlpull.v1.XmlPullParser;
     68 import org.xmlpull.v1.XmlPullParserException;
     69 
     70 import java.io.File;
     71 import java.io.FileReader;
     72 import java.io.IOException;
     73 import java.util.HashMap;
     74 import java.util.Map;
     75 
     76 import javax.imageio.ImageIO;
     77 
     78 public class ApiDemosRenderingTest extends SdkLoadingTestCase {
     79 
     80     /**
     81      * Custom parser that implements {@link ILayoutPullParser} (which itself extends
     82      * {@link XmlPullParser}).
     83      */
     84     private final static class TestParser extends KXmlParser implements ILayoutPullParser {
     85         /**
     86          * Since we're not going to go through the result of the rendering/layout, we can return
     87          * null for the View Key.
     88          */
     89         @Override
     90         public Object getViewCookie() {
     91             return null;
     92         }
     93 
     94         @Override
     95         public ILayoutPullParser getParser(String layoutName) {
     96             return null;
     97         }
     98     }
     99 
    100     private final static class ProjectCallBack implements IProjectCallback {
    101         // resource id counter.
    102         // We start at 0x7f000000 to avoid colliding with the framework id
    103         // since we have no access to the project R.java and we need to generate them automatically.
    104         private int mIdCounter = 0x7f000000;
    105 
    106         // in some cases, the id that getResourceValue(String type, String name) returns
    107         // will be sent back to get the type/name. This map stores the id/type/name we generate
    108         // to be able to do the reverse resolution.
    109         private Map<Integer, Pair<ResourceType, String>> mResourceMap =
    110             new HashMap<Integer, Pair<ResourceType, String>>();
    111 
    112         private boolean mCustomViewAttempt = false;
    113 
    114         @Override
    115         public String getNamespace() {
    116             // TODO: read from the ApiDemos manifest.
    117             return "com.example.android.apis";
    118         }
    119 
    120         @Override
    121         @SuppressWarnings("unchecked")
    122         public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
    123                 throws ClassNotFoundException, Exception {
    124             mCustomViewAttempt = true;
    125             return null;
    126         }
    127 
    128         @Override
    129         public Integer getResourceId(ResourceType type, String name) {
    130             Integer result = ++mIdCounter;
    131             mResourceMap.put(result, Pair.of(type, name));
    132             return result;
    133         }
    134 
    135         @Override
    136         public Pair<ResourceType, String> resolveResourceId(int id) {
    137             return mResourceMap.get(id);
    138         }
    139 
    140         @Override
    141         public String resolveResourceId(int[] id) {
    142             return null;
    143         }
    144 
    145         @Override
    146         public ILayoutPullParser getParser(String layoutName) {
    147             return null;
    148         }
    149 
    150         @Override
    151         public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie,
    152                 ResourceReference itemRef, int fullPosition, int typePosition,
    153                 int fullChildPosition, int typeChildPosition,
    154                 ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) {
    155             return null;
    156         }
    157 
    158         @Override
    159         public AdapterBinding getAdapterBinding(ResourceReference adapterView,
    160                 Object adapterCookie, Object viewObject) {
    161             return null;
    162         }
    163 
    164         @Override
    165         public ILayoutPullParser getParser(ResourceValue layoutResource) {
    166             return null;
    167         }
    168 
    169         @Override
    170         public ActionBarCallback getActionBarCallback() {
    171             return new ActionBarCallback();
    172         }
    173     }
    174 
    175     public void testApiDemos() throws IOException, XmlPullParserException {
    176         findApiDemos();
    177     }
    178 
    179     private void findApiDemos() throws IOException, XmlPullParserException {
    180         IAndroidTarget[] targets = getSdk().getTargets();
    181 
    182         for (IAndroidTarget target : targets) {
    183             String path = target.getPath(IAndroidTarget.SAMPLES);
    184             File samples = new File(path);
    185             if (samples.isDirectory()) {
    186                 File[] files = samples.listFiles();
    187                 for (File file : files) {
    188                     if ("apidemos".equalsIgnoreCase(file.getName())) {
    189                         testSample(target, file);
    190                         return;
    191                     }
    192                 }
    193             }
    194         }
    195 
    196         fail("Failed to find ApiDemos!");
    197     }
    198 
    199     private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
    200         AndroidTargetData data = getSdk().getTargetData(target);
    201         if (data == null) {
    202             fail("No AndroidData!");
    203         }
    204 
    205         LayoutLibrary layoutLib = data.getLayoutLibrary();
    206         if (layoutLib.getStatus() != LoadStatus.LOADED) {
    207             fail("Fail to load the bridge: " + layoutLib.getLoadMessage());
    208         }
    209 
    210         FolderWrapper resFolder = new FolderWrapper(sampleProject, SdkConstants.FD_RES);
    211         if (resFolder.exists() == false) {
    212             fail("Sample project has no res folder!");
    213         }
    214 
    215         // look for the layout folder
    216         File layoutFolder = new File(resFolder, SdkConstants.FD_RES_LAYOUT);
    217         if (layoutFolder.isDirectory() == false) {
    218             fail("Sample project has no layout folder!");
    219         }
    220 
    221         // first load the project's target framework resource
    222         ResourceRepository framework = ResourceManager.getInstance().loadFrameworkResources(target);
    223 
    224         // now load the project resources
    225         ResourceRepository project = new ResourceRepository(resFolder, false) {
    226             @Override
    227             protected ResourceItem createResourceItem(String name) {
    228                 return new ResourceItem(name);
    229             }
    230 
    231         };
    232 
    233         // Create a folder configuration that will be used for the rendering:
    234         FolderConfiguration config = getConfiguration();
    235 
    236         // get the configured resources
    237         Map<ResourceType, Map<String, ResourceValue>> configuredFramework =
    238                 framework.getConfiguredResources(config);
    239         Map<ResourceType, Map<String, ResourceValue>> configuredProject =
    240                 project.getConfiguredResources(config);
    241 
    242         boolean saveFiles = System.getenv("save_file") != null;
    243 
    244         // loop on the layouts and render them
    245         File[] layouts = layoutFolder.listFiles();
    246         for (File layout : layouts) {
    247             // create a parser for the layout file
    248             TestParser parser = new TestParser();
    249             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
    250             parser.setInput(new FileReader(layout));
    251 
    252             System.out.println("Rendering " + layout.getName());
    253 
    254             ProjectCallBack projectCallBack = new ProjectCallBack();
    255 
    256             ResourceResolver resolver = ResourceResolver.create(
    257                     configuredProject, configuredFramework,
    258                     "Theme", false /*isProjectTheme*/);
    259 
    260             HardwareConfig hardwareConfig = new HardwareConfig(
    261                     320,
    262                     480,
    263                     Density.MEDIUM,
    264                     160, //xdpi
    265                     160, // ydpi
    266                     ScreenSize.NORMAL,
    267                     ScreenOrientation.PORTRAIT,
    268                     false /*software buttons */);
    269 
    270             RenderSession session = layoutLib.createSession(new SessionParams(
    271                     parser,
    272                     RenderingMode.NORMAL,
    273                     null /*projectKey*/,
    274                     hardwareConfig,
    275                     resolver,
    276                     projectCallBack,
    277                     1, // minSdkVersion
    278                     1, // targetSdkVersion
    279                     null //logger
    280                     ));
    281 
    282             if (session.getResult().isSuccess() == false) {
    283                 if (projectCallBack.mCustomViewAttempt == false) {
    284                     System.out.println("FAILED");
    285                     fail(String.format("Rendering %1$s: %2$s", layout.getName(),
    286                             session.getResult().getErrorMessage()));
    287                 } else {
    288                     System.out.println("Ignore custom views for now");
    289                 }
    290             } else {
    291                 if (saveFiles) {
    292                     File tmp = File.createTempFile(layout.getName(), ".png");
    293                     ImageIO.write(session.getImage(), "png", tmp);
    294                 }
    295                 System.out.println("Success!");
    296             }
    297         }
    298     }
    299 
    300     /**
    301      * Returns a config. This must be a valid config like a device would return. This is to
    302      * prevent issues where some resources don't exist in all cases and not in the default
    303      * (for instance only available in hdpi and mdpi but not in default).
    304      * @return
    305      */
    306     private FolderConfiguration getConfiguration() {
    307         FolderConfiguration config = new FolderConfiguration();
    308 
    309         // this matches an ADP1.
    310         config.addQualifier(new SmallestScreenWidthQualifier(320));
    311         config.addQualifier(new ScreenWidthQualifier(320));
    312         config.addQualifier(new ScreenHeightQualifier(480));
    313         config.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL));
    314         config.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG));
    315         config.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT));
    316         config.addQualifier(new DensityQualifier(Density.MEDIUM));
    317         config.addQualifier(new TouchScreenQualifier(TouchScreen.FINGER));
    318         config.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN));
    319         config.addQualifier(new TextInputMethodQualifier(Keyboard.QWERTY));
    320         config.addQualifier(new NavigationStateQualifier(NavigationState.HIDDEN));
    321         config.addQualifier(new NavigationMethodQualifier(Navigation.TRACKBALL));
    322         config.addQualifier(new ScreenDimensionQualifier(480, 320));
    323 
    324         config.updateScreenWidthAndHeight();
    325 
    326         return config;
    327     }
    328 }
    329