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:
@@ -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>
|
||||
@@ -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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
37
packages/overlays/tests/Android.bp
Normal file
37
packages/overlays/tests/Android.bp
Normal 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"],
|
||||
}
|
||||
37
packages/overlays/tests/AndroidManifest.xml
Normal file
37
packages/overlays/tests/AndroidManifest.xml
Normal 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>
|
||||
29
packages/overlays/tests/AndroidTest.xml
Normal file
29
packages/overlays/tests/AndroidTest.xml
Normal 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>
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user