• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
create(File platformFolder)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      */
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)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