Add lint check for custom attributes in library projects

Also adds a lint check for unused namespace declarations,
and migrates the TypoDetector code into this new namespace
detector.

Change-Id: I5ec2214ea4c59e14194f8eaecef422ea19baa35e
diff --git a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
index 09d378b..9d9cf6d 100644
--- a/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
+++ b/lint/libs/lint_api/src/com/android/tools/lint/detector/api/LintConstants.java
@@ -250,6 +250,7 @@
     public static final String ANDROID_STRING_RESOURCE_PREFIX = "@android:string/";   //$NON-NLS-1$
 
     // Packages
+    public static final String ANDROID_PKG_PREFIX = "android.";         //$NON-NLS-1$
     public static final String WIDGET_PKG_PREFIX = "android.widget.";   //$NON-NLS-1$
     public static final String VIEW_PKG_PREFIX = "android.view.";       //$NON-NLS-1$
 
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
index 4b5ebe2..e3deb3f 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/BuiltinIssueRegistry.java
@@ -53,7 +53,8 @@
     private static final List<Issue> sIssues;
 
     static {
-        List<Issue> issues = new ArrayList<Issue>(75);
+        final int initialCapacity = 77;
+        List<Issue> issues = new ArrayList<Issue>(initialCapacity);
 
         issues.add(AccessibilityDetector.ISSUE);
         issues.add(MathDetector.ISSUE);
@@ -126,11 +127,15 @@
         issues.add(ViewTypeDetector.ISSUE);
         issues.add(WrongImportDetector.ISSUE);
         issues.add(ViewConstructorDetector.ISSUE);
-        issues.add(TypoDetector.ISSUE);
+        issues.add(NamespaceDetector.CUSTOMVIEW);
+        issues.add(NamespaceDetector.UNUSED);
+        issues.add(NamespaceDetector.TYPO);
         issues.add(AlwaysShowActionDetector.ISSUE);
         issues.add(JavaPerformanceDetector.PAINT_ALLOC);
         issues.add(JavaPerformanceDetector.USE_SPARSEARRAY);
 
+        assert initialCapacity >= issues.size() : issues.size();
+
         addCustomIssues(issues);
 
         sIssues = Collections.unmodifiableList(issues);
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
index d0cf2d4..6ea7899 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/DetectMissingPrefix.java
@@ -16,6 +16,7 @@
 
 package com.android.tools.lint.checks;
 
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX;
 import static com.android.tools.lint.detector.api.LintConstants.ATTR_CLASS;
 import static com.android.tools.lint.detector.api.LintConstants.ATTR_LAYOUT;
 import static com.android.tools.lint.detector.api.LintConstants.ATTR_STYLE;
@@ -114,6 +115,6 @@
             return true;
         }
 
-        return tag.indexOf('.') != -1 && !tag.startsWith("android."); //$NON-NLS-1$
+        return tag.indexOf('.') != -1 && !tag.startsWith(ANDROID_PKG_PREFIX);
     }
 }
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
index 96b677e..97cb5f6 100644
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/ManifestOrderDetector.java
@@ -66,7 +66,7 @@
 
     /** Missing a {@code <uses-sdk>} element */
     public static final Issue USES_SDK = Issue.create(
-            "UsesSdkMinTarget", //$NON-NLS-1$
+            "UsesMinSdkAttributes", //$NON-NLS-1$
             "Checks that the minimum SDK and target SDK attributes are defined",
 
             "The manifest should contain a <uses-sdk> element which defines the " +
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java
new file mode 100644
index 0000000..60ae3a3
--- /dev/null
+++ b/lint/libs/lint_checks/src/com/android/tools/lint/checks/NamespaceDetector.java
@@ -0,0 +1,217 @@
+/*
+ * 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.tools.lint.detector.api.LintConstants.ANDROID_PKG_PREFIX;
+import static com.android.tools.lint.detector.api.LintConstants.ANDROID_URI;
+import static com.android.tools.lint.detector.api.LintConstants.XMLNS_PREFIX;
+
+import com.android.tools.lint.detector.api.Category;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.LayoutDetector;
+import com.android.tools.lint.detector.api.LintUtils;
+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.Attr;
+import org.w3c.dom.Document;
+import org.w3c.dom.Element;
+import org.w3c.dom.NamedNodeMap;
+import org.w3c.dom.Node;
+import org.w3c.dom.NodeList;
+
+import java.util.HashMap;
+import java.util.Map;
+
+/**
+ * Checks for various issues related to XML namespaces
+ */
+public class NamespaceDetector extends LayoutDetector {
+    /** Typos in the namespace */
+    public static final Issue TYPO = Issue.create(
+            "NamespaceTypo", //$NON-NLS-1$
+            "Looks for misspellings in namespace declarations",
+
+            "Accidental misspellings in namespace declarations can lead to some very " +
+            "obscure error messages. This check looks for potential misspellings to " +
+            "help track these down.",
+            Category.CORRECTNESS,
+            8,
+            Severity.WARNING,
+            NamespaceDetector.class,
+            Scope.RESOURCE_FILE_SCOPE);
+
+    /** Unused namespace declarations */
+    public static final Issue UNUSED = Issue.create(
+            "UnusedNamespace", //$NON-NLS-1$
+            "Finds unused namespaces in XML documents",
+
+            "Unused namespace declarations take up space and require processing that is not " +
+            "necessary",
+
+            Category.CORRECTNESS,
+            1,
+            Severity.WARNING,
+            NamespaceDetector.class,
+            Scope.RESOURCE_FILE_SCOPE);
+
+    /** Using custom namespace attributes in a library project */
+    public static final Issue CUSTOMVIEW = Issue.create(
+            "LibraryCustomView", //$NON-NLS-1$
+            "Flags custom views in libraries, which currently do not work",
+
+            "Using a custom view in a library project (where the custom view " +
+            "requires XML attributes from a custom namespace) does not yet " +
+            "work.",
+            Category.CORRECTNESS,
+            6,
+            Severity.ERROR,
+            NamespaceDetector.class,
+            Scope.RESOURCE_FILE_SCOPE);
+
+    /** Prefix relevant for custom namespaces */
+    private static final String XMLNS_ANDROID = "xmlns:android";                    //$NON-NLS-1$
+    private static final String XMLNS_A = "xmlns:a";                                //$NON-NLS-1$
+    private static final String URI_PREFIX = "http://schemas.android.com/apk/res/"; //$NON-NLS-1$
+
+    private Map<String, Attr> mUnusedNamespaces;
+    private boolean mCheckUnused;
+    private boolean mCheckCustomAttrs;
+
+    /** Constructs a new {@link NamespaceDetector} */
+    public NamespaceDetector() {
+    }
+
+    @Override
+    public Speed getSpeed() {
+        return Speed.FAST;
+    }
+
+    @Override
+    public void visitDocument(XmlContext context, Document document) {
+        boolean haveCustomNamespace = false;
+        Element root = document.getDocumentElement();
+        NamedNodeMap attributes = root.getAttributes();
+        for (int i = 0, n = attributes.getLength(); i < n; i++) {
+            Node item = attributes.item(i);
+            if (item.getNodeName().startsWith(XMLNS_PREFIX)) {
+                String value = item.getNodeValue();
+
+                if (!value.equals(ANDROID_URI)) {
+                    Attr attribute = (Attr) item;
+
+                    if (value.startsWith(URI_PREFIX)) {
+                        haveCustomNamespace = true;
+                        if (mUnusedNamespaces == null) {
+                            mUnusedNamespaces = new HashMap<String, Attr>();
+                        }
+                        mUnusedNamespaces.put(item.getNodeName().substring(XMLNS_PREFIX.length()),
+                                attribute);
+                    }
+
+                    String name = attribute.getName();
+                    if (!name.equals(XMLNS_ANDROID) && !name.equals(XMLNS_A)) {
+                        continue;
+                    }
+
+                    if (!context.isEnabled(TYPO)) {
+                        continue;
+                    }
+
+                    if (name.equals(XMLNS_A)) {
+                        // For the "android" prefix we always assume that the namespace prefix
+                        // should be our expected prefix, but for the "a" prefix we make sure
+                        // that it's at least "close"; if you're bound it to something completely
+                        // different, don't complain.
+                        if (LintUtils.editDistance(ANDROID_URI, value) > 4) {
+                            continue;
+                        }
+                    }
+
+                    if (value.equalsIgnoreCase(ANDROID_URI)) {
+                        context.report(TYPO, attribute, context.getLocation(attribute),
+                                String.format(
+                                    "URI is case sensitive: was \"%1$s\", expected \"%2$s\"",
+                                    value, ANDROID_URI), null);
+                    } else {
+                        context.report(TYPO, attribute, context.getLocation(attribute),
+                                String.format(
+                                    "Unexpected namespace URI bound to the \"android\" " +
+                                    "prefix, was %1$s, expected %2$s", value, ANDROID_URI),
+                                null);
+                    }
+                }
+            }
+        }
+
+        if (haveCustomNamespace) {
+            mCheckCustomAttrs = context.isEnabled(CUSTOMVIEW) && context.getProject().isLibrary();
+            mCheckUnused = context.isEnabled(UNUSED);
+            checkElement(context, document.getDocumentElement());
+
+            if (mCheckUnused && mUnusedNamespaces.size() > 0) {
+                for (Map.Entry<String, Attr> entry : mUnusedNamespaces.entrySet()) {
+                    String prefix = entry.getKey();
+                    Attr attribute = entry.getValue();
+                    context.report(UNUSED, attribute, context.getLocation(attribute),
+                            String.format("Unused namespace %1$s", prefix), null);
+                }
+            }
+        }
+    }
+
+    private void checkElement(XmlContext context, Node node) {
+        if (node.getNodeType() == Node.ELEMENT_NODE) {
+            if (mCheckCustomAttrs) {
+                String tag = node.getNodeName();
+                if (tag.indexOf('.') != -1
+                        // Don't consider android.support.* and android.app.FragmentBreadCrumbs etc
+                        && !tag.startsWith(ANDROID_PKG_PREFIX)) {
+                    NamedNodeMap attributes = ((Element) node).getAttributes();
+                    for (int i = 0, n = attributes.getLength(); i < n; i++) {
+                        Attr attribute = (Attr) attributes.item(i);
+                        String uri = attribute.getNamespaceURI();
+                        if (uri != null && uri.length() > 0 && uri.startsWith(URI_PREFIX)
+                                && !uri.equals(ANDROID_URI)) {
+                            context.report(CUSTOMVIEW, attribute, context.getLocation(attribute),
+                                "Using a custom namespace attributes in a library project does " +
+                                "not yet work", null);
+                        }
+                    }
+                }
+            }
+
+            if (mCheckUnused) {
+                NamedNodeMap attributes = ((Element) node).getAttributes();
+                for (int i = 0, n = attributes.getLength(); i < n; i++) {
+                    Attr attribute = (Attr) attributes.item(i);
+                    String prefix = attribute.getPrefix();
+                    if (prefix != null) {
+                        mUnusedNamespaces.remove(prefix);
+                    }
+                }
+            }
+
+            NodeList childNodes = node.getChildNodes();
+            for (int i = 0, n = childNodes.getLength(); i < n; i++) {
+                checkElement(context, childNodes.item(i));
+            }
+        }
+    }
+}
diff --git a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypoDetector.java b/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypoDetector.java
deleted file mode 100644
index 3fcbcf5..0000000
--- a/lint/libs/lint_checks/src/com/android/tools/lint/checks/TypoDetector.java
+++ /dev/null
@@ -1,95 +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.tools.lint.detector.api.LintConstants.ANDROID_URI;
-
-import com.android.tools.lint.detector.api.Category;
-import com.android.tools.lint.detector.api.Issue;
-import com.android.tools.lint.detector.api.LintUtils;
-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.Attr;
-
-import java.util.Arrays;
-import java.util.Collection;
-
-/**
- * Check which looks for likely typos in various places.
- */
-public class TypoDetector extends ResourceXmlDetector {
-    private static final String XMLNS_ANDROID = "xmlns:android";    //$NON-NLS-1$
-    private static final String XMLNS_A = "xmlns:a";                //$NON-NLS-1$
-
-    /** The main issue discovered by this detector */
-    public static final Issue ISSUE = Issue.create(
-            "NamespaceTypo", //$NON-NLS-1$
-            "Looks for misspellings in namespace declarations",
-
-            "Accidental misspellings in namespace declarations can lead to some very " +
-            "obscure error messages. This check looks for potential misspellings to " +
-            "help track these down.",
-            Category.CORRECTNESS,
-            8,
-            Severity.WARNING,
-            TypoDetector.class,
-            Scope.RESOURCE_FILE_SCOPE);
-
-    /** Constructs a new {@link TypoDetector} */
-    public TypoDetector() {
-    }
-
-    @Override
-    public Speed getSpeed() {
-        return Speed.FAST;
-    }
-
-    @Override
-    public Collection<String> getApplicableAttributes() {
-        return Arrays.asList(XMLNS_ANDROID, XMLNS_A);
-    }
-
-    @Override
-    public void visitAttribute(XmlContext context, Attr attribute) {
-        String value = attribute.getValue();
-        if (!value.equals(ANDROID_URI)) {
-            if (attribute.getName().equals(XMLNS_A)) {
-                // For the "android" prefix we always assume that the namespace prefix
-                // should be our expected prefix, but for the "a" prefix we make sure
-                // that it's at least "close"; if you're bound it to something completely
-                // different, don't complain.
-                if (LintUtils.editDistance(ANDROID_URI, value) > 4) {
-                    return;
-                }
-            }
-
-            if (value.equalsIgnoreCase(ANDROID_URI)) {
-                context.report(ISSUE, attribute, context.getLocation(attribute),
-                        String.format("URI is case sensitive: was \"%1$s\", expected \"%2$s\"",
-                                value, ANDROID_URI), null);
-            } else {
-                context.report(ISSUE, attribute, context.getLocation(attribute),
-                        String.format("Unexpected namespace URI bound to the \"android\" " +
-                                "prefix, was %1$s, expected %2$s", value, ANDROID_URI), null);
-            }
-        }
-    }
-}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java
new file mode 100644
index 0000000..3570f5c
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/NamespaceDetectorTest.java
@@ -0,0 +1,102 @@
+/*
+ * 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 com.android.tools.lint.detector.api.Detector;
+
+@SuppressWarnings("javadoc")
+public class NamespaceDetectorTest extends AbstractCheckTest {
+    @Override
+    protected Detector getDetector() {
+        return new NamespaceDetector();
+    }
+
+    public void testCustom() throws Exception {
+        assertEquals(
+            "customview.xml:16: Error: Using a custom namespace attributes in a library project does not yet work",
+
+            lintProject(
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+                    "multiproject/library.properties=>project.properties",
+                    "res/layout/customview.xml"
+            ));
+    }
+
+    public void testCustomOk() throws Exception {
+        assertEquals(
+            "No warnings.",
+
+            lintProject(
+                    "multiproject/library-manifest.xml=>AndroidManifest.xml",
+
+                    // Use a standard project properties instead: no warning since it's
+                    // not a library project:
+                    //"multiproject/library.properties=>project.properties",
+
+                    "res/layout/customview.xml"
+            ));
+    }
+
+    public void testTypo() throws Exception {
+        assertEquals(
+                "wrong_namespace.xml:2: Warning: Unexpected namespace URI bound to the " +
+                "\"android\" prefix, was http://schemas.android.com/apk/res/andriod, " +
+                "expected http://schemas.android.com/apk/res/android",
+
+                lintProject("res/layout/wrong_namespace.xml"));
+    }
+
+    public void testTypo2() throws Exception {
+        assertEquals(
+                "wrong_namespace2.xml:2: Warning: URI is case sensitive: was " +
+                "\"http://schemas.android.com/apk/res/Android\", expected " +
+                "\"http://schemas.android.com/apk/res/android\"",
+
+                lintProject("res/layout/wrong_namespace2.xml"));
+    }
+
+    public void testTypo3() throws Exception {
+        assertEquals(
+                "wrong_namespace3.xml:2: Warning: Unexpected namespace URI bound to the " +
+                "\"android\" prefix, was http://schemas.android.com/apk/res/androi, " +
+                "expected http://schemas.android.com/apk/res/android",
+
+                lintProject("res/layout/wrong_namespace3.xml"));
+    }
+
+    public void testTypoOk() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject("res/layout/wrong_namespace4.xml"));
+    }
+
+    public void testUnused() throws Exception {
+        assertEquals(
+                "unused_namespace.xml:3: Warning: Unused namespace unused1\n" +
+                "unused_namespace.xml:4: Warning: Unused namespace unused2",
+
+                lintProject("res/layout/unused_namespace.xml"));
+    }
+
+    public void testUnusedOk() throws Exception {
+        assertEquals(
+                "No warnings.",
+
+                lintProject("res/layout/layout1.xml"));
+    }
+}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TypoDetectorTest.java b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TypoDetectorTest.java
deleted file mode 100644
index b9af7a2..0000000
--- a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/TypoDetectorTest.java
+++ /dev/null
@@ -1,61 +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 com.android.tools.lint.detector.api.Detector;
-
-@SuppressWarnings("javadoc")
-public class TypoDetectorTest extends AbstractCheckTest {
-    @Override
-    protected Detector getDetector() {
-        return new TypoDetector();
-    }
-
-    public void test() throws Exception {
-        assertEquals(
-                "wrong_namespace.xml:2: Warning: Unexpected namespace URI bound to the " +
-                "\"android\" prefix, was http://schemas.android.com/apk/res/andriod, " +
-                "expected http://schemas.android.com/apk/res/android",
-
-                lintProject("res/layout/wrong_namespace.xml"));
-    }
-
-    public void test2() throws Exception {
-        assertEquals(
-                "wrong_namespace2.xml:2: Warning: URI is case sensitive: was " +
-                "\"http://schemas.android.com/apk/res/Android\", expected " +
-                "\"http://schemas.android.com/apk/res/android\"",
-
-                lintProject("res/layout/wrong_namespace2.xml"));
-    }
-
-    public void test3() throws Exception {
-        assertEquals(
-                "wrong_namespace3.xml:2: Warning: Unexpected namespace URI bound to the " +
-                "\"android\" prefix, was http://schemas.android.com/apk/res/androi, " +
-                "expected http://schemas.android.com/apk/res/android",
-
-                lintProject("res/layout/wrong_namespace3.xml"));
-    }
-
-    public void testOk() throws Exception {
-        assertEquals(
-                "No warnings.",
-
-                lintProject("res/layout/wrong_namespace4.xml"));
-    }
-}
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml
new file mode 100644
index 0000000..976d636
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/customview.xml
@@ -0,0 +1,30 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:tools="http://schemas.android.com/tools"
+    xmlns:other="http://schemas.foo.bar.com/other"
+    xmlns:foo="http://schemas.android.com/apk/res/foo"
+    android:id="@+id/newlinear"
+    android:layout_width="match_parent"
+    android:layout_height="match_parent"
+    android:orientation="vertical" >
+
+    <foo.bar.Baz
+        android:id="@+id/button1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Button1"
+        foo:misc="Custom attribute"
+        tools:ignore="HardcodedText" >
+    </foo.bar.Baz>
+
+    <!-- Wrong namespace uri prefix: Don't warn -->
+    <foo.bar.Baz
+        android:id="@+id/button1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Button1"
+        other:misc="Custom attribute"
+        tools:ignore="HardcodedText" >
+    </foo.bar.Baz>
+
+</LinearLayout>
diff --git a/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
new file mode 100644
index 0000000..f633e4b
--- /dev/null
+++ b/lint/libs/lint_checks/tests/src/com/android/tools/lint/checks/data/res/layout/unused_namespace.xml
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<foo.bar.LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:unused1="http://schemas.android.com/apk/res/unused1"
+    xmlns:unused2="http://schemas.android.com/apk/res/unused1"
+    xmlns:unused3="http://foo.bar.com/foo"
+    xmlns:notunused="http://schemas.android.com/apk/res/notunused"
+    xmlns:tools="http://schemas.android.com/tools" >
+
+    <foo.bar.Button
+        notunused:foo="Foo"
+        tools:ignore="HardcodedText" >
+    </foo.bar.Button>
+
+</foo.bar.LinearLayout>