Add test target to f/b/packages/overlays.
- Added IconPackOverlayTest to ensure that there icon pack overlays do not
get out of sync with the underlying drawables in each apk.
- Remove overlayable_icons_test.xml from each app since this test makes
them no longer necessary
- Add apcts target OverlayTests.apk - TODO add this to presubmit config
for settings and systemui
Bug: 134566901
Test: atest IconPackOverlayTest
Change-Id: I99243db4839ac41dad01abf9f83ebda757dbbb77
diff --git a/core/tests/coretests/res/values/overlayable_icons_test.xml b/core/tests/coretests/res/values/overlayable_icons_test.xml
deleted file mode 100644
index 6503f3e..0000000
--- a/core/tests/coretests/res/values/overlayable_icons_test.xml
+++ /dev/null
@@ -1,86 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<resources>
- <!-- overlayable_icons references all of the drawables in this package
- that are being overlayed by resource overlays. If you remove/rename
- any of these resources, you must also change the resource overlay icons.-->
- <array name="overlayable_icons">
- <item>@*android:drawable/ic_audio_alarm</item>
- <item>@*android:drawable/ic_audio_alarm_mute</item>
- <item>@*android:drawable/ic_battery_80_24dp</item>
- <item>@*android:drawable/ic_bluetooth_share_icon</item>
- <item>@*android:drawable/ic_bt_headphones_a2dp</item>
- <item>@*android:drawable/ic_bt_headset_hfp</item>
- <item>@*android:drawable/ic_bt_hearing_aid</item>
- <item>@*android:drawable/ic_bt_laptop</item>
- <item>@*android:drawable/ic_bt_misc_hid</item>
- <item>@*android:drawable/ic_bt_network_pan</item>
- <item>@*android:drawable/ic_bt_pointing_hid</item>
- <item>@*android:drawable/ic_corp_badge</item>
- <item>@*android:drawable/ic_expand_more</item>
- <item>@*android:drawable/ic_faster_emergency</item>
- <item>@*android:drawable/ic_file_copy</item>
- <item>@*android:drawable/ic_lock</item>
- <item>@*android:drawable/ic_lock_bugreport</item>
- <item>@*android:drawable/ic_lock_open</item>
- <item>@*android:drawable/ic_lock_power_off</item>
- <item>@*android:drawable/ic_lockscreen_ime</item>
- <item>@*android:drawable/ic_mode_edit</item>
- <item>@*android:drawable/ic_notifications_alerted</item>
- <item>@*android:drawable/ic_phone</item>
- <item>@*android:drawable/ic_qs_airplane</item>
- <item>@*android:drawable/ic_qs_auto_rotate</item>
- <item>@*android:drawable/ic_qs_battery_saver</item>
- <item>@*android:drawable/ic_qs_bluetooth</item>
- <item>@*android:drawable/ic_qs_dnd</item>
- <item>@*android:drawable/ic_qs_flashlight</item>
- <item>@*android:drawable/ic_qs_night_display_on</item>
- <item>@*android:drawable/ic_qs_ui_mode_night</item>
- <item>@*android:drawable/ic_restart</item>
- <item>@*android:drawable/ic_screenshot</item>
- <item>@*android:drawable/ic_settings_bluetooth</item>
- <item>@*android:drawable/ic_signal_cellular_0_4_bar</item>
- <item>@*android:drawable/ic_signal_cellular_0_5_bar</item>
- <item>@*android:drawable/ic_signal_cellular_1_4_bar</item>
- <item>@*android:drawable/ic_signal_cellular_1_5_bar</item>
- <item>@*android:drawable/ic_signal_cellular_2_4_bar</item>
- <item>@*android:drawable/ic_signal_cellular_2_5_bar</item>
- <item>@*android:drawable/ic_signal_cellular_3_4_bar</item>
- <item>@*android:drawable/ic_signal_cellular_3_5_bar</item>
- <item>@*android:drawable/ic_signal_cellular_4_4_bar</item>
- <item>@*android:drawable/ic_signal_cellular_4_5_bar</item>
- <item>@*android:drawable/ic_signal_cellular_5_5_bar</item>
- <item>@*android:drawable/ic_signal_location</item>
- <item>@*android:drawable/ic_wifi_signal_0</item>
- <item>@*android:drawable/ic_wifi_signal_1</item>
- <item>@*android:drawable/ic_wifi_signal_2</item>
- <item>@*android:drawable/ic_wifi_signal_3</item>
- <item>@*android:drawable/ic_wifi_signal_4</item>
- <item>@*android:drawable/perm_group_activity_recognition</item>
- <item>@*android:drawable/perm_group_aural</item>
- <item>@*android:drawable/perm_group_calendar</item>
- <item>@*android:drawable/perm_group_call_log</item>
- <item>@*android:drawable/perm_group_camera</item>
- <item>@*android:drawable/perm_group_contacts</item>
- <item>@*android:drawable/perm_group_location</item>
- <item>@*android:drawable/perm_group_microphone</item>
- <item>@*android:drawable/perm_group_phone_calls</item>
- <item>@*android:drawable/perm_group_sensors</item>
- <item>@*android:drawable/perm_group_sms</item>
- <item>@*android:drawable/perm_group_storage</item>
- <item>@*android:drawable/perm_group_visual</item>
- </array>
-</resources>
diff --git a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml b/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
deleted file mode 100644
index 24cd8cb..0000000
--- a/packages/SystemUI/tests/res/values/overlayable_icons_test.xml
+++ /dev/null
@@ -1,72 +0,0 @@
-<!--
- Copyright (C) 2019 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.
--->
-<resources>
- <!-- overlayable_icons references all of the drawables in this package
- that are being overlayed by resource overlays. If you remove/rename
- any of these resources, you must also change the resource overlay icons.-->
- <array name="overlayable_icons">
- <item>@drawable/ic_alarm</item>
- <item>@drawable/ic_alarm_dim</item>
- <item>@drawable/ic_arrow_back</item>
- <item>@drawable/ic_bluetooth_connected</item>
- <item>@drawable/ic_brightness_thumb</item>
- <item>@drawable/ic_camera</item>
- <item>@drawable/ic_cast</item>
- <item>@drawable/ic_cast_connected</item>
- <item>@drawable/ic_close_white</item>
- <item>@drawable/ic_data_saver</item>
- <item>@drawable/ic_data_saver_off</item>
- <item>@drawable/ic_drag_handle</item>
- <item>@drawable/ic_headset</item>
- <item>@drawable/ic_headset_mic</item>
- <item>@drawable/ic_hotspot</item>
- <item>@drawable/ic_info</item>
- <item>@drawable/ic_info_outline</item>
- <item>@drawable/ic_invert_colors</item>
- <item>@drawable/ic_location</item>
- <item>@drawable/ic_lockscreen_ime</item>
- <item>@drawable/ic_notifications_alert</item>
- <item>@drawable/ic_notifications_silence</item>
- <item>@drawable/ic_power_low</item>
- <item>@drawable/ic_power_saver</item>
- <item>@drawable/ic_qs_bluetooth_connecting</item>
- <item>@drawable/ic_qs_cancel</item>
- <item>@drawable/ic_qs_no_sim</item>
- <item>@drawable/ic_qs_wifi_0</item>
- <item>@drawable/ic_qs_wifi_1</item>
- <item>@drawable/ic_qs_wifi_2</item>
- <item>@drawable/ic_qs_wifi_3</item>
- <item>@drawable/ic_qs_wifi_4</item>
- <item>@drawable/ic_qs_wifi_disconnected</item>
- <item>@drawable/ic_screenshot_delete</item>
- <item>@drawable/ic_settings</item>
- <item>@drawable/ic_swap_vert</item>
- <item>@drawable/ic_tune_black_16dp</item>
- <item>@drawable/ic_volume_alarm_mute</item>
- <item>@drawable/ic_volume_bt_sco</item>
- <item>@drawable/ic_volume_media</item>
- <item>@drawable/ic_volume_media_mute</item>
- <item>@drawable/ic_volume_odi_captions</item>
- <item>@drawable/ic_volume_odi_captions_disabled</item>
- <item>@drawable/ic_volume_ringer</item>
- <item>@drawable/ic_volume_ringer_mute</item>
- <item>@drawable/ic_volume_ringer_vibrate</item>
- <item>@drawable/ic_volume_voice</item>
- <item>@drawable/stat_sys_managed_profile_status</item>
- <item>@drawable/stat_sys_mic_none</item>
- <item>@drawable/stat_sys_vpn_ic</item>
- </array>
-</resources>
diff --git a/packages/SystemUI/tests/src/com/android/systemui/IconPackOverlayTest.java b/packages/SystemUI/tests/src/com/android/systemui/IconPackOverlayTest.java
deleted file mode 100644
index ccc9afc..0000000
--- a/packages/SystemUI/tests/src/com/android/systemui/IconPackOverlayTest.java
+++ /dev/null
@@ -1,192 +0,0 @@
-/*
- * Copyright (C) 2019 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.systemui;
-
-import static junit.framework.Assert.fail;
-
-import static org.junit.Assert.assertEquals;
-
-import android.annotation.DrawableRes;
-import android.content.pm.PackageManager;
-import android.content.res.Resources;
-import android.content.res.TypedArray;
-import android.content.res.XmlResourceParser;
-import android.text.TextUtils;
-import android.util.TypedValue;
-
-import androidx.test.filters.MediumTest;
-import androidx.test.runner.AndroidJUnit4;
-
-import com.android.internal.util.XmlUtils;
-
-import org.junit.After;
-import org.junit.Before;
-import org.junit.Test;
-import org.junit.runner.RunWith;
-import org.xmlpull.v1.XmlPullParserException;
-
-import java.io.IOException;
-
-@RunWith(AndroidJUnit4.class)
-@MediumTest
-public class IconPackOverlayTest extends SysuiTestCase {
-
- private static final String[] ICON_PACK_OVERLAY_PACKAGES = {
- "com.android.theme.icon_pack.circular.systemui",
- "com.android.theme.icon_pack.rounded.systemui",
- "com.android.theme.icon_pack.filled.systemui",
- };
-
- private static final int[] VECTOR_ATTRIBUTES = {
- android.R.attr.tint,
- android.R.attr.height,
- android.R.attr.width,
- android.R.attr.alpha,
- android.R.attr.autoMirrored,
- };
-
- private final TypedValue mTargetTypedValue = new TypedValue();
- private final TypedValue mOverlayTypedValue = new TypedValue();
-
- private Resources mResources;
- private TypedArray mOverlayableIcons;
-
- @Before
- public void setup() {
- mResources = mContext.getResources();
- mOverlayableIcons = mResources.obtainTypedArray(R.array.overlayable_icons);
- }
-
- @After
- public void teardown() {
- mOverlayableIcons.recycle();
- }
-
- /**
- * Ensure that all icons contained in overlayable_icons_test.xml exist in all 3 overlay icon
- * packs for systemui. This test fails if you remove or rename an overlaid icon. If so,
- * make the same change to the corresponding drawables in {@link #ICON_PACK_OVERLAY_PACKAGES}.
- */
- @Test
- public void testIconPack_containAllOverlayedIcons() {
- StringBuilder errors = new StringBuilder();
-
- for (String overlayPackage : ICON_PACK_OVERLAY_PACKAGES) {
- Resources overlayResources;
- try {
- overlayResources = mContext.getPackageManager()
- .getResourcesForApplication(overlayPackage);
- } catch (PackageManager.NameNotFoundException e) {
- continue; // No need to test overlay resources if apk is not on the system.
- }
-
- for (int i = 0; i < mOverlayableIcons.length(); i++) {
- int sysuiRid = mOverlayableIcons.getResourceId(i, 0);
- String sysuiResourceName = mResources.getResourceName(sysuiRid);
- String overlayResourceName = sysuiResourceName
- .replace(mContext.getPackageName(), overlayPackage);
- if (overlayResources.getIdentifier(overlayResourceName, null, null)
- == Resources.ID_NULL) {
- errors.append(String.format("[%s] is not contained in overlay package [%s]",
- overlayResourceName, overlayPackage));
- }
- }
- }
-
- if (!TextUtils.isEmpty(errors)) {
- fail(errors.toString());
- }
- }
-
- /**
- * Ensures that all overlay icons have the same values for {@link #VECTOR_ATTRIBUTES} as the
- * underlying drawable in systemui. To fix this test, make the attribute change to all of the
- * corresponding drawables in {@link #ICON_PACK_OVERLAY_PACKAGES}.
- */
- @Test
- public void testIconPacks_haveEqualVectorDrawableAttributes() {
- StringBuilder errors = new StringBuilder();
-
- for (String overlayPackage : ICON_PACK_OVERLAY_PACKAGES) {
- Resources overlayResources;
- try {
- overlayResources = mContext.getPackageManager()
- .getResourcesForApplication(overlayPackage);
- } catch (PackageManager.NameNotFoundException e) {
- continue; // No need to test overlay resources if apk is not on the system.
- }
-
- for (int i = 0; i < mOverlayableIcons.length(); i++) {
- int sysuiRid = mOverlayableIcons.getResourceId(i, 0);
- String sysuiResourceName = mResources.getResourceName(sysuiRid);
- TypedArray sysuiAttrs = getAVDAttributes(mResources, sysuiRid);
- if (sysuiAttrs == null) {
- errors.append(String.format("[%s] does not exist or is not a valid AVD.",
- sysuiResourceName));
- continue;
- }
-
- String overlayResourceName = sysuiResourceName
- .replace(mContext.getPackageName(), overlayPackage);
- int overlayRid = overlayResources.getIdentifier(overlayResourceName, null, null);
- TypedArray overlayAttrs = getAVDAttributes(overlayResources, overlayRid);
- if (overlayAttrs == null) {
- errors.append(String.format("[%s] does not exist or is not a valid AVD.",
- overlayResourceName));
- continue;
- }
-
- if (!attributesEquals(sysuiAttrs, overlayAttrs)) {
- errors.append(String.format("[%s] AVD attributes do not match [%s]\n",
- sysuiResourceName, overlayResourceName));
- }
- sysuiAttrs.recycle();
- overlayAttrs.recycle();
- }
- }
-
- if (!TextUtils.isEmpty(errors)) {
- fail(errors.toString());
- }
- }
-
- private TypedArray getAVDAttributes(Resources resources, @DrawableRes int rid) {
- try {
- XmlResourceParser parser = resources.getXml(rid);
- XmlUtils.nextElement(parser);
- return resources.obtainAttributes(parser, VECTOR_ATTRIBUTES);
- } catch (XmlPullParserException | IOException | Resources.NotFoundException e) {
- return null;
- }
- }
-
- private boolean attributesEquals(TypedArray target, TypedArray overlay) {
- assertEquals(target.length(), overlay.length());
- for (int i = 0; i < target.length(); i++) {
- target.getValue(i, mTargetTypedValue);
- overlay.getValue(i, mOverlayTypedValue);
- if (!attributesEquals(mTargetTypedValue, mOverlayTypedValue)) {
- return false;
- }
- }
- return true;
- }
-
- private boolean attributesEquals(TypedValue target, TypedValue overlay) {
- return target.type == overlay.type && target.data == overlay.data;
- }
-}
diff --git a/packages/overlays/tests/Android.bp b/packages/overlays/tests/Android.bp
new file mode 100644
index 0000000..343367a
--- /dev/null
+++ b/packages/overlays/tests/Android.bp
@@ -0,0 +1,37 @@
+// Copyright (C) 2019 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.
+
+android_test {
+ name: "OverlayTests",
+
+ certificate: "platform",
+
+ srcs: ["src/**/*.java"],
+
+ libs: [
+ "android.test.runner",
+ "android.test.base",
+ ],
+
+ platform_apis: true,
+
+ static_libs: [
+ "androidx.test.rules",
+ "androidx.test.espresso.core",
+ "mockito-target-minus-junit4",
+ "truth-prebuilt",
+ ],
+
+ dxflags: ["--multi-dex"],
+}
diff --git a/packages/overlays/tests/AndroidManifest.xml b/packages/overlays/tests/AndroidManifest.xml
new file mode 100644
index 0000000..6ebc555
--- /dev/null
+++ b/packages/overlays/tests/AndroidManifest.xml
@@ -0,0 +1,37 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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="com.android.overlays">
+
+ <uses-permission android:name="android.permission.INTERACT_ACROSS_USERS_FULL" />
+ <uses-permission android:name="android.permission.MANAGE_USERS" />
+ <uses-permission android:name="android.permission.MANAGE_NETWORK_POLICY"/>
+ <uses-permission android:name="android.permission.SET_TIME_ZONE" />
+ <uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
+ <uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
+ <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" />
+
+ <application>
+ <uses-library android:name="android.test.runner" />
+ </application>
+
+ <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner"
+ android:targetPackage="com.android.systemui"
+ android:label="Tests for Overlays">
+ </instrumentation>
+</manifest>
diff --git a/packages/overlays/tests/AndroidTest.xml b/packages/overlays/tests/AndroidTest.xml
new file mode 100644
index 0000000..8843d62
--- /dev/null
+++ b/packages/overlays/tests/AndroidTest.xml
@@ -0,0 +1,29 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2019 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
+ -->
+<configuration description="Runs Tests for Overlays.">
+ <target_preparer class="com.android.tradefed.targetprep.TestAppInstallSetup">
+ <option name="test-file-name" value="OverlayTests.apk" />
+ </target_preparer>
+
+ <option name="test-suite-tag" value="apct" />
+ <option name="test-tag" value="OverlayTests" />
+ <test class="com.android.tradefed.testtype.AndroidJUnitTest" >
+ <option name="package" value="com.android.overlays" />
+ <option name="runner" value="androidx.test.runner.AndroidJUnitRunner" />
+ <option name="hidden-api-checks" value="false"/>
+ </test>
+</configuration>
diff --git a/packages/overlays/tests/src/com/android/theme/icon/IconPackOverlayTest.java b/packages/overlays/tests/src/com/android/theme/icon/IconPackOverlayTest.java
new file mode 100644
index 0000000..6bc56ba
--- /dev/null
+++ b/packages/overlays/tests/src/com/android/theme/icon/IconPackOverlayTest.java
@@ -0,0 +1,293 @@
+/*
+ * Copyright (C) 2019 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.theme.icon;
+
+import static junit.framework.Assert.fail;
+
+import static org.junit.Assert.assertEquals;
+
+import android.annotation.DrawableRes;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.res.Resources;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.text.TextUtils;
+import android.util.TypedValue;
+
+import androidx.test.InstrumentationRegistry;
+import androidx.test.filters.MediumTest;
+import androidx.test.runner.AndroidJUnit4;
+
+import com.android.internal.util.XmlUtils;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.util.ArrayList;
+import java.util.Enumeration;
+import java.util.List;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+@RunWith(AndroidJUnit4.class)
+@MediumTest
+public class IconPackOverlayTest {
+ private static final String SYSTEMUI_PACKAGE = "com.android.systemui";
+ private static final String[] SYSTEMUI_ICON_PACK_OVERLAY_PACKAGES = {
+ "com.android.theme.icon_pack.circular.systemui",
+ "com.android.theme.icon_pack.rounded.systemui",
+ "com.android.theme.icon_pack.filled.systemui",
+ };
+ private static final String ANDROID_PACKAGE = "android";
+ private static final String[] ANDROID_ICON_PACK_OVERLAY_PACKAGES = {
+ "com.android.theme.icon_pack.circular.android",
+ "com.android.theme.icon_pack.rounded.android",
+ "com.android.theme.icon_pack.filled.android",
+ };
+ private static final String SETTINGS_PACKAGE = "com.android.settings";
+ private static final String[] SETTINGS_ICON_PACK_OVERLAY_PACKAGES = {
+ "com.android.theme.icon_pack.circular.settings",
+ "com.android.theme.icon_pack.rounded.settings",
+ "com.android.theme.icon_pack.filled.settings",
+ };
+
+ private static final int[] VECTOR_ATTRIBUTES = {
+ android.R.attr.tint,
+ android.R.attr.height,
+ android.R.attr.width,
+ android.R.attr.alpha,
+ android.R.attr.autoMirrored,
+ };
+
+ private final TypedValue mTargetTypedValue = new TypedValue();
+ private final TypedValue mOverlayTypedValue = new TypedValue();
+ private Context mContext;
+
+ @Before
+ public void setup() {
+ mContext = InstrumentationRegistry.getContext();
+ }
+
+ /**
+ * Ensure that drawable icons in icon packs targeting android have corresponding underlying
+ * drawables in android. This test fails if you remove/rename an overlaid icon in android.
+ * If so, make the same change to the corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testAndroidFramework_containsAllOverlayedIcons() {
+ containsAllOverlayedIcons(ANDROID_PACKAGE, ANDROID_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ /**
+ * Ensure that drawable icons in icon packs targeting settings have corresponding underlying
+ * drawables in settings. This test fails if you remove/rename an overlaid icon in settings.
+ * If so, make the same change to the corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testSettings_containsAllOverlayedIcons() {
+ containsAllOverlayedIcons(SETTINGS_PACKAGE, SETTINGS_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ /**
+ * Ensure that drawable icons in icon packs targeting systemui have corresponding underlying
+ * drawables in systemui. This test fails if you remove/rename an overlaid icon in systemui.
+ * If so, make the same change to the corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testSystemUI_containAllOverlayedIcons() {
+ containsAllOverlayedIcons(SYSTEMUI_PACKAGE, SYSTEMUI_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ /**
+ * Ensures that all overlay icons have the same values for {@link #VECTOR_ATTRIBUTES} as the
+ * underlying drawable in android. To fix this test, make the attribute change to all of the
+ * corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testAndroidFramework_hasEqualVectorDrawableAttributes() {
+ hasEqualVectorDrawableAttributes(ANDROID_PACKAGE, ANDROID_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ /**
+ * Ensures that all overlay icons have the same values for {@link #VECTOR_ATTRIBUTES} as the
+ * underlying drawable in settings. To fix this test, make the attribute change to all of the
+ * corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testSettings_hasEqualVectorDrawableAttributes() {
+ hasEqualVectorDrawableAttributes(SETTINGS_PACKAGE, SETTINGS_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ /**
+ * Ensures that all overlay icons have the same values for {@link #VECTOR_ATTRIBUTES} as the
+ * underlying drawable in systemui. To fix this test, make the attribute change to all of the
+ * corresponding drawables in the overlay packages.
+ */
+ @Test
+ public void testSystemUI_hasEqualVectorDrawableAttributes() {
+ hasEqualVectorDrawableAttributes(SYSTEMUI_PACKAGE, SYSTEMUI_ICON_PACK_OVERLAY_PACKAGES);
+ }
+
+ private void containsAllOverlayedIcons(String targetPkg, String[] overlayPkgs) {
+ final Resources targetResources;
+ try {
+ targetResources = mContext.getPackageManager()
+ .getResourcesForApplication(targetPkg);
+ } catch (PackageManager.NameNotFoundException e) {
+ return; // No need to test overlays if target package does not exist on the system.
+ }
+
+ StringBuilder errors = new StringBuilder();
+ for (String overlayPackage : overlayPkgs) {
+ final ApplicationInfo info;
+ try {
+ info = mContext.getPackageManager().getApplicationInfo(overlayPackage, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue; // No need to test overlay resources if apk is not on the system.
+ }
+ final List<String> iconPackDrawables = getDrawablesFromOverlay(info);
+ for (int i = 0; i < iconPackDrawables.size(); i++) {
+ String resourceName = iconPackDrawables.get(i);
+ int targetRid = targetResources.getIdentifier(resourceName, "drawable", targetPkg);
+ if (targetRid == Resources.ID_NULL) {
+ errors.append(String.format("[%s] is not contained in the target package [%s]",
+ resourceName, targetPkg));
+ }
+ }
+ }
+
+ if (!TextUtils.isEmpty(errors)) {
+ fail(errors.toString());
+ }
+ }
+
+ private void hasEqualVectorDrawableAttributes(String targetPkg, String[] overlayPackages) {
+ final Resources targetRes;
+ try {
+ targetRes = mContext.getPackageManager().getResourcesForApplication(targetPkg);
+ } catch (PackageManager.NameNotFoundException e) {
+ return; // No need to test overlays if target package does not exist on the system.
+ }
+
+ StringBuilder errors = new StringBuilder();
+
+ for (String overlayPkg : overlayPackages) {
+ final ApplicationInfo info;
+ try {
+ info = mContext.getPackageManager().getApplicationInfo(overlayPkg, 0);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue; // No need to test overlay resources if apk is not on the system.
+ }
+ final List<String> iconPackDrawables = getDrawablesFromOverlay(info);
+ final Resources overlayRes;
+ try {
+ overlayRes = mContext.getPackageManager().getResourcesForApplication(overlayPkg);
+ } catch (PackageManager.NameNotFoundException e) {
+ continue; // No need to test overlay resources if apk is not on the system.
+ }
+
+ for (int i = 0; i < iconPackDrawables.size(); i++) {
+ String resourceName = iconPackDrawables.get(i);
+ int targetRid = targetRes.getIdentifier(resourceName, "drawable", targetPkg);
+ int overlayRid = overlayRes.getIdentifier(resourceName, "drawable", overlayPkg);
+ TypedArray targetAttrs = getAVDAttributes(targetRes, targetRid);
+ if (targetAttrs == null) {
+ errors.append(String.format(
+ "[%s] in pkg [%s] does not exist or is not a valid vector drawable.\n",
+ resourceName, targetPkg));
+ continue;
+ }
+
+ TypedArray overlayAttrs = getAVDAttributes(overlayRes, overlayRid);
+ if (overlayAttrs == null) {
+ errors.append(String.format(
+ "[%s] in pkg [%s] does not exist or is not a valid vector drawable.\n",
+ resourceName, overlayPkg));
+ continue;
+ }
+
+ if (!attributesEquals(targetAttrs, overlayAttrs)) {
+ errors.append(String.format("[drawable/%s] in [%s] does not have the same "
+ + "attributes as the corresponding drawable from [%s]\n",
+ resourceName, targetPkg, overlayPkg));
+ }
+ targetAttrs.recycle();
+ overlayAttrs.recycle();
+ }
+ }
+
+ if (!TextUtils.isEmpty(errors)) {
+ fail(errors.toString());
+ }
+ }
+
+ private TypedArray getAVDAttributes(Resources resources, @DrawableRes int rid) {
+ try {
+ XmlResourceParser parser = resources.getXml(rid);
+ XmlUtils.nextElement(parser);
+ // Always use the the test apk theme to resolve attributes.
+ return mContext.getTheme().obtainStyledAttributes(parser, VECTOR_ATTRIBUTES, 0, 0);
+ } catch (XmlPullParserException | IOException | Resources.NotFoundException e) {
+ return null;
+ }
+ }
+
+ private boolean attributesEquals(TypedArray target, TypedArray overlay) {
+ assertEquals(target.length(), overlay.length());
+ for (int i = 0; i < target.length(); i++) {
+ target.getValue(i, mTargetTypedValue);
+ overlay.getValue(i, mOverlayTypedValue);
+ if (!attributesEquals(mTargetTypedValue, mOverlayTypedValue)) {
+ return false;
+ }
+ }
+ return true;
+ }
+
+ private static boolean attributesEquals(TypedValue target, TypedValue overlay) {
+ return target.type == overlay.type && target.data == overlay.data;
+ }
+
+ private static List<String> getDrawablesFromOverlay(ApplicationInfo applicationInfo) {
+ try {
+ final ArrayList<String> drawables = new ArrayList<>();
+ ZipFile file = new ZipFile(applicationInfo.sourceDir);
+ Enumeration<? extends ZipEntry> entries = file.entries();
+ while (entries.hasMoreElements()) {
+ ZipEntry element = entries.nextElement();
+ String name = element.getName();
+ if (name.contains("/drawable/")) {
+ name = name.substring(name.lastIndexOf('/') + 1);
+ if (name.contains(".")) {
+ name = name.substring(0, name.indexOf('.'));
+ }
+ drawables.add(name);
+ }
+ }
+ return drawables;
+ } catch (IOException e) {
+ fail(String.format("Failed to retrieve drawables from package [%s] with message [%s]",
+ applicationInfo.packageName, e.getMessage()));
+ return null;
+ }
+ }
+}