| /* |
| * 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.lint.checks; |
| |
| import com.android.tools.lint.detector.api.Detector; |
| |
| public class ManifestResourceDetectorTest extends AbstractCheckTest { |
| @Override |
| protected Detector getDetector() { |
| return new ManifestResourceDetector(); |
| } |
| |
| public void test() throws Exception { |
| assertEquals( |
| "No warnings.", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"foo.bar2\"\n" |
| + " android:versionCode=\"1\"\n" |
| + " android:versionName=\"1.0\" >\n" |
| + "\n" |
| + " <uses-sdk android:minSdkVersion=\"14\" />\n" |
| + "\n" |
| + " <application\n" |
| + " android:icon=\"@drawable/ic_launcher\"\n" |
| + " android:label=\"@string/app_name\" >\n" |
| + " <receiver android:enabled=\"@bool/has_honeycomb\" android:name=\"com.google.android.apps.iosched.appwidget.ScheduleWidgetProvider\">\n" |
| + " <intent-filter>\n" |
| + " <action android:name=\"android.appwidget.action.APPWIDGET_UPDATE\"/>\n" |
| + " </intent-filter>\n" |
| + " <!-- This specifies the widget provider info -->\n" |
| + " <meta-data android:name=\"android.appwidget.provider\" android:resource=\"@xml/widgetinfo\"/>\n" |
| + " </receiver>\n" |
| + " </application>\n" |
| + "\n" |
| + "</manifest>"), |
| xml( |
| "res/values/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"app_name\">App Name (Default)</string>\n" |
| + " <bool name=\"has_honeycomb\">false</bool>" |
| + "</resources>"), |
| xml( |
| "res/values-v11/values.xml", |
| "" |
| + "<resources>\n" |
| + " <bool name=\"has_honeycomb\">true</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/values-en-rUS/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"app_name\">App Name (English)</string>\n" |
| + "</resources>"), |
| xml( |
| "res/values-xlarge/values.xml", |
| "" |
| + "<resources>\n" |
| + " <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n" |
| + "</resources>"))); |
| } |
| |
| public void testInvalidManifestReference() throws Exception { |
| assertEquals( |
| "" |
| + "AndroidManifest.xml:6: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in mcc [ManifestResource]\n" |
| + " <application android:fullBackupContent=\"@xml/backup\">\n" |
| + " ~~~~~~~~~~~\n" |
| + "AndroidManifest.xml:8: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in en-rUS [ManifestResource]\n" |
| + " android:process=\"@string/location_process\"\n" |
| + " ~~~~~~~~~~~~~~~~~~~~~~~~\n" |
| + "AndroidManifest.xml:9: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in watch-v20 [ManifestResource]\n" |
| + " android:enabled=\"@bool/enable_wearable_location_service\">\n" |
| + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" |
| + "3 errors, 0 warnings\n", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"test.pkg\">\n" |
| + " <uses-sdk android:minSdkVersion=\"14\" />\n" |
| + "\n" |
| + " <application android:fullBackupContent=\"@xml/backup\">\n" |
| + " <service\n" // missing stuff here, not important for test |
| + " android:process=\"@string/location_process\"\n" |
| + " android:enabled=\"@bool/enable_wearable_location_service\">\n" |
| + " </service>" |
| + " </application>\n" |
| + "\n" |
| + "</manifest>"), |
| xml( |
| "res/values/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"location_process\">Location Process</string>\n" |
| + "</resources>"), |
| xml( |
| "res/values/bools.xml", |
| "" |
| + "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" |
| + " <bool name=\"enable_wearable_location_service\">true</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/values-en-rUS/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"location_process\">Location Process (English)</string>\n" |
| + "</resources>"), |
| xml( |
| "res/values-watch/bools.xml", |
| "" |
| + "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" |
| + " <bool name=\"enable_wearable_location_service\">false</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/xml/backup.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<full-backup-content>\n" |
| + " <include domain=\"file\" path=\"dd\"/>\n" |
| + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n" |
| + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n" |
| + "</full-backup-content>"), |
| xml( |
| "res/xml-mcc/backup.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<full-backup-content>\n" |
| + " <include domain=\"file\" path=\"mcc\"/>\n" |
| + "</full-backup-content>"))); |
| } |
| |
| public void testBatchAnalysis() throws Exception { |
| assertEquals( |
| "" |
| + "AndroidManifest.xml:11: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in mcc [ManifestResource]\n" |
| + " android:fullBackupContent=\"@xml/backup\"\n" |
| + " ~~~~~~~~~~~\n" |
| + " res/xml-mcc/backup.xml:2: This value will not be used\n" |
| + "AndroidManifest.xml:21: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in en-rUS [ManifestResource]\n" |
| + " android:process=\"@string/location_process\"\n" |
| + " ~~~~~~~~~~~~~~~~~~~~~~~~\n" |
| + " res/values-en-rUS/values.xml:2: This value will not be used\n" |
| + "AndroidManifest.xml:22: Error: Resources referenced from the manifest cannot vary by configuration (except for version qualifiers, e.g. -v21). Found variation in watch [ManifestResource]\n" |
| + " android:enabled=\"@bool/enable_wearable_location_service\">\n" |
| + " ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~\n" |
| + " res/values-watch/bools.xml:2: This value will not be used\n" |
| + "3 errors, 0 warnings\n", |
| lintProject( |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"foo.bar2\"\n" |
| + " android:versionCode=\"1\"\n" |
| + " android:versionName=\"1.0\" >\n" |
| + "\n" |
| + " <uses-sdk android:minSdkVersion=\"14\" />\n" |
| + "\n" |
| + " <application\n" |
| + " android:icon=\"@drawable/ic_launcher\"\n" |
| + " android:fullBackupContent=\"@xml/backup\"\n" |
| + " android:label=\"@string/app_name\" >\n" |
| + " <receiver android:enabled=\"@bool/has_honeycomb\" android:name=\"com.google.android.apps.iosched.appwidget.ScheduleWidgetProvider\">\n" |
| + " <intent-filter>\n" |
| + " <action android:name=\"android.appwidget.action.APPWIDGET_UPDATE\"/>\n" |
| + " </intent-filter>\n" |
| + " <!-- This specifies the widget provider info -->\n" |
| + " <meta-data android:name=\"android.appwidget.provider\" android:resource=\"@xml/widgetinfo\"/>\n" |
| + " </receiver>\n" |
| + " <service\n" |
| + " android:process=\"@string/location_process\"\n" |
| + " android:enabled=\"@bool/enable_wearable_location_service\">\n" |
| + " </service>" |
| + " </application>\n" |
| + "\n" |
| + "</manifest>"), |
| xml( |
| "res/values/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"location_process\">Location Process</string>\n" |
| + " <string name=\"app_name\">App Name (Default)</string>\n" |
| + " <bool name=\"has_honeycomb\">false</bool>" |
| + "</resources>"), |
| xml( |
| "res/values/bools.xml", |
| "" |
| + "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" |
| + " <bool name=\"enable_wearable_location_service\">true</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/values-en-rUS/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"location_process\">Location Process (English)</string>\n" |
| + " <string name=\"app_name\">App Name (English)</string>\n" |
| + "</resources>"), |
| xml( |
| "res/values-watch/bools.xml", |
| "" |
| + "<resources xmlns:xliff=\"urn:oasis:names:tc:xliff:document:1.2\">\n" |
| + " <bool name=\"enable_wearable_location_service\">false</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/values-v11/values.xml", |
| "" |
| + "<resources>\n" |
| + " <bool name=\"has_honeycomb\">true</bool>\n" |
| + "</resources>"), |
| xml( |
| "res/values-xlarge/values.xml", |
| "" |
| + "<resources>\n" |
| + " <dimen name=\"activity_horizontal_margin\">16dp</dimen>\n" |
| + "</resources>"), |
| xml( |
| "res/xml/backup.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<full-backup-content>\n" |
| + " <include domain=\"file\" path=\"dd\"/>\n" |
| + " <exclude domain=\"file\" path=\"dd/fo3o.txt\"/>\n" |
| + " <exclude domain=\"file\" path=\"dd/ss/foo.txt\"/>\n" |
| + "</full-backup-content>"), |
| xml( |
| "res/xml-mcc/backup.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<full-backup-content>\n" |
| + " <include domain=\"file\" path=\"mcc\"/>\n" |
| + "</full-backup-content>"))); |
| } |
| |
| public void testAllowPermissionNameLocalizations() throws Exception { |
| assertEquals( |
| "No warnings.", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"foo.bar2\"\n" |
| + " android:versionCode=\"1\"\n" |
| + " android:versionName=\"1.0\" >\n" |
| + "\n" |
| + " <permission-group android:name=\"android.permission-group.CONTACTS\"\n" |
| + " android:icon=\"@drawable/perm_group_contacts\"\n" |
| + " android:label=\"@string/permgrouplab_contacts\"\n" |
| + " android:description=\"@string/permgroupdesc_contacts\"\n" |
| + " android:priority=\"100\" />\n" |
| + "\n" |
| + " <permission android:name=\"android.permission.READ_CONTACTS\"\n" |
| + " android:permissionGroup=\"android.permission-group.CONTACTS\"\n" |
| + " android:label=\"@string/permlab_readContacts\"\n" |
| + " android:description=\"@string/permdesc_readContacts\"\n" |
| + " android:protectionLevel=\"dangerous\" />" |
| + "</manifest>"), |
| xml( |
| "res/values/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"permgrouplab_contacts\">Contacts</string>\n" |
| + " <string name=\"permgroupdesc_contacts\">access your contacts</string>\n" |
| + " <string name=\"permlab_readContacts\">read your contacts</string>\n" |
| + " <string name=\"permdesc_readContacts\">Allows the app to...</string>" |
| + "</resources>"), |
| xml( |
| "res/values-nb/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"permgrouplab_contacts\">\"Kontakter\"</string>\n" |
| + " <string name=\"permgroupdesc_contacts\">\"se kontaktene dine\"</string>\n" |
| + " <string name=\"permlab_readContacts\">\"lese kontaktene dine\"</string>\n" |
| + " <string name=\"permdesc_readContacts\">\"Lar appen lese...</string>" |
| + "</resources>"))); |
| } |
| |
| public void testAllowMetadataResources() throws Exception { |
| // Regression test for https://code.google.com/p/android/issues/detail?id=216279 |
| // <meta-data> elements are free to reference resources as they see fit. |
| assertEquals( |
| "No warnings.", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " xmlns:tools=\"http://schemas.android.com/tools\"\n" |
| + " package=\"foo.bar2\">\n" |
| + " <application>\n" |
| + " <receiver android:name=\".MyReceiver\"\n" |
| + " android:label=\"@string/app_name\">\n" |
| + " <meta-data\n" |
| + " android:name=\"com.example.sdk.ApplicationId\"\n" |
| + " android:value=\"@string/app_id\"/>\n" |
| + " <meta-data\n" |
| + " android:name=\"com.android.systemui.action_assist_icon\"\n" |
| + " android:resource=\"@mipmap/ic_launcher\"/>\n" |
| + " </receiver>\n" |
| + " </application>\n" |
| + "</manifest>\n"), |
| xml( |
| "res/values/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"app_id\">Id</string>\n" |
| + " <mipmap name=\"ic_launcher\">@mipmap/other</mipmap>\n" |
| + "</resources>"), |
| xml( |
| "res/values-nb/values.xml", |
| "" |
| + "<resources>\n" |
| + " <string name=\"app_id\">\"Id\"</string>\n" |
| + "</resources>"), |
| xml( |
| "res/values-hdpi/values.xml", |
| "" |
| + "<resources>\n" |
| + " <mipmap name=\"ic_launcher\">@mipmap/other</mipmap>\n" |
| + "</resources>"))); |
| } |
| |
| public void testRoundIcon() throws Exception { |
| // Allow round icons in density folders |
| // Regression test for https://code.google.com/p/android/issues/detail?id=225711 |
| assertEquals( |
| "No warnings.", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"webp.test.tools.android.com.myapplication\" >\n" |
| + " <application\n" |
| + " android:roundIcon=\"@mipmap/round_icon\" />\n" |
| + "</manifest>"), |
| image("res/mipmap-mdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-hdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-xhdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-xxhdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-xxxhdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF))); |
| } |
| |
| public void testAnyDensity() throws Exception { |
| // Allow round icons in density folders |
| assertEquals( |
| "No warnings.", |
| lintProjectIncrementally( |
| "AndroidManifest.xml", |
| xml( |
| "AndroidManifest.xml", |
| "" |
| + "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n" |
| + "<manifest xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" |
| + " package=\"webp.test.tools.android.com.myapplication\" >\n" |
| + " <application\n" |
| + " android:randomAttribute=\"@mipmap/round_icon\" />\n" |
| + "</manifest>"), |
| image("res/mipmap-mdpi/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-hdpi-v4/round_icon.png", 472, 290).fill(0xFFFFFFFF), |
| image("res/mipmap-v21/round_icon.png", 472, 290).fill(0xFFFFFFFF))); |
| } |
| } |