| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0 |
| * |
| * 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.example.android.render; |
| |
| import com.android.ide.common.log.ILogger; |
| import com.android.ide.common.rendering.LayoutLibrary; |
| import com.android.ide.common.rendering.api.AttrResourceValue; |
| import com.android.ide.common.rendering.api.DeclareStyleableResourceValue; |
| import com.android.ide.common.rendering.api.IProjectCallback; |
| import com.android.ide.common.rendering.api.ResourceValue; |
| import com.android.ide.common.resources.FrameworkResources; |
| 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.resources.configuration.VersionQualifier; |
| import com.android.ide.common.sdk.LoadStatus; |
| import com.android.io.FileWrapper; |
| 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.SdkConstants; |
| import com.android.sdklib.internal.project.ProjectProperties; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.util.HashMap; |
| import java.util.Map; |
| |
| /** |
| * RenderService Factory. This is initialized for a given platform from the SDK. |
| * |
| * Also contains some utility method to create {@link FolderConfiguration} and |
| * {@link ResourceResolver} |
| * |
| */ |
| public class RenderServiceFactory { |
| |
| private LayoutLibrary mLibrary; |
| private FrameworkResources mResources; |
| |
| public static RenderServiceFactory create(File platformFolder) { |
| |
| // create the factory |
| RenderServiceFactory factory = new RenderServiceFactory(); |
| if (factory.loadLibrary(platformFolder)) { |
| return factory; |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Creates 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). |
| * |
| * @param size1 |
| * @param size2 |
| * @param screenSize |
| * @param screenRatio |
| * @param orientation |
| * @param density |
| * @param touchScreen |
| * @param keyboardState |
| * @param keyboard |
| * @param navigationState |
| * @param navigation |
| * @param apiLevel |
| * @return |
| */ |
| public static FolderConfiguration createConfig( |
| int size1, |
| int size2, |
| ScreenSize screenSize, |
| ScreenRatio screenRatio, |
| ScreenOrientation orientation, |
| Density density, |
| TouchScreen touchScreen, |
| KeyboardState keyboardState, |
| Keyboard keyboard, |
| NavigationState navigationState, |
| Navigation navigation, |
| int apiLevel) { |
| FolderConfiguration config = new FolderConfiguration(); |
| |
| int width = size1, height = size2; |
| switch (orientation) { |
| case LANDSCAPE: |
| width = size1 < size2 ? size2 : size1; |
| height = size1 < size2 ? size1 : size2; |
| break; |
| case PORTRAIT: |
| width = size1 < size2 ? size1 : size2; |
| height = size1 < size2 ? size2 : size1; |
| break; |
| case SQUARE: |
| width = height = size1; |
| break; |
| } |
| |
| int wdp = (width * Density.DEFAULT_DENSITY) / density.getDpiValue(); |
| int hdp = (height * Density.DEFAULT_DENSITY) / density.getDpiValue(); |
| |
| config.addQualifier(new SmallestScreenWidthQualifier(wdp < hdp ? wdp : hdp)); |
| config.addQualifier(new ScreenWidthQualifier(wdp)); |
| config.addQualifier(new ScreenHeightQualifier(hdp)); |
| |
| config.addQualifier(new ScreenSizeQualifier(screenSize)); |
| config.addQualifier(new ScreenRatioQualifier(screenRatio)); |
| config.addQualifier(new ScreenOrientationQualifier(orientation)); |
| config.addQualifier(new DensityQualifier(density)); |
| config.addQualifier(new TouchScreenQualifier(touchScreen)); |
| config.addQualifier(new KeyboardStateQualifier(keyboardState)); |
| config.addQualifier(new TextInputMethodQualifier(keyboard)); |
| config.addQualifier(new NavigationStateQualifier(navigationState)); |
| config.addQualifier(new NavigationMethodQualifier(navigation)); |
| config.addQualifier(width > height ? new ScreenDimensionQualifier(width, height) : |
| new ScreenDimensionQualifier(height, width)); |
| config.addQualifier(new VersionQualifier(apiLevel)); |
| |
| config.updateScreenWidthAndHeight(); |
| |
| return config; |
| } |
| |
| /** |
| * Returns a {@link ResourceResolver} for a given config and project resource. |
| * |
| * @param config |
| * @param projectResources |
| * @param themeName |
| * @param isProjectTheme |
| * @return |
| */ |
| public ResourceResolver createResourceResolver( |
| FolderConfiguration config, |
| ResourceRepository projectResources, |
| String themeName, |
| boolean isProjectTheme) { |
| |
| Map<ResourceType, Map<String, ResourceValue>> configedProjectRes = |
| projectResources.getConfiguredResources(config); |
| |
| Map<ResourceType, Map<String, ResourceValue>> configedFrameworkRes = |
| mResources.getConfiguredResources(config); |
| |
| return ResourceResolver.create(configedProjectRes, configedFrameworkRes, |
| themeName, isProjectTheme); |
| } |
| |
| /** |
| * Creates a RenderService |
| * |
| * @param resources |
| * @param config |
| * @param projectCallback |
| * @return |
| */ |
| public RenderService createService( |
| ResourceResolver resources, |
| FolderConfiguration config, |
| IProjectCallback projectCallback) { |
| RenderService renderService = new RenderService( |
| mLibrary, resources, config, projectCallback); |
| |
| return renderService; |
| |
| } |
| |
| /** |
| * Creates a RenderService. This is less efficient than |
| * {@link #createService(ResourceResolver, FolderConfiguration, IProjectCallback)} since the |
| * {@link ResourceResolver} object is not cached by the caller. |
| * |
| * @param projectResources |
| * @param themeName |
| * @param isProjectTheme |
| * @param config |
| * @param projectCallback |
| * @return |
| */ |
| public RenderService createService( |
| ResourceRepository projectResources, |
| String themeName, |
| boolean isProjectTheme, |
| FolderConfiguration config, |
| IProjectCallback projectCallback) { |
| ResourceResolver resources = createResourceResolver( |
| config, projectResources, themeName, isProjectTheme); |
| |
| RenderService renderService = new RenderService( |
| mLibrary, resources, config, projectCallback); |
| |
| return renderService; |
| } |
| |
| private RenderServiceFactory() { |
| |
| } |
| |
| private boolean loadLibrary(File platformFolder) { |
| if (platformFolder.isDirectory() == false) { |
| throw new IllegalArgumentException("platform folder does not exist."); |
| } |
| |
| File dataFolder = new File(platformFolder, "data"); |
| if (dataFolder.isDirectory() == false) { |
| throw new IllegalArgumentException("platform data folder does not exist."); |
| } |
| |
| File layoutLibJar = new File(dataFolder, "layoutlib.jar"); |
| if (layoutLibJar.isFile() == false) { |
| throw new IllegalArgumentException("platform layoutlib.jar does not exist."); |
| } |
| |
| File resFolder = new File(dataFolder, "res"); |
| if (resFolder.isDirectory() == false) { |
| throw new IllegalArgumentException("platform res folder does not exist."); |
| } |
| |
| File fontFolder = new File(dataFolder, "fonts"); |
| if (fontFolder.isDirectory() == false) { |
| throw new IllegalArgumentException("platform font folder does not exist."); |
| } |
| |
| FileWrapper buildProp = new FileWrapper(platformFolder, SdkConstants.FN_BUILD_PROP); |
| if (buildProp.isFile() == false) { |
| throw new IllegalArgumentException("platform build.prop does not exist."); |
| } |
| |
| StdOutLogger log = new StdOutLogger(); |
| |
| mLibrary = LayoutLibrary.load(layoutLibJar.getAbsolutePath(), log, |
| "LayoutLibRenderer"); |
| if (mLibrary.getStatus() != LoadStatus.LOADED) { |
| throw new IllegalArgumentException(mLibrary.getLoadMessage()); |
| } |
| |
| // load the framework resources |
| mResources = loadResources(resFolder, log); |
| |
| // get all the attr values. |
| HashMap<String, Map<String, Integer>> enumMap = new HashMap<String, Map<String, Integer>>(); |
| |
| FolderConfiguration config = new FolderConfiguration(); |
| Map<ResourceType, Map<String, ResourceValue>> res = |
| mResources.getConfiguredResources(config); |
| |
| // get the ATTR values |
| Map<String, ResourceValue> attrItems = res.get(ResourceType.ATTR); |
| for (ResourceValue value : attrItems.values()) { |
| if (value instanceof AttrResourceValue) { |
| AttrResourceValue attr = (AttrResourceValue) value; |
| Map<String, Integer> values = attr.getAttributeValues(); |
| if (values != null) { |
| enumMap.put(attr.getName(), values); |
| } |
| } |
| } |
| |
| // get the declare-styleable values |
| Map<String, ResourceValue> styleableItems = res.get(ResourceType.DECLARE_STYLEABLE); |
| |
| // get the attr from the styleable |
| for (ResourceValue value : styleableItems.values()) { |
| if (value instanceof DeclareStyleableResourceValue) { |
| DeclareStyleableResourceValue dsrc = (DeclareStyleableResourceValue) value; |
| Map<String, AttrResourceValue> attrs = dsrc.getAllAttributes(); |
| if (attrs != null && attrs.size() > 0) { |
| for (AttrResourceValue attr : attrs.values()) { |
| Map<String, Integer> values = attr.getAttributeValues(); |
| if (values != null) { |
| enumMap.put(attr.getName(), values); |
| } |
| } |
| } |
| } |
| } |
| |
| // we need to parse the build.prop for this |
| Map<String, String> buildPropMap = ProjectProperties.parsePropertyFile(buildProp, log); |
| |
| return mLibrary.init(buildPropMap, fontFolder, enumMap, log); |
| } |
| |
| private FrameworkResources loadResources(File resFolder, ILogger log) { |
| FrameworkResources resources = new FrameworkResources(); |
| |
| try { |
| FolderWrapper path = new FolderWrapper(resFolder); |
| resources.loadResources(path); |
| resources.loadPublicResources(path, log); |
| return resources; |
| } catch (IOException e) { |
| // since we test that folders are folders, and files are files, this shouldn't |
| // happen. We can ignore it. |
| } |
| |
| return null; |
| } |
| |
| } |