Home | History | Annotate | Download | only in sdk
      1 /*
      2  * Copyright (C) 2009 The Android Open Source Project
      3  *
      4  * Licensed under the Eclipse Public License, Version 1.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.eclipse.org/org/documents/epl-v10.php
      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.android.ide.eclipse.adt.internal.sdk;
     18 
     19 import com.android.ide.common.resources.configuration.CountryCodeQualifier;
     20 import com.android.ide.common.resources.configuration.DensityQualifier;
     21 import com.android.ide.common.resources.configuration.FolderConfiguration;
     22 import com.android.ide.common.resources.configuration.KeyboardStateQualifier;
     23 import com.android.ide.common.resources.configuration.NavigationMethodQualifier;
     24 import com.android.ide.common.resources.configuration.NavigationStateQualifier;
     25 import com.android.ide.common.resources.configuration.NetworkCodeQualifier;
     26 import com.android.ide.common.resources.configuration.ScreenDimensionQualifier;
     27 import com.android.ide.common.resources.configuration.ScreenOrientationQualifier;
     28 import com.android.ide.common.resources.configuration.ScreenRatioQualifier;
     29 import com.android.ide.common.resources.configuration.ScreenSizeQualifier;
     30 import com.android.ide.common.resources.configuration.TextInputMethodQualifier;
     31 import com.android.ide.common.resources.configuration.TouchScreenQualifier;
     32 
     33 import org.w3c.dom.Document;
     34 import org.w3c.dom.Element;
     35 
     36 import java.util.ArrayList;
     37 import java.util.Collections;
     38 import java.util.List;
     39 
     40 /**
     41  * Class representing a layout device.
     42  *
     43  * A Layout device is a collection of {@link FolderConfiguration} that can be used to render Android
     44  * layout files.
     45  *
     46  * It also contains a single xdpi/ydpi that is independent of the {@link FolderConfiguration}.
     47  *
     48  * If the device is meant to represent a true device, then most of the FolderConfigurations' content
     49  * should be identical, with only a few qualifiers (orientation, keyboard state) that would differ.
     50  * However it is simpler to reuse the FolderConfiguration class (with the non changing qualifiers
     51  * duplicated in each configuration) as it's what's being used by the rendering library.
     52  *
     53  * To create, edit and delete LayoutDevice objects, see {@link LayoutDeviceManager}.
     54  * The class is not technically immutable but behaves as such outside of its package.
     55  */
     56 public class LayoutDevice {
     57 
     58     private final String mName;
     59 
     60     /**
     61      * Wrapper around a {@link FolderConfiguration}.
     62      * <p/>This adds a name, accessible through {@link #getName()}.
     63      * <p/>The folder config can be accessed through {@link #getConfig()}.
     64      *
     65      */
     66     public final static class DeviceConfig {
     67         private final String mName;
     68         private final FolderConfiguration mConfig;
     69 
     70         DeviceConfig(String name, FolderConfiguration config) {
     71             mName = name;
     72             mConfig = config;
     73         }
     74 
     75         public String getName() {
     76             return mName;
     77         }
     78 
     79         public FolderConfiguration getConfig() {
     80             return mConfig;
     81         }
     82     }
     83 
     84     /** editable list of the config */
     85     private final ArrayList<DeviceConfig> mConfigs = new ArrayList<DeviceConfig>();
     86     /** Read-only list */
     87     private List<DeviceConfig> mROList;
     88 
     89     private float mXDpi = Float.NaN;
     90     private float mYDpi = Float.NaN;
     91 
     92     LayoutDevice(String name) {
     93         mName = name;
     94     }
     95 
     96     /**
     97      * Saves the Layout Device into a document under a given node
     98      * @param doc the document.
     99      * @param parentNode the parent node.
    100      */
    101     void saveTo(Document doc, Element parentNode) {
    102         // create the device node
    103         Element deviceNode = createNode(doc, parentNode, LayoutDevicesXsd.NODE_DEVICE);
    104 
    105         // create the name attribute (no namespace on this one).
    106         deviceNode.setAttribute(LayoutDevicesXsd.ATTR_NAME, mName);
    107 
    108         // create a default with the x/y dpi
    109         Element defaultNode = createNode(doc, deviceNode, LayoutDevicesXsd.NODE_DEFAULT);
    110         if (Float.isNaN(mXDpi) == false) {
    111             Element xdpiNode = createNode(doc, defaultNode, LayoutDevicesXsd.NODE_XDPI);
    112             xdpiNode.setTextContent(Float.toString(mXDpi));
    113         }
    114         if (Float.isNaN(mYDpi) == false) {
    115             Element xdpiNode = createNode(doc, defaultNode, LayoutDevicesXsd.NODE_YDPI);
    116             xdpiNode.setTextContent(Float.toString(mYDpi));
    117         }
    118 
    119         // then save all the configs.
    120         synchronized (mConfigs) {
    121             for (DeviceConfig config : mConfigs) {
    122                 saveConfigTo(doc, deviceNode, config.getName(), config.getConfig());
    123             }
    124         }
    125     }
    126 
    127     /**
    128      * Creates and returns a new NS-enabled node.
    129      * @param doc the {@link Document}
    130      * @param parentNode the parent node. The new node is appended to this one as a child.
    131      * @param name the name of the node.
    132      * @return the newly created node.
    133      */
    134     private Element createNode(Document doc, Element parentNode, String name) {
    135         Element newNode = doc.createElementNS(
    136                 LayoutDevicesXsd.NS_LAYOUT_DEVICE_XSD, name);
    137         newNode.setPrefix(doc.lookupPrefix(LayoutDevicesXsd.NS_LAYOUT_DEVICE_XSD));
    138         parentNode.appendChild(newNode);
    139 
    140         return newNode;
    141     }
    142 
    143     /**
    144      * Saves a {@link FolderConfiguration} in a {@link Document}.
    145      * @param doc the Document in which to save
    146      * @param parent the parent node
    147      * @param configName the name of the config
    148      * @param config the config to save
    149      */
    150     private void saveConfigTo(Document doc, Element parent, String configName,
    151             FolderConfiguration config) {
    152         Element configNode = createNode(doc, parent, LayoutDevicesXsd.NODE_CONFIG);
    153 
    154         // create the name attribute (no namespace on this one).
    155         configNode.setAttribute(LayoutDevicesXsd.ATTR_NAME, configName);
    156 
    157         // now do the qualifiers
    158         CountryCodeQualifier ccq = config.getCountryCodeQualifier();
    159         if (ccq != null) {
    160             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_COUNTRY_CODE);
    161             node.setTextContent(Integer.toString(ccq.getCode()));
    162         }
    163 
    164         NetworkCodeQualifier ncq = config.getNetworkCodeQualifier();
    165         if (ncq != null) {
    166             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NETWORK_CODE);
    167             node.setTextContent(Integer.toString(ncq.getCode()));
    168         }
    169 
    170         ScreenSizeQualifier slsq = config.getScreenSizeQualifier();
    171         if (slsq != null) {
    172             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_SIZE);
    173             node.setTextContent(slsq.getFolderSegment());
    174         }
    175 
    176         ScreenRatioQualifier srq = config.getScreenRatioQualifier();
    177         if (srq != null) {
    178             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_RATIO);
    179             node.setTextContent(srq.getFolderSegment());
    180         }
    181 
    182         ScreenOrientationQualifier soq = config.getScreenOrientationQualifier();
    183         if (soq != null) {
    184             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_ORIENTATION);
    185             node.setTextContent(soq.getFolderSegment());
    186         }
    187 
    188         DensityQualifier dq = config.getDensityQualifier();
    189         if (dq != null) {
    190             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_PIXEL_DENSITY);
    191             node.setTextContent(dq.getFolderSegment());
    192         }
    193 
    194         TouchScreenQualifier ttq = config.getTouchTypeQualifier();
    195         if (ttq != null) {
    196             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_TOUCH_TYPE);
    197             node.setTextContent(ttq.getFolderSegment());
    198         }
    199 
    200         KeyboardStateQualifier ksq = config.getKeyboardStateQualifier();
    201         if (ksq != null) {
    202             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_KEYBOARD_STATE);
    203             node.setTextContent(ksq.getFolderSegment());
    204         }
    205 
    206         TextInputMethodQualifier timq = config.getTextInputMethodQualifier();
    207         if (timq != null) {
    208             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_TEXT_INPUT_METHOD);
    209             node.setTextContent(timq.getFolderSegment());
    210         }
    211 
    212         NavigationStateQualifier nsq = config.getNavigationStateQualifier();
    213         if (nsq != null) {
    214             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NAV_STATE);
    215             node.setTextContent(nsq.getFolderSegment());
    216         }
    217 
    218         NavigationMethodQualifier nmq = config.getNavigationMethodQualifier();
    219         if (nmq != null) {
    220             Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NAV_METHOD);
    221             node.setTextContent(nmq.getFolderSegment());
    222         }
    223 
    224         ScreenDimensionQualifier sdq = config.getScreenDimensionQualifier();
    225         if (sdq != null) {
    226             Element sizeNode = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_DIMENSION);
    227 
    228             Element node = createNode(doc, sizeNode, LayoutDevicesXsd.NODE_SIZE);
    229             node.setTextContent(Integer.toString(sdq.getValue1()));
    230 
    231             node = createNode(doc, sizeNode, LayoutDevicesXsd.NODE_SIZE);
    232             node.setTextContent(Integer.toString(sdq.getValue2()));
    233         }
    234     }
    235 
    236     /**
    237      * Adds config to the LayoutDevice.
    238      * <p/>This ensures that no two configurations have the same. If a config already exists
    239      * with the same name, the new config replaces it.
    240      *
    241      * @param name the name of the config.
    242      * @param config the config.
    243      */
    244     void addConfig(String name, FolderConfiguration config) {
    245         synchronized (mConfigs) {
    246             doAddConfig(name, config);
    247             seal();
    248         }
    249     }
    250 
    251     /**
    252      * Adds a list of config to the LayoutDevice
    253      * <p/>This ensures that no two configurations have the same. If a config already exists
    254      * with the same name, the new config replaces it.
    255 
    256      * @param configs the configs to add.
    257      */
    258     void addConfigs(List<DeviceConfig> configs) {
    259         synchronized (mConfigs) {
    260             // add the configs manually one by one, to check for no duplicate.
    261             for (DeviceConfig config : configs) {
    262                 String name = config.getName();
    263 
    264                 for (DeviceConfig c : mConfigs) {
    265                     if (c.getName().equals(name)) {
    266                         mConfigs.remove(c);
    267                         break;
    268                     }
    269                 }
    270 
    271                 mConfigs.add(config);
    272             }
    273 
    274             seal();
    275         }
    276     }
    277 
    278     /**
    279      * Removes a config by its name.
    280      * @param name the name of the config to remove.
    281      */
    282     void removeConfig(String name) {
    283         synchronized (mConfigs) {
    284             for (DeviceConfig config : mConfigs) {
    285                 if (config.getName().equals(name)) {
    286                     mConfigs.remove(config);
    287                     seal();
    288                     return;
    289                 }
    290             }
    291         }
    292     }
    293 
    294     /**
    295      * Adds config to the LayoutDevice. This is to be used to add plenty of
    296      * configurations. It must be followed by {@link #_seal()}.
    297      * <p/>This ensures that no two configurations have the same. If a config already exists
    298      * with the same name, the new config replaces it.
    299      * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong>
    300      *
    301      * @param name the name of the config
    302      * @param config the config.
    303      */
    304     private void doAddConfig(String name, FolderConfiguration config) {
    305         // remove config that would have the same name to ensure no duplicate
    306         for (DeviceConfig c : mConfigs) {
    307             if (c.getName().equals(name)) {
    308                 mConfigs.remove(c);
    309                 break;
    310             }
    311         }
    312         mConfigs.add(new DeviceConfig(name, config));
    313     }
    314 
    315     /**
    316      * Seals the layout device by setting up {@link #mROList}.
    317      * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong>
    318      */
    319     private void seal() {
    320         mROList = Collections.unmodifiableList(mConfigs);
    321     }
    322 
    323     void setXDpi(float xdpi) {
    324         mXDpi = xdpi;
    325     }
    326 
    327     void setYDpi(float ydpi) {
    328         mYDpi = ydpi;
    329     }
    330 
    331     public String getName() {
    332         return mName;
    333     }
    334 
    335     /**
    336      * Returns an unmodifiable list of all the {@link DeviceConfig}.
    337      */
    338     public List<DeviceConfig> getConfigs() {
    339         synchronized (mConfigs) {
    340             return mROList;
    341         }
    342     }
    343 
    344     /**
    345      * Returns a {@link DeviceConfig} by its name.
    346      */
    347     public DeviceConfig getDeviceConfigByName(String name) {
    348         synchronized (mConfigs) {
    349             for (DeviceConfig config : mConfigs) {
    350                 if (config.getName().equals(name)) {
    351                     return config;
    352                 }
    353             }
    354         }
    355 
    356         return null;
    357     }
    358 
    359     /**
    360      * Returns a {@link FolderConfiguration} by its name.
    361      */
    362     public FolderConfiguration getFolderConfigByName(String name) {
    363         synchronized (mConfigs) {
    364             for (DeviceConfig config : mConfigs) {
    365                 if (config.getName().equals(name)) {
    366                     return config.getConfig();
    367                 }
    368             }
    369         }
    370 
    371         return null;
    372     }
    373 
    374     /**
    375      * Returns the dpi of the Device screen in X.
    376      * @return the dpi of screen or {@link Float#NaN} if it's not set.
    377      */
    378     public float getXDpi() {
    379         return mXDpi;
    380     }
    381 
    382     /**
    383      * Returns the dpi of the Device screen in Y.
    384      * @return the dpi of screen or {@link Float#NaN} if it's not set.
    385      */
    386     public float getYDpi() {
    387         return mYDpi;
    388     }
    389  }
    390