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