/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); you * may not use this file except in compliance with the License. You may obtain a * copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package com.android.ide.eclipse.tests.functests.layoutRendering; import com.android.SdkConstants; import com.android.ide.common.rendering.LayoutLibrary; import com.android.ide.common.rendering.api.AdapterBinding; import com.android.ide.common.rendering.api.HardwareConfig; import com.android.ide.common.rendering.api.ILayoutPullParser; import com.android.ide.common.rendering.api.IProjectCallback; import com.android.ide.common.rendering.api.RenderSession; import com.android.ide.common.rendering.api.ResourceReference; import com.android.ide.common.rendering.api.ResourceValue; import com.android.ide.common.rendering.api.SessionParams; import com.android.ide.common.rendering.api.SessionParams.RenderingMode; import com.android.ide.common.resources.ResourceItem; import com.android.ide.common.resources.ResourceRepository; import com.android.ide.common.resources.ResourceResolver; import com.android.ide.common.resources.configuration.DensityQualifier; import com.android.ide.common.resources.configuration.FolderConfiguration; import com.android.ide.common.resources.configuration.KeyboardStateQualifier; import com.android.ide.common.resources.configuration.NavigationMethodQualifier; import com.android.ide.common.resources.configuration.NavigationStateQualifier; import com.android.ide.common.resources.configuration.ScreenDimensionQualifier; import com.android.ide.common.resources.configuration.ScreenHeightQualifier; import com.android.ide.common.resources.configuration.ScreenOrientationQualifier; import com.android.ide.common.resources.configuration.ScreenRatioQualifier; import com.android.ide.common.resources.configuration.ScreenSizeQualifier; import com.android.ide.common.resources.configuration.ScreenWidthQualifier; import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier; import com.android.ide.common.resources.configuration.TextInputMethodQualifier; import com.android.ide.common.resources.configuration.TouchScreenQualifier; import com.android.ide.common.sdk.LoadStatus; import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager; import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData; import com.android.ide.eclipse.tests.SdkLoadingTestCase; import com.android.io.FolderWrapper; import com.android.resources.Density; import com.android.resources.Keyboard; import com.android.resources.KeyboardState; import com.android.resources.Navigation; import com.android.resources.NavigationState; import com.android.resources.ResourceType; import com.android.resources.ScreenOrientation; import com.android.resources.ScreenRatio; import com.android.resources.ScreenSize; import com.android.resources.TouchScreen; import com.android.sdklib.IAndroidTarget; import com.android.util.Pair; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; import java.io.File; import java.io.FileReader; import java.io.IOException; import java.util.HashMap; import java.util.Map; import javax.imageio.ImageIO; public class ApiDemosRenderingTest extends SdkLoadingTestCase { /** * Custom parser that implements {@link ILayoutPullParser} (which itself extends * {@link XmlPullParser}). */ private final static class TestParser extends KXmlParser implements ILayoutPullParser { /** * Since we're not going to go through the result of the rendering/layout, we can return * null for the View Key. */ @Override public Object getViewCookie() { return null; } @Override public ILayoutPullParser getParser(String layoutName) { return null; } } private final static class ProjectCallBack implements IProjectCallback { // resource id counter. // We start at 0x7f000000 to avoid colliding with the framework id // since we have no access to the project R.java and we need to generate them automatically. private int mIdCounter = 0x7f000000; // in some cases, the id that getResourceValue(String type, String name) returns // will be sent back to get the type/name. This map stores the id/type/name we generate // to be able to do the reverse resolution. private Map<Integer, Pair<ResourceType, String>> mResourceMap = new HashMap<Integer, Pair<ResourceType, String>>(); private boolean mCustomViewAttempt = false; @Override public String getNamespace() { // TODO: read from the ApiDemos manifest. return "com.example.android.apis"; } @Override @SuppressWarnings("unchecked") public Object loadView(String name, Class[] constructorSignature, Object[] constructorArgs) throws ClassNotFoundException, Exception { mCustomViewAttempt = true; return null; } @Override public Integer getResourceId(ResourceType type, String name) { Integer result = ++mIdCounter; mResourceMap.put(result, Pair.of(type, name)); return result; } @Override public Pair<ResourceType, String> resolveResourceId(int id) { return mResourceMap.get(id); } @Override public String resolveResourceId(int[] id) { return null; } @Override public ILayoutPullParser getParser(String layoutName) { return null; } @Override public Object getAdapterItemValue(ResourceReference adapterView, Object adapterCookie, ResourceReference itemRef, int fullPosition, int typePosition, int fullChildPosition, int typeChildPosition, ResourceReference viewRef, ViewAttribute viewAttribute, Object defaultValue) { return null; } @Override public AdapterBinding getAdapterBinding(ResourceReference adapterView, Object adapterCookie, Object viewObject) { return null; } @Override public ILayoutPullParser getParser(ResourceValue layoutResource) { return null; } } public void testApiDemos() throws IOException, XmlPullParserException { findApiDemos(); } private void findApiDemos() throws IOException, XmlPullParserException { IAndroidTarget[] targets = getSdk().getTargets(); for (IAndroidTarget target : targets) { String path = target.getPath(IAndroidTarget.SAMPLES); File samples = new File(path); if (samples.isDirectory()) { File[] files = samples.listFiles(); for (File file : files) { if ("apidemos".equalsIgnoreCase(file.getName())) { testSample(target, file); return; } } } } fail("Failed to find ApiDemos!"); } private void testSample(IAndroidTarget target, File sampleProject) throws IOException, XmlPullParserException { AndroidTargetData data = getSdk().getTargetData(target); if (data == null) { fail("No AndroidData!"); } LayoutLibrary layoutLib = data.getLayoutLibrary(); if (layoutLib.getStatus() != LoadStatus.LOADED) { fail("Fail to load the bridge: " + layoutLib.getLoadMessage()); } FolderWrapper resFolder = new FolderWrapper(sampleProject, SdkConstants.FD_RES); if (resFolder.exists() == false) { fail("Sample project has no res folder!"); } // look for the layout folder File layoutFolder = new File(resFolder, SdkConstants.FD_RES_LAYOUT); if (layoutFolder.isDirectory() == false) { fail("Sample project has no layout folder!"); } // first load the project's target framework resource ResourceRepository framework = ResourceManager.getInstance().loadFrameworkResources(target); // now load the project resources ResourceRepository project = new ResourceRepository(resFolder, false) { @Override protected ResourceItem createResourceItem(String name) { return new ResourceItem(name); } }; // Create a folder configuration that will be used for the rendering: FolderConfiguration config = getConfiguration(); // get the configured resources Map<ResourceType, Map<String, ResourceValue>> configuredFramework = framework.getConfiguredResources(config); Map<ResourceType, Map<String, ResourceValue>> configuredProject = project.getConfiguredResources(config); boolean saveFiles = System.getenv("save_file") != null; // loop on the layouts and render them File[] layouts = layoutFolder.listFiles(); for (File layout : layouts) { // create a parser for the layout file TestParser parser = new TestParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(new FileReader(layout)); System.out.println("Rendering " + layout.getName()); ProjectCallBack projectCallBack = new ProjectCallBack(); ResourceResolver resolver = ResourceResolver.create( configuredProject, configuredFramework, "Theme", false /*isProjectTheme*/); HardwareConfig hardwareConfig = new HardwareConfig( 320, 480, Density.MEDIUM, 160, //xdpi 160, // ydpi ScreenSize.NORMAL, ScreenOrientation.PORTRAIT, false /*software buttons */); RenderSession session = layoutLib.createSession(new SessionParams( parser, RenderingMode.NORMAL, null /*projectKey*/, hardwareConfig, resolver, projectCallBack, 1, // minSdkVersion 1, // targetSdkVersion null //logger )); if (session.getResult().isSuccess() == false) { if (projectCallBack.mCustomViewAttempt == false) { System.out.println("FAILED"); fail(String.format("Rendering %1$s: %2$s", layout.getName(), session.getResult().getErrorMessage())); } else { System.out.println("Ignore custom views for now"); } } else { if (saveFiles) { File tmp = File.createTempFile(layout.getName(), ".png"); ImageIO.write(session.getImage(), "png", tmp); } System.out.println("Success!"); } } } /** * Returns a config. This must be a valid config like a device would return. This is to * prevent issues where some resources don't exist in all cases and not in the default * (for instance only available in hdpi and mdpi but not in default). * @return */ private FolderConfiguration getConfiguration() { FolderConfiguration config = new FolderConfiguration(); // this matches an ADP1. config.addQualifier(new SmallestScreenWidthQualifier(320)); config.addQualifier(new ScreenWidthQualifier(320)); config.addQualifier(new ScreenHeightQualifier(480)); config.addQualifier(new ScreenSizeQualifier(ScreenSize.NORMAL)); config.addQualifier(new ScreenRatioQualifier(ScreenRatio.NOTLONG)); config.addQualifier(new ScreenOrientationQualifier(ScreenOrientation.PORTRAIT)); config.addQualifier(new DensityQualifier(Density.MEDIUM)); config.addQualifier(new TouchScreenQualifier(TouchScreen.FINGER)); config.addQualifier(new KeyboardStateQualifier(KeyboardState.HIDDEN)); config.addQualifier(new TextInputMethodQualifier(Keyboard.QWERTY)); config.addQualifier(new NavigationStateQualifier(NavigationState.HIDDEN)); config.addQualifier(new NavigationMethodQualifier(Navigation.TRACKBALL)); config.addQualifier(new ScreenDimensionQualifier(480, 320)); config.updateScreenWidthAndHeight(); return config; } }