1 /* 2 * Copyright (C) 2011 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 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, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.example.android.render; 18 19 import com.android.ide.common.log.ILogger; 20 import com.android.ide.common.rendering.LayoutLibrary; 21 import com.android.ide.common.rendering.api.AttrResourceValue; 22 import com.android.ide.common.rendering.api.DeclareStyleableResourceValue; 23 import com.android.ide.common.rendering.api.IProjectCallback; 24 import com.android.ide.common.rendering.api.ResourceValue; 25 import com.android.ide.common.resources.FrameworkResources; 26 import com.android.ide.common.resources.ResourceRepository; 27 import com.android.ide.common.resources.ResourceResolver; 28 import com.android.ide.common.resources.configuration.DensityQualifier; 29 import com.android.ide.common.resources.configuration.FolderConfiguration; 30 import com.android.ide.common.resources.configuration.KeyboardStateQualifier; 31 import com.android.ide.common.resources.configuration.NavigationMethodQualifier; 32 import com.android.ide.common.resources.configuration.NavigationStateQualifier; 33 import com.android.ide.common.resources.configuration.ScreenDimensionQualifier; 34 import com.android.ide.common.resources.configuration.ScreenHeightQualifier; 35 import com.android.ide.common.resources.configuration.ScreenOrientationQualifier; 36 import com.android.ide.common.resources.configuration.ScreenRatioQualifier; 37 import com.android.ide.common.resources.configuration.ScreenSizeQualifier; 38 import com.android.ide.common.resources.configuration.ScreenWidthQualifier; 39 import com.android.ide.common.resources.configuration.SmallestScreenWidthQualifier; 40 import com.android.ide.common.resources.configuration.TextInputMethodQualifier; 41 import com.android.ide.common.resources.configuration.TouchScreenQualifier; 42 import com.android.ide.common.resources.configuration.VersionQualifier; 43 import com.android.ide.common.sdk.LoadStatus; 44 import com.android.io.FileWrapper; 45 import com.android.io.FolderWrapper; 46 import com.android.resources.Density; 47 import com.android.resources.Keyboard; 48 import com.android.resources.KeyboardState; 49 import com.android.resources.Navigation; 50 import com.android.resources.NavigationState; 51 import com.android.resources.ResourceType; 52 import com.android.resources.ScreenOrientation; 53 import com.android.resources.ScreenRatio; 54 import com.android.resources.ScreenSize; 55 import com.android.resources.TouchScreen; 56 import com.android.sdklib.SdkConstants; 57 import com.android.sdklib.internal.project.ProjectProperties; 58 59 import java.io.File; 60 import java.io.IOException; 61 import java.util.HashMap; 62 import java.util.Map; 63 64 /** 65 * RenderService Factory. This is initialized for a given platform from the SDK. 66 * 67 * Also contains some utility method to create {@link FolderConfiguration} and 68 * {@link ResourceResolver} 69 * 70 */ 71 public class RenderServiceFactory { 72 73 private LayoutLibrary mLibrary; 74 private FrameworkResources mResources; 75 76 public static RenderServiceFactory create(File platformFolder) { 77 78 // create the factory 79 RenderServiceFactory factory = new RenderServiceFactory(); 80 if (factory.loadLibrary(platformFolder)) { 81 return factory; 82 } 83 84 return null; 85 } 86 87 /** 88 * Creates a config. This must be a valid config like a device would return. This is to 89 * prevent issues where some resources don't exist in all cases and not in the default 90 * (for instance only available in hdpi and mdpi but not in default). 91 * 92 * @param size1 93 * @param size2 94 * @param screenSize 95 * @param screenRatio 96 * @param orientation 97 * @param density 98 * @param touchScreen 99 * @param keyboardState 100 * @param keyboard 101 * @param navigationState 102 * @param navigation 103 * @param apiLevel 104 * @return 105 */ 106 public static FolderConfiguration createConfig( 107 int size1, 108 int size2, 109 ScreenSize screenSize, 110 ScreenRatio screenRatio, 111 ScreenOrientation orientation, 112 Density density, 113 TouchScreen touchScreen, 114 KeyboardState keyboardState, 115 Keyboard keyboard, 116 NavigationState navigationState, 117 Navigation navigation, 118 int apiLevel) { 119 FolderConfiguration config = new FolderConfiguration(); 120 121 int width = size1, height = size2; 122 switch (orientation) { 123 case LANDSCAPE: 124 width = size1 < size2 ? size2 : size1; 125 height = size1 < size2 ? size1 : size2; 126 break; 127 case PORTRAIT: 128 width = size1 < size2 ? size1 : size2; 129 height = size1 < size2 ? size2 : size1; 130 break; 131 case SQUARE: 132 width = height = size1; 133 break; 134 } 135 136 int wdp = (width * Density.DEFAULT_DENSITY) / density.getDpiValue(); 137 int hdp = (height * Density.DEFAULT_DENSITY) / density.getDpiValue(); 138 139 config.addQualifier(new SmallestScreenWidthQualifier(wdp < hdp ? wdp : hdp)); 140 config.addQualifier(new ScreenWidthQualifier(wdp)); 141 config.addQualifier(new ScreenHeightQualifier(hdp)); 142 143 config.addQualifier(new ScreenSizeQualifier(screenSize)); 144 config.addQualifier(new ScreenRatioQualifier(screenRatio)); 145 config.addQualifier(new ScreenOrientationQualifier(orientation)); 146 config.addQualifier(new DensityQualifier(density)); 147 config.addQualifier(new TouchScreenQualifier(touchScreen)); 148 config.addQualifier(new KeyboardStateQualifier(keyboardState)); 149 config.addQualifier(new TextInputMethodQualifier(keyboard)); 150 config.addQualifier(new NavigationStateQualifier(navigationState)); 151 config.addQualifier(new NavigationMethodQualifier(navigation)); 152 config.addQualifier(width > height ? new ScreenDimensionQualifier(width, height) : 153 new ScreenDimensionQualifier(height, width)); 154 config.addQualifier(new VersionQualifier(apiLevel)); 155 156 config.updateScreenWidthAndHeight(); 157 158 return config; 159 } 160 161 /** 162 * Returns a {@link ResourceResolver} for a given config and project resource. 163 * 164 * @param config 165 * @param projectResources 166 * @param themeName 167 * @param isProjectTheme 168 * @return 169 */ 170 public ResourceResolver createResourceResolver( 171 FolderConfiguration config, 172 ResourceRepository projectResources, 173 String themeName, 174 boolean isProjectTheme) { 175 176 Map<ResourceType, Map<String, ResourceValue>> configedProjectRes = 177 projectResources.getConfiguredResources(config); 178 179 Map<ResourceType, Map<String, ResourceValue>> configedFrameworkRes = 180 mResources.getConfiguredResources(config); 181 182 return ResourceResolver.create(configedProjectRes, configedFrameworkRes, 183 themeName, isProjectTheme); 184 } 185 186 /** 187 * Creates a RenderService 188 * 189 * @param resources 190 * @param config 191 * @param projectCallback 192 * @return 193 */ 194 public RenderService createService( 195 ResourceResolver resources, 196 FolderConfiguration config, 197 IProjectCallback projectCallback) { 198 RenderService renderService = new RenderService( 199 mLibrary, resources, config, projectCallback); 200 201 return renderService; 202 203 } 204 205 /** 206 * Creates a RenderService. This is less efficient than 207 * {@link #createService(ResourceResolver, FolderConfiguration, IProjectCallback)} since the 208 * {@link ResourceResolver} object is not cached by the caller. 209 * 210 * @param projectResources 211 * @param themeName 212 * @param isProjectTheme 213 * @param config 214 * @param projectCallback 215 * @return 216 */ 217 public RenderService createService( 218 ResourceRepository projectResources, 219 String themeName, 220 boolean isProjectTheme, 221 FolderConfiguration config, 222 IProjectCallback projectCallback) { 223 ResourceResolver resources = createResourceResolver( 224 config, projectResources, themeName, isProjectTheme); 225 226 RenderService renderService = new RenderService( 227 mLibrary, resources, config, projectCallback); 228 229 return renderService; 230 } 231 232 private RenderServiceFactory() { 233 234 } 235 236 private boolean loadLibrary(File platformFolder) { 237 if (platformFolder.isDirectory() == false) { 238 throw new IllegalArgumentException("platform folder does not exist."); 239 } 240 241 File dataFolder = new File(platformFolder, "data"); 242 if (dataFolder.isDirectory() == false) { 243 throw new IllegalArgumentException("platform data folder does not exist."); 244 } 245 246 File layoutLibJar = new File(dataFolder, "layoutlib.jar"); 247 if (layoutLibJar.isFile() == false) { 248 throw new IllegalArgumentException("platform layoutlib.jar does not exist."); 249 } 250 251 File resFolder = new File(dataFolder, "res"); 252 if (resFolder.isDirectory() == false) { 253 throw new IllegalArgumentException("platform res folder does not exist."); 254 } 255 256 File fontFolder = new File(dataFolder, "fonts"); 257 if (fontFolder.isDirectory() == false) { 258 throw new IllegalArgumentException("platform font folder does not exist."); 259 } 260 261 FileWrapper buildProp = new FileWrapper(platformFolder, SdkConstants.FN_BUILD_PROP); 262 if (buildProp.isFile() == false) { 263 throw new IllegalArgumentException("platform build.prop does not exist."); 264 } 265 266 StdOutLogger log = new StdOutLogger(); 267 268 mLibrary = LayoutLibrary.load(layoutLibJar.getAbsolutePath(), log, 269 "LayoutLibRenderer"); 270 if (mLibrary.getStatus() != LoadStatus.LOADED) { 271 throw new IllegalArgumentException(mLibrary.getLoadMessage()); 272 } 273 274 // load the framework resources 275 mResources = loadResources(resFolder, log); 276 277 // get all the attr values. 278 HashMap<String, Map<String, Integer>> enumMap = new HashMap<String, Map<String, Integer>>(); 279 280 FolderConfiguration config = new FolderConfiguration(); 281 Map<ResourceType, Map<String, ResourceValue>> res = 282 mResources.getConfiguredResources(config); 283 284 // get the ATTR values 285 Map<String, ResourceValue> attrItems = res.get(ResourceType.ATTR); 286 for (ResourceValue value : attrItems.values()) { 287 if (value instanceof AttrResourceValue) { 288 AttrResourceValue attr = (AttrResourceValue) value; 289 Map<String, Integer> values = attr.getAttributeValues(); 290 if (values != null) { 291 enumMap.put(attr.getName(), values); 292 } 293 } 294 } 295 296 // get the declare-styleable values 297 Map<String, ResourceValue> styleableItems = res.get(ResourceType.DECLARE_STYLEABLE); 298 299 // get the attr from the styleable 300 for (ResourceValue value : styleableItems.values()) { 301 if (value instanceof DeclareStyleableResourceValue) { 302 DeclareStyleableResourceValue dsrc = (DeclareStyleableResourceValue) value; 303 Map<String, AttrResourceValue> attrs = dsrc.getAllAttributes(); 304 if (attrs != null && attrs.size() > 0) { 305 for (AttrResourceValue attr : attrs.values()) { 306 Map<String, Integer> values = attr.getAttributeValues(); 307 if (values != null) { 308 enumMap.put(attr.getName(), values); 309 } 310 } 311 } 312 } 313 } 314 315 // we need to parse the build.prop for this 316 Map<String, String> buildPropMap = ProjectProperties.parsePropertyFile(buildProp, log); 317 318 return mLibrary.init(buildPropMap, fontFolder, enumMap, log); 319 } 320 321 private FrameworkResources loadResources(File resFolder, ILogger log) { 322 FrameworkResources resources = new FrameworkResources(); 323 324 try { 325 FolderWrapper path = new FolderWrapper(resFolder); 326 resources.loadResources(path); 327 resources.loadPublicResources(path, log); 328 return resources; 329 } catch (IOException e) { 330 // since we test that folders are folders, and files are files, this shouldn't 331 // happen. We can ignore it. 332 } 333 334 return null; 335 } 336 337 } 338