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
This commit is contained in:
Amin Shaikh
2019-07-10 16:37:53 -04:00
parent 2d6b238ea3
commit b9da8f0cfa
7 changed files with 396 additions and 350 deletions

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}
}

View File

@@ -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"],
}

View File

@@ -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>

View File

@@ -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>

View File

@@ -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;
}
}
}