Home | History | Annotate | Download | only in render
      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