/* * Copyright (C) 2009 The Android Open Source Project * * Licensed under the Eclipse Public License, Version 1.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.eclipse.org/org/documents/epl-v10.php * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.android.ide.eclipse.adt.internal.sdk; import com.android.ide.eclipse.adt.internal.resources.configurations.CountryCodeQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.FolderConfiguration; import com.android.ide.eclipse.adt.internal.resources.configurations.KeyboardStateQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationMethodQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.NavigationStateQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.NetworkCodeQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.PixelDensityQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenDimensionQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenOrientationQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenRatioQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.ScreenSizeQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.TextInputMethodQualifier; import com.android.ide.eclipse.adt.internal.resources.configurations.TouchScreenQualifier; import org.w3c.dom.Document; import org.w3c.dom.Element; import java.util.ArrayList; import java.util.Collections; import java.util.List; /** * Class representing a layout device. * * A Layout device is a collection of {@link FolderConfiguration} that can be used to render Android * layout files. * * It also contains a single xdpi/ydpi that is independent of the {@link FolderConfiguration}. * * If the device is meant to represent a true device, then most of the FolderConfigurations' content * should be identical, with only a few qualifiers (orientation, keyboard state) that would differ. * However it is simpler to reuse the FolderConfiguration class (with the non changing qualifiers * duplicated in each configuration) as it's what's being used by the rendering library. * * To create, edit and delete LayoutDevice objects, see {@link LayoutDeviceManager}. * The class is not technically immutable but behaves as such outside of its package. */ public class LayoutDevice { private final String mName; /** * Wrapper around a {@link FolderConfiguration}. * <p/>This adds a name, accessible through {@link #getName()}. * <p/>The folder config can be accessed through {@link #getConfig()}. * */ public final static class DeviceConfig { private final String mName; private final FolderConfiguration mConfig; DeviceConfig(String name, FolderConfiguration config) { mName = name; mConfig = config; } public String getName() { return mName; } public FolderConfiguration getConfig() { return mConfig; } } /** editable list of the config */ private final ArrayList<DeviceConfig> mConfigs = new ArrayList<DeviceConfig>(); /** Read-only list */ private List<DeviceConfig> mROList; private float mXDpi = Float.NaN; private float mYDpi = Float.NaN; LayoutDevice(String name) { mName = name; } /** * Saves the Layout Device into a document under a given node * @param doc the document. * @param parentNode the parent node. */ void saveTo(Document doc, Element parentNode) { // create the device node Element deviceNode = createNode(doc, parentNode, LayoutDevicesXsd.NODE_DEVICE); // create the name attribute (no namespace on this one). deviceNode.setAttribute(LayoutDevicesXsd.ATTR_NAME, mName); // create a default with the x/y dpi Element defaultNode = createNode(doc, deviceNode, LayoutDevicesXsd.NODE_DEFAULT); if (Float.isNaN(mXDpi) == false) { Element xdpiNode = createNode(doc, defaultNode, LayoutDevicesXsd.NODE_XDPI); xdpiNode.setTextContent(Float.toString(mXDpi)); } if (Float.isNaN(mYDpi) == false) { Element xdpiNode = createNode(doc, defaultNode, LayoutDevicesXsd.NODE_YDPI); xdpiNode.setTextContent(Float.toString(mYDpi)); } // then save all the configs. synchronized (mConfigs) { for (DeviceConfig config : mConfigs) { saveConfigTo(doc, deviceNode, config.getName(), config.getConfig()); } } } /** * Creates and returns a new NS-enabled node. * @param doc the {@link Document} * @param parentNode the parent node. The new node is appended to this one as a child. * @param name the name of the node. * @return the newly created node. */ private Element createNode(Document doc, Element parentNode, String name) { Element newNode = doc.createElementNS( LayoutDevicesXsd.NS_LAYOUT_DEVICE_XSD, name); newNode.setPrefix(doc.lookupPrefix(LayoutDevicesXsd.NS_LAYOUT_DEVICE_XSD)); parentNode.appendChild(newNode); return newNode; } /** * Saves a {@link FolderConfiguration} in a {@link Document}. * @param doc the Document in which to save * @param parent the parent node * @param configName the name of the config * @param config the config to save */ private void saveConfigTo(Document doc, Element parent, String configName, FolderConfiguration config) { Element configNode = createNode(doc, parent, LayoutDevicesXsd.NODE_CONFIG); // create the name attribute (no namespace on this one). configNode.setAttribute(LayoutDevicesXsd.ATTR_NAME, configName); // now do the qualifiers CountryCodeQualifier ccq = config.getCountryCodeQualifier(); if (ccq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_COUNTRY_CODE); node.setTextContent(Integer.toString(ccq.getCode())); } NetworkCodeQualifier ncq = config.getNetworkCodeQualifier(); if (ncq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NETWORK_CODE); node.setTextContent(Integer.toString(ncq.getCode())); } ScreenSizeQualifier ssq = config.getScreenSizeQualifier(); if (ssq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_SIZE); node.setTextContent(ssq.getFolderSegment()); } ScreenRatioQualifier srq = config.getScreenRatioQualifier(); if (srq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_RATIO); node.setTextContent(srq.getFolderSegment()); } ScreenOrientationQualifier soq = config.getScreenOrientationQualifier(); if (soq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_ORIENTATION); node.setTextContent(soq.getFolderSegment()); } PixelDensityQualifier pdq = config.getPixelDensityQualifier(); if (pdq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_PIXEL_DENSITY); node.setTextContent(pdq.getFolderSegment()); } TouchScreenQualifier ttq = config.getTouchTypeQualifier(); if (ttq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_TOUCH_TYPE); node.setTextContent(ttq.getFolderSegment()); } KeyboardStateQualifier ksq = config.getKeyboardStateQualifier(); if (ksq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_KEYBOARD_STATE); node.setTextContent(ksq.getFolderSegment()); } TextInputMethodQualifier timq = config.getTextInputMethodQualifier(); if (timq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_TEXT_INPUT_METHOD); node.setTextContent(timq.getFolderSegment()); } NavigationStateQualifier nsq = config.getNavigationStateQualifier(); if (nsq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NAV_STATE); node.setTextContent(nsq.getFolderSegment()); } NavigationMethodQualifier nmq = config.getNavigationMethodQualifier(); if (nmq != null) { Element node = createNode(doc, configNode, LayoutDevicesXsd.NODE_NAV_METHOD); node.setTextContent(nmq.getFolderSegment()); } ScreenDimensionQualifier sdq = config.getScreenDimensionQualifier(); if (sdq != null) { Element sizeNode = createNode(doc, configNode, LayoutDevicesXsd.NODE_SCREEN_DIMENSION); Element node = createNode(doc, sizeNode, LayoutDevicesXsd.NODE_SIZE); node.setTextContent(Integer.toString(sdq.getValue1())); node = createNode(doc, sizeNode, LayoutDevicesXsd.NODE_SIZE); node.setTextContent(Integer.toString(sdq.getValue2())); } } /** * Adds config to the LayoutDevice. * <p/>This ensures that no two configurations have the same. If a config already exists * with the same name, the new config replaces it. * * @param name the name of the config. * @param config the config. */ void addConfig(String name, FolderConfiguration config) { synchronized (mConfigs) { doAddConfig(name, config); seal(); } } /** * Adds a list of config to the LayoutDevice * <p/>This ensures that no two configurations have the same. If a config already exists * with the same name, the new config replaces it. * @param configs the configs to add. */ void addConfigs(List<DeviceConfig> configs) { synchronized (mConfigs) { // add the configs manually one by one, to check for no duplicate. for (DeviceConfig config : configs) { String name = config.getName(); for (DeviceConfig c : mConfigs) { if (c.getName().equals(name)) { mConfigs.remove(c); break; } } mConfigs.add(config); } seal(); } } /** * Removes a config by its name. * @param name the name of the config to remove. */ void removeConfig(String name) { synchronized (mConfigs) { for (DeviceConfig config : mConfigs) { if (config.getName().equals(name)) { mConfigs.remove(config); seal(); return; } } } } /** * Adds config to the LayoutDevice. This is to be used to add plenty of * configurations. It must be followed by {@link #_seal()}. * <p/>This ensures that no two configurations have the same. If a config already exists * with the same name, the new config replaces it. * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong> * * @param name the name of the config * @param config the config. */ private void doAddConfig(String name, FolderConfiguration config) { // remove config that would have the same name to ensure no duplicate for (DeviceConfig c : mConfigs) { if (c.getName().equals(name)) { mConfigs.remove(c); break; } } mConfigs.add(new DeviceConfig(name, config)); } /** * Seals the layout device by setting up {@link #mROList}. * <p/><strong>This must be called inside a <code>synchronized(mConfigs)</code> block.</strong> */ private void seal() { mROList = Collections.unmodifiableList(mConfigs); } void setXDpi(float xdpi) { mXDpi = xdpi; } void setYDpi(float ydpi) { mYDpi = ydpi; } public String getName() { return mName; } /** * Returns an unmodifiable list of all the {@link DeviceConfig}. */ public List<DeviceConfig> getConfigs() { synchronized (mConfigs) { return mROList; } } /** * Returns a {@link DeviceConfig} by its name. */ public DeviceConfig getDeviceConfigByName(String name) { synchronized (mConfigs) { for (DeviceConfig config : mConfigs) { if (config.getName().equals(name)) { return config; } } } return null; } /** * Returns a {@link FolderConfiguration} by its name. */ public FolderConfiguration getFolderConfigByName(String name) { synchronized (mConfigs) { for (DeviceConfig config : mConfigs) { if (config.getName().equals(name)) { return config.getConfig(); } } } return null; } /** * Returns the dpi of the Device screen in X. * @return the dpi of screen or {@link Float#NaN} if it's not set. */ public float getXDpi() { return mXDpi; } /** * Returns the dpi of the Device screen in Y. * @return the dpi of screen or {@link Float#NaN} if it's not set. */ public float getYDpi() { return mYDpi; } }