*/
public interface IViewMetadata {
/**
- * Returns true if this view is a potential parent (e.g. it <b>can</b> have children).
- *
- * @return true if this view can have children
- */
- public boolean isParent();
-
- /**
* Returns the display name views of this type (a name suitable to display to the
* user, normally capitalized and usually but not necessarily tied to the
* implementation class). To be clear, a specific view may have an id attribute and a
public String getDisplayName();
/**
- * Returns the tooltip for this view, if any
- *
- * @return a tooltip, or null
- */
- public String getTooltip();
-
- /**
* Returns the {@link FillPreference} of this view
*
* @return the {@link FillPreference} of this view
if (fullActionId.equals(WIDTH_ID)) {
final String newAttrValue = getValue(valueId, customWidth);
if (newAttrValue != null) {
- node.editXml("Change attribute " + ATTR_LAYOUT_WIDTH,
+ node.editXml("Change Attribute " + ATTR_LAYOUT_WIDTH,
new PropertySettingNodeHandler(ANDROID_URI,
ATTR_LAYOUT_WIDTH, newAttrValue));
}
// Ask the user
final String newAttrValue = getValue(valueId, customHeight);
if (newAttrValue != null) {
- node.editXml("Change attribute " + ATTR_LAYOUT_HEIGHT,
+ node.editXml("Change Attribute " + ATTR_LAYOUT_HEIGHT,
new PropertySettingNodeHandler(ANDROID_URI,
ATTR_LAYOUT_HEIGHT, newAttrValue));
}
if (!newId.startsWith(NEW_ID_PREFIX)) {
newId = NEW_ID_PREFIX + stripIdPrefix(newId);
}
- node.editXml("Change id", new PropertySettingNodeHandler(ANDROID_URI,
+ node.editXml("Change ID", new PropertySettingNodeHandler(ANDROID_URI,
ATTR_ID, newId));
}
} else if (fullActionId.equals(EDIT_TEXT_ID)) {
oldText = ensureValidString(oldText);
String newText = mRulesEngine.displayResourceInput("string", oldText); //$NON-NLS-1$
if (newText != null) {
- node.editXml("Change text", new PropertySettingNodeHandler(ANDROID_URI,
+ node.editXml("Change Text", new PropertySettingNodeHandler(ANDROID_URI,
ATTR_TEXT, newText));
}
}
final String customValue = prop.isStringEdit()
? inputAttributeValue(node, actionId) : null;
- node.editXml("Change attribute " + actionId, new INodeHandler() {
+ node.editXml("Change Attribute " + actionId, new INodeHandler() {
public void handle(INode n) {
if (prop.isToggle()) {
// case of toggle
package com.android.ide.eclipse.adt.internal.editors.layout;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
LayoutDescriptors descriptors = data.getLayoutDescriptors();
mLayoutNames = new HashSet<String>();
- List<ElementDescriptor> layoutDescriptors = descriptors.getLayoutDescriptors();
- for (ElementDescriptor desc : layoutDescriptors) {
+ List<ViewElementDescriptor> layoutDescriptors = descriptors.getLayoutDescriptors();
+ for (ViewElementDescriptor desc : layoutDescriptors) {
mLayoutNames.add(desc.getXmlLocalName());
}
import com.android.ide.common.rendering.api.ILayoutPullParser;
import com.android.ide.common.rendering.api.ResourceDensity;
import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiAttributeNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
private final boolean mExplodedRendering;
private boolean mZeroAttributeIsPadding = false;
private boolean mIncreaseExistingPadding = false;
- private List<ElementDescriptor> mLayoutDescriptors;
+ private List<ViewElementDescriptor> mLayoutDescriptors;
private final int mDensityValue;
private final float mXdpi;
if (mExplodedRendering) {
// first get the node name
String xml = node.getDescriptor().getXmlLocalName();
- for (ElementDescriptor descriptor : mLayoutDescriptors) {
+ for (ViewElementDescriptor descriptor : mLayoutDescriptors) {
if (xml.equals(descriptor.getXmlLocalName())) {
NamedNodeMap attributes = node.getXmlNode().getAttributes();
Node padding = attributes.getNamedItemNS(SdkConstants.NS_RESOURCES, "padding");
import com.android.ide.common.resources.platform.ViewClassInfo;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.ide.eclipse.adt.internal.sdk.Sdk;
import com.android.sdklib.IAndroidTarget;
private ViewElementDescriptor createViewDescriptor(IType type, IProject project,
ITypeHierarchy typeHierarchy) {
// check if the type is a built-in View class.
- List<ElementDescriptor> builtInList = null;
+ List<ViewElementDescriptor> builtInList = null;
Sdk currentSdk = Sdk.getCurrent();
if (currentSdk != null) {
String fqcn = type.getFullyQualifiedName();
if (builtInList != null) {
- for (ElementDescriptor desc : builtInList) {
- if (desc instanceof ViewElementDescriptor) {
- ViewElementDescriptor viewDescriptor = (ViewElementDescriptor)desc;
- if (fqcn.equals(viewDescriptor.getFullClassName())) {
- return viewDescriptor;
- }
+ for (ViewElementDescriptor viewDescriptor : builtInList) {
+ if (fqcn.equals(viewDescriptor.getFullClassName())) {
+ return viewDescriptor;
}
}
}
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
+import java.util.Map;
import java.util.Map.Entry;
new DocumentDescriptor("layout_doc", null); //$NON-NLS-1$
/** The list of all known ViewLayout descriptors. */
- private ArrayList<ElementDescriptor> mLayoutDescriptors = new ArrayList<ElementDescriptor>();
+ private List<ViewElementDescriptor> mLayoutDescriptors =
+ new ArrayList<ViewElementDescriptor>();
/** Read-Only list of View Descriptors. */
- private List<ElementDescriptor> mROLayoutDescriptors;
+ private List<ViewElementDescriptor> mROLayoutDescriptors;
/** The list of all known View (not ViewLayout) descriptors. */
- private ArrayList<ElementDescriptor> mViewDescriptors = new ArrayList<ElementDescriptor>();
+ private List<ViewElementDescriptor> mViewDescriptors = new ArrayList<ViewElementDescriptor>();
/** Read-Only list of View Descriptors. */
- private List<ElementDescriptor> mROViewDescriptors;
+ private List<ViewElementDescriptor> mROViewDescriptors;
/** The descriptor matching android.view.View. */
private ViewElementDescriptor mBaseViewDescriptor;
}
/** Returns the read-only list of all known ViewLayout descriptors. */
- public List<ElementDescriptor> getLayoutDescriptors() {
+ public List<ViewElementDescriptor> getLayoutDescriptors() {
return mROLayoutDescriptors;
}
/** Returns the read-only list of all known View (not ViewLayout) descriptors. */
- public List<ElementDescriptor> getViewDescriptors() {
+ public List<ViewElementDescriptor> getViewDescriptors() {
return mROViewDescriptors;
}
HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap =
new HashMap<ViewClassInfo, ViewElementDescriptor>();
- ArrayList<ElementDescriptor> newViews = new ArrayList<ElementDescriptor>();
+ ArrayList<ViewElementDescriptor> newViews = new ArrayList<ViewElementDescriptor>();
if (views != null) {
for (ViewClassInfo info : views) {
- ElementDescriptor desc = convertView(info, infoDescMap);
+ ViewElementDescriptor desc = convertView(info, infoDescMap);
newViews.add(desc);
}
}
// Note: ViewStub is already described by attrs.xml
insertInclude(newViews);
- ArrayList<ElementDescriptor> newLayouts = new ArrayList<ElementDescriptor>();
+ List<ViewElementDescriptor> newLayouts = new ArrayList<ViewElementDescriptor>();
if (layouts != null) {
for (ViewClassInfo info : layouts) {
- ElementDescriptor desc = convertView(info, infoDescMap);
+ ViewElementDescriptor desc = convertView(info, infoDescMap);
newLayouts.add(desc);
}
}
- ArrayList<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
+ List<ElementDescriptor> newDescriptors = new ArrayList<ElementDescriptor>();
newDescriptors.addAll(newLayouts);
newDescriptors.addAll(newViews);
// Link all layouts to everything else here.. recursively
- for (ElementDescriptor layoutDesc : newLayouts) {
+ for (ViewElementDescriptor layoutDesc : newLayouts) {
layoutDesc.setChildren(newDescriptors);
}
// The <merge> tag can only be a root tag, so it is added at the end.
// It gets everything else as children but it is not made a child itself.
- ElementDescriptor mergeTag = createMerge(newLayouts);
+ ViewElementDescriptor mergeTag = createMerge(newLayouts);
mergeTag.setChildren(newDescriptors); // mergeTag makes a copy of the list
newDescriptors.add(mergeTag);
newLayouts.add(mergeTag);
* @param infoDescMap This map links every ViewClassInfo to the ElementDescriptor it created.
* It is filled by here and used later to fix the super-class hierarchy.
*/
- private ElementDescriptor convertView(
+ private ViewElementDescriptor convertView(
ViewClassInfo info,
HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
String xml_name = info.getShortClassName();
* @param knownViews A list of view descriptors being populated. Also used to find the
* View descriptor and extract its layout attributes.
*/
- private void insertInclude(ArrayList<ElementDescriptor> knownViews) {
+ private void insertInclude(List<ViewElementDescriptor> knownViews) {
String xml_name = VIEW_INCLUDE;
// Create the include custom attributes
* @param knownLayouts A list of all known layout view descriptors, used to find the
* FrameLayout descriptor and extract its layout attributes.
*/
- private ElementDescriptor createMerge(ArrayList<ElementDescriptor> knownLayouts) {
+ private ViewElementDescriptor createMerge(List<ViewElementDescriptor> knownLayouts) {
String xml_name = VIEW_MERGE;
// Find View and inherit all its layout attributes
*/
private AttributeDescriptor[] findViewLayoutAttributes(
String viewFqcn,
- ArrayList<ElementDescriptor> knownViews) {
+ List<ViewElementDescriptor> knownViews) {
- for (ElementDescriptor desc : knownViews) {
- if (desc instanceof ViewElementDescriptor) {
- ViewElementDescriptor viewDesc = (ViewElementDescriptor) desc;
- if (viewFqcn.equals(viewDesc.getFullClassName())) {
- return viewDesc.getLayoutAttributes();
- }
+ for (ViewElementDescriptor viewDesc : knownViews) {
+ if (viewFqcn.equals(viewDesc.getFullClassName())) {
+ return viewDesc.getLayoutAttributes();
}
}
* Set the super-class of each {@link ViewElementDescriptor} by using the super-class
* information available in the {@link ViewClassInfo}.
*/
- private void fixSuperClasses(HashMap<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
+ private void fixSuperClasses(Map<ViewClassInfo, ViewElementDescriptor> infoDescMap) {
for (Entry<ViewClassInfo, ViewElementDescriptor> entry : infoDescMap.entrySet()) {
ViewClassInfo info = entry.getKey();
import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeFactory;
import com.android.ide.eclipse.adt.internal.editors.layout.gre.NodeProxy;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
GridDataBuilder.create(mRoot).hGrab().hFill();
if (targetData != null) {
+ // TODO: Use metadata categories instead:
+ //List<Pair<String,List<ViewElementDescriptor>>> paletteEntries =
+ // ViewMetadataRepository.get().getPaletteEntries(targetData);
+ //for (Pair<String,List<ViewElementDescriptor>> pair : paletteEntries) {
+ // addGroup(mRoot, pair.getFirst(), pair.getSecond());
+ //}
addGroup(mRoot, "Views", targetData.getLayoutDescriptors().getViewDescriptors());
addGroup(mRoot, "Layouts", targetData.getLayoutDescriptors().getLayoutDescriptors());
}
private void addGroup(Composite parent,
String uiName,
- List<ElementDescriptor> descriptors) {
+ List<ViewElementDescriptor> descriptors) {
Composite group = new Composite(parent, SWT.NONE);
GridLayoutBuilder.create(group).columns(1).columnsEqual().spacing(0).noMargins();
Toggle toggle = new Toggle(group, uiName);
GridDataBuilder.create(toggle).hFill().hGrab();
- for (ElementDescriptor desc : descriptors) {
+ for (ViewElementDescriptor desc : descriptors) {
// Exclude the <include> and <merge> tags from the View palette.
// We don't have drop support for it right now, although someday we should.
}
public IViewMetadata getMetadata(final String fqcn) {
- return new ViewMetadata(mEditor.getLayoutEditor(), fqcn);
+ return new IViewMetadata() {
+ public String getDisplayName() {
+ // This also works when there is no "."
+ return fqcn.substring(fqcn.lastIndexOf('.') + 1);
+ }
+
+ public FillPreference getFillPreference() {
+ return ViewMetadataRepository.get().getFillPreference(fqcn);
+ }
+ };
}
public int getMinApiLevel() {
+++ /dev/null
-/*
- * Copyright (C) 2010 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.editors.layout.gre;
-
-/**
- * An implementation of {@link IViewMetadata} which consults the
- * SDK descriptors to answer metadata questions about views.
- */
-import com.android.ide.common.api.IViewMetadata;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.layout.LayoutEditor;
-
-import java.util.HashMap;
-import java.util.Map;
-
-final class ViewMetadata implements IViewMetadata {
- /** The {@link ElementDescriptor} for this view, computed lazily */
- private ElementDescriptor mDescriptor;
-
- /** The fully qualified class name of the view whose metadata this class represents */
- private String mFqcn;
-
- /** The {@link LayoutEditor} associated with the view we're looking up */
- private LayoutEditor mEditor;
-
- /**
- * A map from class names to {@link IViewMetadata.FillPreference} which indicates how each view
- * prefers to grow when added in various layout contexts
- */
- private static final Map<String, FillPreference> mFill = new HashMap<String,FillPreference>();
- static {
- // Hardcoded metadata about fill preferences for various known views. We should
- // work to try to get this into the platform as designtime annotations.
-
- mFill.put("android.widget.EditText", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.DialerFilter", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.SeekBar", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.Spinner", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.AutoComplete", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.ListView", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.GridView", FillPreference.OPPOSITE); //$NON-NLS-1$
- mFill.put("android.widget.Gallery", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.TabWidget", FillPreference.WIDTH_IN_VERTICAL); //$NON-NLS-1$
- mFill.put("android.widget.MapView", FillPreference.OPPOSITE); //$NON-NLS-1$
- mFill.put("android.widget.WebView", FillPreference.OPPOSITE); //$NON-NLS-1$
-
- // In addition, all layouts are FillPreference.OPPOSITE - these are computed
- // lazily rather than enumerating them here
-
- // For any other view, the fallback fill preference is FillPreference.NONE.
- }
-
- public ViewMetadata(LayoutEditor editor, String fqcn) {
- super();
- mFqcn = fqcn;
- mEditor = editor;
- }
-
- /** Lazily look up the descriptor for the FQCN of this metadata object */
- private boolean findDescriptor() {
- if (mDescriptor == null) {
- // Look up the corresponding view element node. We don't need the graphical part;
- // we just need the project context. Maybe I should extract this code into
- // a utility.
- mDescriptor = mEditor.getFqcnViewDescriptor(mFqcn);
- }
-
- return mDescriptor != null;
- }
-
- public boolean isParent() {
- if (findDescriptor()) {
- return mDescriptor.hasChildren();
- }
-
- return false;
- }
-
- public String getDisplayName() {
- if (findDescriptor()) {
- return mDescriptor.getUiName();
- }
-
- return mFqcn.substring(mFqcn.lastIndexOf('.') + 1); // This also works when there is no "."
- }
-
- public String getTooltip() {
- if (findDescriptor()) {
- return mDescriptor.getTooltip();
- }
-
- return null;
- }
-
- /** Returns true if this view represents a layout */
- private boolean isLayout() {
- if (findDescriptor()) {
- return mDescriptor.hasChildren();
- }
- return false;
- }
-
- public FillPreference getFillPreference() {
- FillPreference fillPreference = mFill.get(mFqcn);
- if (fillPreference == null) {
- if (isLayout()) {
- fillPreference = FillPreference.OPPOSITE;
- } else {
- fillPreference = FillPreference.NONE;
- }
- mFill.put(mFqcn, fillPreference);
- }
-
- return fillPreference;
- }
-}
--- /dev/null
+/*
+ * Copyright (C) 2011 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.editors.layout.gre;
+
+import static com.android.ide.common.api.IViewMetadata.FillPreference.NONE;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_INCLUDE;
+import static com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors.VIEW_MERGE;
+
+import com.android.ide.common.api.IViewMetadata.FillPreference;
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
+import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
+import com.android.sdklib.util.Pair;
+
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+import org.xml.sax.InputSource;
+
+import java.io.BufferedInputStream;
+import java.io.InputStream;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.xml.parsers.DocumentBuilder;
+import javax.xml.parsers.DocumentBuilderFactory;
+
+/**
+ * The {@link ViewMetadataRepository} contains additional metadata for Android view
+ * classes
+ */
+public class ViewMetadataRepository {
+ /** Singleton instance */
+ private static ViewMetadataRepository sInstance = new ViewMetadataRepository();
+
+ /**
+ * Returns the singleton instance
+ *
+ * @return the {@link ViewMetadataRepository}
+ */
+ public static ViewMetadataRepository get() {
+ return sInstance;
+ }
+
+ /**
+ * Ever increasing counter used to assign natural ordering numbers to views and
+ * categories
+ */
+ private static int sNextOrdinal = 0;
+
+ /**
+ * List of categories (which contain views); constructed lazily so use
+ * {@link #getCategories()}
+ */
+ private List<CategoryData> mCategories;
+
+ /**
+ * Map from class names to view data objects; constructed lazily so use
+ * {@link #getClassToView}
+ */
+ private Map<String, ViewData> mClassToView;
+
+ /** Hidden constructor: Create via factory {@link #get()} instead */
+ private ViewMetadataRepository() {
+ }
+
+ /** Returns a map from class fully qualified names to {@link ViewData} objects */
+ private Map<String, ViewData> getClassToView() {
+ if (mClassToView == null) {
+ int initialSize = 75;
+ mClassToView = new HashMap<String, ViewData>(initialSize);
+ List<CategoryData> categories = getCategories();
+ for (CategoryData category : categories) {
+ for (ViewData view : category) {
+ mClassToView.put(view.getFcqn(), view);
+ }
+ }
+ assert mClassToView.size() <= initialSize;
+ }
+
+ return mClassToView;
+ }
+
+ /** Returns an ordered list of categories and views, parsed from a metadata file */
+ private List<CategoryData> getCategories() {
+ if (mCategories == null) {
+ mCategories = new ArrayList<CategoryData>();
+
+ DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
+ String fileName = "extra-view-metadata.xml"; //$NON-NLS-1$
+ InputStream inputStream = ViewMetadataRepository.class.getResourceAsStream(fileName);
+ InputSource is = new InputSource(new BufferedInputStream(inputStream));
+ try {
+ factory.setNamespaceAware(true);
+ factory.setValidating(false);
+ DocumentBuilder builder = factory.newDocumentBuilder();
+ Document document = builder.parse(is);
+ Map<String, FillPreference> fillTypes = new HashMap<String, FillPreference>();
+ for (FillPreference pref : FillPreference.values()) {
+ fillTypes.put(pref.toString().toLowerCase(), pref);
+ }
+
+ NodeList categoryNodes = document.getDocumentElement().getChildNodes();
+ for (int i = 0, n = categoryNodes.getLength(); i < n; i++) {
+ Node node = categoryNodes.item(i);
+ if (node.getNodeType() == Node.ELEMENT_NODE) {
+ Element element = (Element) node;
+ if (element.getNodeName().equals("category")) { //$NON-NLS-1$
+ String name = element.getAttribute("name"); //$NON-NLS-1$
+ CategoryData category = new CategoryData(name);
+ NodeList children = element.getChildNodes();
+ for (int j = 0, m = children.getLength(); j < m; j++) {
+ Node childNode = children.item(j);
+ if (childNode.getNodeType() == Node.ELEMENT_NODE) {
+ Element child = (Element) childNode;
+ String fqcn = child.getAttribute("class"); //$NON-NLS-1$
+ String fill = child.getAttribute("fill"); //$NON-NLS-1$
+ FillPreference fillPreference = null;
+ if (fill.length() > 0) {
+ fillPreference = fillTypes.get(fill);
+ }
+ if (fillPreference == null) {
+ fillPreference = NONE;
+ }
+ ViewData view = new ViewData(category, fqcn, fillPreference);
+ category.addView(view);
+ }
+ }
+
+ mCategories.add(category);
+ }
+ }
+ }
+ } catch (Exception e) {
+ AdtPlugin.log(e, "Invalid palette metadata"); //$NON-NLS-1$
+ }
+ }
+
+ return mCategories;
+ }
+
+ /**
+ * Computes the palette entries for the given {@link AndroidTargetData}, looking up the
+ * available node descriptors, categorizing and sorting them.
+ *
+ * @param targetData the target data for which to compute palette entries
+ * @return a list of pairs where each pair contains of the category label and an
+ * ordered list of elements to be included in that category
+ */
+ public List<Pair<String, List<ViewElementDescriptor>>> getPaletteEntries(
+ AndroidTargetData targetData) {
+ List<Pair<String, List<ViewElementDescriptor>>> result =
+ new ArrayList<Pair<String, List<ViewElementDescriptor>>>();
+
+ final Map<String, ViewData> viewMap = getClassToView();
+ Map<CategoryData, List<ViewElementDescriptor>> categories =
+ new TreeMap<CategoryData, List<ViewElementDescriptor>>();
+
+ // Locate the "Other" category
+ CategoryData other = null;
+ for (CategoryData category : getCategories()) {
+ if (category.getViewCount() == 0) {
+ other = category;
+ break;
+ }
+ }
+
+ List<List<ViewElementDescriptor>> lists = new ArrayList<List<ViewElementDescriptor>>(2);
+ LayoutDescriptors layoutDescriptors = targetData.getLayoutDescriptors();
+ lists.add(layoutDescriptors.getViewDescriptors());
+ lists.add(layoutDescriptors.getLayoutDescriptors());
+
+ for (List<ViewElementDescriptor> list : lists) {
+ for (ViewElementDescriptor view : list) {
+ String name = view.getXmlLocalName();
+
+ // Exclude the <include> and <merge> tags from the View palette.
+ // We don't have drop support for it right now, although someday we should.
+ if (VIEW_INCLUDE.equals(name) || VIEW_MERGE.equals(name)) {
+ continue;
+ }
+
+ ViewData viewData = getClassToView().get(view.getFullClassName());
+ CategoryData category = other;
+ if (viewData != null) {
+ category = viewData.getCategory();
+ }
+
+ List<ViewElementDescriptor> viewList = categories.get(category);
+ if (viewList == null) {
+ viewList = new ArrayList<ViewElementDescriptor>();
+ categories.put(category, viewList);
+ }
+ viewList.add(view);
+
+ }
+ }
+
+ for (Map.Entry<CategoryData, List<ViewElementDescriptor>> entry : categories.entrySet()) {
+ String name = entry.getKey().getName();
+ List<ViewElementDescriptor> items = entry.getValue();
+ if (items == null) {
+ continue; // empty category
+ }
+
+ // Natural sort of the descriptors
+ Comparator<ViewElementDescriptor> comparator = new Comparator<ViewElementDescriptor>() {
+ public int compare(ViewElementDescriptor v1, ViewElementDescriptor v2) {
+ String fqcn1 = v1.getFullClassName();
+ String fqcn2 = v2.getFullClassName();
+ if (fqcn1 == null) {
+ // <view> and <merge> tags etc
+ fqcn1 = v1.getUiName();
+ }
+ if (fqcn2 == null) {
+ fqcn2 = v2.getUiName();
+ }
+ ViewData d1 = viewMap.get(fqcn1);
+ ViewData d2 = viewMap.get(fqcn2);
+
+ // Use natural sorting order of the view data
+ // Sort unknown views to the end (and alphabetically among themselves)
+ if (d1 != null) {
+ if (d2 != null) {
+ return d1.getOrdinal() - d2.getOrdinal();
+ } else {
+ return 1;
+ }
+ } else {
+ if (d2 == null) {
+ return v1.getUiName().compareTo(v2.getUiName());
+ } else {
+ return -1;
+ }
+ }
+ }
+ };
+ Collections.sort(items, comparator);
+ result.add(Pair.of(name, items));
+ }
+
+ return result;
+ }
+
+ /**
+ * Metadata holder for a particular category - contains the name of the category, its
+ * ordinal (for natural/logical sorting order) and views contained in the category
+ */
+ private static class CategoryData implements Iterable<ViewData>, Comparable<CategoryData> {
+ /** Category name */
+ private final String mName;
+ /** Views included in this category */
+ private final List<ViewData> mViews = new ArrayList<ViewData>();
+ /** Natural ordering rank */
+ private final int mOrdinal = sNextOrdinal++;
+
+ /** Constructs a new category with the given name */
+ private CategoryData(String name) {
+ super();
+ mName = name;
+ }
+
+ /** Adds a new view into this category */
+ private void addView(ViewData view) {
+ mViews.add(view);
+ }
+
+ private String getName() {
+ return mName;
+ }
+
+ public int getViewCount() {
+ return mViews.size();
+ }
+
+ // Implements Iterable<ViewData> such that we can use for-each on the category to
+ // enumerate its views
+ public Iterator<ViewData> iterator() {
+ return mViews.iterator();
+ }
+
+ // Implements Comparable<CategoryData> such that categories can be naturally sorted
+ public int compareTo(CategoryData other) {
+ return mOrdinal - other.mOrdinal;
+ }
+ }
+
+ /** Metadata holder for a view of a given fully qualified class name */
+ private static class ViewData implements Comparable<ViewData> {
+ /** The fully qualified class name of the view */
+ private final String mFqcn;
+ /** Fill preference of the view */
+ private final FillPreference mFillPreference;
+ /** The category that the view belongs to */
+ private final CategoryData mCategory;
+ /** The relative rank of the view for natural ordering */
+ private final int mOrdinal = sNextOrdinal++;
+
+ /** Constructs a new view data for the given class */
+ private ViewData(CategoryData category, String fqcn, FillPreference fillPreference) {
+ super();
+ mCategory = category;
+ mFqcn = fqcn;
+ mFillPreference = fillPreference;
+ }
+
+ /** Returns the category for views of this type */
+ private CategoryData getCategory() {
+ return mCategory;
+ }
+
+ /** Returns the {@link FillPreference} for views of this type */
+ private FillPreference getFillPreference() {
+ return mFillPreference;
+ }
+
+ /** Fully qualified class name of views of this type */
+ private String getFcqn() {
+ return mFqcn;
+ }
+
+ /** Relative rank of this view type */
+ private int getOrdinal() {
+ return mOrdinal;
+ }
+
+ // Implements Comparable<ViewData> such that views can be sorted naturally
+ public int compareTo(ViewData other) {
+ return mOrdinal - other.mOrdinal;
+ }
+ }
+
+ /**
+ * Returns the {@link FillPreference} for classes with the given fully qualified class
+ * name
+ *
+ * @param fqcn the fully qualified class name of the view
+ * @return a suitable {@link FillPreference} for the given view type
+ */
+ public FillPreference getFillPreference(String fqcn) {
+ ViewData view = getClassToView().get(fqcn);
+ if (view != null) {
+ return view.getFillPreference();
+ }
+
+ return FillPreference.NONE;
+ }
+}
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+ Palette Metadata
+
+ This document provides additional designtime metadata for various Android views,
+ such as logical palette categories (as well as a natural ordering of the views within
+ their categories, fill-preferences (how a view will sets its width and height attributes
+ when dropped into other views), and so on.
+ -->
+<!DOCTYPE metadata [
+<!--- The metadata consists of a series of category definitions -->
+<!ELEMENT metadata (category)*>
+<!--- Each category has a name and contains a list of views in order -->
+<!ELEMENT category (view)*>
+<!ATTLIST category name CDATA #IMPLIED>
+<!--- Each view is identified by its full class name and has various
+ other attributes such as a fill preference -->
+<!ELEMENT view EMPTY>
+<!ATTLIST view
+ class CDATA #REQUIRED
+ previewXml CDATA #IMPLIED
+ fill ( none|both|width|height|opposite|
+ width_in_vertical|height_in_horizontal) "none"
+>
+]>
+<metadata>
+ <category name="Form Widgets">
+ <view class="android.widget.TextView" />
+ <view class="android.widget.Button" />
+ <view class="android.widget.CheckBox" />
+ <view class="android.widget.ToggleButton" />
+ <view class="android.widget.RadioButton" />
+ <view class="android.widget.CheckedTextView" />
+ <view class="android.widget.Spinner" fill="width_in_vertical" />
+ <view class="android.widget.EditText" fill="width_in_vertical" />
+ <view class="android.widget.AutoCompleteTextView" fill="width_in_vertical" />
+ <view class="android.widget.MultiAutoCompleteTextView" fill="width_in_vertical" />
+ <view class="android.widget.ProgressBar" />
+ <view class="android.widget.QuickContactBadge" />
+ <view class="android.widget.RadioGroup" />
+ <view class="android.widget.RatingBar" />
+ <view class="android.widget.SeekBar" fill="width_in_vertical" />
+ </category>
+ <category name="Composite">
+ <view class="android.widget.ListView" fill="width_in_vertical" />
+ <view class="android.widget.ExpandableListView" fill="width_in_vertical" />
+ <view class="android.widget.TwoLineListItem" />
+ <view class="android.widget.GridView" fill="opposite"/>
+ <view class="android.widget.ScrollView" fill="opposite"/>
+ <view class="android.widget.HorizontalScrollView" />
+ <view class="android.widget.SearchView" />
+ <view class="android.widget.SlidingDrawer" />
+ <view class="android.widget.TabHost" fill="width_in_vertical" />
+ <view class="android.widget.TabWidget" />
+ <view class="android.webkit.WebView" fill="opposite"/>
+ </category>
+ <category name="Images & Media">
+ <view class="android.widget.ImageView" />
+ <view class="android.widget.ImageButton" />
+ <view class="android.widget.Gallery" fill="width_in_vertical" />
+ <view class="android.widget.MediaController" />
+ <view class="android.widget.VideoView" fill="opposite" />
+ </category>
+ <category name="Layouts">
+ <view class="android.widget.LinearLayout" fill="opposite"/>
+ <view class="android.widget.RelativeLayout" fill="opposite"/>
+ <view class="android.widget.FrameLayout" fill="opposite"/>
+ <view class="android.widget.AbsoluteLayout" fill="opposite"/>
+ <view class="android.widget.TableLayout" fill="opposite"/>
+ <view class="android.widget.TableRow" fill="opposite"/>
+ </category>
+ <category name="Time & Date">
+ <view class="android.widget.TimePicker" />
+ <view class="android.widget.DatePicker" />
+ <view class="android.widget.CalendarView" />
+ <view class="android.widget.Chronometer" />
+ <view class="android.widget.AnalogClock" />
+ <view class="android.widget.DigitalClock" />
+ </category>
+ <category name="Transitions">
+ <view class="android.widget.ImageSwitcher" />
+ <view class="android.widget.AdapterViewFlipper" fill="opposite"/>
+ <view class="android.widget.StackView" fill="opposite"/>
+ <view class="android.widget.TextSwitcher" fill="opposite"/>
+ <view class="android.widget.ViewAnimator" fill="opposite"/>
+ <view class="android.widget.ViewFlipper" fill="opposite"/>
+ <view class="android.widget.ViewSwitcher" fill="opposite"/>
+ </category>
+ <category name="Advanced">
+ <view class="android.view.View" />
+ <view class="android.view.ViewStub" />
+ <view class="android.gesture.GestureOverlayView" />
+ <view class="android.view.SurfaceView" />
+ <view class="android.widget.NumberPicker" />
+ <view class="android.widget.ZoomButton" />
+ <view class="android.widget.ZoomControls" />
+ <view class="android.widget.DialerFilter" fill="width_in_vertical" />
+ </category>
+ <category name="Other">
+ <!-- This is the catch-all category which contains unknown views if we encounter any -->
+ </category>
+ <!-- TODO: Add-ons? -->
+</metadata>
import com.android.ide.common.layout.LayoutConstants;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
-import com.android.ide.eclipse.adt.internal.editors.descriptors.ElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.XmlnsAttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.ViewElementDescriptor;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiDocumentNode;
// owned by a FrameLayout.
// TODO replace by something user-configurable.
- List<ElementDescriptor> layoutDescriptors = null;
+ List<ViewElementDescriptor> layoutDescriptors = null;
IProject project = getEditor().getProject();
if (project != null) {
Sdk currentSdk = Sdk.getCurrent();
}
if (layoutDescriptors != null) {
- for (ElementDescriptor desc : layoutDescriptors) {
- if (desc instanceof ViewElementDescriptor &&
- desc.getXmlName().equals(SdkConstants.CLASS_NAME_FRAMELAYOUT)) {
- layout_attrs = ((ViewElementDescriptor) desc).getLayoutAttributes();
+ for (ViewElementDescriptor desc : layoutDescriptors) {
+ if (desc.getXmlName().equals(SdkConstants.CLASS_NAME_FRAMELAYOUT)) {
+ layout_attrs = desc.getLayoutAttributes();
need_xmlns = true;
break;
}
public void testLogger3() throws Exception {
RenderLogger l = new RenderLogger("foo");
assertFalse(l.hasProblems());
- l.error("timeout", "Sample Error", new RuntimeException());
+ l.error("timeout", "Sample Error", new RuntimeException(), null);
l.warning("slow", "Sample warning", null);
assertTrue(l.hasProblems());
assertEquals("Sample Error\n" + "Sample warning\n"
--- /dev/null
+/*
+ * Copyright (C) 2011 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.editors.layout.gre;
+
+import com.android.ide.common.api.IViewMetadata.FillPreference;
+
+import junit.framework.TestCase;
+
+public class ViewMetadataRepositoryTest extends TestCase {
+ public void testSingleton() throws Exception {
+ assertSame(ViewMetadataRepository.get(), ViewMetadataRepository.get());
+ }
+
+ public void testBasic() throws Exception {
+ ViewMetadataRepository repository = ViewMetadataRepository.get();
+
+ assertEquals(FillPreference.WIDTH_IN_VERTICAL,
+ repository.getFillPreference("android.widget.Spinner"));
+ assertEquals(FillPreference.NONE,
+ repository.getFillPreference("foo.bar"));
+ }
+}