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 getViewCookie()90 public Object getViewCookie() { 91 return null; 92 } 93 94 @Override getParser(String layoutName)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 getNamespace()115 public String getNamespace() { 116 // TODO: read from the ApiDemos manifest. 117 return "com.example.android.apis"; 118 } 119 120 @Override 121 @SuppressWarnings("unchecked") loadView(String name, Class[] constructorSignature, Object[] constructorArgs)122 public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) 123 throws ClassNotFoundException, Exception { 124 mCustomViewAttempt = true; 125 return null; 126 } 127 128 @Override getResourceId(ResourceType type, String name)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 resolveResourceId(int id)136 public Pair<ResourceType, String> resolveResourceId(int id) { 137 return mResourceMap.get(id); 138 } 139 140 @Override resolveResourceId(int[] id)141 public String resolveResourceId(int[] id) { 142 return null; 143 } 144 145 @Override getParser(String layoutName)146 public ILayoutPullParser getParser(String layoutName) { 147 return null; 148 } 149 150 @Override getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int typePosition, int fullChildPosition, int typeChildPosition, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue)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 getAdapterBinding(ResourceReference adapterView, Object adapterCookie, Object viewObject)159 public AdapterBinding getAdapterBinding(ResourceReference adapterView, 160 Object adapterCookie, Object viewObject) { 161 return null; 162 } 163 164 @Override getParser(ResourceValue layoutResource)165 public ILayoutPullParser getParser(ResourceValue layoutResource) { 166 return null; 167 } 168 169 @Override getActionBarCallback()170 public ActionBarCallback getActionBarCallback() { 171 return new ActionBarCallback(); 172 } 173 } 174 testApiDemos()175 public void testApiDemos() throws IOException, XmlPullParserException { 176 findApiDemos(); 177 } 178 findApiDemos()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 testSample(IAndroidTarget target, File sampleProject)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 */ getConfiguration()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