diff --git a/idea/src/META-INF/android-lint.xml b/idea/src/META-INF/android-lint.xml index 867b8f794d2..4318ec2d1d6 100644 --- a/idea/src/META-INF/android-lint.xml +++ b/idea/src/META-INF/android-lint.xml @@ -2,61 +2,26 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -70,132 +35,57 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java deleted file mode 100644 index 2fc267c59b5..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AccessibilityDetector.java +++ /dev/null @@ -1,132 +0,0 @@ -/* - * Copyright (C) 2011 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.klint.checks; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_CONTENT_DESCRIPTION; -import static com.android.SdkConstants.ATTR_HINT; -import static com.android.SdkConstants.ATTR_IMPORTANT_FOR_ACCESSIBILITY; -import static com.android.SdkConstants.IMAGE_BUTTON; -import static com.android.SdkConstants.IMAGE_VIEW; -import static com.android.SdkConstants.VALUE_NO; - -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.LayoutDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; - -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; - -/** - * Check which looks for accessibility problems like missing content descriptions - *

- * TODO: Resolve styles and don't warn where styles are defining the content description - * (though this seems unusual; content descriptions are not typically generic enough to - * put in styles) - */ -public class AccessibilityDetector extends LayoutDetector { - /** The main issue discovered by this detector */ - public static final Issue ISSUE = Issue.create( - "ContentDescription", //$NON-NLS-1$ - "Image without `contentDescription`", - "Non-textual widgets like ImageViews and ImageButtons should use the " + - "`contentDescription` attribute to specify a textual description of " + - "the widget such that screen readers and other accessibility tools " + - "can adequately describe the user interface.\n" + - "\n" + - "Note that elements in application screens that are purely decorative " + - "and do not provide any content or enable a user action should not " + - "have accessibility content descriptions. In this case, just suppress the " + - "lint warning with a tools:ignore=\"ContentDescription\" attribute.\n" + - "\n" + - "Note that for text fields, you should not set both the `hint` and the " + - "`contentDescription` attributes since the hint will never be shown. Just " + - "set the `hint`. See " + - "http://developer.android.com/guide/topics/ui/accessibility/checklist.html#special-cases.", - - Category.A11Y, - 3, - Severity.WARNING, - new Implementation( - AccessibilityDetector.class, - Scope.RESOURCE_FILE_SCOPE)); - - /** Constructs a new {@link AccessibilityDetector} */ - public AccessibilityDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection getApplicableElements() { - return Arrays.asList( - IMAGE_BUTTON, - IMAGE_VIEW - ); - } - - @Override - @Nullable - public Collection getApplicableAttributes() { - return Collections.singletonList(ATTR_CONTENT_DESCRIPTION); - } - - @Override - public void visitAttribute(@NonNull XmlContext context, @NonNull Attr attribute) { - Element element = attribute.getOwnerElement(); - if (element.hasAttributeNS(ANDROID_URI, ATTR_HINT)) { - context.report(ISSUE, element, context.getLocation(attribute), - "Do not set both `contentDescription` and `hint`: the `contentDescription` " + - "will mask the `hint`"); - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - if (!element.hasAttributeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION)) { - // Ignore views that are explicitly not important for accessibility - if (VALUE_NO.equals(element.getAttributeNS(ANDROID_URI, - ATTR_IMPORTANT_FOR_ACCESSIBILITY))) { - return; - } - context.report(ISSUE, element, context.getLocation(element), - "[Accessibility] Missing `contentDescription` attribute on image"); - } else { - Attr attributeNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_CONTENT_DESCRIPTION); - String attribute = attributeNode.getValue(); - if (attribute.isEmpty() || attribute.equals("TODO")) { //$NON-NLS-1$ - context.report(ISSUE, attributeNode, context.getLocation(attributeNode), - "[Accessibility] Empty `contentDescription` attribute on image"); - } - } - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java deleted file mode 100644 index 5ccd6c98963..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/AppIndexingApiDetector.java +++ /dev/null @@ -1,287 +0,0 @@ -/* - * Copyright (C) 2015 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.klint.checks; - -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_HOST; -import static com.android.SdkConstants.ATTR_PATH; -import static com.android.SdkConstants.ATTR_PATH_PATTERN; -import static com.android.SdkConstants.ATTR_PATH_PREFIX; -import static com.android.SdkConstants.ATTR_SCHEME; - -import static com.android.xml.AndroidManifest.ATTRIBUTE_MIME_TYPE; -import static com.android.xml.AndroidManifest.ATTRIBUTE_NAME; -import static com.android.xml.AndroidManifest.ATTRIBUTE_PORT; -import static com.android.xml.AndroidManifest.NODE_ACTION; -import static com.android.xml.AndroidManifest.NODE_CATEGORY; -import static com.android.xml.AndroidManifest.NODE_DATA; -import static com.android.xml.AndroidManifest.NODE_INTENT; - -import com.android.SdkConstants; -import com.android.annotations.NonNull; -import com.android.annotations.Nullable; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.res2.AbstractResourceRepository; -import com.android.ide.common.res2.ResourceItem; -import com.android.ide.common.resources.ResourceUrl; -import com.android.resources.ResourceType; -import com.android.tools.klint.client.api.LintClient; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Detector; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.Project; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.NamedNodeMap; -import org.w3c.dom.Node; -import org.w3c.dom.NodeList; - -import java.util.Collection; -import java.util.Collections; -import java.util.List; - - -/** - * Check if the usage of App Indexing is correct. - */ -public class AppIndexingApiDetector extends Detector implements Detector.XmlScanner { - - private static final Implementation IMPLEMENTATION = new Implementation( - AppIndexingApiDetector.class, Scope.MANIFEST_SCOPE); - - public static final Issue ISSUE_ERROR = Issue.create("AppIndexingError", //$NON-NLS-1$ - "Wrong Usage of App Indexing", - "Ensures the app can correctly handle deep links and integrate with " + - "App Indexing for Google search.", - Category.USABILITY, 5, Severity.ERROR, IMPLEMENTATION) - .addMoreInfo("https://g.co/AppIndexing"); - - public static final Issue ISSUE_WARNING = Issue.create("AppIndexingWarning", //$NON-NLS-1$ - "Missing App Indexing Support", - "Ensures the app can correctly handle deep links and integrate with " + - "App Indexing for Google search.", - Category.USABILITY, 5, Severity.WARNING, IMPLEMENTATION) - .addMoreInfo("https://g.co/AppIndexing"); - - private static final String[] PATH_ATTR_LIST = new String[]{ATTR_PATH_PREFIX, ATTR_PATH, - ATTR_PATH_PATTERN}; - - @Override - @Nullable - public Collection getApplicableElements() { - return Collections.singletonList(NODE_INTENT); - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element intent) { - boolean actionView = hasActionView(intent); - boolean browsable = isBrowsable(intent); - boolean isHttp = false; - boolean hasScheme = false; - boolean hasHost = false; - boolean hasPort = false; - boolean hasPath = false; - boolean hasMimeType = false; - Element firstData = null; - NodeList children = intent.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE && child.getNodeName().equals(NODE_DATA)) { - Element data = (Element) child; - if (firstData == null) { - firstData = data; - } - if (isHttpSchema(data)) { - isHttp = true; - } - checkSingleData(context, data); - - for (String name : PATH_ATTR_LIST) { - if (data.hasAttributeNS(ANDROID_URI, name)) { - hasPath = true; - } - } - - if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) { - hasScheme = true; - } - - if (data.hasAttributeNS(ANDROID_URI, ATTR_HOST)) { - hasHost = true; - } - - if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) { - hasPort = true; - } - - if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_MIME_TYPE)) { - hasMimeType = true; - } - } - } - - // In data field, a URL is consisted by - // ://:[||] - // Each part of the URL should not have illegal character. - if ((hasPath || hasHost || hasPort) && !hasScheme) { - context.report(ISSUE_ERROR, firstData, context.getLocation(firstData), - "android:scheme missing"); - } - - if ((hasPath || hasPort) && !hasHost) { - context.report(ISSUE_ERROR, firstData, context.getLocation(firstData), - "android:host missing"); - } - - if (actionView && browsable) { - if (firstData == null) { - // If this activity is an ACTION_VIEW action with category BROWSABLE, but doesn't - // have data node, it may be a mistake and we will report error. - context.report(ISSUE_ERROR, intent, context.getLocation(intent), - "Missing data node?"); - } else if (!hasScheme && !hasMimeType) { - // If this activity is an action view, is browsable, but has neither a - // URL nor mimeType, it may be a mistake and we will report error. - context.report(ISSUE_ERROR, firstData, context.getLocation(firstData), - "Missing URL for the intent filter?"); - } - } - - // If this activity is an ACTION_VIEW action, has a http URL but doesn't have - // BROWSABLE, it may be a mistake and and we will report warning. - if (actionView && isHttp && !browsable) { - context.report(ISSUE_WARNING, intent, context.getLocation(intent), - "Activity supporting ACTION_VIEW is not set as BROWSABLE"); - } - } - - private static boolean hasActionView(Element intent) { - NodeList children = intent.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE && - child.getNodeName().equals(NODE_ACTION)) { - Element action = (Element) child; - if (action.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) { - Attr attr = action.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME); - if (attr.getValue().equals("android.intent.action.VIEW")) { - return true; - } - } - } - } - return false; - } - - private static boolean isBrowsable(Element intent) { - NodeList children = intent.getChildNodes(); - for (int i = 0; i < children.getLength(); i++) { - Node child = children.item(i); - if (child.getNodeType() == Node.ELEMENT_NODE && - child.getNodeName().equals(NODE_CATEGORY)) { - Element e = (Element) child; - if (e.hasAttributeNS(ANDROID_URI, ATTRIBUTE_NAME)) { - Attr attr = e.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_NAME); - if (attr.getNodeValue().equals("android.intent.category.BROWSABLE")) { - return true; - } - } - } - } - return false; - } - - private static boolean isHttpSchema(Element data) { - if (data.hasAttributeNS(ANDROID_URI, ATTR_SCHEME)) { - String value = data.getAttributeNodeNS(ANDROID_URI, ATTR_SCHEME).getValue(); - if (value.equalsIgnoreCase("http") || value.equalsIgnoreCase("https")) { - return true; - } - } - return false; - } - - private static void checkSingleData(XmlContext context, Element data) { - // path, pathPrefix and pathPattern should starts with /. - for (String name : PATH_ATTR_LIST) { - if (data.hasAttributeNS(ANDROID_URI, name)) { - Attr attr = data.getAttributeNodeNS(ANDROID_URI, name); - String path = replaceUrlWithValue(context, attr.getValue()); - if (!path.startsWith("/") && !path.startsWith(SdkConstants.PREFIX_RESOURCE_REF)) { - context.report(ISSUE_ERROR, attr, context.getLocation(attr), - "android:" + name + " attribute should start with '/', but it is : " - + path); - } - } - } - - // port should be a legal number. - if (data.hasAttributeNS(ANDROID_URI, ATTRIBUTE_PORT)) { - Attr attr = data.getAttributeNodeNS(ANDROID_URI, ATTRIBUTE_PORT); - try { - String port = replaceUrlWithValue(context, attr.getValue()); - Integer.parseInt(port); - } catch (NumberFormatException e) { - context.report(ISSUE_ERROR, attr, context.getLocation(attr), - "android:port is not a legal number"); - } - } - - // Each field should be non empty. - NamedNodeMap attrs = data.getAttributes(); - for (int i = 0; i < attrs.getLength(); i++) { - Node item = attrs.item(i); - if (item.getNodeType() == Node.ATTRIBUTE_NODE) { - Attr attr = (Attr) attrs.item(i); - if (attr.getValue().isEmpty()) { - context.report(ISSUE_ERROR, attr, context.getLocation(attr), - attr.getName() + " cannot be empty"); - } - } - } - } - - private static String replaceUrlWithValue(@NonNull XmlContext context, - @NonNull String str) { - Project project = context.getProject(); - LintClient client = context.getClient(); - if (!client.supportsProjectResources()) { - return str; - } - ResourceUrl style = ResourceUrl.parse(str); - if (style == null || style.type != ResourceType.STRING || style.framework) { - return str; - } - AbstractResourceRepository resources = client.getProjectResources(project, true); - if (resources == null) { - return str; - } - List items = resources.getResourceItem(ResourceType.STRING, style.name); - if (items == null || items.isEmpty()) { - return str; - } - ResourceValue resourceValue = items.get(0).getResourceValue(false); - if (resourceValue == null) { - return str; - } - return resourceValue.getValue() == null ? str : resourceValue.getValue(); - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java deleted file mode 100644 index 83ccf7ae3a5..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ArraySizeDetector.java +++ /dev/null @@ -1,324 +0,0 @@ -/* - * Copyright (C) 2011 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.klint.checks; - - -import static com.android.SdkConstants.ATTR_NAME; -import static com.android.SdkConstants.TAG_ARRAY; -import static com.android.SdkConstants.TAG_INTEGER_ARRAY; -import static com.android.SdkConstants.TAG_STRING_ARRAY; - -import com.android.annotations.NonNull; -import com.android.ide.common.rendering.api.ArrayResourceValue; -import com.android.ide.common.rendering.api.ResourceValue; -import com.android.ide.common.res2.AbstractResourceRepository; -import com.android.ide.common.res2.ResourceFile; -import com.android.ide.common.res2.ResourceItem; -import com.android.resources.ResourceFolderType; -import com.android.resources.ResourceType; -import com.android.tools.klint.client.api.LintClient; -import com.android.tools.klint.client.api.LintDriver; -import com.android.tools.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Context; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.LintUtils; -import com.android.tools.klint.detector.api.Location; -import com.android.tools.klint.detector.api.Project; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.XmlContext; -import com.android.utils.Pair; -import com.google.common.collect.ArrayListMultimap; -import com.google.common.collect.Multimap; - -import org.w3c.dom.Attr; -import org.w3c.dom.Element; -import org.w3c.dom.Node; - -import java.io.File; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collection; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; - -/** - * Checks for arrays with inconsistent item counts - */ -public class ArraySizeDetector extends ResourceXmlDetector { - - /** Are there differences in how many array elements are declared? */ - public static final Issue INCONSISTENT = Issue.create( - "InconsistentArrays", //$NON-NLS-1$ - "Inconsistencies in array element counts", - "When an array is translated in a different locale, it should normally have " + - "the same number of elements as the original array. When adding or removing " + - "elements to an array, it is easy to forget to update all the locales, and this " + - "lint warning finds inconsistencies like these.\n" + - "\n" + - "Note however that there may be cases where you really want to declare a " + - "different number of array items in each configuration (for example where " + - "the array represents available options, and those options differ for " + - "different layout orientations and so on), so use your own judgement to " + - "decide if this is really an error.\n" + - "\n" + - "You can suppress this error type if it finds false errors in your project.", - Category.CORRECTNESS, - 7, - Severity.WARNING, - new Implementation( - ArraySizeDetector.class, - Scope.RESOURCE_FILE_SCOPE)); - - private Multimap> mFileToArrayCount; - - /** Locations for each array name. Populated during phase 2, if necessary */ - private Map mLocations; - - /** Error messages for each array name. Populated during phase 2, if necessary */ - private Map mDescriptions; - - /** Constructs a new {@link ArraySizeDetector} */ - public ArraySizeDetector() { - } - - @Override - public boolean appliesTo(@NonNull ResourceFolderType folderType) { - return folderType == ResourceFolderType.VALUES; - } - - @Override - public Collection getApplicableElements() { - return Arrays.asList( - TAG_ARRAY, - TAG_STRING_ARRAY, - TAG_INTEGER_ARRAY - ); - } - - @Override - public void beforeCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - mFileToArrayCount = ArrayListMultimap.create(30, 5); - } - } - - @Override - public void afterCheckProject(@NonNull Context context) { - if (context.getPhase() == 1) { - boolean haveAllResources = context.getScope().contains(Scope.ALL_RESOURCE_FILES); - if (!haveAllResources) { - return; - } - - // Check that all arrays for the same name have the same number of translations - - Set alreadyReported = new HashSet(); - Map countMap = new HashMap(); - Map fileMap = new HashMap(); - - // Process the file in sorted file order to ensure stable output - List keys = new ArrayList(mFileToArrayCount.keySet()); - Collections.sort(keys); - - for (File file : keys) { - Collection> pairs = mFileToArrayCount.get(file); - for (Pair pair : pairs) { - String name = pair.getFirst(); - - if (alreadyReported.contains(name)) { - continue; - } - Integer count = pair.getSecond(); - - Integer current = countMap.get(name); - if (current == null) { - countMap.put(name, count); - fileMap.put(name, file); - } else if (!count.equals(current)) { - alreadyReported.add(name); - - if (mLocations == null) { - mLocations = new HashMap(); - mDescriptions = new HashMap(); - } - mLocations.put(name, null); - - String thisName = file.getParentFile().getName() + File.separator - + file.getName(); - File otherFile = fileMap.get(name); - String otherName = otherFile.getParentFile().getName() + File.separator - + otherFile.getName(); - String message = String.format( - "Array `%1$s` has an inconsistent number of items (%2$d in `%3$s`, %4$d in `%5$s`)", - name, count, thisName, current, otherName); - mDescriptions.put(name, message); - } - } - } - - //noinspection VariableNotUsedInsideIf - if (mLocations != null) { - // Request another scan through the resources such that we can - // gather the actual locations - context.getDriver().requestRepeat(this, Scope.ALL_RESOURCES_SCOPE); - } - mFileToArrayCount = null; - } else { - if (mLocations != null) { - List names = new ArrayList(mLocations.keySet()); - Collections.sort(names); - for (String name : names) { - Location location = mLocations.get(name); - if (location == null) { - // Suppressed; see visitElement - continue; - } - // We were prepending locations, but we want to prefer the base folders - location = Location.reverse(location); - - // Make sure we still have a conflict, in case one or more of the - // elements were marked with tools:ignore - int count = -1; - LintDriver driver = context.getDriver(); - boolean foundConflict = false; - Location curr; - for (curr = location; curr != null; curr = curr.getSecondary()) { - Object clientData = curr.getClientData(); - if (clientData instanceof Node) { - Node node = (Node) clientData; - if (driver.isSuppressed(null, INCONSISTENT, node)) { - continue; - } - int newCount = LintUtils.getChildCount(node); - if (newCount != count) { - if (count == -1) { - count = newCount; // first number encountered - } else { - foundConflict = true; - break; - } - } - } else { - foundConflict = true; - break; - } - } - - // Through one or more tools:ignore, there is no more conflict so - // ignore this element - if (!foundConflict) { - continue; - } - - String message = mDescriptions.get(name); - context.report(INCONSISTENT, location, message); - } - } - - mLocations = null; - mDescriptions = null; - } - } - - @Override - public void visitElement(@NonNull XmlContext context, @NonNull Element element) { - int phase = context.getPhase(); - - Attr attribute = element.getAttributeNode(ATTR_NAME); - if (attribute == null || attribute.getValue().isEmpty()) { - if (phase != 1) { - return; - } - context.report(INCONSISTENT, element, context.getLocation(element), - String.format("Missing name attribute in `%1$s` declaration", - element.getTagName())); - } else { - String name = attribute.getValue(); - if (phase == 1) { - if (context.getProject().getReportIssues()) { - int childCount = LintUtils.getChildCount(element); - - if (!context.getScope().contains(Scope.ALL_RESOURCE_FILES) && - context.getClient().supportsProjectResources()) { - incrementalCheckCount(context, element, name, childCount); - return; - } - - mFileToArrayCount.put(context.file, Pair.of(name, childCount)); - } - } else { - assert phase == 2; - if (mLocations.containsKey(name)) { - if (context.getDriver().isSuppressed(context, INCONSISTENT, element)) { - return; - } - Location location = context.getLocation(element); - location.setClientData(element); - location.setMessage(String.format("Declaration with array size (%1$d)", - LintUtils.getChildCount(element))); - location.setSecondary(mLocations.get(name)); - mLocations.put(name, location); - } - } - } - } - - private static void incrementalCheckCount(@NonNull XmlContext context, @NonNull Element element, - @NonNull String name, int childCount) { - LintClient client = context.getClient(); - Project project = context.getMainProject(); - AbstractResourceRepository resources = client.getProjectResources(project, true); - if (resources == null) { - return; - } - List items = resources.getResourceItem(ResourceType.ARRAY, name); - if (items != null) { - for (ResourceItem item : items) { - ResourceFile source = item.getSource(); - if (source != null && LintUtils.isSameResourceFile(context.file, - source.getFile())) { - continue; - } - ResourceValue rv = item.getResourceValue(false); - if (rv instanceof ArrayResourceValue) { - ArrayResourceValue arv = (ArrayResourceValue) rv; - if (childCount != arv.getElementCount()) { - String thisName = context.file.getParentFile().getName() + File.separator - + context.file.getName(); - assert source != null; - File otherFile = source.getFile(); - String otherName = otherFile.getParentFile().getName() + File.separator - + otherFile.getName(); - String message = String.format( - "Array `%1$s` has an inconsistent number of items (%2$d in `%3$s`, %4$d in `%5$s`)", - name, childCount, thisName, arv.getElementCount(), otherName); - - context.report(INCONSISTENT, element, context.getLocation(element), - message); - } - } - } - } - } -} diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java index c9a23040926..3d6e16e9cc0 100644 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java +++ b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/BuiltinIssueRegistry.java @@ -35,7 +35,6 @@ public class BuiltinIssueRegistry extends IssueRegistry { static { List issues = new ArrayList(INITIAL_CAPACITY); - issues.add(AccessibilityDetector.ISSUE); issues.add(AddJavascriptInterfaceDetector.ISSUE); issues.add(AlarmDetector.ISSUE); issues.add(AlwaysShowActionDetector.ISSUE); @@ -46,54 +45,20 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ApiDetector.UNUSED); issues.add(AppCompatCallDetector.ISSUE); issues.add(AppCompatResourceDetector.ISSUE); - issues.add(AppIndexingApiDetector.ISSUE_ERROR); - issues.add(AppIndexingApiDetector.ISSUE_WARNING); - issues.add(ArraySizeDetector.INCONSISTENT); issues.add(AssertDetector.ISSUE); - issues.add(ButtonDetector.BACK_BUTTON); - issues.add(ButtonDetector.CASE); - issues.add(ButtonDetector.ORDER); - issues.add(ButtonDetector.STYLE); - issues.add(ByteOrderMarkDetector.BOM); issues.add(CallSuperDetector.ISSUE); - issues.add(ChildCountDetector.ADAPTER_VIEW_ISSUE); - issues.add(ChildCountDetector.SCROLLVIEW_ISSUE); issues.add(CipherGetInstanceDetector.ISSUE); issues.add(CleanupDetector.COMMIT_FRAGMENT); issues.add(CleanupDetector.RECYCLE_RESOURCE); - issues.add(ClickableViewAccessibilityDetector.ISSUE); issues.add(CommentDetector.EASTER_EGG); issues.add(CommentDetector.STOP_SHIP); issues.add(CustomViewDetector.ISSUE); issues.add(CutPasteDetector.ISSUE); issues.add(DateFormatDetector.DATE_FORMAT); - issues.add(DeprecationDetector.ISSUE); - issues.add(DetectMissingPrefix.MISSING_NAMESPACE); - issues.add(DosLineEndingDetector.ISSUE); - issues.add(DuplicateIdDetector.CROSS_LAYOUT); - issues.add(DuplicateIdDetector.WITHIN_LAYOUT); - issues.add(DuplicateResourceDetector.ISSUE); - issues.add(DuplicateResourceDetector.TYPE_MISMATCH); - issues.add(ExtraTextDetector.ISSUE); - issues.add(FieldGetterDetector.ISSUE); issues.add(FullBackupContentDetector.ISSUE); issues.add(FragmentDetector.ISSUE); issues.add(GetSignaturesDetector.ISSUE); - issues.add(GradleDetector.COMPATIBILITY); - issues.add(GradleDetector.GRADLE_PLUGIN_COMPATIBILITY); - issues.add(GradleDetector.DEPENDENCY); - issues.add(GradleDetector.DEPRECATED); - issues.add(GradleDetector.GRADLE_GETTER); - issues.add(GradleDetector.IDE_SUPPORT); - issues.add(GradleDetector.PATH); - issues.add(GradleDetector.PLUS); - issues.add(GradleDetector.STRING_INTEGER); - issues.add(GradleDetector.REMOTE_VERSION); - issues.add(GradleDetector.ACCIDENTAL_OCTAL); - issues.add(GridLayoutDetector.ISSUE); issues.add(HandlerDetector.ISSUE); - issues.add(HardcodedDebugModeDetector.ISSUE); - issues.add(HardcodedValuesDetector.ISSUE); issues.add(IconDetector.DUPLICATES_CONFIGURATIONS); issues.add(IconDetector.DUPLICATES_NAMES); issues.add(IconDetector.GIF_USAGE); @@ -108,93 +73,29 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(IconDetector.ICON_MIX_9PNG); issues.add(IconDetector.ICON_NODPI); issues.add(IconDetector.ICON_XML_AND_PNG); - issues.add(IncludeDetector.ISSUE); - issues.add(InefficientWeightDetector.BASELINE_WEIGHTS); - issues.add(InefficientWeightDetector.INEFFICIENT_WEIGHT); - issues.add(InefficientWeightDetector.NESTED_WEIGHTS); - issues.add(InefficientWeightDetector.ORIENTATION); - issues.add(InefficientWeightDetector.WRONG_0DP); - issues.add(InvalidPackageDetector.ISSUE); issues.add(JavaPerformanceDetector.PAINT_ALLOC); issues.add(JavaPerformanceDetector.USE_SPARSE_ARRAY); issues.add(JavaPerformanceDetector.USE_VALUE_OF); issues.add(JavaScriptInterfaceDetector.ISSUE); - issues.add(LabelForDetector.ISSUE); issues.add(LayoutConsistencyDetector.INCONSISTENT_IDS); issues.add(LayoutInflationDetector.ISSUE); - issues.add(LocaleDetector.STRING_LOCALE); - issues.add(LocaleFolderDetector.DEPRECATED_CODE); - issues.add(LocaleFolderDetector.INVALID_FOLDER); - issues.add(LocaleFolderDetector.WRONG_REGION); - issues.add(LocaleFolderDetector.USE_ALPHA_2); issues.add(LogDetector.CONDITIONAL); issues.add(LogDetector.LONG_TAG); issues.add(LogDetector.WRONG_TAG); - issues.add(ManifestDetector.ALLOW_BACKUP); - issues.add(ManifestDetector.APPLICATION_ICON); - issues.add(ManifestDetector.DEVICE_ADMIN); - issues.add(ManifestDetector.DUPLICATE_ACTIVITY); - issues.add(ManifestDetector.DUPLICATE_USES_FEATURE); - issues.add(ManifestDetector.GRADLE_OVERRIDES); - issues.add(ManifestDetector.ILLEGAL_REFERENCE); - issues.add(ManifestDetector.MIPMAP); - issues.add(ManifestDetector.MOCK_LOCATION); - issues.add(ManifestDetector.MULTIPLE_USES_SDK); - issues.add(ManifestDetector.ORDER); - issues.add(ManifestDetector.SET_VERSION); - issues.add(ManifestDetector.TARGET_NEWER); - issues.add(ManifestDetector.UNIQUE_PERMISSION); - issues.add(ManifestDetector.USES_SDK); - issues.add(ManifestDetector.WRONG_PARENT); - issues.add(ManifestTypoDetector.ISSUE); - issues.add(MathDetector.ISSUE); issues.add(MergeRootFrameLayoutDetector.ISSUE); - issues.add(MissingClassDetector.INNERCLASS); - issues.add(MissingClassDetector.INSTANTIATABLE); - issues.add(MissingClassDetector.MISSING); - issues.add(MissingIdDetector.ISSUE); - issues.add(NamespaceDetector.CUSTOM_VIEW); - issues.add(NamespaceDetector.RES_AUTO); - issues.add(NamespaceDetector.TYPO); - issues.add(NamespaceDetector.UNUSED); - issues.add(NegativeMarginDetector.ISSUE); - issues.add(NestedScrollingWidgetDetector.ISSUE); issues.add(NfcTechListDetector.ISSUE); issues.add(NonInternationalizedSmsDetector.ISSUE); - issues.add(ObsoleteLayoutParamsDetector.ISSUE); - issues.add(OnClickDetector.ISSUE); issues.add(OverdrawDetector.ISSUE); - issues.add(OverrideDetector.ISSUE); issues.add(OverrideConcreteDetector.ISSUE); issues.add(ParcelDetector.ISSUE); - issues.add(PluralsDetector.EXTRA); - issues.add(PluralsDetector.MISSING); - issues.add(PluralsDetector.IMPLIED_QUANTITY); issues.add(PreferenceActivityDetector.ISSUE); - issues.add(PrivateKeyDetector.ISSUE); issues.add(PrivateResourceDetector.ISSUE); - issues.add(ProguardDetector.SPLIT_CONFIG); - issues.add(ProguardDetector.WRONG_KEEP); - issues.add(PropertyFileDetector.ESCAPE); - issues.add(PropertyFileDetector.HTTP); - issues.add(PxUsageDetector.DP_ISSUE); - issues.add(PxUsageDetector.IN_MM_ISSUE); - issues.add(PxUsageDetector.PX_ISSUE); - issues.add(PxUsageDetector.SMALL_SP_ISSUE); - issues.add(RegistrationDetector.ISSUE); - issues.add(RelativeOverlapDetector.ISSUE); issues.add(RequiredAttributeDetector.ISSUE); - issues.add(ResourceCycleDetector.CRASH); - issues.add(ResourceCycleDetector.CYCLE); - issues.add(ResourcePrefixDetector.ISSUE); issues.add(RtlDetector.COMPAT); issues.add(RtlDetector.ENABLED); issues.add(RtlDetector.SYMMETRY); issues.add(RtlDetector.USE_START); - issues.add(ScrollViewChildDetector.ISSUE); issues.add(SdCardDetector.ISSUE); - issues.add(SecureRandomDetector.ISSUE); - issues.add(SecureRandomGeneratorDetector.ISSUE); issues.add(SecurityDetector.EXPORTED_PROVIDER); issues.add(SecurityDetector.EXPORTED_RECEIVER); issues.add(SecurityDetector.EXPORTED_SERVICE); @@ -204,9 +105,7 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(ServiceCastDetector.ISSUE); issues.add(SetJavaScriptEnabledDetector.ISSUE); issues.add(SharedPrefsDetector.ISSUE); - issues.add(SignatureOrSystemDetector.ISSUE); issues.add(SQLiteDetector.ISSUE); - issues.add(StateListDetector.ISSUE); issues.add(StringFormatDetector.ARG_COUNT); issues.add(StringFormatDetector.ARG_TYPES); issues.add(StringFormatDetector.INVALID); @@ -219,42 +118,15 @@ public class BuiltinIssueRegistry extends IssueRegistry { issues.add(SupportAnnotationDetector.RESOURCE_TYPE); issues.add(SupportAnnotationDetector.THREAD); issues.add(SupportAnnotationDetector.TYPE_DEF); - issues.add(SystemPermissionsDetector.ISSUE); - issues.add(TextFieldDetector.ISSUE); - issues.add(TextViewDetector.ISSUE); - issues.add(TextViewDetector.SELECTABLE); issues.add(TitleDetector.ISSUE); issues.add(ToastDetector.ISSUE); - issues.add(TooManyViewsDetector.TOO_DEEP); - issues.add(TooManyViewsDetector.TOO_MANY); - issues.add(TranslationDetector.EXTRA); - issues.add(TranslationDetector.MISSING); - issues.add(TypoDetector.ISSUE); - issues.add(TypographyDetector.DASHES); - issues.add(TypographyDetector.ELLIPSIS); - issues.add(TypographyDetector.FRACTIONS); - issues.add(TypographyDetector.OTHER); - issues.add(TypographyDetector.QUOTES); issues.add(UnusedResourceDetector.ISSUE); issues.add(UnusedResourceDetector.ISSUE_IDS); - issues.add(UseCompoundDrawableDetector.ISSUE); - issues.add(UselessViewDetector.USELESS_LEAF); - issues.add(UselessViewDetector.USELESS_PARENT); - issues.add(Utf8Detector.ISSUE); issues.add(ViewConstructorDetector.ISSUE); issues.add(ViewHolderDetector.ISSUE); - issues.add(ViewTagDetector.ISSUE); issues.add(ViewTypeDetector.ISSUE); - issues.add(WakelockDetector.ISSUE); - issues.add(WebViewDetector.ISSUE); issues.add(WrongCallDetector.ISSUE); - issues.add(WrongCaseDetector.WRONG_CASE); - issues.add(WrongIdDetector.INVALID); - issues.add(WrongIdDetector.NOT_SIBLING); - issues.add(WrongIdDetector.UNKNOWN_ID); - issues.add(WrongIdDetector.UNKNOWN_ID_LAYOUT); issues.add(WrongImportDetector.ISSUE); - issues.add(WrongLocationDetector.ISSUE); sIssues = Collections.unmodifiableList(issues); } diff --git a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java b/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java deleted file mode 100644 index 43774708c1d..00000000000 --- a/plugins/lint/lint-checks/src/com/android/tools/klint/checks/ButtonDetector.java +++ /dev/null @@ -1,784 +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.klint.checks; - -import static com.android.SdkConstants.ANDROID_STRING_PREFIX; -import static com.android.SdkConstants.ANDROID_URI; -import static com.android.SdkConstants.ATTR_BACKGROUND; -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_STYLE; -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_SELECTABLE_ITEM_BACKGROUND; -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.klint.detector.api.Category; -import com.android.tools.klint.detector.api.Context; -import com.android.tools.klint.detector.api.Implementation; -import com.android.tools.klint.detector.api.Issue; -import com.android.tools.klint.detector.api.LintUtils; -import com.android.tools.klint.detector.api.Location; -import com.android.tools.klint.detector.api.ResourceXmlDetector; -import com.android.tools.klint.detector.api.Scope; -import com.android.tools.klint.detector.api.Severity; -import com.android.tools.klint.detector.api.Speed; -import com.android.tools.klint.detector.api.XmlContext; - -import org.w3c.dom.Attr; -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." - *

- * 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. - *

- * 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"; - /** Yes */ - private static final String YES_LABEL = "Yes"; - /** No */ - private static final String NO_LABEL = "No"; - - /** 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$ - /** Layout text attribute reference to {@code @android:string/yes} */ - private static final String ANDROID_YES_RESOURCE = - ANDROID_STRING_PREFIX + "yes"; //$NON-NLS-1$ - /** Layout text attribute reference to {@code @android:string/no} */ - private static final String ANDROID_NO_RESOURCE = - ANDROID_STRING_PREFIX + "no"; //$NON-NLS-1$ - - private static final Implementation IMPLEMENTATION = new Implementation( - ButtonDetector.class, - Scope.RESOURCE_FILE_SCOPE); - - /** The main issue discovered by this detector */ - public static final Issue ORDER = Issue.create( - "ButtonOrder", //$NON-NLS-1$ - "Button order", - - "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, - IMPLEMENTATION) - .addMoreInfo( - "http://developer.android.com/design/building-blocks/dialogs.html"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue STYLE = Issue.create( - "ButtonStyle", //$NON-NLS-1$ - "Button should be borderless", - - "Button bars typically use a borderless style for the buttons. Set the " + - "`style=\"?android:attr/buttonBarButtonStyle\"` attribute " + - "on each of the buttons, and set `style=\"?android:attr/buttonBarStyle\"` on " + - "the parent layout", - - Category.USABILITY, - 5, - Severity.WARNING, - IMPLEMENTATION) - .addMoreInfo( - "http://developer.android.com/design/building-blocks/buttons.html"); //$NON-NLS-1$ - - /** The main issue discovered by this detector */ - public static final Issue BACK_BUTTON = Issue.create( - "BackButton", //$NON-NLS-1$ - "Back button", - // 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, - IMPLEMENTATION) - .setEnabledByDefault(false) - .addMoreInfo( - "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$ - "Cancel/OK dialog button 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, - IMPLEMENTATION); - - /** Set of resource names whose value was either OK or Cancel */ - private Set 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 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 mIgnore; - - /** Constructs a new {@link ButtonDetector} */ - public ButtonDetector() { - } - - @NonNull - @Override - public Speed getSpeed() { - return Speed.FAST; - } - - @Override - public Collection 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 static 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