aboutsummaryrefslogtreecommitdiffstats
path: root/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
diff options
context:
space:
mode:
Diffstat (limited to 'lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java')
-rw-r--r--lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java682
1 files changed, 0 insertions, 682 deletions
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
deleted file mode 100644
index 4b5bc84..0000000
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ButtonDetector.java
+++ /dev/null
@@ -1,682 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.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.apache.org/licenses/LICENSE-2.0
- *
- * 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.tools.lint.checks;
-
-import static com.android.SdkConstants.ANDROID_STRING_PREFIX;
-import static com.android.SdkConstants.ANDROID_URI;
-import static com.android.SdkConstants.ATTR_ID;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_LEFT;
-import static com.android.SdkConstants.ATTR_LAYOUT_ALIGN_PARENT_RIGHT;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_LEFT_OF;
-import static com.android.SdkConstants.ATTR_LAYOUT_TO_RIGHT_OF;
-import static com.android.SdkConstants.ATTR_NAME;
-import static com.android.SdkConstants.ATTR_ORIENTATION;
-import static com.android.SdkConstants.ATTR_TEXT;
-import static com.android.SdkConstants.BUTTON;
-import static com.android.SdkConstants.LINEAR_LAYOUT;
-import static com.android.SdkConstants.RELATIVE_LAYOUT;
-import static com.android.SdkConstants.STRING_PREFIX;
-import static com.android.SdkConstants.TABLE_ROW;
-import static com.android.SdkConstants.TAG_STRING;
-import static com.android.SdkConstants.VALUE_TRUE;
-import static com.android.SdkConstants.VALUE_VERTICAL;
-
-import com.android.SdkConstants;
-import com.android.annotations.NonNull;
-import com.android.resources.ResourceFolderType;
-import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Context;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.LintUtils;
-import com.android.tools.lint.detector.api.Location;
-import com.android.tools.lint.detector.api.ResourceXmlDetector;
-import com.android.tools.lint.detector.api.Scope;
-import com.android.tools.lint.detector.api.Severity;
-import com.android.tools.lint.detector.api.Speed;
-import com.android.tools.lint.detector.api.XmlContext;
-
-import org.w3c.dom.Element;
-import org.w3c.dom.Node;
-import org.w3c.dom.NodeList;
-
-import java.io.File;
-import java.util.ArrayList;
-import java.util.Arrays;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.HashSet;
-import java.util.List;
-import java.util.Map;
-import java.util.Set;
-
-/**
- * Check which looks at the order of buttons in dialogs and makes sure that
- * "the dismissive action of a dialog is always on the left whereas the affirmative actions
- * are on the right."
- * <p>
- * This only looks for the affirmative and dismissive actions named "OK" and "Cancel";
- * "Cancel" usually works, but the affirmative action often has many other names -- "Done",
- * "Send", "Go", etc.
- * <p>
- * TODO: Perhaps we should look for Yes/No dialogs and suggested they be rephrased as
- * Cancel/OK dialogs? Similarly, consider "Abort" a synonym for "Cancel" ?
- */
-public class ButtonDetector extends ResourceXmlDetector {
- /** Name of cancel value ("Cancel") */
- private static final String CANCEL_LABEL = "Cancel";
- /** Name of OK value ("Cancel") */
- private static final String OK_LABEL = "OK";
- /** Name of Back value ("Back") */
- private static final String BACK_LABEL = "Back";
-
- /** Layout text attribute reference to {@code @android:string/ok} */
- private static final String ANDROID_OK_RESOURCE =
- ANDROID_STRING_PREFIX + "ok"; //$NON-NLS-1$
- /** Layout text attribute reference to {@code @android:string/cancel} */
- private static final String ANDROID_CANCEL_RESOURCE =
- ANDROID_STRING_PREFIX + "cancel"; //$NON-NLS-1$
-
- /** The main issue discovered by this detector */
- public static final Issue ORDER = Issue.create(
- "ButtonOrder", //$NON-NLS-1$
- "Ensures the dismissive action of a dialog is on the left and affirmative on " +
- "the right",
-
- "According to the Android Design Guide,\n" +
- "\n" +
- "\"Action buttons are typically Cancel and/or OK, with OK indicating the preferred " +
- "or most likely action. However, if the options consist of specific actions such " +
- "as Close or Wait rather than a confirmation or cancellation of the action " +
- "described in the content, then all the buttons should be active verbs. As a rule, " +
- "the dismissive action of a dialog is always on the left whereas the affirmative " +
- "actions are on the right.\"\n" +
- "\n" +
- "This check looks for button bars and buttons which look like cancel buttons, " +
- "and makes sure that these are on the left.",
-
- Category.USABILITY,
- 8,
- Severity.WARNING,
- ButtonDetector.class,
- Scope.RESOURCE_FILE_SCOPE)
- .setMoreInfo(
- "http://developer.android.com/design/building-blocks/dialogs.html"); //$NON-NLS-1$
-
- /** The main issue discovered by this detector */
- public static final Issue BACKBUTTON = Issue.create(
- "BackButton", //$NON-NLS-1$
- "Looks for Back buttons, which are not common on the Android platform.",
- // TODO: Look for ">" as label suffixes as well
-
- "According to the Android Design Guide,\n" +
- "\n" +
- "\"Other platforms use an explicit back button with label to allow the user " +
- "to navigate up the application's hierarchy. Instead, Android uses the main " +
- "action bar's app icon for hierarchical navigation and the navigation bar's " +
- "back button for temporal navigation.\"" +
- "\n" +
- "This check is not very sophisticated (it just looks for buttons with the " +
- "label \"Back\"), so it is disabled by default to not trigger on common " +
- "scenarios like pairs of Back/Next buttons to paginate through screens.",
-
- Category.USABILITY,
- 6,
- Severity.WARNING,
- ButtonDetector.class,
- Scope.RESOURCE_FILE_SCOPE)
- .setEnabledByDefault(false)
- .setMoreInfo(
- "http://developer.android.com/design/patterns/pure-android.html"); //$NON-NLS-1$
-
- /** The main issue discovered by this detector */
- public static final Issue CASE = Issue.create(
- "ButtonCase", //$NON-NLS-1$
- "Ensures that Cancel/OK dialog buttons use the canonical capitalization",
-
- "The standard capitalization for OK/Cancel dialogs is \"OK\" and \"Cancel\". " +
- "To ensure that your dialogs use the standard strings, you can use " +
- "the resource strings @android:string/ok and @android:string/cancel.",
-
- Category.USABILITY,
- 2,
- Severity.WARNING,
- ButtonDetector.class,
- Scope.RESOURCE_FILE_SCOPE);
-
- /** Set of resource names whose value was either OK or Cancel */
- private Set<String> mApplicableResources;
-
- /**
- * Map of resource names we'd like resolved into strings in phase 2. The
- * values should be filled in with the actual string contents.
- */
- private Map<String, String> mKeyToLabel;
-
- /**
- * Set of elements we've already warned about. If we've already complained
- * about a cancel button, don't also report the OK button (since it's listed
- * for the warnings on OK buttons).
- */
- private Set<Element> mIgnore;
-
- /** Constructs a new {@link ButtonDetector} */
- public ButtonDetector() {
- }
-
- @Override
- public @NonNull Speed getSpeed() {
- return Speed.FAST;
- }
-
- @Override
- public Collection<String> getApplicableElements() {
- return Arrays.asList(BUTTON, TAG_STRING);
- }
-
- @Override
- public boolean appliesTo(@NonNull ResourceFolderType folderType) {
- return folderType == ResourceFolderType.LAYOUT || folderType == ResourceFolderType.VALUES;
- }
-
- @Override
- public void afterCheckProject(@NonNull Context context) {
- int phase = context.getPhase();
- if (phase == 1 && mApplicableResources != null) {
- // We found resources for the string "Cancel"; perform a second pass
- // where we check layout text attributes against these strings.
- context.getDriver().requestRepeat(this, Scope.RESOURCE_FILE_SCOPE);
- }
- }
-
- private String stripLabel(String text) {
- text = text.trim();
- if (text.length() > 2
- && (text.charAt(0) == '"' || text.charAt(0) == '\'')
- && (text.charAt(0) == text.charAt(text.length() - 1))) {
- text = text.substring(1, text.length() - 1);
- }
-
- return text;
- }
-
- @Override
- public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
- // This detector works in two passes.
- // In pass 1, it looks in layout files for hardcoded strings of "Cancel", or
- // references to @string/cancel or @android:string/cancel.
- // It also looks in values/ files for strings whose value is "Cancel",
- // and if found, stores the corresponding keys in a map. (This is necessary
- // since value files are processed after layout files).
- // Then, if at the end of phase 1 any "Cancel" string resources were
- // found in the value files, then it requests a *second* phase,
- // where it looks only for <Button>'s whose text matches one of the
- // cancel string resources.
- int phase = context.getPhase();
- String tagName = element.getTagName();
- if (phase == 1 && tagName.equals(TAG_STRING)) {
- NodeList childNodes = element.getChildNodes();
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node child = childNodes.item(i);
- if (child.getNodeType() == Node.TEXT_NODE) {
- String text = child.getNodeValue();
- for (int j = 0, len = text.length(); j < len; j++) {
- char c = text.charAt(j);
- if (!Character.isWhitespace(c)) {
- if (c == '"' || c == '\'') {
- continue;
- }
- if (LintUtils.startsWith(text, CANCEL_LABEL, j)) {
- String label = stripLabel(text);
- if (label.equalsIgnoreCase(CANCEL_LABEL)) {
- String name = element.getAttribute(ATTR_NAME);
- foundResource(context, name, element);
-
- if (!label.equals(CANCEL_LABEL)
- && isEnglishResource(context)
- && context.isEnabled(CASE)) {
- assert label.equalsIgnoreCase(CANCEL_LABEL);
- context.report(CASE, child, context.getLocation(child),
- String.format(
- "The standard Android way to capitalize %1$s " +
- "is \"Cancel\" (tip: use @android:string/ok instead)",
- label), null);
- }
- }
- } else if (LintUtils.startsWith(text, OK_LABEL, j)) {
- String label = stripLabel(text);
- if (label.equalsIgnoreCase(OK_LABEL)) {
- String name = element.getAttribute(ATTR_NAME);
- foundResource(context, name, element);
-
- if (!label.equals(OK_LABEL)
- && isEnglishResource(context)
- && context.isEnabled(CASE)) {
- assert text.equalsIgnoreCase(OK_LABEL);
- context.report(CASE, child, context.getLocation(child),
- String.format(
- "The standard Android way to capitalize %1$s " +
- "is \"OK\" (tip: use @android:string/ok instead)",
- label), null);
- }
- }
- } else if (LintUtils.startsWith(text, BACK_LABEL, j) &&
- stripLabel(text).equalsIgnoreCase(BACK_LABEL)) {
- String name = element.getAttribute(ATTR_NAME);
- foundResource(context, name, element);
- }
- break;
- }
- }
- }
- }
- } else if (tagName.equals(BUTTON)) {
- String text = element.getAttributeNS(ANDROID_URI, ATTR_TEXT);
- if (context.getDriver().getPhase() == 2) {
- if (mApplicableResources.contains(text)) {
- String key = text;
- if (key.startsWith(STRING_PREFIX)) {
- key = key.substring(STRING_PREFIX.length());
- }
- String label = mKeyToLabel.get(key);
- boolean isCancel = CANCEL_LABEL.equalsIgnoreCase(label);
- if (isCancel) {
- if (isWrongCancelPosition(element)) {
- reportCancelPosition(context, element);
- }
- } else if (OK_LABEL.equalsIgnoreCase(label)) {
- if (isWrongOkPosition(element)) {
- reportOkPosition(context, element);
- }
- } else {
- assert BACK_LABEL.equalsIgnoreCase(label);
- Location location = context.getLocation(element);
- if (context.isEnabled(BACKBUTTON)) {
- context.report(BACKBUTTON, element, location,
- "Back buttons are not standard on Android; see design guide's " +
- "navigation section", null);
- }
- }
- }
- } else if (text.equals(CANCEL_LABEL) || text.equals(ANDROID_CANCEL_RESOURCE)) {
- if (isWrongCancelPosition(element)) {
- reportCancelPosition(context, element);
- }
- } else if (text.equals(OK_LABEL) || text.equals(ANDROID_OK_RESOURCE)) {
- if (isWrongOkPosition(element)) {
- reportOkPosition(context, element);
- }
- }
- }
- }
-
- /** Report the given OK button as being in the wrong position */
- private void reportOkPosition(XmlContext context, Element element) {
- report(context, element, false /*isCancel*/);
- }
-
- /** Report the given Cancel button as being in the wrong position */
- private void reportCancelPosition(XmlContext context, Element element) {
- report(context, element, true /*isCancel*/);
- }
-
-
- /** The Ok/Cancel detector only works with default and English locales currently.
- * TODO: Add in patterns for other languages. We can use the
- * @android:string/ok and @android:string/cancel localizations to look
- * up the canonical ones. */
- private boolean isEnglishResource(XmlContext context) {
- String folder = context.file.getParentFile().getName();
- if (folder.indexOf('-') != -1) {
- String[] qualifiers = folder.split("-"); //$NON-NLS-1$
- for (String qualifier : qualifiers) {
- if (qualifier.equals("en")) { //$NON-NLS-1$
- return true;
- }
- }
- return false;
- }
-
- // Default folder ("values") - may not be English but we'll consider matches
- // on "OK", "Cancel" and "Back" as matches there
- return true;
- }
-
- /**
- * We've found a resource reference to some label we're interested in ("OK",
- * "Cancel", "Back", ...). Record the corresponding name such that in the
- * next pass through the layouts we can check the context (for OK/Cancel the
- * button order etc).
- */
- private void foundResource(XmlContext context, String name, Element element) {
- if (!isEnglishResource(context)) {
- return;
- }
-
- if (mApplicableResources == null) {
- mApplicableResources = new HashSet<String>();
- }
-
- mApplicableResources.add(STRING_PREFIX + name);
-
- // ALSO record all the other string resources in this file to pick up other
- // labels. If you define "OK" in one resource file and "Cancel" in another
- // this won't work, but that's probably not common and has lower overhead.
- Node parentNode = element.getParentNode();
-
- List<Element> items = LintUtils.getChildren(parentNode);
- if (mKeyToLabel == null) {
- mKeyToLabel = new HashMap<String, String>(items.size());
- }
- for (Element item : items) {
- String itemName = item.getAttribute(ATTR_NAME);
- NodeList childNodes = item.getChildNodes();
- for (int i = 0, n = childNodes.getLength(); i < n; i++) {
- Node child = childNodes.item(i);
- if (child.getNodeType() == Node.TEXT_NODE) {
- String text = stripLabel(child.getNodeValue());
- if (text.length() > 0) {
- mKeyToLabel.put(itemName, text);
- break;
- }
- }
- }
- }
- }
-
- /** Report the given OK/Cancel button as being in the wrong position */
- private void report(XmlContext context, Element element, boolean isCancel) {
- if (!context.isEnabled(ORDER)) {
- return;
- }
-
- if (mIgnore != null && mIgnore.contains(element)) {
- return;
- }
-
- int target = context.getProject().getTargetSdk();
- if (target < 14) {
- // If you're only targeting pre-ICS UI's, this is not an issue
- return;
- }
-
- boolean mustCreateIcsLayout = false;
- if (context.getProject().getMinSdk() < 14) {
- // If you're *also* targeting pre-ICS UIs, then this reverse button
- // order is correct for layouts intended for pre-ICS and incorrect for
- // ICS layouts.
- //
- // Therefore, we need to know if this layout is an ICS layout or
- // a pre-ICS layout.
- boolean isIcsLayout = context.getFolderVersion() >= 14;
- if (!isIcsLayout) {
- // This layout is not an ICS layout. However, there *must* also be
- // an ICS layout here, or this button order will be wrong:
- File res = context.file.getParentFile().getParentFile();
- File[] resFolders = res.listFiles();
- String fileName = context.file.getName();
- if (resFolders != null) {
- for (File folder : resFolders) {
- String folderName = folder.getName();
- if (folderName.startsWith(SdkConstants.FD_RES_LAYOUT)
- && folderName.contains("-v14")) { //$NON-NLS-1$
- File layout = new File(folder, fileName);
- if (layout.exists()) {
- // Yes, a v14 specific layout is available so this pre-ICS
- // layout order is not a problem
- return;
- }
- }
- }
- }
- mustCreateIcsLayout = true;
- }
- }
-
- List<Element> buttons = LintUtils.getChildren(element.getParentNode());
-
- if (mIgnore == null) {
- mIgnore = new HashSet<Element>();
- }
- for (Element button : buttons) {
- // Mark all the siblings in the ignore list to ensure that we don't
- // report *both* the Cancel and the OK button in "OK | Cancel"
- mIgnore.add(button);
- }
-
- String message;
- if (isCancel) {
- message = "Cancel button should be on the left";
- } else {
- message = "OK button should be on the right";
- }
-
- if (mustCreateIcsLayout) {
- message = String.format(
- "Layout uses the wrong button order for API >= 14: Create a " +
- "layout-v14/%1$s file with opposite order: %2$s",
- context.file.getName(), message);
- }
-
- // Show existing button order? We can only do that for LinearLayouts
- // since in for example a RelativeLayout the order of the elements may
- // not be the same as the visual order
- String layout = element.getParentNode().getNodeName();
- if (layout.equals(LINEAR_LAYOUT) || layout.equals(TABLE_ROW)) {
- List<String> labelList = getLabelList(buttons);
- String wrong = describeButtons(labelList);
- sortButtons(labelList);
- String right = describeButtons(labelList);
- message += String.format(" (was \"%1$s\", should be \"%2$s\")", wrong, right);
- }
-
- Location location = context.getLocation(element);
- context.report(ORDER, location, message, null);
- }
-
- /**
- * Sort a list of label buttons into the expected order (Cancel on the left,
- * OK on the right
- */
- private void sortButtons(List<String> labelList) {
- for (int i = 0, n = labelList.size(); i < n; i++) {
- String label = labelList.get(i);
- if (label.equalsIgnoreCase(CANCEL_LABEL) && i > 0) {
- swap(labelList, 0, i);
- } else if (label.equalsIgnoreCase(OK_LABEL) && i < n - 1) {
- swap(labelList, n - 1, i);
- }
- }
- }
-
- /** Swaps the strings at positions i and j */
- private static void swap(List<String> strings, int i, int j) {
- if (i != j) {
- String temp = strings.get(i);
- strings.set(i, strings.get(j));
- strings.set(j, temp);
- }
- }
-
- /** Creates a display string for a list of button labels, such as "Cancel | OK" */
- private String describeButtons(List<String> labelList) {
- StringBuilder sb = new StringBuilder();
- for (String label : labelList) {
- if (sb.length() > 0) {
- sb.append(" | "); //$NON-NLS-1$
- }
- sb.append(label);
- }
-
- return sb.toString();
- }
-
- /** Returns the ordered list of button labels */
- private List<String> getLabelList(List<Element> views) {
- List<String> labels = new ArrayList<String>();
-
- if (mIgnore == null) {
- mIgnore = new HashSet<Element>();
- }
-
- for (Element view : views) {
- if (view.getTagName().equals(BUTTON)) {
- String text = view.getAttributeNS(ANDROID_URI, ATTR_TEXT);
- String label = getLabel(text);
- labels.add(label);
-
- // Mark all the siblings in the ignore list to ensure that we don't
- // report *both* the Cancel and the OK button in "OK | Cancel"
- mIgnore.add(view);
- }
- }
-
- return labels;
- }
-
- private String getLabel(String key) {
- String label = null;
- if (key.startsWith(ANDROID_STRING_PREFIX)) {
- if (key.equals(ANDROID_OK_RESOURCE)) {
- label = OK_LABEL;
- } else if (key.equals(ANDROID_CANCEL_RESOURCE)) {
- label = CANCEL_LABEL;
- }
- } else if (mKeyToLabel != null) {
- if (key.startsWith(STRING_PREFIX)) {
- label = mKeyToLabel.get(key.substring(STRING_PREFIX.length()));
- }
- }
-
- if (label == null) {
- label = key;
- }
-
- if (label.indexOf(' ') != -1 && label.indexOf('"') == -1) {
- label = '"' + label + '"';
- }
-
- return label;
- }
-
- /** Is the cancel button in the wrong position? It has to be on the left. */
- private boolean isWrongCancelPosition(Element element) {
- return isWrongPosition(element, true /*isCancel*/);
- }
-
- /** Is the OK button in the wrong position? It has to be on the right. */
- private boolean isWrongOkPosition(Element element) {
- return isWrongPosition(element, false /*isCancel*/);
- }
-
- /** Is the given button in the wrong position? */
- private boolean isWrongPosition(Element element, boolean isCancel) {
- Node parentNode = element.getParentNode();
- if (parentNode.getNodeType() != Node.ELEMENT_NODE) {
- return false;
- }
- Element parent = (Element) parentNode;
-
- // Don't warn about single Cancel / OK buttons
- if (LintUtils.getChildCount(parent) < 2) {
- return false;
- }
-
- String layout = parent.getTagName();
- if (layout.equals(LINEAR_LAYOUT) || layout.equals(TABLE_ROW)) {
- String orientation = parent.getAttributeNS(ANDROID_URI, ATTR_ORIENTATION);
- if (VALUE_VERTICAL.equals(orientation)) {
- return false;
- }
-
- if (isCancel) {
- Node n = element.getPreviousSibling();
- while (n != null) {
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- return true;
- }
- n = n.getPreviousSibling();
- }
- } else {
- Node n = element.getNextSibling();
- while (n != null) {
- if (n.getNodeType() == Node.ELEMENT_NODE) {
- return true;
- }
- n = n.getNextSibling();
- }
- }
-
- return false;
- } else if (layout.equals(RELATIVE_LAYOUT)) {
- // In RelativeLayouts, look for attachments which look like a clear sign
- // that the OK or Cancel buttons are out of order:
- // -- a left attachment on a Cancel button (where the left attachment
- // is a button; we don't want to complain if it's pointing to a spacer
- // or image or progress indicator etc)
- // -- a right-side parent attachment on a Cancel button (unless it's also
- // attached on the left, e.g. a cancel button stretching across the
- // layout)
- // etc.
- if (isCancel) {
- if (element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_TO_RIGHT_OF)
- && isButtonId(parent, element.getAttributeNS(ANDROID_URI,
- ATTR_LAYOUT_TO_RIGHT_OF))) {
- return true;
- }
- if (isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_RIGHT) &&
- !isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_LEFT)) {
- return true;
- }
- } else {
- if (element.hasAttributeNS(ANDROID_URI, ATTR_LAYOUT_TO_LEFT_OF)
- && isButtonId(parent, element.getAttributeNS(ANDROID_URI,
- ATTR_LAYOUT_TO_RIGHT_OF))) {
- return true;
- }
- if (isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_LEFT) &&
- !isTrue(element, ATTR_LAYOUT_ALIGN_PARENT_RIGHT)) {
- return true;
- }
- }
-
- return false;
- } else {
- // TODO: Consider other button layouts - GridLayouts, custom views extending
- // LinearLayout etc?
- return false;
- }
- }
-
- /**
- * Returns true if the given attribute (in the Android namespace) is set to
- * true on the given element
- */
- private static boolean isTrue(Element element, String attribute) {
- return VALUE_TRUE.equals(element.getAttributeNS(ANDROID_URI, attribute));
- }
-
- /** Is the given target id the id of a {@code <Button>} within this RelativeLayout? */
- private boolean isButtonId(Element parent, String targetId) {
- for (Element child : LintUtils.getChildren(parent)) {
- String id = child.getAttributeNS(ANDROID_URI, ATTR_ID);
- if (LintUtils.idReferencesMatch(id, targetId)) {
- return child.getTagName().equals(BUTTON);
- }
- }
- return false;
- }
-}