aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java126
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java7
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml36
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml53
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml16
-rw-r--r--lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml1
-rw-r--r--lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java166
-rw-r--r--lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java6
-rw-r--r--lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java8
9 files changed, 369 insertions, 50 deletions
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
index 4edf345..27875e8 100644
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/MissingClassDetectorTest.java
@@ -16,17 +16,29 @@
package com.android.tools.lint.checks;
+import static com.android.tools.lint.checks.MissingClassDetector.INNERCLASS;
+import static com.android.tools.lint.checks.MissingClassDetector.INSTANTIATABLE;
+import static com.android.tools.lint.checks.MissingClassDetector.MISSING;
+
+import com.android.annotations.NonNull;
+import com.android.tools.lint.client.api.LintClient;
import com.android.tools.lint.detector.api.Detector;
+import com.android.tools.lint.detector.api.Issue;
+import com.android.tools.lint.detector.api.Project;
import com.android.tools.lint.detector.api.Scope;
+import com.google.common.collect.Sets;
import java.io.File;
import java.util.Arrays;
import java.util.EnumSet;
+import java.util.HashSet;
import java.util.List;
+import java.util.Set;
@SuppressWarnings("javadoc")
public class MissingClassDetectorTest extends AbstractCheckTest {
private EnumSet<Scope> mScopes;
+ private Set<Issue> mEnabled = new HashSet<Issue>();
@Override
protected Detector getDetector() {
@@ -38,8 +50,19 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
return mScopes;
}
+ @Override
+ protected TestConfiguration getConfiguration(LintClient client, Project project) {
+ return new TestConfiguration(client, project, null) {
+ @Override
+ public boolean isEnabled(@NonNull Issue issue) {
+ return super.isEnabled(issue) && mEnabled.contains(issue);
+ }
+ };
+ }
+
public void test() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING);
assertEquals(
"AndroidManifest.xml:13: Error: Class referenced in the manifest, test.pkg.TestProvider, was not found in the project or the libraries [MissingRegistered]\n" +
" <activity android:name=\".TestProvider\" />\n" +
@@ -67,6 +90,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testIncrementalInManifest() throws Exception {
mScopes = Scope.MANIFEST_SCOPE;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"No warnings.",
@@ -78,6 +102,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testNoWarningBeforeBuild() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"No warnings.",
@@ -89,11 +114,12 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testOkClasses() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"No warnings.",
lintProject(
- "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/AndroidManifestRegs.xml=>AndroidManifest.xml",
"bytecode/.classpath=>.classpath",
"bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
"bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class",
@@ -110,11 +136,12 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testOkLibraries() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"No warnings.",
lintProject(
- "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/AndroidManifestRegs.xml=>AndroidManifest.xml",
"bytecode/.classpath=>.classpath",
"bytecode/classes.jar=>libs/classes.jar"
));
@@ -122,10 +149,13 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testLibraryProjects() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
File master = getProjectDir("MasterProject",
// Master project
- "bytecode/AndroidManifestWrongRegs.xml=>AndroidManifest.xml",
+ "bytecode/AndroidManifestRegs.xml=>AndroidManifest.xml",
"multiproject/main.properties=>project.properties",
+ "bytecode/TestService.java.txt=>src/test/pkg/TestService.java",
+ "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class",
"bytecode/.classpath=>.classpath"
);
File library = getProjectDir("LibraryProject",
@@ -134,25 +164,24 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
"multiproject/library.properties=>project.properties",
"bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
"bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class",
- "bytecode/TestService.java.txt=>src/test/pkg/TestService.java",
- "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class",
"bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java",
"bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class",
"bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java",
"bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class"
// Missing TestReceiver: Test should complain about just that class
);
- assertEquals(
- "MasterProject/AndroidManifest.xml:17: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n" +
- " <service android:name=\"TestReceiver\" />\n" +
- " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" +
- "1 errors, 0 warnings\n",
+ assertEquals(""
+ + "MasterProject/AndroidManifest.xml:32: Error: Class referenced in the manifest, test.pkg.TestReceiver, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <receiver android:name=\"TestReceiver\" />\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "1 errors, 0 warnings\n",
checkLint(Arrays.asList(master, library)));
}
public void testInnerClassStatic() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"src/test/pkg/Foo.java:8: Warning: This inner class should be static (test.pkg.Foo.Baz) [Instantiatable]\n" +
" public class Baz extends Activity {\n" +
@@ -171,6 +200,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testInnerClassPublic() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"src/test/pkg/Foo/Bar.java:6: Warning: The default constructor must be public [Instantiatable]\n" +
" private Bar() {\n" +
@@ -187,6 +217,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testInnerClass() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
" <activity\n" +
@@ -206,6 +237,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testInnerClass2() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
" <activity\n" +
@@ -222,6 +254,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testWrongSeparator1() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
" <activity\n" +
@@ -238,6 +271,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testWrongSeparator2() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"AndroidManifest.xml:14: Error: Class referenced in the manifest, test.pkg.Foo.Bar, was not found in the project or the libraries [MissingRegistered]\n" +
" <activity\n" +
@@ -257,6 +291,7 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
public void testNoClassesWithLibraries() throws Exception {
mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
assertEquals(
"No warnings.",
@@ -267,4 +302,75 @@ public class MissingClassDetectorTest extends AbstractCheckTest {
));
}
+ public void testFragment() throws Exception {
+ mScopes = null;
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
+ assertEquals(""
+ + "res/layout/fragment2.xml:7: Error: Class referenced in the layout file, my.app.Fragment, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <fragment\n"
+ + " ^\n"
+ + "res/layout/fragment2.xml:12: Error: Class referenced in the layout file, my.app.MyView, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <view\n"
+ + " ^\n"
+ + "res/layout/fragment2.xml:17: Error: Class referenced in the layout file, my.app.Fragment2, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <fragment\n"
+ + " ^\n"
+ + "3 errors, 0 warnings\n",
+
+ lintProject(
+ "bytecode/AndroidManifestRegs.xml=>AndroidManifest.xml",
+ "bytecode/.classpath=>.classpath",
+ "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
+ "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class",
+ "bytecode/TestService.java.txt=>src/test/pkg/TestService.java",
+ "bytecode/TestService.class.data=>bin/classes/test/pkg/TestService.class",
+ "bytecode/TestProvider.java.txt=>src/test/pkg/TestProvider.java",
+ "bytecode/TestProvider.class.data=>bin/classes/test/pkg/TestProvider.class",
+ "bytecode/TestProvider2.java.txt=>src/test/pkg/TestProvider2.java",
+ "bytecode/TestProvider2.class.data=>bin/classes/test/pkg/TestProvider2.class",
+ "bytecode/TestReceiver.java.txt=>src/test/pkg/TestReceiver.java",
+ "bytecode/TestReceiver.class.data=>bin/classes/test/pkg/TestReceiver.class",
+ "registration/Foo.java.txt=>src/test/pkg/Foo.java",
+ "registration/Foo.class.data=>bin/classes/test/pkg/Foo.class",
+ "registration/Bar.java.txt=>src/test/pkg/Foo/Bar.java",
+ "registration/Bar.class.data=>bin/classes/test/pkg/Foo/Bar.class",
+
+ "res/layout/fragment2.xml"
+ ));
+ }
+
+ public void testAnalytics() throws Exception {
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
+ assertEquals(""
+ + "res/values/analytics.xml:13: Error: Class referenced in the analytics file, com.example.app.BaseActivity, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <string name=\"com.example.app.BaseActivity\">Home</string>\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "res/values/analytics.xml:14: Error: Class referenced in the analytics file, com.example.app.PrefsActivity, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <string name=\"com.example.app.PrefsActivity\">Preferences</string>\n"
+ + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n"
+ + "2 errors, 0 warnings\n",
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "res/values/analytics.xml",
+ "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
+ "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class"
+ ));
+ }
+
+ public void testCustomView() throws Exception {
+ mEnabled = Sets.newHashSet(MISSING, INSTANTIATABLE, INNERCLASS);
+ assertEquals(""
+ + "res/layout/customview.xml:21: Error: Class referenced in the layout file, foo.bar.Baz, was not found in the project or the libraries [MissingRegistered]\n"
+ + " <foo.bar.Baz\n"
+ + " ^\n"
+ + "1 errors, 0 warnings\n",
+
+ lintProject(
+ "bytecode/.classpath=>.classpath",
+ "res/layout/customview.xml",
+ "bytecode/OnClickActivity.java.txt=>src/test/pkg/OnClickActivity.java",
+ "bytecode/OnClickActivity.class.data=>bin/classes/test/pkg/OnClickActivity.class"
+ ));
+ }
}
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java b/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
index 16d0107..5173011 100644
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/TypographyDetectorTest.java
@@ -86,6 +86,13 @@ public class TypographyDetectorTest extends AbstractCheckTest {
lintProject("res/values/typography.xml"));
}
+ public void testAnalytics() throws Exception {
+ assertEquals(""
+ + "No warnings.",
+
+ lintProject("res/values/analytics.xml"));
+ }
+
public void testSingleQuotesRange() {
assertTrue(SINGLE_QUOTE.matcher("Foo: 'bar'").matches());
assertTrue(SINGLE_QUOTE.matcher("'Foo': bar").matches());
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
new file mode 100644
index 0000000..e52f73c
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/bytecode/AndroidManifestRegs.xml
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ 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.
+ -->
+
+<manifest xmlns:android="http://schemas.android.com/apk/res/android"
+ package="test.pkg"
+ android:versionCode="1"
+ android:versionName="1.0" >
+
+ <uses-sdk android:minSdkVersion="10" />
+
+ <application
+ android:icon="@drawable/ic_launcher"
+ android:label="@string/app_name" >
+ <provider android:name=".TestProvider" />
+ <provider android:name="test.pkg.TestProvider2" />
+ <service android:name=".TestService" />
+ <activity android:name="OnClickActivity" />
+ <receiver android:name="TestReceiver" />
+
+ </application>
+
+</manifest>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
new file mode 100644
index 0000000..3a672d1
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/layout/fragment2.xml
@@ -0,0 +1,53 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
+ android:layout_width="match_parent"
+ android:layout_height="match_parent"
+ android:orientation="vertical" >
+
+ <fragment
+ class="my.app.Fragment"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <view
+ class="my.app.MyView"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment
+ android:name="my.app.Fragment2"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <view
+ android:name="test.pkg.TestService"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <view
+ class="test.pkg.TestService"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment
+ android:name="test.pkg.TestService"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment
+ class="test.pkg.TestService"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment
+ class="test.pkg.Foo$Bar"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content" />
+
+ <fragment
+ class="test.pkg.Nonexistent"
+ android:layout_width="match_parent"
+ android:layout_height="wrap_content"
+ tools:ignore="MissingRegistered" />
+
+</LinearLayout>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
new file mode 100644
index 0000000..8ea40a8
--- /dev/null
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/analytics.xml
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8" ?>
+<resources>
+ <!--Replace placeholder ID with your tracking ID-->
+ <string name="ga_trackingId">UA-12345678-1</string>
+
+ <!--Enable Activity tracking-->
+ <bool name="ga_autoActivityTracking">true</bool>
+
+ <!--Enable automatic exception tracking-->
+ <bool name="ga_reportUncaughtExceptions">true</bool>
+
+ <!-- The screen names that will appear in your reporting -->
+ <string name="com.example.app.BaseActivity">Home</string>
+ <string name="com.example.app.PrefsActivity">Preferences</string>
+ <string name="test.pkg.OnClickActivity">Clicks</string>
+</resources>
diff --git a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
index 1dd4845..02ffb1c 100644
--- a/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
+++ b/lint/cli/src/test/java/com/android/tools/lint/checks/data/res/values/typography.xml
@@ -25,5 +25,6 @@
<item>Age 5 1/2</item>
</string-array>
<string name="ndash">X Y Z: 10 10 -1</string>
+ <string name="ga_trackingId">UA-0000-0</string>
</resources>
diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java
index ada2fbf..d3dbe33 100644
--- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java
+++ b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/MissingClassDetector.java
@@ -18,6 +18,7 @@ package com.android.tools.lint.checks;
import static com.android.SdkConstants.ANDROID_PKG_PREFIX;
import static com.android.SdkConstants.ANDROID_URI;
+import static com.android.SdkConstants.ATTR_CLASS;
import static com.android.SdkConstants.ATTR_NAME;
import static com.android.SdkConstants.ATTR_PACKAGE;
import static com.android.SdkConstants.CONSTRUCTOR_NAME;
@@ -26,8 +27,12 @@ import static com.android.SdkConstants.TAG_APPLICATION;
import static com.android.SdkConstants.TAG_PROVIDER;
import static com.android.SdkConstants.TAG_RECEIVER;
import static com.android.SdkConstants.TAG_SERVICE;
+import static com.android.SdkConstants.TAG_STRING;
+import static com.android.SdkConstants.VIEW_FRAGMENT;
+import static com.android.SdkConstants.VIEW_TAG;
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.ClassContext;
import com.android.tools.lint.detector.api.Context;
@@ -41,21 +46,25 @@ 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 com.android.utils.SdkUtils;
import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
import org.objectweb.asm.Opcodes;
import org.objectweb.asm.tree.ClassNode;
import org.objectweb.asm.tree.MethodNode;
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.EnumSet;
import java.util.List;
import java.util.Map;
+import java.util.Set;
/**
* Checks to ensure that classes referenced in the manifest actually exist and are included
@@ -76,8 +85,8 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
8,
Severity.ERROR,
MissingClassDetector.class,
- EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE, Scope.JAVA_LIBRARIES)).setMoreInfo(
- "http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$
+ EnumSet.of(Scope.MANIFEST, Scope.CLASS_FILE, Scope.JAVA_LIBRARIES, Scope.RESOURCE_FILE))
+ .setMoreInfo("http://developer.android.com/guide/topics/manifest/manifest-intro.html"); //$NON-NLS-1$
/** Are activity, service, receiver etc subclasses instantiatable? */
public static final Issue INSTANTIATABLE = Issue.create(
@@ -114,6 +123,7 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
Scope.MANIFEST_SCOPE);
private Map<String, Location.Handle> mReferencedClasses;
+ private Set<String> mCustomViews;
private boolean mHaveClasses;
/** Constructs a new {@link MissingClassDetector} */
@@ -130,40 +140,93 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
@Override
public Collection<String> getApplicableElements() {
- return Arrays.asList(
- TAG_APPLICATION,
- TAG_ACTIVITY,
- TAG_SERVICE,
- TAG_RECEIVER,
- TAG_PROVIDER
- );
+ return ALL;
}
@Override
- public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
+ public boolean appliesTo(@NonNull ResourceFolderType folderType) {
+ return folderType == ResourceFolderType.VALUES || folderType == ResourceFolderType.LAYOUT;
+ }
- Element root = element.getOwnerDocument().getDocumentElement();
- Attr classNameNode = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME);
- if (classNameNode == null) {
- return;
+ @Override
+ public void visitElement(@NonNull XmlContext context, @NonNull Element element) {
+ String pkg = null;
+ Node classNameNode;
+ String className;
+ String tag = element.getTagName();
+ ResourceFolderType folderType = context.getResourceFolderType();
+ if (folderType == ResourceFolderType.VALUES) {
+ if (!tag.equals(TAG_STRING)) {
+ return;
+ }
+ Attr attr = element.getAttributeNode(ATTR_NAME);
+ if (attr == null) {
+ return;
+ }
+ className = attr.getValue();
+ classNameNode = attr;
+ } else if (folderType == ResourceFolderType.LAYOUT) {
+ if (tag.indexOf('.') > 0) {
+ className = tag;
+ classNameNode = element;
+ } else if (tag.equals(VIEW_FRAGMENT) || tag.equals(VIEW_TAG)) {
+ Attr attr = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME);
+ if (attr == null) {
+ attr = element.getAttributeNode(ATTR_CLASS);
+ }
+ if (attr == null) {
+ return;
+ }
+ className = attr.getValue();
+ classNameNode = attr;
+ } else {
+ return;
+ }
+ } else {
+ // Manifest file
+ if (TAG_APPLICATION.equals(tag)
+ || TAG_ACTIVITY.equals(tag)
+ || TAG_SERVICE.equals(tag)
+ || TAG_RECEIVER.equals(tag)
+ || TAG_PROVIDER.equals(tag)) {
+ Element root = element.getOwnerDocument().getDocumentElement();
+ pkg = root.getAttribute(ATTR_PACKAGE);
+ Attr attr = element.getAttributeNodeNS(ANDROID_URI, ATTR_NAME);
+ if (attr == null) {
+ return;
+ }
+ className = attr.getValue();
+ classNameNode = attr;
+ } else {
+ return;
+ }
}
- String className = classNameNode.getValue();
if (className.isEmpty()) {
return;
}
- String pkg = root.getAttribute(ATTR_PACKAGE);
String fqcn;
- if (className.startsWith(".")) { //$NON-NLS-1$
- fqcn = pkg + className;
- } else if (className.indexOf('.') == -1) {
- // According to the <activity> manifest element documentation, this is not
- // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html )
- // but it appears in manifest files and appears to be supported by the runtime
- // so handle this in code as well:
- fqcn = pkg + '.' + className;
+ int dotIndex = className.indexOf('.');
+ if (dotIndex <= 0) {
+ if (pkg == null) {
+ return; // value file
+ }
+ if (dotIndex == 0) {
+ fqcn = pkg + className;
+ } else {
+ // According to the <activity> manifest element documentation, this is not
+ // valid ( http://developer.android.com/guide/topics/manifest/activity-element.html )
+ // but it appears in manifest files and appears to be supported by the runtime
+ // so handle this in code as well:
+ fqcn = pkg + '.' + className;
+ }
} else { // else: the class name is already a fully qualified class name
fqcn = className;
+ // Only look for fully qualified tracker names in analytics files
+ if (folderType == ResourceFolderType.VALUES
+ && !SdkUtils.endsWith(context.file.getPath(), "analytics.xml")) { //$NON-NLS-1$
+ return;
+ }
}
String signature = ClassContext.getInternalName(fqcn);
@@ -176,14 +239,21 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
return;
}
- if (mReferencedClasses == null) {
- mReferencedClasses = Maps.newHashMapWithExpectedSize(16);
- }
+ Handle handle = null;
+ if (!context.getDriver().isSuppressed(MISSING, element)) {
+ if (mReferencedClasses == null) {
+ mReferencedClasses = Maps.newHashMapWithExpectedSize(16);
+ mCustomViews = Sets.newHashSetWithExpectedSize(8);
+ }
- Handle handle = context.parser.createLocationHandle(context, element);
- mReferencedClasses.put(signature, handle);
+ handle = context.parser.createLocationHandle(context, element);
+ mReferencedClasses.put(signature, handle);
+ if (folderType == ResourceFolderType.LAYOUT && !tag.equals(VIEW_FRAGMENT)) {
+ mCustomViews.add(className);
+ }
+ }
- if (signature.indexOf('$') != -1) {
+ if (signature.indexOf('$') != -1 && pkg != null) {
if (className.indexOf('$') == -1 && className.indexOf('.', 1) > 0) {
boolean haveUpperCase = false;
for (int i = 0, n = pkg.length(); i < n; i++) {
@@ -209,8 +279,10 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
// class named Baz, we register *both* into the reference map.
// When generating errors we'll look for these an rip them back out if
// it looks like one of the two variations have been seen.
- signature = signature.replace('$', '/');
- mReferencedClasses.put(signature, handle);
+ if (handle != null) {
+ signature = signature.replace('$', '/');
+ mReferencedClasses.put(signature, handle);
+ }
}
}
@@ -238,10 +310,30 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
}
mReferencedClasses.remove(owner);
+ // Ignore usages of platform libraries
+ if (owner.startsWith("android/")) { //$NON-NLS-1$
+ continue;
+ }
+
String message = String.format(
"Class referenced in the manifest, %1$s, was not found in the " +
- "project or the libraries", fqcn);
+ "project or the libraries", fqcn);
Location location = handle.resolve();
+ File parentFile = location.getFile().getParentFile();
+ if (parentFile != null) {
+ String parent = parentFile.getName();
+ ResourceFolderType type = ResourceFolderType.getFolderType(parent);
+ if (type == ResourceFolderType.LAYOUT) {
+ message = String.format(
+ "Class referenced in the layout file, %1$s, was not found in "
+ + "the project or the libraries", fqcn);
+ } else if (type == ResourceFolderType.VALUES) {
+ message = String.format(
+ "Class referenced in the analytics file, %1$s, was not "
+ + "found in the project or the libraries", fqcn);
+ }
+ }
+
context.report(MISSING, location, message, null);
}
}
@@ -251,11 +343,13 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
@Override
public void checkClass(@NonNull ClassContext context, @NonNull ClassNode classNode) {
- if (!context.isFromClassLibrary()) {
+ if (!mHaveClasses && !context.isFromClassLibrary()
+ && context.getProject() == context.getMainProject()) {
mHaveClasses = true;
}
String curr = classNode.name;
if (mReferencedClasses != null && mReferencedClasses.containsKey(curr)) {
+ boolean isCustomView = mCustomViews.contains(curr);
mReferencedClasses.remove(curr);
// Ensure that the class is public, non static and has a null constructor!
@@ -299,7 +393,7 @@ public class MissingClassDetector extends LayoutDetector implements ClassScanner
}
}
- if (!hasDefaultConstructor) {
+ if (!hasDefaultConstructor && !isCustomView) {
context.report(INSTANTIATABLE, context.getLocation(classNode), String.format(
"This class should provide a default constructor (a public " +
"constructor with no arguments) (%1$s)",
diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
index 56d07e7..c4fd0a7 100644
--- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
+++ b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/RegistrationDetector.java
@@ -218,7 +218,7 @@ public class RegistrationDetector extends LayoutDetector implements ClassScanner
TAG_ACTIVITY,
TAG_SERVICE,
TAG_RECEIVER,
- TAG_PROVIDER
+ TAG_PROVIDER,
// Keep synchronized with {@link #sClasses}
};
@@ -227,7 +227,7 @@ public class RegistrationDetector extends LayoutDetector implements ClassScanner
ANDROID_APP_ACTIVITY,
ANDROID_APP_SERVICE,
ANDROID_CONTENT_BROADCAST_RECEIVER,
- ANDROID_CONTENT_CONTENT_PROVIDER
+ ANDROID_CONTENT_CONTENT_PROVIDER,
// Keep synchronized with {@link #sTags}
};
@@ -242,7 +242,7 @@ public class RegistrationDetector extends LayoutDetector implements ClassScanner
return null;
}
- /** Looks up the manifest tag a given framework class should be registered with */
+ /** Looks up the tag a given framework class should be registered with */
private static String classToTag(String className) {
for (int i = 0, n = sClasses.length; i < n; i++) {
if (sClasses[i].equals(className)) {
diff --git a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java
index 209b3d7..8f2ed47 100644
--- a/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java
+++ b/lint/libs/lint_checks/src/main/java/com/android/tools/lint/checks/TypographyDetector.java
@@ -16,6 +16,7 @@
package com.android.tools.lint.checks;
+import static com.android.SdkConstants.ATTR_NAME;
import static com.android.SdkConstants.TAG_STRING;
import static com.android.SdkConstants.TAG_STRING_ARRAY;
@@ -267,7 +268,7 @@ public class TypographyDetector extends ResourceXmlDetector {
!Character.isWhitespace(matcher.group(2).charAt(0)) &&
Character.isWhitespace(matcher.group(1).charAt(
matcher.group(1).length() - 1));
- if (!isNegativeNumber) {
+ if (!isNegativeNumber && !isAnalyticsTrackingId((Element) element)) {
context.report(DASHES, element, context.getLocation(textNode),
EN_DASH_MESSAGE,
null);
@@ -373,6 +374,11 @@ public class TypographyDetector extends ResourceXmlDetector {
}
}
+ private static boolean isAnalyticsTrackingId(Element element) {
+ String name = element.getAttribute(ATTR_NAME);
+ return "ga_trackingId".equals(name); //$NON-NLS-1$
+ }
+
/**
* An object describing a single edit to be made. The offset points to a
* location to start editing; the length is the number of characters to