tooltips
traceview
translucency
+typo
ui
uncomment
undescribed
package com.android.ide.common.layout;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_URI;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DIP;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
import com.android.ide.common.api.DrawingStyle;
import com.android.ide.common.api.DropFeedback;
}
newChild.setAttribute(ANDROID_URI, "layout_x", //$NON-NLS-1$
- String.format(VALUE_N_DIP, x));
+ String.format(VALUE_N_DP, x));
newChild.setAttribute(ANDROID_URI, "layout_y", //$NON-NLS-1$
- String.format(VALUE_N_DIP, y));
+ String.format(VALUE_N_DP, y));
addInnerElements(newChild, element, idMap);
}
public static final String ATTR_TEXT = "text"; //$NON-NLS-1$
public static final String ATTR_ID = "id"; //$NON-NLS-1$
+ public static final String ATTR_STYLE = "style"; //$NON-NLS-1$
public static final String ATTR_HANDLE = "handle"; //$NON-NLS-1$
public static final String ATTR_CONTENT = "content"; //$NON-NLS-1$
public static final String ATTR_CHECKED = "checked"; //$NON-NLS-1$
public static final String VALUE_FILL_PARENT = "fill_parent"; //$NON-NLS-1$
public static final String VALUE_TRUE = "true"; //$NON-NLS-1$
public static final String VALUE_FALSE= "false"; //$NON-NLS-1$
- public static final String VALUE_N_DIP = "%ddip"; //$NON-NLS-1$
+ public static final String VALUE_N_DP = "%ddp"; //$NON-NLS-1$
public static final String VALUE_CENTER_VERTICAL = "centerVertical"; //$NON-NLS-1$
public static final String VALUE_CENTER_IN_PARENT = "centerInParent"; //$NON-NLS-1$
package com.android.ide.eclipse.adt.internal.editors;
+import static com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor.ATTRIBUTE_ICON_FILENAME;
+
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiResourceAttributeNode;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
import com.android.sdklib.SdkConstants;
+import com.android.util.Pair;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.IRegion;
import org.eclipse.jface.text.ITextViewer;
import org.eclipse.jface.text.TextSelection;
import org.eclipse.jface.text.contentassist.CompletionProposal;
import org.eclipse.ui.IWorkbenchPage;
import org.eclipse.ui.IWorkbenchWindow;
import org.eclipse.ui.PlatformUI;
+import org.w3c.dom.Element;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
import java.util.regex.Pattern;
/**
return null;
}
+ boolean isNew = true;
+ int replaceLength = 0;
if (currentNode.getNodeType() == Node.ELEMENT_NODE) {
parent = currentNode.getNodeName();
- if (wordPrefix.equals(parent)) {
+ // We're not editing the current node name, so we might be editing its
+ // attributes instead...
+ AttribInfo info = parseAttributeInfo(viewer, offset);
+ if (info != null) {
+ isAttribute = true;
+ // We're editing attributes in an element node (either the attributes' names
+ // or their values).
+ choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
+
+ if (info.isInValue) {
+ // Note - this must be done before the prefix correction below since
+ // otherwise, when the prefix gets cleared out (for a flag list) we
+ // end up with a wrong count for the prefix length
+ Element element = (Element) currentNode;
+ String attribute = element.getAttribute(info.name);
+ if (attribute != null) { // Eclipse DOM bug
+ replaceLength = attribute.length() - wordPrefix.length();
+ if (info.needTag != 0) {
+ replaceLength += 2;
+ }
+ }
+ } else {
+ replaceLength = info.name.length() - wordPrefix.length();
+ }
+
+ if (info.correctedPrefix != null) {
+ wordPrefix = info.correctedPrefix;
+ }
+ needTag = info.needTag;
+ isNew = info.name.length() == 0;
+ } else if (parent.startsWith(wordPrefix)) {
// We are still editing the element's tag name, not the attributes
// (the element's tag name may not even be complete)
isElement = true;
choices = getChoicesForElement(parent, currentNode);
- } else {
- // We're not editing the current node name, so we might be editing its
- // attributes instead...
- isAttribute = true;
- AttribInfo info = parseAttributeInfo(viewer, offset);
- if (info != null) {
- // We're editing attributes in an element node (either the attributes' names
- // or their values).
- choices = getChoicesForAttribute(parent, currentNode, currentUiNode, info);
-
- if (info.correctedPrefix != null) {
- wordPrefix = info.correctedPrefix;
- }
- needTag = info.needTag;
+ replaceLength = parent.length() - wordPrefix.length();
+ if (wordPrefix.length() == 0) {
+ replaceLength = 0; // Only do this if on <
}
+ isNew = replaceLength == 0;
}
} else if (currentNode.getNodeType() == Node.TEXT_NODE) {
isElement = true;
}
return computeProposals(offset, currentNode, choices, wordPrefix, needTag,
- isAttribute, selectionLength);
+ isAttribute, isNew, selectionLength + replaceLength);
}
/**
// A "flag" can consist of several values separated by "or" (|).
// If the correct prefix contains such a pipe character, we change
// it so that only the currently edited value is completed.
- pos = value.indexOf('|');
+ pos = value.lastIndexOf('|');
if (pos >= 0) {
attrInfo.correctedPrefix = value = value.substring(pos + 1);
attrInfo.needTag = 0;
}
}
- }
- if (choices == null && value.startsWith("@")) { //$NON-NLS-1$
- // Special case: If the attribute value looks like a reference to a
- // resource, offer to complete it, since in many cases our metadata
- // does not correctly state whether a resource value is allowed. We don't
- // offer these for an empty completion context, but if the user has
- // actually typed "@", in that case list resource matches.
- // For example, for android:minHeight this makes completion on @dimen/
- // possible.
- choices = UiResourceAttributeNode.computeResourceStringMatches(currentUiNode,
- value);
+ // Should we do suffix completion on dimension units etc?
+ choices = completeSuffix(choices, value, currAttrNode);
+
+ // Check to see if the user is attempting resource completion
+ AttributeDescriptor attributeDescriptor = currAttrNode.getDescriptor();
+ IAttributeInfo attributeInfo = attributeDescriptor.getAttributeInfo();
+ if (value.startsWith("@") //$NON-NLS-1$
+ && !Format.REFERENCE.in(attributeInfo.getFormats())) {
+ // Special case: If the attribute value looks like a reference to a
+ // resource, offer to complete it, since in many cases our metadata
+ // does not correctly state whether a resource value is allowed. We don't
+ // offer these for an empty completion context, but if the user has
+ // actually typed "@", in that case list resource matches.
+ // For example, for android:minHeight this makes completion on @dimen/
+ // possible.
+ choices = UiResourceAttributeNode.computeResourceStringMatches(
+ currentUiNode, attributeDescriptor, value);
+ }
}
}
* - AttributeDescriptor: a possible attribute descriptor which XML name should be completed.
* - String: string values to display as-is to the user. Typically those are possible
* values for a given attribute.
+ * - Pair of Strings: the first value is the keyword to insert, and the second value
+ * is the tooltip/help for the value to be displayed in the documentation popup.
*
* @return The ICompletionProposal[] to display to the user.
*/
private ICompletionProposal[] computeProposals(int offset, Node currentNode,
- Object[] choices, String wordPrefix, char need_tag,
- boolean is_attribute, int selectionLength) {
- ArrayList<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
- HashMap<String, String> nsUriMap = new HashMap<String, String>();
+ Object[] choices, String wordPrefix, char needTag,
+ boolean isAttribute, boolean isNew, int replaceLength) {
+ List<CompletionProposal> proposals = new ArrayList<CompletionProposal>();
+ Map<String, String> nsUriMap = new HashMap<String, String>();
for (Object choice : choices) {
String keyword = null;
} else if (choice instanceof String) {
keyword = (String) choice;
+ if (isAttribute) {
+ icon = IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME);
+ }
+ } else if (choice instanceof Pair<?, ?>) {
+ @SuppressWarnings("unchecked")
+ Pair<String, String> pair = (Pair<String, String>) choice;
+ keyword = pair.getFirst();
+ tooltip = pair.getSecond();
+ if (isAttribute) {
+ icon = IconFactory.getInstance().getIcon(ATTRIBUTE_ICON_FILENAME);
+ }
} else {
continue; // discard unknown choice
}
if (nsPrefix != null) {
keyword = nsPrefix + keyword;
}
- String end_tag = ""; //$NON-NLS-1$
- if (need_tag != 0) {
- if (need_tag == '"') {
- keyword = need_tag + keyword;
- end_tag = String.valueOf(need_tag);
- } else if (need_tag == '<') {
+ String endTag = ""; //$NON-NLS-1$
+ if (needTag != 0) {
+ if (needTag == '"') {
+ keyword = needTag + keyword;
+ endTag = String.valueOf(needTag);
+ } else if (needTag == '<') {
if (elementCanHaveChildren(choice)) {
- end_tag = String.format("></%1$s>", keyword); //$NON-NLS-1$
- keyword = need_tag + keyword;
+ endTag = String.format("></%1$s>", keyword); //$NON-NLS-1$
+ keyword = needTag + keyword;
} else {
- keyword = need_tag + keyword;
- end_tag = "/>"; //$NON-NLS-1$
+ keyword = needTag + keyword;
+ endTag = "/>"; //$NON-NLS-1$
}
}
}
// For attributes, automatically insert ns:attribute="" and place the cursor
// inside the quotes.
- if (choice instanceof AttributeDescriptor) {
+ if (choice instanceof AttributeDescriptor && isNew) {
// Special case for attributes: insert ="" stuff and locate caret inside ""
String suffix = "=\"\""; //$NON-NLS-1$
proposal = new CompletionProposal(
keyword + suffix , // String replacementString
offset - wordPrefix.length(), // int replacementOffset
- wordPrefix.length() + selectionLength, // int replacementLength
+ wordPrefix.length() + replaceLength, // int replacementLength
keyword.length() + suffix.length() - 1, // cursorPosition
icon, // Image image
keyword, // displayString - don't include =""
tooltip // String additionalProposalInfo
);
} else {
+ int cursorPosition = keyword.length();
+ if (choice instanceof ElementDescriptor && isNew) {
+ endTag = ' ' + endTag;
+ cursorPosition += 1;
+ }
proposal = new CompletionProposal(
- keyword + end_tag, // String replacementString
+ keyword + endTag, // String replacementString
offset - wordPrefix.length(), // int replacementOffset
- wordPrefix.length() + selectionLength, // int replacementLength
- keyword.length(), // int cursorPosition (rel. to rplcmntOffset)
+ wordPrefix.length() + replaceLength, // int replacementLength
+ cursorPosition, // int cursorPosition (rel. to rplcmntOffset)
icon, // Image image
null, // String displayString
null, // IContextInformation contextInformation
* or if one of the attributes is a TextValueDescriptor.
*
* @param descriptor An ElementDescriptor or an AttributeDescriptor
- * @return True if the descriptor is an ElementDescriptor that can have children or a text value
+ * @return True if the descriptor is an ElementDescriptor that can have children or a text
+ * value
*/
private boolean elementCanHaveChildren(Object descriptor) {
if (descriptor instanceof ElementDescriptor) {
if (desc.hasChildren()) {
return true;
}
- for (AttributeDescriptor attr_desc : desc.getAttributes()) {
- if (attr_desc instanceof TextValueDescriptor) {
+ for (AttributeDescriptor attrDesc : desc.getAttributes()) {
+ if (attrDesc instanceof TextValueDescriptor) {
return true;
}
}
*/
private AttribInfo parseAttributeInfo(ITextViewer viewer, int offset) {
AttribInfo info = new AttribInfo();
+ int originalOffset = offset;
IDocument document = viewer.getDocument();
int n = document.getLength();
if (offset <= n) {
try {
+ // Look to the right to make sure we aren't sitting on the boundary of the
+ // beginning of a new element with whitespace before it
+ if (offset < n && document.getChar(offset) == '<') {
+ return null;
+ }
+
n = offset;
for (;offset > 0; --offset) {
char ch = document.getChar(offset - 1);
+ if (ch == '>') break;
if (ch == '<') break;
}
if (pos_equal == -1) {
info.isInValue = false;
info.name = text.trim();
+
+ // info.name is currently just the prefix of the attribute name.
+ // Look at the text buffer to find the complete name (since we need
+ // to know its bounds in order to replace it when a different attribute
+ // that matches this prefix is chosen)
+ IRegion lineInfo = document.getLineInformationOfOffset(originalOffset);
+ String line = document.get(lineInfo.getOffset(), lineInfo.getLength());
+ int nameStart = originalOffset - lineInfo.getOffset();
+ for (int nameEnd = nameStart; nameEnd < line.length(); nameEnd++) {
+ char c = line.charAt(nameEnd);
+ if (!(Character.isLetter(c) || c == ':' || c == '_')) {
+ String nameSuffix = line.substring(nameStart, nameEnd);
+ info.name = text.trim() + nameSuffix;
+ break;
+ }
+ }
} else {
info.isInValue = true;
info.name = text.substring(0, pos_equal).trim();
if (page != null) {
IEditorPart editor = page.getActiveEditor();
if (editor instanceof AndroidXmlEditor) {
- ISourceViewer ssviewer = ((AndroidXmlEditor) editor).getStructuredSourceViewer();
+ ISourceViewer ssviewer =
+ ((AndroidXmlEditor) editor).getStructuredSourceViewer();
if (ssviewer == viewer) {
return (AndroidXmlEditor) editor;
}
return null;
}
+ /**
+ * Fixed list of dimension units, along with user documentation, for use by
+ * {@link #completeSuffix}.
+ */
+ private static final String[] sDimensionUnits = new String[] {
+ "dp", //$NON-NLS-1$
+ "<b>Density-independent Pixels</b> - an abstract unit that is based on the physical "
+ + "density of the screen.",
+
+ "sp", //$NON-NLS-1$
+ "<b>Scale-independent Pixels</b> - this is like the dp unit, but it is also scaled by "
+ + "the user's font size preference.",
+
+ "pt", //$NON-NLS-1$
+ "<b>Points</b> - 1/72 of an inch based on the physical size of the screen.",
+
+ "mm", //$NON-NLS-1$
+ "<b>Millimeters</b> - based on the physical size of the screen.",
+
+ "in", //$NON-NLS-1$
+ "<b>Inches</b> - based on the physical size of the screen.",
+
+ "px", //$NON-NLS-1$
+ "<b>Pixels</b> - corresponds to actual pixels on the screen. Not recommended.",
+ };
+
+ /**
+ * Fixed list of fractional units, along with user documentation, for use by
+ * {@link #completeSuffix}
+ */
+ private static final String[] sFractionUnits = new String[] {
+ "%", //$NON-NLS-1$
+ "<b>Fraction</b> - a percentage of the base size",
+
+ "%p", //$NON-NLS-1$
+ "<b>Fraction</b> - a percentage relative to parent container",
+ };
+ /**
+ * Completes suffixes for applicable types (like dimensions and fractions) such that
+ * after a dimension number you get completion on unit types like "px".
+ */
+ private Object[] completeSuffix(Object[] choices, String value, UiAttributeNode currAttrNode) {
+ IAttributeInfo attributeInfo = currAttrNode.getDescriptor().getAttributeInfo();
+ Format[] formats = attributeInfo.getFormats();
+ List<Object> suffixes = new ArrayList<Object>();
+
+ if (value.length() > 0 && Character.isDigit(value.charAt(0))) {
+ boolean hasDimension = Format.DIMENSION.in(formats);
+ boolean hasFraction = Format.FRACTION.in(formats);
+
+ if (hasDimension || hasFraction) {
+ // Split up the value into a numeric part (the prefix) and the
+ // unit part (the suffix)
+ int suffixBegin = 0;
+ for (; suffixBegin < value.length(); suffixBegin++) {
+ if (!Character.isDigit(value.charAt(suffixBegin))) {
+ break;
+ }
+ }
+ String number = value.substring(0, suffixBegin);
+ String suffix = value.substring(suffixBegin);
+
+ // Add in the matching dimension and/or fraction units, if any
+ if (hasDimension) {
+ // Each item has two entries in the array of strings: the first odd numbered
+ // ones are the unit names and the second even numbered ones are the
+ // corresponding descriptions.
+ for (int i = 0; i < sDimensionUnits.length; i += 2) {
+ String unit = sDimensionUnits[i];
+ if (startsWith(unit, suffix)) {
+ String description = sDimensionUnits[i + 1];
+ suffixes.add(Pair.of(number + unit, description));
+ }
+ }
+ // Allow "dip" completion but don't offer it ("dp" is preferred)
+ if (suffix.startsWith("di") || suffix.startsWith("dip")) { //$NON-NLS-1$ //$NON-NLS-2$
+ suffixes.add(Pair.of(number + "dip", "Alternative name for \"dp\"")); //$NON-NLS-1$
+ }
+ }
+ if (hasFraction) {
+ for (int i = 0; i < sFractionUnits.length; i += 2) {
+ String unit = sFractionUnits[i];
+ if (startsWith(unit, suffix)) {
+ String description = sFractionUnits[i + 1];
+ suffixes.add(Pair.of(number + unit, description));
+ }
+ }
+ }
+ }
+ }
+
+ boolean hasFlag = Format.FLAG.in(formats);
+ if (hasFlag) {
+ boolean isDone = false;
+ String[] flagValues = attributeInfo.getFlagValues();
+ for (String flagValue : flagValues) {
+ if (flagValue.equals(value)) {
+ isDone = true;
+ break;
+ }
+ }
+ if (isDone) {
+ // Add in all the new values with a separator of |
+ String currentValue = currAttrNode.getCurrentValue();
+ for (String flagValue : flagValues) {
+ if (currentValue == null || !currentValue.contains(flagValue)) {
+ suffixes.add(value + '|' + flagValue);
+ }
+ }
+ }
+ }
+
+ if (suffixes.size() > 0) {
+ // Merge previously added choices (from attribute enums etc) with the new matches
+ List<Object> all = new ArrayList<Object>();
+ if (choices != null) {
+ for (Object s : choices) {
+ all.add(s);
+ }
+ }
+ all.addAll(suffixes);
+ choices = all.toArray();
+ }
+
+ return choices;
+ }
}
import org.eclipse.jface.text.IAutoEditStrategy;
-import org.eclipse.jface.text.IDocument;
import org.eclipse.jface.text.ITextHover;
+import org.eclipse.jface.text.ITextViewer;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
import org.eclipse.jface.text.contentassist.IContentAssistProcessor;
import org.eclipse.jface.text.contentassist.IContentAssistant;
+import org.eclipse.jface.text.contentassist.IContextInformation;
+import org.eclipse.jface.text.contentassist.IContextInformationValidator;
import org.eclipse.jface.text.formatter.IContentFormatter;
import org.eclipse.jface.text.source.ISourceViewer;
-import org.eclipse.jface.viewers.IInputProvider;
import org.eclipse.wst.sse.core.text.IStructuredPartitions;
import org.eclipse.wst.xml.core.text.IXMLPartitions;
import org.eclipse.wst.xml.ui.StructuredTextViewerConfigurationXML;
+import org.eclipse.wst.xml.ui.internal.contentassist.XMLContentAssistProcessor;
import java.util.ArrayList;
+import java.util.List;
import java.util.Map;
/**
* Base Source Viewer Configuration for Android resources.
*/
+@SuppressWarnings("restriction") // XMLContentAssistProcessor
public class AndroidSourceViewerConfig extends StructuredTextViewerConfigurationXML {
/** Content Assist Processor to use for all handled partitions. */
if (partitionType == IStructuredPartitions.UNKNOWN_PARTITION ||
partitionType == IStructuredPartitions.DEFAULT_PARTITION ||
partitionType == IXMLPartitions.XML_DEFAULT) {
- if (sourceViewer instanceof IInputProvider) {
- IInputProvider input = (IInputProvider) sourceViewer;
- Object a = input.getInput();
- if (a != null)
- a.toString();
- }
-
- IDocument doc = sourceViewer.getDocument();
- if (doc != null)
- doc.toString();
-
processors.add(mProcessor);
}
partitionType);
if (others != null && others.length > 0) {
for (IContentAssistProcessor p : others) {
- processors.add(p);
+ // Builtin Eclipse WTP code completion assistant? If so,
+ // wrap it with our own filter which hides some unwanted completions.
+ if (p instanceof XMLContentAssistProcessor) {
+ processors.add(new FilteringContentAssistProcessor(p));
+ } else {
+ processors.add(p);
+ }
}
}
return targets;
}
+ /**
+ * A delegating {@link IContentAssistProcessor} whose purpose is to filter out some
+ * default Eclipse XML completions which are distracting in Android XML files
+ */
+ private class FilteringContentAssistProcessor implements IContentAssistProcessor {
+ private IContentAssistProcessor mDelegate;
+
+ public FilteringContentAssistProcessor(IContentAssistProcessor delegate) {
+ super();
+ mDelegate = delegate;
+ }
+
+ public ICompletionProposal[] computeCompletionProposals(ITextViewer viewer, int offset) {
+ ICompletionProposal[] result = mDelegate.computeCompletionProposals(viewer, offset);
+ if (result == null) {
+ return null;
+ }
+
+ List<ICompletionProposal> proposals =
+ new ArrayList<ICompletionProposal>(result.length);
+ for (ICompletionProposal proposal : result) {
+ String replacement = proposal.getDisplayString();
+ if (replacement.charAt(0) == '"' &&
+ replacement.charAt(replacement.length() - 1) == '"') {
+ // Filter out attribute values. In Android XML files (where there is no DTD
+ // etc) the default Eclipse XML code completion simply provides the
+ // existing value as a completion. This is often misleading, since if you
+ // for example have a typo, completion will show your current (wrong)
+ // value as a valid completion.
+ } else if (replacement.contains("Namespace") //$NON-NLS-1$
+ || replacement.contains("Schema")) { //$NON-NLS-1$
+ // Eclipse adds in a number of namespace and schema related completions which
+ // are not usually applicable in our files.
+ } else {
+ proposals.add(proposal);
+ }
+ }
+
+ if (proposals.size() == result.length) {
+ return result;
+ } else {
+ return proposals.toArray(new ICompletionProposal[proposals.size()]);
+ }
+ }
+
+ public IContextInformation[] computeContextInformation(ITextViewer viewer, int offset) {
+ return mDelegate.computeContextInformation(viewer, offset);
+ }
+
+ public char[] getCompletionProposalAutoActivationCharacters() {
+ return mDelegate.getCompletionProposalAutoActivationCharacters();
+ }
+
+ public char[] getContextInformationAutoActivationCharacters() {
+ return mDelegate.getContextInformationAutoActivationCharacters();
+ }
+
+ public IContextInformationValidator getContextInformationValidator() {
+ return mDelegate.getContextInformationValidator();
+ }
+
+ public String getErrorMessage() {
+ return mDelegate.getErrorMessage();
+ }
+ }
}
* the correct UiAttributeNode-derived class.
*/
public abstract class AttributeDescriptor {
- private static final String ATTRIBUTE_ICON_FILENAME = "attribute"; //$NON-NLS-1$
+ public static final String ATTRIBUTE_ICON_FILENAME = "attribute"; //$NON-NLS-1$
private final String mXmlLocalName;
private final String mNsUri;
package com.android.ide.eclipse.adt.internal.editors.layout;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
/**
* Content Assist Processor for /res/layout XML files
*/
-class LayoutContentAssist extends AndroidContentAssist {
+@VisibleForTesting
+public final class LayoutContentAssist extends AndroidContentAssist {
/**
- * Constructor for LayoutContentAssist
+ * Constructor for LayoutContentAssist
*/
public LayoutContentAssist() {
super(AndroidTargetData.DESCRIPTOR_LAYOUT);
import static com.android.ide.common.layout.LayoutConstants.NEW_ID_PREFIX;
import static com.android.ide.common.layout.LayoutConstants.RELATIVE_LAYOUT;
import static com.android.ide.common.layout.LayoutConstants.VALUE_FALSE;
-import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DIP;
+import static com.android.ide.common.layout.LayoutConstants.VALUE_N_DP;
import static com.android.ide.common.layout.LayoutConstants.VALUE_TRUE;
import static com.android.ide.common.layout.LayoutConstants.VALUE_VERTICAL;
import static com.android.ide.common.layout.LayoutConstants.VALUE_WRAP_CONTENT;
view.addHorizConstraint(attachLeftProperty, attachLeftValue);
if (marginLeft > 0) {
view.addHorizConstraint(ATTR_LAYOUT_MARGIN_LEFT,
- String.format(VALUE_N_DIP, marginLeft));
+ String.format(VALUE_N_DP, marginLeft));
marginLeft = 0;
}
} else {
view.addVerticalConstraint(attachTopProperty, attachTopValue);
if (marginTop > 0) {
view.addVerticalConstraint(ATTR_LAYOUT_MARGIN_TOP,
- String.format(VALUE_N_DIP, marginTop));
+ String.format(VALUE_N_DP, marginTop));
marginTop = 0;
}
} else {
package com.android.ide.eclipse.adt.internal.editors.manifest;
+import com.android.annotations.VisibleForTesting;
import com.android.ide.eclipse.adt.internal.editors.AndroidContentAssist;
import com.android.ide.eclipse.adt.internal.sdk.AndroidTargetData;
/**
* Content Assist Processor for AndroidManifest.xml
*/
-final class ManifestContentAssist extends AndroidContentAssist {
+@VisibleForTesting
+public final class ManifestContentAssist extends AndroidContentAssist {
/**
- * Constructor for ManifestContentAssist
+ * Constructor for ManifestContentAssist
*/
public ManifestContentAssist() {
super(AndroidTargetData.DESCRIPTOR_MANIFEST);
package com.android.ide.eclipse.adt.internal.editors.uimodel;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_ID;
+import static com.android.ide.common.layout.LayoutConstants.ATTR_STYLE;
import static com.android.ide.eclipse.adt.AdtConstants.ANDROID_PKG;
+import com.android.ide.common.api.IAttributeInfo;
+import com.android.ide.common.api.IAttributeInfo.Format;
import com.android.ide.eclipse.adt.internal.editors.AndroidXmlEditor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.AttributeDescriptor;
import com.android.ide.eclipse.adt.internal.editors.descriptors.DescriptorsUtils;
import com.android.ide.eclipse.adt.internal.editors.descriptors.TextAttributeDescriptor;
+import com.android.ide.eclipse.adt.internal.editors.layout.descriptors.LayoutDescriptors;
import com.android.ide.eclipse.adt.internal.editors.ui.SectionHelper;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceItem;
import com.android.ide.eclipse.adt.internal.resources.manager.ResourceManager;
import java.util.ArrayList;
import java.util.Collection;
+import java.util.Collections;
+import java.util.Comparator;
import java.util.EnumSet;
+import java.util.List;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
* See {@link UiTextAttributeNode} for more information.
*/
public class UiResourceAttributeNode extends UiTextAttributeNode {
+ /** The resource prefix @android: */
+ private static final String ANDROID_RESOURCE_PREFIX = '@' + ANDROID_PKG + ':';
private ResourceType mType;
*/
@Override
public String[] getPossibleValues(String prefix) {
- return computeResourceStringMatches(getUiParent(), prefix);
+ return computeResourceStringMatches(getUiParent(), getDescriptor(), prefix);
}
- public static String[] computeResourceStringMatches(UiElementNode uiNode, String prefix) {
+ public static String[] computeResourceStringMatches(UiElementNode uiNode,
+ AttributeDescriptor attributeDescriptor, String prefix) {
ResourceRepository repository = null;
boolean isSystem = false;
AndroidXmlEditor editor = uiNode.getEditor();
}
// Now collect results
- ArrayList<String> results = new ArrayList<String>();
+ List<String> results = new ArrayList<String>();
if (typeName == null) {
// This prefix does not have a / in it, so the resource string is either empty
for (ResourceType resType : resTypes) {
if (isSystem) {
- results.add("@" + ANDROID_PKG + ':' + resType.getName() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ results.add(ANDROID_RESOURCE_PREFIX + resType.getName() + '/');
} else {
- results.add("@" + resType.getName() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ results.add('@' + resType.getName() + '/');
}
if (resType == ResourceType.ID) {
// Also offer the + version to create an id from scratch
- results.add("@+" + resType.getName() + "/"); //$NON-NLS-1$ //$NON-NLS-2$
+ results.add("@+" + resType.getName() + '/'); //$NON-NLS-1$
}
}
// "@an" we offer to complete it.
if (prefix == null ||
ANDROID_PKG.regionMatches(0, prefix, 1, prefix.length() - 1)) {
- results.add('@' + ANDROID_PKG + ':');
+ results.add(ANDROID_RESOURCE_PREFIX);
}
} else if (repository != null) {
// We have a style name and a repository. Find all resources that match this
}
}
+ sortAttributeChoices(attributeDescriptor, results);
+
return results.toArray(new String[results.size()]);
}
+
+ /**
+ * Attempts to sort the attribute values to bubble up the most likely choices to
+ * the top.
+ * <p>
+ * For example, if you are editing a style attribute, it's likely that among the
+ * resource values you would rather see @style or @android than @string.
+ */
+ private static void sortAttributeChoices(AttributeDescriptor descriptor,
+ List<String> choices) {
+ final IAttributeInfo attributeInfo = descriptor.getAttributeInfo();
+ Collections.sort(choices, new Comparator<String>() {
+ public int compare(String s1, String s2) {
+ int compare = score(attributeInfo, s1) - score(attributeInfo, s2);
+ if (compare == 0) {
+ // Sort alphabetically as a fallback
+ compare = s1.compareTo(s2);
+ }
+ return compare;
+ }
+ });
+ }
+
+ /** Compute a suitable sorting score for the given */
+ private static final int score(IAttributeInfo attributeInfo, String value) {
+ if (value.equals(ANDROID_RESOURCE_PREFIX)) {
+ return -1;
+ }
+
+ for (Format format : attributeInfo.getFormats()) {
+ String type = null;
+ switch (format) {
+ case BOOLEAN:
+ type = "bool"; //$NON-NLS-1$
+ break;
+ case COLOR:
+ type = "color"; //$NON-NLS-1$
+ break;
+ case DIMENSION:
+ type = "dimen"; //$NON-NLS-1$
+ break;
+ case INTEGER:
+ type = "integer"; //$NON-NLS-1$
+ break;
+ case STRING:
+ type = "string"; //$NON-NLS-1$
+ break;
+ // default: REFERENCE, FLAG, ENUM, etc - don't have type info about individual
+ // elements to help make a decision
+ }
+
+ if (type != null) {
+ if (value.startsWith('@' + type + '/')) {
+ return -2;
+ }
+
+ if (value.startsWith(ANDROID_RESOURCE_PREFIX + type + '/')) {
+ return -2;
+ }
+ }
+ }
+
+ // Handle a few more cases not covered by the Format metadata check
+ String type = null;
+
+ String attribute = attributeInfo.getName();
+ if (attribute.equals(ATTR_ID)) {
+ type = "id"; //$NON-NLS-1$
+ } else if (attribute.equals(ATTR_STYLE)) {
+ type = "style"; //$NON-NLS-1$
+ } else if (attribute.equals(LayoutDescriptors.ATTR_LAYOUT)) {
+ type = "layout"; //$NON-NLS-1$
+ }
+
+ if (type != null) {
+ if (value.startsWith('@' + type + '/')) {
+ return -2;
+ }
+
+ if (value.startsWith(ANDROID_RESOURCE_PREFIX + type + '/')) {
+ return -2;
+ }
+ }
+
+ return 0;
+ }
}
--- /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;
+
+import com.android.ide.eclipse.adt.AdtPlugin;
+import com.android.ide.eclipse.adt.internal.editors.layout.LayoutContentAssist;
+import com.android.ide.eclipse.adt.internal.editors.layout.refactoring.AdtProjectTest;
+import com.android.ide.eclipse.adt.internal.editors.manifest.ManifestContentAssist;
+
+import org.eclipse.core.resources.IFile;
+import org.eclipse.jface.text.Document;
+import org.eclipse.jface.text.IDocument;
+import org.eclipse.jface.text.contentassist.ICompletionProposal;
+import org.eclipse.jface.text.source.ISourceViewer;
+import org.eclipse.swt.graphics.Point;
+import org.eclipse.ui.IEditorPart;
+import org.eclipse.ui.IWorkbenchPage;
+import org.eclipse.ui.PlatformUI;
+import org.eclipse.ui.ide.IDE;
+
+public class AndroidContentAssistTest extends AdtProjectTest {
+ private static final String CARET = "^"; //$NON-NLS-1$
+
+ public void testStartsWith() {
+ assertTrue(AndroidContentAssist.startsWith("", ""));
+ assertTrue(AndroidContentAssist.startsWith("a", ""));
+ assertTrue(AndroidContentAssist.startsWith("A", ""));
+ assertTrue(AndroidContentAssist.startsWith("A", "a"));
+ assertTrue(AndroidContentAssist.startsWith("A", "A"));
+ assertTrue(AndroidContentAssist.startsWith("Ab", "a"));
+ assertTrue(AndroidContentAssist.startsWith("ab", "A"));
+ assertTrue(AndroidContentAssist.startsWith("ab", "AB"));
+ assertFalse(AndroidContentAssist.startsWith("ab", "ABc"));
+ assertFalse(AndroidContentAssist.startsWith("", "ABc"));
+ }
+
+ public void testCompletion1() throws Exception {
+ // Change attribute name completion
+ checkLayoutCompletion("completion1.xml", "layout_w^idth=\"fill_parent\"");
+ }
+
+ public void testCompletion2() throws Exception {
+ // Check attribute value completion for enum
+ checkLayoutCompletion("completion1.xml", "layout_width=\"^fill_parent\"");
+ }
+
+ public void testCompletion3() throws Exception {
+ // Check attribute value completion for enum with a prefix
+ checkLayoutCompletion("completion1.xml", "layout_width=\"fi^ll_parent\"");
+ }
+
+ public void testCompletion4() throws Exception {
+ // Check attribute value completion on units
+ checkLayoutCompletion("completion1.xml", "marginBottom=\"50^\"");
+ }
+
+ public void testCompletion5() throws Exception {
+ // Check attribute value completion on units with prefix
+ checkLayoutCompletion("completion1.xml", "layout_marginLeft=\"50d^p\"");
+ }
+
+ public void testCompletion6() throws Exception {
+ // Check resource sorting - "style" should bubble to the top for a style attribute
+ checkLayoutCompletion("completion1.xml", "style=\"@android:^style/Widget.Button\"");
+ }
+
+ public void testCompletion7a() throws Exception {
+ // Check flags (multiple values inside a single XML value, separated by | - where
+ // the prefix is reset as soon as you pass each | )
+ checkLayoutCompletion("completion1.xml", "android:gravity=\"l^eft|bottom\"");
+ }
+
+ public void testCompletion7b() throws Exception {
+ checkLayoutCompletion("completion1.xml", "android:gravity=\"left|b^ottom\"");
+ }
+
+ public void testCompletion8() throws Exception {
+ // Test completion right at the "=" sign; this will be taken to be the last
+ // character of the attribute name (the caret is between the last char and before
+ // the = characters), so it should match a single attribute
+ checkLayoutCompletion("completion1.xml", "layout_width^=\"fill_parent\"");
+ }
+
+ public void testCompletion9() throws Exception {
+ // Test completion right after the "=" sign; this will be taken to be the beginning
+ // of the attribute value, but all values will also include a leading quote
+ checkLayoutCompletion("completion1.xml", "layout_width=^\"fill_parent\"");
+ }
+
+ public void testCompletion10() throws Exception {
+ // Test completion of element names
+ checkLayoutCompletion("completion1.xml", "<T^extView");
+ }
+
+ public void testCompletion11() throws Exception {
+ // Test completion of element names at the outside of the <. This should include
+ // all the elements too (along with the leading <).
+ checkLayoutCompletion("completion1.xml", "^<TextView");
+ }
+
+ public void testCompletion12() throws Exception {
+ // Test completion of element names inside a nested XML; ensure that this will
+ // correctly compute element names, not previous attribute
+ checkLayoutCompletion("completion1.xml", "btn_default\">^</FrameLayout>");
+ }
+
+ public void testCompletion13a() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom|^cen");
+ }
+
+ public void testCompletion13b() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom|cen^");
+ }
+
+ public void testCompletion13c() throws Exception {
+ checkLayoutCompletion("completion2.xml", "gravity=\"left|bottom^|cen");
+ }
+
+ public void testCompletion14() throws Exception {
+ // Test completion of permissions
+ checkManifestCompletion("manifest.xml", "android.permission.ACC^ESS_NETWORK_STATE");
+ }
+
+ public void testCompletion15() throws Exception {
+ // Test completion of intents
+ checkManifestCompletion("manifest.xml", "android.intent.category.L^AUNCHER");
+ }
+
+ public void testCompletion16() throws Exception {
+ // Test completion of top level elements
+ checkManifestCompletion("manifest.xml", "<^application android:i");
+ }
+
+ public void testCompletion17() throws Exception {
+ // Test completion of attributes on the manifest element
+ checkManifestCompletion("manifest.xml", "^android:versionCode=\"1\"");
+ }
+
+ public void testCompletion18() throws Exception {
+ // Test completion of attributes on the manifest element
+ checkManifestCompletion("manifest.xml",
+ "<activity android:^name=\".TestActivity\"");
+ }
+
+ // ---- Test *applying* code completion ----
+
+ // The following tests check -applying- a specific code completion
+ // match - this verifies that the document is updated correctly, the
+ // caret is moved appropriately, etc.
+
+ public void testApplyCompletion1() throws Exception {
+ // Change attribute name completion
+ checkApplyLayoutCompletion("completion1.xml", "layout_w^idth=\"fill_parent\"",
+ "android:layout_weight");
+ }
+
+ public void testApplyCompletion2() throws Exception {
+ // Check attribute value completion for enum
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=\"^fill_parent\"",
+ "match_parent");
+ }
+
+ public void testApplyCompletion3() throws Exception {
+ // Check attribute value completion for enum with a prefix
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=\"fi^ll_parent\"",
+ "fill_parent");
+ }
+
+ public void testApplyCompletion4() throws Exception {
+ // Check attribute value completion on units
+ checkApplyLayoutCompletion("completion1.xml", "marginBottom=\"50^\"", "50mm");
+ }
+
+ public void testApplyCompletion5() throws Exception {
+ // Check attribute value completion on units with prefix
+ checkApplyLayoutCompletion("completion1.xml", "layout_marginLeft=\"50d^p\"", "50dp");
+ }
+
+ public void testApplyCompletion6() throws Exception {
+ // Check resource sorting - "style" should bubble to the top for a style attribute
+ checkApplyLayoutCompletion("completion1.xml", "style=\"@android:^style/Widget.Button\"",
+ "@android:drawable/");
+ }
+
+ public void testApplyCompletion7a() throws Exception {
+ // Check flags (multiple values inside a single XML value, separated by | - where
+ // the prefix is reset as soon as you pass each | )
+ checkApplyLayoutCompletion("completion1.xml", "android:gravity=\"l^eft|bottom\"",
+ "left");
+ // NOTE - this will replace all flag values with the newly selected value.
+ // That may not be the best behavior - perhaps we should only replace one portion
+ // of the value.
+ }
+
+ public void testApplyCompletion7b() throws Exception {
+ checkApplyLayoutCompletion("completion1.xml", "android:gravity=\"left|b^ottom\"",
+ "bottom");
+ // NOTE - this will replace all flag values with the newly selected value.
+ // That may not be the best behavior - perhaps we should only replace one portion
+ // of the value.
+ }
+
+ public void testApplyCompletion8() throws Exception {
+ // Test completion right at the "=" sign; this will be taken to be the last
+ // character of the attribute name (the caret is between the last char and before
+ // the = characters), so it should match a single attribute
+ checkApplyLayoutCompletion("completion1.xml", "layout_width^=\"fill_parent\"",
+ "android:layout_width");
+ }
+
+ public void testApplyCompletion9() throws Exception {
+ // Test completion right after the "=" sign; this will be taken to be the beginning
+ // of the attribute value, but all values will also include a leading quote
+ checkApplyLayoutCompletion("completion1.xml", "layout_width=^\"fill_parent\"",
+ "\"wrap_content\"");
+ }
+
+ public void testApplyCompletion10() throws Exception {
+ // Test completion of element names
+ checkApplyLayoutCompletion("completion1.xml", "<T^extView", "TableLayout");
+ }
+
+ public void testApplyCompletion11a() throws Exception {
+ // Test completion of element names at the outside of the <. This should include
+ // all the elements too (along with the leading <).
+ checkApplyLayoutCompletion("completion1.xml", "^<TextView", "<RadioGroup ></RadioGroup>");
+ }
+
+ public void testApplyCompletion11b() throws Exception {
+ // Similar to testApplyCompletion11a, but replacing with an element that does not
+ // have children (to test the closing tag insertion code)
+ checkApplyLayoutCompletion("completion1.xml", "^<TextView", "<CheckBox />");
+ }
+
+ public void testApplyCompletion12() throws Exception {
+ // Test completion of element names inside a nested XML; ensure that this will
+ // correctly compute element names, not previous attribute
+ checkApplyLayoutCompletion("completion1.xml", "btn_default\">^</FrameLayout>",
+ "<FrameLayout ></FrameLayout>");
+ }
+
+ public void testApplyCompletion13a() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom|^cen",
+ "fill_vertical");
+ }
+
+ public void testApplyCompletion13b() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom|cen^",
+ "center_horizontal");
+ }
+
+ public void testApplyCompletion13c() throws Exception {
+ checkApplyLayoutCompletion("completion2.xml", "gravity=\"left|bottom^|cen",
+ "bottom|fill_horizontal");
+ }
+
+ // --- Code Completion test infrastructure ----
+
+ private void checkLayoutCompletion(String name, String caretLocation) throws Exception {
+ checkCompletion(name, getLayoutFile(getProject(), name), caretLocation,
+ new LayoutContentAssist());
+ }
+
+ private void checkManifestCompletion(String name, String caretLocation) throws Exception {
+ // Manifest files must be named AndroidManifest.xml. Must overwrite to replace
+ // the default manifest created in the test project.
+ IFile file = getTestDataFile(getProject(), name, "AndroidManifest.xml", true);
+
+ checkCompletion(name, file, caretLocation,
+ new ManifestContentAssist());
+ }
+
+ private void checkApplyLayoutCompletion(String name, String caretLocation,
+ String match) throws Exception {
+ checkApplyCompletion(name, getLayoutFile(getProject(), name), caretLocation,
+ new LayoutContentAssist(), match);
+ }
+
+ private ICompletionProposal[] complete(IFile file, String caretLocation,
+ AndroidContentAssist assist) throws Exception {
+
+ // Determine the offset
+ String fileContent = AdtPlugin.readFile(file);
+ int caretDelta = caretLocation.indexOf(CARET);
+ assertTrue(caretLocation, caretDelta != -1);
+ String caretContext = caretLocation.substring(0, caretDelta)
+ + caretLocation.substring(caretDelta + CARET.length());
+ int caretContextIndex = fileContent.indexOf(caretContext);
+ assertTrue("Caret content " + caretContext + " not found in file",
+ caretContextIndex != -1);
+ int offset = caretContextIndex + caretDelta;
+
+ // Open file
+ IWorkbenchPage page = PlatformUI.getWorkbench().getActiveWorkbenchWindow().getActivePage();
+ assertNotNull(page);
+ IEditorPart editor = IDE.openEditor(page, file);
+ assertTrue(editor instanceof AndroidXmlEditor);
+ AndroidXmlEditor layoutEditor = (AndroidXmlEditor) editor;
+ ISourceViewer viewer = layoutEditor.getStructuredSourceViewer();
+
+ // Run code completion
+ ICompletionProposal[] proposals = assist.computeCompletionProposals(viewer, offset);
+ if (proposals == null) {
+ proposals = new ICompletionProposal[0];
+ }
+
+ return proposals;
+ }
+
+ private void checkApplyCompletion(String basename, IFile file, String caretLocation,
+ AndroidContentAssist assist, String match) throws Exception {
+ ICompletionProposal[] proposals = complete(file, caretLocation, assist);
+ ICompletionProposal chosen = null;
+ for (ICompletionProposal proposal : proposals) {
+ if (proposal.getDisplayString().equals(match)) {
+ chosen = proposal;
+ break;
+ }
+ }
+ assertNotNull(chosen);
+ assert chosen != null; // Eclipse null pointer analysis doesn't believe the JUnit assertion
+
+ String fileContent = AdtPlugin.readFile(file);
+ IDocument document = new Document();
+ document.set(fileContent);
+
+ // Apply code completion
+ chosen.apply(document);
+
+ // Insert caret location as well
+ Point location = chosen.getSelection(document);
+ document.replace(location.x, 0, CARET);
+
+ String actual = document.get();
+
+ String diff = getDiff(fileContent, actual);
+ assertTrue(diff.length() > 0 || fileContent.equals(actual));
+
+ StringBuilder summary = new StringBuilder();
+ summary.append("Code completion in " + basename + " for " + caretLocation + " selecting " + match + ":\n");
+ summary.append(diff);
+
+ //assertEqualsGolden(basename, actual);
+ assertEqualsGolden(basename, summary.toString(), "diff");
+ }
+
+ private void checkCompletion(String basename, IFile file, String caretLocation,
+ AndroidContentAssist assist) throws Exception {
+ ICompletionProposal[] proposals = complete(file, caretLocation, assist);
+ StringBuilder sb = new StringBuilder(1000);
+ sb.append("Code completion in " + basename + " for " + caretLocation + ":\n");
+ for (ICompletionProposal proposal : proposals) {
+ sb.append(proposal.getDisplayString());
+ String help = proposal.getAdditionalProposalInfo();
+ if (help != null && help.trim().length() > 0) {
+ sb.append(" : ");
+ sb.append(help.replace('\n', ' '));
+ }
+ sb.append('\n');
+ }
+ assertEqualsGolden(basename, sb.toString(), "txt");
+ }
+}
*/
package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
+import static com.android.AndroidConstants.FD_RES_LAYOUT;
+import static com.android.sdklib.SdkConstants.FD_RES;
+
import com.android.ide.eclipse.adt.AdtPlugin;
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.wizards.newproject.NewTestProjectCreationPage;
import com.android.ide.eclipse.tests.SdkTestCase;
import com.android.sdklib.IAndroidTarget;
+import com.android.sdklib.SdkConstants;
+import org.eclipse.core.resources.IContainer;
import org.eclipse.core.resources.IFile;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.IPath;
import org.eclipse.core.runtime.NullProgressMonitor;
+import org.eclipse.core.runtime.Path;
import org.eclipse.jface.operation.IRunnableWithProgress;
import org.eclipse.jface.wizard.IWizardContainer;
import org.eclipse.jface.wizard.IWizardPage;
return getTestDataFile(project, name, name);
}
+ protected IFile getLayoutFile(IProject project, String name) throws Exception {
+ return getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
+ }
+
protected IFile getTestDataFile(IProject project, String sourceName,
String destPath) throws Exception {
+ return getTestDataFile(project, sourceName, destPath, false);
+ }
+
+ protected IFile getTestDataFile(IProject project, String sourceName,
+ String destPath, boolean overwrite) throws Exception {
String[] split = destPath.split("/"); //$NON-NLS-1$
- assertTrue(split.length > 1);
- IFolder folder = project.getFolder(split[0]);
- NullProgressMonitor monitor = new NullProgressMonitor();
- if (!folder.exists()) {
- folder.create(true /* force */, true /* local */, monitor);
+ IContainer parent;
+ String name;
+ if (split.length == 1) {
+ parent = project;
+ name = destPath;
+ } else {
+ IFolder folder = project.getFolder(split[0]);
+ NullProgressMonitor monitor = new NullProgressMonitor();
+ if (!folder.exists()) {
+ folder.create(true /* force */, true /* local */, monitor);
+ }
+ for (int i = 1, n = split.length; i < n -1; i++) {
+ IFolder subFolder = folder.getFolder(split[i]);
+ if (!subFolder.exists()) {
+ subFolder.create(true /* force */, true /* local */, monitor);
+ }
+ folder = subFolder;
+ }
+ name = split[split.length - 1];
+ parent = folder;
}
- for (int i = 1, n = split.length; i < n -1; i++) {
- IFolder subFolder = folder.getFolder(split[i]);
- if (!subFolder.exists()) {
- subFolder.create(true /* force */, true /* local */, monitor);
+ IFile file = parent.getFile(new Path(name));
+ if (overwrite && file.exists()) {
+ String currentContents = AdtPlugin.readFile(file);
+ String newContents = readTestFile(sourceName, true);
+ if (currentContents == null || !currentContents.equals(newContents)) {
+ file.delete(true, new NullProgressMonitor());
+ } else {
+ return file;
}
- folder = subFolder;
}
-
- String name = split[split.length - 1];
- IFile file = folder.getFile(name);
if (!file.exists()) {
String xml = readTestFile(sourceName, true);
InputStream bstream = new ByteArrayInputStream(xml.getBytes("UTF-8")); //$NON-NLS-1$
+ NullProgressMonitor monitor = new NullProgressMonitor();
file.create(bstream, false /* force */, monitor);
}
return iproject;
}
+ /**
+ * Very primitive line differ, intended for files where there are very minor changes
+ * (such as code completion apply-tests)
+ */
+ protected String getDiff(String before, String after) {
+
+ // Do line by line analysis
+ String[] beforeLines = before.split("\n");
+ String[] afterLines = after.split("\n");
+
+ int firstDelta = 0;
+ for (; firstDelta < Math.min(beforeLines.length, afterLines.length); firstDelta++) {
+ if (!beforeLines[firstDelta].equals(afterLines[firstDelta])) {
+ break;
+ }
+ }
+
+ if (firstDelta == beforeLines.length && firstDelta == afterLines.length) {
+ return "";
+ }
+
+ // Counts from the end of both arrays
+ int lastDelta = 0;
+ for (; lastDelta < Math.min(beforeLines.length, afterLines.length); lastDelta++) {
+ if (!beforeLines[beforeLines.length - 1 - lastDelta].equals(
+ afterLines[afterLines.length - 1 - lastDelta])) {
+ break;
+ }
+ }
+
+ StringBuilder sb = new StringBuilder();
+ for (int i = firstDelta; i < beforeLines.length - lastDelta; i++) {
+ sb.append("< ");
+ sb.append(beforeLines[i]);
+ sb.append('\n');
+ }
+ sb.append("---\n");
+ for (int i = firstDelta; i < afterLines.length - lastDelta; i++) {
+ sb.append("> ");
+ sb.append(afterLines[i]);
+ sb.append('\n');
+ }
+
+ return sb.toString();
+ }
+
public static ViewElementDescriptor createDesc(String name, String fqn, boolean hasChildren) {
if (hasChildren) {
return new ViewElementDescriptor(name, name, fqn, "", "", new AttributeDescriptor[0],
return xml;
}
+ protected void assertEqualsGolden(String basename, String actual) {
+ assertEqualsGolden(basename, actual, basename.substring(basename.lastIndexOf('.') + 1));
+ }
+
+ protected void assertEqualsGolden(String basename, String actual, String newExtension) {
+ String testName = getName();
+ if (testName.startsWith("test")) {
+ testName = testName.substring(4);
+ if (Character.isUpperCase(testName.charAt(0))) {
+ testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
+ }
+ }
+ String expectedName;
+ String extension = basename.substring(basename.lastIndexOf('.') + 1);
+ if (newExtension == null) {
+ newExtension = extension;
+ }
+ expectedName = basename.substring(0, basename.indexOf('.'))
+ + "-expected-" + testName + '.' + newExtension;
+ String expected = readTestFile(expectedName, false);
+ if (expected == null) {
+ File expectedPath = new File(getTempDir(), expectedName);
+ AdtPlugin.writeFile(expectedPath, actual);
+ System.out.println("Expected - written to " + expectedPath + ":\n");
+ System.out.println(actual);
+ fail("Did not find golden file (" + expectedName + "): Wrote contents as "
+ + expectedPath);
+ } else {
+ if (!expected.equals(actual)) {
+ File expectedPath = new File(getTempDir(), expectedName);
+ File actualPath = new File(getTempDir(),
+ expectedName.replace("expected", "actual"));
+ AdtPlugin.writeFile(expectedPath, expected);
+ AdtPlugin.writeFile(actualPath, actual);
+ System.out.println("The files differ - see " + expectedPath + " versus "
+ + actualPath);
+ assertEquals("The files differ - see " + expectedPath + " versus " + actualPath,
+ expected, actual);
+ }
+ }
+ }
+
+ protected File getTempDir() {
+ if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
+ return new File("/tmp"); //$NON-NLS-1$
+ }
+ return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
+ }
+
/** Special editor context set on the model to be rendered */
protected static class TestLayoutEditor extends LayoutEditor {
private final IFile mFile;
*/
package com.android.ide.eclipse.adt.internal.editors.layout.refactoring;
-import static com.android.AndroidConstants.FD_RES_LAYOUT;
import static com.android.ide.common.layout.LayoutConstants.ANDROID_WIDGET_PREFIX;
-import static com.android.ide.eclipse.adt.AdtConstants.DOT_XML;
-import static com.android.sdklib.SdkConstants.FD_RES;
import com.android.ide.common.rendering.api.ViewInfo;
-import com.android.ide.eclipse.adt.AdtPlugin;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.CanvasViewInfo;
import com.android.ide.eclipse.adt.internal.editors.layout.gle2.DomUtilities;
import com.android.ide.eclipse.adt.internal.editors.layout.uimodel.UiViewElementNode;
import com.android.ide.eclipse.adt.internal.editors.uimodel.UiElementNode;
-import com.android.sdklib.SdkConstants;
import org.eclipse.core.resources.IFile;
-import org.eclipse.core.resources.IProject;
import org.eclipse.core.runtime.IPath;
import org.eclipse.jface.text.BadLocationException;
import org.eclipse.jface.text.Document;
import org.eclipse.wst.xml.core.internal.provisional.document.IDOMModel;
import org.w3c.dom.Element;
-import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Arrays;
@SuppressWarnings("restriction")
public class RefactoringTest extends AdtProjectTest {
- protected IFile getLayoutFile(IProject project, String name) throws Exception {
- return getTestDataFile(project, name, FD_RES + "/" + FD_RES_LAYOUT + "/" + name);
- }
-
protected static Element findElementById(Element root, String id) {
if (id.equals(VisualRefactoring.getId(root))) {
return root;
}
}
- protected void assertEqualsGolden(String basename, String actual) {
- String testName = getName();
- if (testName.startsWith("test")) {
- testName = testName.substring(4);
- if (Character.isUpperCase(testName.charAt(0))) {
- testName = Character.toLowerCase(testName.charAt(0)) + testName.substring(1);
- }
- }
- String expectedName;
- if (basename.endsWith(DOT_XML)) {
- expectedName = basename.substring(0, basename.length() - DOT_XML.length())
- + "-expected-" + testName + DOT_XML;
- } else {
- expectedName = basename + ".expected";
- }
- String expected = readTestFile(expectedName, false);
- if (expected == null) {
- File expectedPath = new File(getTempDir(), expectedName);
- AdtPlugin.writeFile(expectedPath, actual);
- System.out.println("Expected - written to " + expectedPath + ":\n");
- System.out.println(actual);
- fail("Did not find golden file (" + expectedName + "): Wrote contents as "
- + expectedPath);
- } else {
- if (!expected.equals(actual)) {
- File expectedPath = new File(getTempDir(), expectedName);
- File actualPath = new File(getTempDir(),
- expectedName.replace("expected", "actual"));
- AdtPlugin.writeFile(expectedPath, expected);
- AdtPlugin.writeFile(actualPath, actual);
- System.out.println("The files differ - see " + expectedPath + " versus "
- + actualPath);
- assertEquals("The files differ - see " + expectedPath + " versus " + actualPath,
- expected, actual);
- }
- }
- }
-
protected UiViewElementNode createModel(UiViewElementNode parent, Element element) {
List<Element> children = DomUtilities.getChildren(element);
String fqcn = ANDROID_WIDGET_PREFIX + element.getTagName();
return view;
}
- protected File getTempDir() {
- if (SdkConstants.CURRENT_PLATFORM == SdkConstants.PLATFORM_DARWIN) {
- return new File("/tmp"); //$NON-NLS-1$
- }
- return new File(System.getProperty("java.io.tmpdir")); //$NON-NLS-1$
- }
-
protected TestContext setupTestContext(IFile file, String relativePath) throws Exception {
IStructuredModel structuredModel = null;
org.w3c.dom.Document domDocument = null;
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+<!--
+ This file deliberately contains errors - it represents partial keyboard
+ typing for interactive code completion
+-->
+ <TextView
+ android:layout_weight^="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@android:dimen/app_icon_size"
+ android:layout_marginLeft="50dp"
+ android:layout_marginBottom="50"
+ android:textColor="#000000"
+ style="@android:style/Widget.Button"
+ android:gravity="left|bottom"
+ android:text="@string/hello"
+ android:hint="hint" />
+ <FrameLayout android:foreground="@android:drawable/btn_default"></FrameLayout>
+</LinearLayout>
--- /dev/null
+Code completion in completion1.xml for layout_w^idth="fill_parent" selecting android:layout_weight:
+< android:layout_width="fill_parent"
+---
+> android:layout_weight^="fill_parent"
--- /dev/null
+Code completion in completion1.xml for <T^extView selecting TableLayout:
+< <TextView
+---
+> <TableLayout^
--- /dev/null
+Code completion in completion1.xml for ^<TextView selecting <RadioGroup ></RadioGroup>:
+< <TextView
+---
+> <RadioGroup ^></RadioGroup><TextView
--- /dev/null
+Code completion in completion1.xml for ^<TextView selecting <CheckBox />:
+< <TextView
+---
+> <CheckBox ^/><TextView
--- /dev/null
+Code completion in completion1.xml for btn_default">^</FrameLayout> selecting <FrameLayout ></FrameLayout>:
+< <FrameLayout android:foreground="@android:drawable/btn_default"></FrameLayout>
+---
+> <FrameLayout android:foreground="@android:drawable/btn_default"><FrameLayout ^></FrameLayout></FrameLayout>
--- /dev/null
+Code completion in completion1.xml for layout_width="^fill_parent" selecting match_parent:
+< android:layout_width="fill_parent"
+---
+> android:layout_width="match_parent^"
--- /dev/null
+Code completion in completion1.xml for layout_width="fi^ll_parent" selecting fill_parent:
+< android:layout_width="fill_parent"
+---
+> android:layout_width="fill_parent^"
--- /dev/null
+Code completion in completion1.xml for marginBottom="50^" selecting 50mm:
+< android:layout_marginBottom="50"
+---
+> android:layout_marginBottom="50mm^"
--- /dev/null
+Code completion in completion1.xml for layout_marginLeft="50d^p" selecting 50dp:
+< android:layout_marginLeft="50dp"
+---
+> android:layout_marginLeft="50dp^"
--- /dev/null
+Code completion in completion1.xml for style="@android:^style/Widget.Button" selecting @android:drawable/:
+< style="@android:style/Widget.Button"
+---
+> style="@android:drawable/^"
--- /dev/null
+Code completion in completion1.xml for android:gravity="l^eft|bottom" selecting left:
+< android:gravity="left|bottom"
+---
+> android:gravity="left^"
--- /dev/null
+Code completion in completion1.xml for android:gravity="left|b^ottom" selecting bottom:
+< android:gravity="left|bottom"
+---
+> android:gravity="left|bottom^"
--- /dev/null
+Code completion in completion1.xml for layout_width^="fill_parent" selecting android:layout_width:
+< android:layout_width="fill_parent"
+---
+> android:layout_width^="fill_parent"
--- /dev/null
+Code completion in completion1.xml for layout_width=^"fill_parent" selecting "wrap_content":
+< android:layout_width="fill_parent"
+---
+> android:layout_width="wrap_content^"
--- /dev/null
+Code completion in completion1.xml for layout_w^idth="fill_parent":
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
+android:layout_weight : [float]
--- /dev/null
+Code completion in completion1.xml for <T^extView:
+TabHost
+TabWidget
+TableLayout
+TableRow
+TextSwitcher
+TextView
+TimePicker
+ToggleButton
+TwoLineListItem
--- /dev/null
+Code completion in completion1.xml for ^<TextView:
+<AbsoluteLayout ></AbsoluteLayout>
+<AdapterViewFlipper ></AdapterViewFlipper>
+<DialerFilter ></DialerFilter>
+<ExpandableListView ></ExpandableListView>
+<FrameLayout ></FrameLayout>
+<GridView ></GridView>
+<HorizontalScrollView ></HorizontalScrollView>
+<ImageSwitcher ></ImageSwitcher>
+<LinearLayout ></LinearLayout>
+<ListView ></ListView>
+<MediaController ></MediaController>
+<RadioGroup ></RadioGroup>
+<RelativeLayout ></RelativeLayout>
+<ScrollView ></ScrollView>
+<SearchView ></SearchView>
+<SlidingDrawer ></SlidingDrawer> : SlidingDrawer specific attributes.
+<StackView ></StackView>
+<TabHost ></TabHost>
+<TabWidget ></TabWidget>
+<TableLayout ></TableLayout>
+<TableRow ></TableRow>
+<TextSwitcher ></TextSwitcher>
+<ViewAnimator ></ViewAnimator>
+<ViewFlipper ></ViewFlipper>
+<ViewSwitcher ></ViewSwitcher>
+<GestureOverlayView /> : GestureOverlayView specific attributes.
+<SurfaceView />
+<View /> : Attributes that can be used with android.view.View or any of its subclasses.
+<ViewStub /> : A android.view.ViewStub lets you lazily include other XML layouts inside your application at runtime.
+<WebView />
+<AnalogClock />
+<AutoCompleteTextView />
+<Button />
+<CalendarView />
+<CheckBox />
+<CheckedTextView />
+<Chronometer />
+<DatePicker />
+<DigitalClock />
+<EditText />
+<Gallery />
+<ImageButton />
+<ImageView />
+<MultiAutoCompleteTextView />
+<NumberPicker />
+<ProgressBar />
+<QuickContactBadge />
+<RadioButton />
+<RatingBar />
+<SeekBar />
+<Spinner />
+<TextView />
+<TimePicker />
+<ToggleButton />
+<TwoLineListItem />
+<VideoView />
+<ZoomButton />
+<ZoomControls />
+<include /> : Lets you statically include XML layouts inside other XML layouts.
--- /dev/null
+Code completion in completion1.xml for btn_default">^</FrameLayout>:
+<AbsoluteLayout ></AbsoluteLayout>
+<AdapterViewFlipper ></AdapterViewFlipper>
+<DialerFilter ></DialerFilter>
+<ExpandableListView ></ExpandableListView>
+<FrameLayout ></FrameLayout>
+<GridView ></GridView>
+<HorizontalScrollView ></HorizontalScrollView>
+<ImageSwitcher ></ImageSwitcher>
+<LinearLayout ></LinearLayout>
+<ListView ></ListView>
+<MediaController ></MediaController>
+<RadioGroup ></RadioGroup>
+<RelativeLayout ></RelativeLayout>
+<ScrollView ></ScrollView>
+<SearchView ></SearchView>
+<SlidingDrawer ></SlidingDrawer> : SlidingDrawer specific attributes.
+<StackView ></StackView>
+<TabHost ></TabHost>
+<TabWidget ></TabWidget>
+<TableLayout ></TableLayout>
+<TableRow ></TableRow>
+<TextSwitcher ></TextSwitcher>
+<ViewAnimator ></ViewAnimator>
+<ViewFlipper ></ViewFlipper>
+<ViewSwitcher ></ViewSwitcher>
+<GestureOverlayView /> : GestureOverlayView specific attributes.
+<SurfaceView />
+<View /> : Attributes that can be used with android.view.View or any of its subclasses.
+<ViewStub /> : A android.view.ViewStub lets you lazily include other XML layouts inside your application at runtime.
+<WebView />
+<AnalogClock />
+<AutoCompleteTextView />
+<Button />
+<CalendarView />
+<CheckBox />
+<CheckedTextView />
+<Chronometer />
+<DatePicker />
+<DigitalClock />
+<EditText />
+<Gallery />
+<ImageButton />
+<ImageView />
+<MultiAutoCompleteTextView />
+<NumberPicker />
+<ProgressBar />
+<QuickContactBadge />
+<RadioButton />
+<RatingBar />
+<SeekBar />
+<Spinner />
+<TextView />
+<TimePicker />
+<ToggleButton />
+<TwoLineListItem />
+<VideoView />
+<ZoomButton />
+<ZoomControls />
+<include /> : Lets you statically include XML layouts inside other XML layouts.
--- /dev/null
+Code completion in completion1.xml for layout_width="^fill_parent":
+fill_parent
+match_parent
+wrap_content
--- /dev/null
+Code completion in completion1.xml for layout_width="fi^ll_parent":
+fill_parent
--- /dev/null
+Code completion in completion1.xml for marginBottom="50^":
+50dp : <b>Density-independent Pixels</b> - an abstract unit that is based on the physical density of the screen.
+50sp : <b>Scale-independent Pixels</b> - this is like the dp unit, but it is also scaled by the user's font size preference.
+50pt : <b>Points</b> - 1/72 of an inch based on the physical size of the screen.
+50mm : <b>Millimeters</b> - based on the physical size of the screen.
+50in : <b>Inches</b> - based on the physical size of the screen.
+50px : <b>Pixels</b> - corresponds to actual pixels on the screen. Not recommended.
--- /dev/null
+Code completion in completion1.xml for layout_marginLeft="50d^p":
+50dp : <b>Density-independent Pixels</b> - an abstract unit that is based on the physical density of the screen.
--- /dev/null
+Code completion in completion1.xml for style="@android:^style/Widget.Button":
+@android:style/
+@android:anim/
+@android:animator/
+@android:array/
+@android:bool/
+@android:color/
+@android:declare-styleable/
+@android:dimen/
+@android:drawable/
+@android:fraction/
+@android:id/
+@android:integer/
+@android:interpolator/
+@android:layout/
+@android:menu/
+@android:mipmap/
+@android:plurals/
+@android:public/
+@android:raw/
+@android:string/
+@android:xml/
--- /dev/null
+Code completion in completion1.xml for android:gravity="l^eft|bottom":
+left
--- /dev/null
+Code completion in completion1.xml for android:gravity="left|b^ottom":
+bottom
--- /dev/null
+Code completion in completion1.xml for layout_width^="fill_parent":
+android:layout_width : Specifies the basic width of the view. [dimension, enum]
--- /dev/null
+Code completion in completion1.xml for layout_width=^"fill_parent":
+"fill_parent"
+"match_parent"
+"wrap_content"
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+<!--
+ This file deliberately contains errors - it represents partial keyboard
+ typing for interactive code completion
+-->
+ <TextView
+ android:layout_width="fill_parent"
+ android:layout_height="wrap_content"
+ android:layout_marginTop="@android:dimen/app_icon_size"
+ android:layout_marginLeft="50dp"
+ android:layout_marginBottom="50"
+ android:textColor="#000000"
+ style="@android:style/Widget.Button"
+ android:gravity="left|bottom"
+ android:text="@string/hello"
+ android:hint="hint" />
+ <FrameLayout android:foreground="@android:drawable/btn_default"></FrameLayout>
+</LinearLayout>
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom|^cen selecting fill_vertical:
+< <TextView android:gravity="left|bottom|cen"></TextView>
+---
+> <TextView android:gravity="left|bottom|fill_vertical^"></TextView>
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom|cen^ selecting center_horizontal:
+< <TextView android:gravity="left|bottom|cen"></TextView>
+---
+> <TextView android:gravity="left|bottom|center_horizontal^"></TextView>
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom^|cen selecting bottom|fill_horizontal:
+< <TextView android:gravity="left|bottom|cen"></TextView>
+---
+> <TextView android:gravity="left|bottom|fill_horizontal^"></TextView>
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom|^cen:
+top
+bottom
+left
+right
+center_vertical
+fill_vertical
+center_horizontal
+fill_horizontal
+center
+fill
+clip_vertical
+clip_horizontal
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom|cen^:
+center_vertical
+center_horizontal
+center
--- /dev/null
+Code completion in completion2.xml for gravity="left|bottom^|cen:
+bottom
+bottom|top
+bottom|right
+bottom|center_vertical
+bottom|fill_vertical
+bottom|center_horizontal
+bottom|fill_horizontal
+bottom|center
+bottom|fill
+bottom|clip_vertical
+bottom|clip_horizontal
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android">
+ <!-- Test multiple pipes in the flag value -->
+ <TextView android:gravity="left|bottom|cen"></TextView>
+</LinearLayout>
--- /dev/null
+Code completion in manifest.xml for android.permission.ACC^ESS_NETWORK_STATE:
+android.permission.ACCESS_CHECKIN_PROPERTIES
+android.permission.ACCESS_COARSE_LOCATION
+android.permission.ACCESS_FINE_LOCATION
+android.permission.ACCESS_LOCATION_EXTRA_COMMANDS
+android.permission.ACCESS_MOCK_LOCATION
+android.permission.ACCESS_NETWORK_STATE
+android.permission.ACCESS_SURFACE_FLINGER
+android.permission.ACCESS_WIFI_STATE
+android.permission.ACCOUNT_MANAGER
--- /dev/null
+Code completion in manifest.xml for android.intent.category.L^AUNCHER:
+android.intent.category.LAUNCHER
+android.intent.category.LE_DESK_DOCK
--- /dev/null
+Code completion in manifest.xml for <^application android:i:
+application : The "application" tag describes application-level components contained in the package, as well as general application attributes.
+instrumentation : Attributes that can be supplied in an AndroidManifest.xml "instrumentation" tag, a child of the root manifest tag.
+uses-feature : The "uses-feature" tag specifies a specific feature used by the application.
+permission-group : The "permission-group" tag declares a logical grouping of related permissions.
+supports-screens : The "supports-screens" specifies the screen dimensions an application supports.
+protected-broadcast : Private tag to declare system protected broadcast actions.
+uses-configuration : The "uses-configuration" tag specifies a specific hardware configuration value used by the application.
+compatible-screens
+permission : The "permission" tag declares a security permission that can be used to control access from other packages to specific components or features in your package (or other packages).
+uses-sdk : The "uses-sdk" tag describes the SDK features that the containing package must be running on to operate correctly.
+permission-tree : The "permission-tree" tag declares the base of a tree of permission values: it declares that this package has ownership of the given permission name, as well as all names underneath it (separated by '.').
+uses-permission : The "uses-permission" tag requests a "permission" that the containing package must be granted in order for it to operate correctly.
+original-package : Private tag to declare the original package name that this package is based on.
--- /dev/null
+Code completion in manifest.xml for ^android:versionCode="1":
+package : This attribute gives a unique name for the package, using a Java-style naming convention to avoid name collisions. For example, applications published by Google could have names of the form com.google.app.appname
+android:versionCode : Internal version code. [integer]
+android:versionName : The text shown to the user to indicate the version they have. [string]
+android:sharedUserId : Specify the name of a user ID that will be shared between multiple packages. [string]
+android:sharedUserLabel : Specify a label for the shared user UID of this package. [reference]
+android:installLocation : The default install location defined by an application. [enum]
--- /dev/null
+Code completion in manifest.xml for <activity android:^name=".TestActivity":
+android:name : Required name of the class implementing the activity, deriving from android.app.Activity. [string]
+android:theme : The overall theme to use for an activity. [reference]
+android:label : A user-legible name for the given item. [string, reference]
+android:description : Descriptive text for the associated data. [reference]
+android:icon : A Drawable resource providing a graphical representation of its associated item. [reference]
+android:logo : A Drawable resource providing an extended graphical logo for its associated item. [reference]
+android:launchMode : Specify how an activity should be launched. [enum]
+android:screenOrientation : Specify the orientation an activity should be run in. [enum]
+android:configChanges : Specify one or more configuration changes that the activity will handle itself. [flag]
+android:permission : Specify a permission that a client is required to have in order to use the associated object. [string]
+android:multiprocess : Specify whether a component is allowed to have multiple instances of itself running in different processes. [boolean]
+android:process : Specify a specific process that the associated code is to run in. [string]
+android:taskAffinity : Specify a task name that activities have an "affinity" to. [string]
+android:allowTaskReparenting : Specify that an activity can be moved out of a task it is in to the task it has an affinity for when appropriate. [boolean]
+android:finishOnTaskLaunch : Specify whether an activity should be finished when its task is brought to the foreground by relaunching from the home screen. [boolean]
+android:finishOnCloseSystemDialogs : Specify whether an activity should be finished when a "close system windows" request has been made. [boolean]
+android:clearTaskOnLaunch : Specify whether an activity's task should be cleared when it is re-launched from the home screen. [boolean]
+android:noHistory : Specify whether an activity should be kept in its history stack. [boolean]
+android:alwaysRetainTaskState : Specify whether an acitivty's task state should always be maintained by the system, or if it is allowed to reset the task to its initial state in certain situations. [boolean]
+android:stateNotNeeded : Indicates that an Activity does not need to have its freeze state (as returned by onSaveInstanceState retained in order to be restarted. [boolean]
+android:excludeFromRecents : Indicates that an Activity should be excluded from the list of recently launched activities. [boolean]
+android:enabled : Specify whether the activity is enabled or not (that is, can be instantiated by the system). [boolean]
+android:exported : Flag indicating whether the given application component is available to other applications. [boolean]
+android:windowSoftInputMode : Specify the default soft-input mode for the main window of this activity. [flag]
+android:immersive : Flag declaring this activity to be 'immersive'; immersive activities should not be interrupted with other activities or notifications. [boolean]
+android:hardwareAccelerated : <p>Flag indicating whether the application's rendering should be hardware accelerated if possible. [boolean]
--- /dev/null
+<?xml version="1.0" encoding="utf-8"?>
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="foo.bar"
+ android:versionCode="1"
+ android:versionName="1.0">
+ <uses-sdk android:minSdkVersion="11" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+
+ <application android:icon="@drawable/icon" android:label="@string/app_name">
+ <activity android:name=".TestActivity"
+ android:label="@string/app_name">
+ <intent-filter>
+ <action android:name="android.intent.action.MAIN" />
+ <category android:name="android.intent.category.LAUNCHER" />
+ </intent-filter>
+ </activity>
+
+ </application>
+</manifest>
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout android:id="@+id/RelativeLayout1" xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="match_parent" android:orientation="vertical">
<Button android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:text="FirstButton" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content"></Button>
- <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dip" android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dp" android:text="SecondButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button2"></Button>
<Button android:layout_toRightOf="@+id/button2" android:layout_alignBaseline="@+id/button2" android:layout_alignTop="@+id/button2" android:text="ThirdButton" android:layout_width="wrap_content" android:layout_height="wrap_content" android:id="@+id/button3"></Button>
<CheckBox android:layout_toRightOf="@+id/button3" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:id="@+id/checkBox1" android:text="CheckBox" android:layout_width="wrap_content" android:layout_height="wrap_content"></CheckBox>
<Button android:layout_alignParentLeft="true" android:layout_below="@+id/checkBox1" android:layout_height="wrap_content" android:text="FourthButton" android:id="@+id/button4" android:layout_width="match_parent"></Button>
android:id="@+id/button1"
android:layout_width="wrap_content"
android:layout_height="wrap_content"></Button>
- <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dip"
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button2" android:layout_below="@+id/button1" android:layout_marginTop="2dp"
android:text="SecondButton"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
<Button android:layout_centerHorizontal="true" android:layout_alignParentTop="true" android:text="Button" android:id="@+id/button1" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center_horizontal"></Button>
<Button android:layout_centerHorizontal="true" android:layout_below="@+id/button1" android:text="Button" android:id="@+id/button2" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center"></Button>
<Button android:layout_alignParentRight="true" android:layout_below="@+id/button2" android:text="Button" android:id="@+id/button3" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="right"></Button>
- <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button4" android:layout_below="@+id/button3" android:layout_marginTop="70dip" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Button" android:id="@+id/button4" android:layout_width="wrap_content"></Button>
+ <Button android:layout_alignParentLeft="true" android:layout_alignBaseline="@+id/button4" android:layout_below="@+id/button3" android:layout_marginTop="70dp" android:layout_height="wrap_content" android:layout_gravity="center" android:text="Button" android:id="@+id/button4" android:layout_width="wrap_content"></Button>
<Button android:layout_toRightOf="@+id/button4" android:layout_alignBaseline="@+id/button4" android:layout_alignTop="@+id/button4" android:layout_height="wrap_content" android:layout_gravity="center_vertical" android:text="Button" android:id="@+id/button5" android:layout_width="wrap_content"></Button>
<Button android:layout_toRightOf="@+id/button5" android:layout_alignParentBottom="true" android:layout_height="wrap_content" android:text="Button" android:id="@+id/button6" android:layout_width="wrap_content" android:layout_gravity="bottom"></Button>
</RelativeLayout>
// Drop preview
"useStyle(DROP_PREVIEW), drawRect(Rect[30,-10,105,80])");
- assertEquals("30dip", inserted.getStringAttr(ANDROID_URI, "layout_x"));
- assertEquals("-10dip", inserted.getStringAttr(ANDROID_URI, "layout_y"));
+ assertEquals("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x"));
+ assertEquals("-10dp", inserted.getStringAttr(ANDROID_URI, "layout_y"));
// Without drag bounds we should just draw guide lines instead
inserted = dragInto(new Rect(0, 0, 0, 0), new Point(30, -10), 4, -1,
"useStyle(GUIDELINE), drawLine(30,0,30,480), drawLine(0,-10,240,-10)",
// Drop preview
"useStyle(DROP_PREVIEW), drawLine(30,-10,240,-10), drawLine(30,-10,30,480)");
- assertEquals("30dip", inserted.getStringAttr(ANDROID_URI, "layout_x"));
- assertEquals("-10dip", inserted.getStringAttr(ANDROID_URI, "layout_y"));
+ assertEquals("30dp", inserted.getStringAttr(ANDROID_URI, "layout_x"));
+ assertEquals("-10dp", inserted.getStringAttr(ANDROID_URI, "layout_y"));
}
}
+++ /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;
-
-import junit.framework.TestCase;
-
-public class AndroidContentAssistTest extends TestCase {
- public void testStartsWith() {
- assertTrue(AndroidContentAssist.startsWith("", ""));
- assertTrue(AndroidContentAssist.startsWith("a", ""));
- assertTrue(AndroidContentAssist.startsWith("A", ""));
- assertTrue(AndroidContentAssist.startsWith("A", "a"));
- assertTrue(AndroidContentAssist.startsWith("A", "A"));
- assertTrue(AndroidContentAssist.startsWith("Ab", "a"));
- assertTrue(AndroidContentAssist.startsWith("ab", "A"));
- assertTrue(AndroidContentAssist.startsWith("ab", "AB"));
- assertFalse(AndroidContentAssist.startsWith("ab", "ABc"));
- assertFalse(AndroidContentAssist.startsWith("", "ABc"));
- }
-}