• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DeviceConfig(String name, FolderConfiguration config)70         DeviceConfig(String name, FolderConfiguration config) {
71             mName = name;
72             mConfig = config;
73         }
74 
getName()75         public String getName() {
76             return mName;
77         }
78 
getConfig()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 
LayoutDevice(String name)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      */
saveTo(Document doc, Element parentNode)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      */
createNode(Document doc, Element parentNode, String name)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      */
saveConfigTo(Document doc, Element parent, String configName, FolderConfiguration config)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      */
addConfig(String name, FolderConfiguration config)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      */
addConfigs(List<DeviceConfig> configs)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      */
removeConfig(String name)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      */
doAddConfig(String name, FolderConfiguration config)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      */
seal()319     private void seal() {
320         mROList = Collections.unmodifiableList(mConfigs);
321     }
322 
setXDpi(float xdpi)323     void setXDpi(float xdpi) {
324         mXDpi = xdpi;
325     }
326 
setYDpi(float ydpi)327     void setYDpi(float ydpi) {
328         mYDpi = ydpi;
329     }
330 
getName()331     public String getName() {
332         return mName;
333     }
334 
335     /**
336      * Returns an unmodifiable list of all the {@link DeviceConfig}.
337      */
getConfigs()338     public List<DeviceConfig> getConfigs() {
339         synchronized (mConfigs) {
340             return mROList;
341         }
342     }
343 
344     /**
345      * Returns a {@link DeviceConfig} by its name.
346      */
getDeviceConfigByName(String name)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      */
getFolderConfigByName(String name)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      */
getXDpi()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      */
getYDpi()386     public float getYDpi() {
387         return mYDpi;
388     }
389  }
390