• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration;
20 import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier;
21 import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier;
22 import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier;
23 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier;
24 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier;
25 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier;
26 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier;
27 import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier;
28 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier;
29 import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier.KeyboardState;
30 import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier.NavigationMethod;
31 import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier.Density;
32 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier.ScreenOrientation;
33 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier.ScreenRatio;
34 import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier.ScreenSize;
35 import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier.TextInputMethod;
36 import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier.TouchScreenType;
37 import com.android.ide.eclipse.adt.internal.resources.manager.ProjectResources;
38 import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
39 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
40 import com.android.ide.eclipse.adt.internal.sdk.LoadStatus;
41 import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData.LayoutBridge;
42 import com.android.ide.eclipse.tests.SdkTestCase;
43 import com.android.layoutlib.api.ILayoutResult;
44 import com.android.layoutlib.api.IProjectCallback;
45 import com.android.layoutlib.api.IResourceValue;
46 import com.android.layoutlib.api.IXmlPullParser;
47 import com.android.sdklib.IAndroidTarget;
48 import com.android.sdklib.SdkConstants;
49 
50 import org.kxml2.io.KXmlParser;
51 import org.xmlpull.v1.XmlPullParser;
52 import org.xmlpull.v1.XmlPullParserException;
53 
54 import java.io.File;
55 import java.io.FileReader;
56 import java.io.IOException;
57 import java.util.HashMap;
58 import java.util.Map;
59 
60 import javax.imageio.ImageIO;
61 
62 public class ApiDemosRenderingTest extends SdkTestCase {
63 
64     /**
65      * Custom parser that implements {@link IXmlPullParser} (which itself extends
66      * {@link XmlPullParser}).
67      */
68     private final static class TestParser extends KXmlParser implements IXmlPullParser {
69         /**
70          * Since we're not going to go through the result of the rendering/layout, we can return
71          * null for the View Key.
72          */
getViewKey()73         public Object getViewKey() {
74             return null;
75         }
76     }
77 
78     private final static class ProjectCallBack implements IProjectCallback {
79         // resource id counter.
80         // We start at 0x7f000000 to avoid colliding with the framework id
81         // since we have no access to the project R.java and we need to generate them automatically.
82         private int mIdCounter = 0x7f000000;
83 
84         // in some cases, the id that getResourceValue(String type, String name) returns
85         // will be sent back to get the type/name. This map stores the id/type/name we generate
86         // to be able to do the reverse resolution.
87         private Map<Integer, String[]> mResourceMap = new HashMap<Integer, String[]>();
88 
89         private boolean mCustomViewAttempt = false;
90 
getNamespace()91         public String getNamespace() {
92             // TODO: read from the ApiDemos manifest.
93             return "com.example.android.apis";
94         }
95 
getResourceValue(String type, String name)96         public Integer getResourceValue(String type, String name) {
97             Integer result = ++mIdCounter;
98             mResourceMap.put(result, new String[] { name, type });
99             return result;
100         }
101 
102         @SuppressWarnings("unchecked")
loadView(String name, Class[] constructorSignature, Object[] constructorArgs)103         public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs)
104                 throws ClassNotFoundException, Exception {
105             mCustomViewAttempt = true;
106             return null;
107         }
108 
resolveResourceValue(int id)109         public String[] resolveResourceValue(int id) {
110             return mResourceMap.get(id);
111         }
112 
resolveResourceValue(int[] id)113         public String resolveResourceValue(int[] id) {
114             return null;
115         }
116 
117     }
118 
testApiDemos()119     public void testApiDemos() throws IOException, XmlPullParserException {
120         findApiDemos();
121     }
122 
findApiDemos()123     private void findApiDemos() throws IOException, XmlPullParserException {
124         IAndroidTarget[] targets = getSdk().getTargets();
125 
126         for (IAndroidTarget target : targets) {
127             String path = target.getPath(IAndroidTarget.SAMPLES);
128             File samples = new File(path);
129             if (samples.isDirectory()) {
130                 File[] files = samples.listFiles();
131                 for (File file : files) {
132                     if ("apidemos".equalsIgnoreCase(file.getName())) {
133                         testSample(target, file);
134                         return;
135                     }
136                 }
137             }
138         }
139 
140         fail("Failed to find ApiDemos!");
141     }
142 
testSample(IAndroidTarget target, File sampleProject)143     private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException {
144         AndroidTargetData data = getSdk().getTargetData(target);
145         if (data == null) {
146             fail("No AndroidData!");
147         }
148 
149         LayoutBridge bridge = data.getLayoutBridge();
150         if (bridge.status != LoadStatus.LOADED || bridge.bridge == null) {
151             fail("Fail to load the bridge");
152         }
153 
154         File resFolder = new File(sampleProject, SdkConstants.FD_RES);
155         if (resFolder.isDirectory() == false) {
156             fail("Sample project has no res folder!");
157         }
158 
159         // look for the layout folder
160         File layoutFolder = new File(resFolder, SdkConstants.FD_LAYOUT);
161         if (layoutFolder.isDirectory() == false) {
162             fail("Sample project has no layout folder!");
163         }
164 
165         // first load the project's target framework resource
166         ProjectResources framework = ResourceManager.getInstance().loadFrameworkResources(target);
167 
168         // now load the project resources
169         ProjectResources project = new ProjectResources(false /* isFrameworkRepository */);
170         ResourceManager.getInstance().loadResources(project, resFolder);
171 
172 
173         // Create a folder configuration that will be used for the rendering:
174         FolderConfiguration config = getConfiguration();
175 
176         // get the configured resources
177         Map<String, Map<String, IResourceValue>> configuredFramework =
178                 framework.getConfiguredResources(config);
179         Map<String, Map<String, IResourceValue>> configuredProject =
180                 project.getConfiguredResources(config);
181 
182         boolean saveFiles = System.getenv("save_file") != null;
183 
184         // loop on the layouts and render them
185         File[] layouts = layoutFolder.listFiles();
186         for (File layout : layouts) {
187             // create a parser for the layout file
188             TestParser parser = new TestParser();
189             parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true);
190             parser.setInput(new FileReader(layout));
191 
192             System.out.println("Rendering " + layout.getName());
193 
194             ProjectCallBack projectCallBack = new ProjectCallBack();
195 
196             ILayoutResult result = bridge.bridge.computeLayout(
197                     parser,
198                     null /*projectKey*/,
199                     320,
200                     480,
201                     false, //renderFullSize
202                     160, //density
203                     160, //xdpi
204                     160, // ydpi
205                     "Theme", //themeName
206                     false, //isProjectTheme
207                     configuredProject,
208                     configuredFramework,
209                     projectCallBack,
210                     null //logger
211                     );
212 
213             if (result.getSuccess() != ILayoutResult.SUCCESS) {
214                 if (projectCallBack.mCustomViewAttempt == false) {
215                     System.out.println("FAILED");
216                     fail(String.format("Rendering %1$s: %2$s", layout.getName(),
217                             result.getErrorMessage()));
218                 } else {
219                     System.out.println("Ignore custom views for now");
220                 }
221             } else {
222                 if (saveFiles) {
223                     File tmp = File.createTempFile(layout.getName(), ".png");
224                     ImageIO.write(result.getImage(), "png", tmp);
225                 }
226                 System.out.println("Success!");
227             }
228         }
229     }
230 
231     /**
232      * Returns a config. This must be a valid config like a device would return. This is to
233      * prevent issues where some resources don't exist in all cases and not in the default
234      * (for instance only available in hdpi and mdpi but not in default).
235      * @return
236      */
getConfiguration()237     private FolderConfiguration getConfiguration() {
238         FolderConfiguration config = new FolderConfiguration();
239 
240         // this matches an ADP1.
241         config.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL));
242         config.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG));
243         config.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT));
244         config.addQualifier(new PixelDensityQualifier(Density.MEDIUM));
245         config.addQualifier(new TouchScreenQualifier(TouchScreenType.FINGER));
246         config.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN));
247         config.addQualifier(new TextInputMethodQualifier(TextInputMethod.QWERTY));
248         config.addQualifier(new NavigationMethodQualifier(NavigationMethod.TRACKBALL));
249         config.addQualifier(new ScreenDimensionQualifier(480, 320));
250 
251         return config;
252     }
253 }
254