aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorXin Li <delphij@google.com>2024-03-06 09:29:58 -0800
committerXin Li <delphij@google.com>2024-03-06 09:29:58 -0800
commit079b068bc74a02f8f38fc9831337fe59827e7f48 (patch)
tree9a97c5f50f426283c00d26751ed5dd76afff7663
parent9a90f5ac6830432e2641304629d9a84f29603a4c (diff)
parent7a8f5cc819db70e2645299a057c1c45a669904e6 (diff)
downloadlayoutlib-079b068bc74a02f8f38fc9831337fe59827e7f48.tar.gz
Merge Android 14 QPR2 to AOSP mainHEADmastermain
Bug: 319669529 Merged-In: I26dce9d10300386d9f9d0a8f6024514eb2e40e90 Change-Id: I1898bd8427f593690406ff61b295dbb85f0c6295
-rw-r--r--.idea/misc.xml2
-rw-r--r--Android.bp2
-rw-r--r--bridge/bridge_client/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java9
-rw-r--r--bridge/src/android/content/res/BridgeTypedArray.java2
-rw-r--r--bridge/src/android/content/res/Resources_Delegate.java2
-rw-r--r--bridge/src/android/os/NullVibratorManager.java52
-rw-r--r--bridge/src/android/permission/PermissionManager_Delegate.java2
-rw-r--r--bridge/src/android/provider/Settings_Config_Delegate.java35
-rw-r--r--bridge/src/android/util/BridgeXmlPullAttributes.java3
-rw-r--r--bridge/src/android/view/AttachInfo_Accessor.java6
-rw-r--r--bridge/src/android/view/BridgeInflater.java3
-rw-r--r--bridge/src/android/view/DisplayEventReceiver_Delegate.java4
-rw-r--r--bridge/src/android/view/LayoutInflater_Delegate.java11
-rw-r--r--bridge/src/android/view/SurfaceView.java73
-rw-r--r--bridge/src/android/view/ViewRootImpl_Accessor.java15
-rw-r--r--bridge/src/android/view/WindowManagerImpl.java18
-rw-r--r--bridge/src/android/view/accessibility/AccessibilityInteractionClient_Accessor.java27
-rw-r--r--bridge/src/android/view/accessibility/AccessibilityManager_Delegate.java49
-rw-r--r--bridge/src/com/android/layoutlib/bridge/Bridge.java2
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java40
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java5
-rw-r--r--bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java5
-rw-r--r--bridge/src/com/android/layoutlib/bridge/bars/Config.java7
-rw-r--r--bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java48
-rw-r--r--bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java59
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/Layout.java21
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java20
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java56
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java97
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java4
-rw-r--r--bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java5
-rw-r--r--bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java8
-rw-r--r--bridge/src/dalvik/system/VMRuntime_Delegate.java5
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/activity.pngbin89998 -> 88377 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.pngbin23893 -> 24116 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.pngbin26576 -> 26796 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_green.pngbin25586 -> 26231 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_orange.pngbin25703 -> 26348 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.pngbin24654 -> 24874 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.pngbin27241 -> 27462 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/allwidgets.pngbin161814 -> 157879 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.pngbin62790 -> 61523 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/animated_vector.pngbin42570 -> 42995 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.pngbin37425 -> 37864 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/asset.pngbin415465 -> 415699 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.pngbin1462 -> 1428 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/bitmap_decoder.pngbin1567 -> 1573 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.pngbin20275 -> 20502 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/dark_status_bar.pngbin0 -> 27186 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.pngbin9922 -> 8091 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.pngbin17097 -> 15384 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/four_corners.pngbin50506 -> 48696 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/light_status_bar.pngbin0 -> 26769 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.pngbin32322 -> 33105 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.pngbin22662 -> 22926 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.pngbin20498 -> 20738 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.pngbin50572 -> 51042 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.pngbin33933 -> 32054 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.pngbin43315 -> 41450 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.pngbin38459 -> 36476 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/simple_activity.pngbin27254 -> 25314 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.pngbin27185 -> 25119 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/software_layer.pngbin0 -> 17458 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.pngbin35615 -> 36084 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.pngbin37923 -> 38214 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.pngbin62205 -> 62460 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.pngbin82624 -> 80981 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.pngbin43863 -> 42194 bytes
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/SoftwareTextView.java52
-rw-r--r--bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml9
-rwxr-xr-xbridge/tests/run_tests.sh3
-rw-r--r--bridge/tests/src/android/content/res/BridgeTypedArrayTest.java1
-rw-r--r--bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java78
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/AccessibilityTest.java104
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java67
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/android/DynamicRenderResourcesTest.java6
-rw-r--r--bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java161
-rw-r--r--bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java8
-rw-r--r--bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java1
-rw-r--r--common/src/com/android/tools/layoutlib/create/NativeConfig.java5
-rw-r--r--create/Android.bp2
-rw-r--r--create/src/com/android/tools/layoutlib/create/AsmGenerator.java10
-rw-r--r--create/src/com/android/tools/layoutlib/create/CreateInfo.java16
-rw-r--r--create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java2
-rw-r--r--create/src/com/android/tools/layoutlib/create/ICreateInfo.java7
-rw-r--r--create/src/com/android/tools/layoutlib/create/RemoveFinalModifierFieldClassAdapter.java46
-rw-r--r--create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java2
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java2
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java5
-rw-r--r--create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java2
-rwxr-xr-xsplit_universal_binary.sh1
-rw-r--r--validator/resources/strings.properties14
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorResult.java20
-rw-r--r--validator/src/com/android/tools/idea/validator/ValidatorUtil.java4
-rw-r--r--validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java18
95 files changed, 1081 insertions, 262 deletions
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0a1e4e0324..d47ec03f54 100644
--- a/.idea/misc.xml
+++ b/.idea/misc.xml
@@ -53,7 +53,7 @@
</value>
</option>
</component>
- <component name="ProjectRootManager" version="2" languageLevel="JDK_17" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
+ <component name="ProjectRootManager" version="2" languageLevel="JDK_11" project-jdk-name="jbr-17" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/out" />
</component>
</project> \ No newline at end of file
diff --git a/Android.bp b/Android.bp
index 76bc916b23..5fd342d124 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,7 +30,7 @@ java_genrule_host {
tools: ["layoutlib_create"],
out: ["temp_layoutlib.jar"],
srcs: [
- ":atf-prebuilt-502584086{.jar}",
+ ":atf-prebuilt-557133692{.jar}",
":core-icu4j-for-host{.jar}",
":core-libart-for-host{.jar}",
":framework-all{.jar}",
diff --git a/bridge/bridge_client/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java b/bridge/bridge_client/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
index 35ade3454c..c0e22d3619 100644
--- a/bridge/bridge_client/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
+++ b/bridge/bridge_client/src/com/android/layoutlib/bridge/intensive/util/SessionParamsBuilder.java
@@ -56,6 +56,7 @@ public class SessionParamsBuilder {
private LayoutlibCallback mLayoutlibCallback;
private int mTargetSdk;
private int mMinSdk = 0;
+ private int mSimulatedSdk = 0;
private ILayoutLog mLayoutLog;
private Map<SessionParams.Key, Object> mFlags = new HashMap<>();
private AssetRepository mAssetRepository = null;
@@ -135,6 +136,12 @@ public class SessionParamsBuilder {
}
@NonNull
+ public SessionParamsBuilder setSimulatedSdk(int simulatedSdk) {
+ mSimulatedSdk = simulatedSdk;
+ return this;
+ }
+
+ @NonNull
public SessionParamsBuilder setLayoutLog(@NonNull ILayoutLog layoutLog) {
mLayoutLog = layoutLog;
return this;
@@ -202,7 +209,7 @@ public class SessionParamsBuilder {
SessionParams params = new SessionParams(mLayoutParser, mRenderingMode, mProjectKey /* for
caching */, mConfigGenerator.getHardwareConfig(), resourceResolver, mLayoutlibCallback,
- mMinSdk, mTargetSdk, mLayoutLog);
+ mMinSdk, mTargetSdk, mLayoutLog, mSimulatedSdk);
params.setFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR, enableLayoutValidator);
params.setFlag(
RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR_IMAGE_CHECK,
diff --git a/bridge/src/android/content/res/BridgeTypedArray.java b/bridge/src/android/content/res/BridgeTypedArray.java
index da59fb79a6..ae6f538178 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -907,7 +907,7 @@ public final class BridgeTypedArray extends TypedArray {
boolean found = false;
String value = mResourceData[index].getValue();
- if (!value.isEmpty()) {
+ if (value != null && !value.isEmpty()) {
// Check if the value string is already representing an integer and return it if so.
// Resources coming from res.apk in an AAR may have flags and enums in integer form.
char c = value.charAt(0);
diff --git a/bridge/src/android/content/res/Resources_Delegate.java b/bridge/src/android/content/res/Resources_Delegate.java
index 7aa02f9464..4eb6bddeed 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -87,6 +87,8 @@ public class Resources_Delegate {
"Resources_Delegate.initSystem called twice before disposeSystem was called";
Resources resources = new Resources(Resources_Delegate.class.getClassLoader());
resources.setImpl(new ResourcesImpl(assets, metrics, config, new DisplayAdjustments()));
+ resources.getConfiguration().windowConfiguration.setMaxBounds(0, 0, metrics.widthPixels,
+ metrics.heightPixels);
sContexts.put(resources, Objects.requireNonNull(context));
sLayoutlibCallbacks.put(resources, Objects.requireNonNull(layoutlibCallback));
return Resources.mSystem = resources;
diff --git a/bridge/src/android/os/NullVibratorManager.java b/bridge/src/android/os/NullVibratorManager.java
new file mode 100644
index 0000000000..fe269c7d66
--- /dev/null
+++ b/bridge/src/android/os/NullVibratorManager.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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 android.os;
+
+public class NullVibratorManager extends VibratorManager {
+ private static final NullVibratorManager sInstance = new NullVibratorManager();
+
+ public static NullVibratorManager getInstance() {
+ return sInstance;
+ }
+
+ private NullVibratorManager() { }
+
+ @Override
+ public int[] getVibratorIds() {
+ return new int[0];
+ }
+
+ @Override
+ public Vibrator getVibrator(int vibratorId) {
+ return NullVibrator.getInstance();
+ }
+
+ @Override
+ public Vibrator getDefaultVibrator() {
+ return NullVibrator.getInstance();
+ }
+
+ @Override
+ public void vibrate(int uid, String opPkg, CombinedVibration effect, String reason,
+ VibrationAttributes attributes) { }
+
+ @Override
+ public void cancel() { }
+
+ @Override
+ public void cancel(int usageFilter) { }
+}
diff --git a/bridge/src/android/permission/PermissionManager_Delegate.java b/bridge/src/android/permission/PermissionManager_Delegate.java
index 1aad83062d..642b015a2b 100644
--- a/bridge/src/android/permission/PermissionManager_Delegate.java
+++ b/bridge/src/android/permission/PermissionManager_Delegate.java
@@ -23,7 +23,7 @@ import android.content.pm.PackageManager;
public class PermissionManager_Delegate {
@LayoutlibDelegate
- public static int checkPermission(String permission, int pid, int uid) {
+ public static int checkPermission(String permission, int pid, int uid, int deviceId) {
return PackageManager.PERMISSION_GRANTED;
}
}
diff --git a/bridge/src/android/provider/Settings_Config_Delegate.java b/bridge/src/android/provider/Settings_Config_Delegate.java
new file mode 100644
index 0000000000..ae802ddc2b
--- /dev/null
+++ b/bridge/src/android/provider/Settings_Config_Delegate.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2023 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 android.provider;
+
+import com.android.layoutlib.bridge.impl.RenderAction;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.ContentResolver;
+
+/**
+ * Delegate that provides alternative implementation for methods in {@link Settings.Config}
+ * <p/>
+ * Through the layoutlib_create tool, selected methods of DeviceConfig have been replaced by
+ * calls to methods of the same name in this delegate class.
+ */
+public class Settings_Config_Delegate {
+ @LayoutlibDelegate
+ static ContentResolver getContentResolver() {
+ return RenderAction.getCurrentContext().getContentResolver();
+ }
+}
diff --git a/bridge/src/android/util/BridgeXmlPullAttributes.java b/bridge/src/android/util/BridgeXmlPullAttributes.java
index 8a78fa3843..07bc437b12 100644
--- a/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -151,6 +151,9 @@ public class BridgeXmlPullAttributes extends XmlPullAttributes implements Resolv
@Override
public int getAttributeResourceValue(String namespace, String attribute, int defaultValue) {
String value = getAttributeValue(namespace, attribute);
+ if (value == null) {
+ return defaultValue;
+ }
return resolveResourceValue(value, defaultValue);
}
diff --git a/bridge/src/android/view/AttachInfo_Accessor.java b/bridge/src/android/view/AttachInfo_Accessor.java
index cd35899c43..b8c8f0e060 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -37,6 +37,8 @@ public class AttachInfo_Accessor {
info.mInTouchMode = false; // this is so that we can display selections.
info.mHardwareAccelerated = false;
info.mApplicationScale = 1.0f;
+ ViewRootImpl_Accessor.setChild(root, view);
+ view.assignParent(root);
view.dispatchAttachedToWindow(info, 0);
}
@@ -46,7 +48,11 @@ public class AttachInfo_Accessor {
public static void detachFromWindow(final View view) {
if (view != null) {
+ final View.AttachInfo attachInfo = view.mAttachInfo;
view.dispatchDetachedFromWindow();
+ if (attachInfo != null) {
+ ViewRootImpl_Accessor.detachFromWindow(attachInfo.mViewRootImpl);
+ }
}
}
diff --git a/bridge/src/android/view/BridgeInflater.java b/bridge/src/android/view/BridgeInflater.java
index ec8476dfb5..ad9a442da7 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -250,6 +250,9 @@ public final class BridgeInflater extends LayoutInflater {
*/
@Nullable
private View createViewFromCustomInflater(@NotNull String name, @NotNull AttributeSet attrs) {
+ if (!mLayoutlibCallback.shouldUseCustomInflater()) {
+ return null;
+ }
if (mCustomInflater == null) {
Context context = getContext();
context = getBaseContext(context);
diff --git a/bridge/src/android/view/DisplayEventReceiver_Delegate.java b/bridge/src/android/view/DisplayEventReceiver_Delegate.java
index f1d4e65725..0ca568cd86 100644
--- a/bridge/src/android/view/DisplayEventReceiver_Delegate.java
+++ b/bridge/src/android/view/DisplayEventReceiver_Delegate.java
@@ -20,6 +20,7 @@ import com.android.layoutlib.bridge.impl.DelegateManager;
import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
import android.os.MessageQueue;
+import android.view.DisplayEventReceiver.VsyncEventData;
import java.lang.ref.WeakReference;
@@ -32,7 +33,8 @@ public class DisplayEventReceiver_Delegate {
@LayoutlibDelegate
/*package*/ static long nativeInit(WeakReference<DisplayEventReceiver> receiver,
- MessageQueue messageQueue, int vsyncSource, int eventRegistration, long layerHandle) {
+ WeakReference<VsyncEventData> vsyncEventData, MessageQueue messageQueue,
+ int vsyncSource, int eventRegistration, long layerHandle) {
return sManager.addNewDelegate(new DisplayEventReceiver_Delegate());
}
diff --git a/bridge/src/android/view/LayoutInflater_Delegate.java b/bridge/src/android/view/LayoutInflater_Delegate.java
index cb446e7610..51c413d47d 100644
--- a/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -233,15 +233,4 @@ public class LayoutInflater_Delegate {
LayoutInflater.consumeChildElements(parser);
}
-
- @LayoutlibDelegate
- /* package */ static void initPrecompiledViews(LayoutInflater thisInflater) {
- initPrecompiledViews(thisInflater, false);
- }
-
- @LayoutlibDelegate
- /* package */ static void initPrecompiledViews(LayoutInflater thisInflater,
- boolean enablePrecompiledViews) {
- thisInflater.initPrecompiledViews_Original(enablePrecompiledViews);
- }
}
diff --git a/bridge/src/android/view/SurfaceView.java b/bridge/src/android/view/SurfaceView.java
index ebb2af4532..2c1d6747e7 100644
--- a/bridge/src/android/view/SurfaceView.java
+++ b/bridge/src/android/view/SurfaceView.java
@@ -18,11 +18,17 @@ package android.view;
import com.android.layoutlib.bridge.MockView;
+import android.annotation.NonNull;
+import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.Region;
+import android.os.IBinder;
import android.util.AttributeSet;
+import android.view.SurfaceControl.Transaction;
+
+import java.util.function.Consumer;
/**
* Mock version of the SurfaceView.
@@ -50,6 +56,11 @@ public class SurfaceView extends MockView {
super(context, attrs, defStyleAttr, defStyleRes);
}
+ public SurfaceView(@NonNull Context context, @Nullable AttributeSet attrs, int defStyleAttr,
+ int defStyleRes, boolean disableBackgroundLayer) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ }
+
public boolean gatherTransparentRegion(Region region) {
return false;
}
@@ -60,6 +71,14 @@ public class SurfaceView extends MockView {
public void setZOrderOnTop(boolean onTop) {
}
+ public boolean isZOrderedOnTop() {
+ return false;
+ }
+
+ public boolean setZOrderedOnTop(boolean onTop, boolean allowDynamicChange) {
+ return true;
+ }
+
public void setSecure(boolean isSecure) {
}
@@ -67,6 +86,60 @@ public class SurfaceView extends MockView {
return mSurfaceHolder;
}
+ public void setUseAlpha() {
+ }
+
+ public void setEnableSurfaceClipping(boolean enabled) {
+ }
+
+ public void setCornerRadius(float cornerRadius) {
+ }
+
+ public float getCornerRadius() {
+ return 0;
+ }
+
+ public void setSurfaceLifecycle(int lifecycleStrategy) {
+ }
+
+ public String getName() {
+ return "MockSurfaceView";
+ }
+
+ public void requestUpdateSurfacePositionAndScale() {
+ }
+
+ public @NonNull Rect getSurfaceRenderPosition() {
+ return new Rect();
+ }
+
+ public boolean isFixedSize() {
+ return true;
+ }
+
+ public void setResizeBackgroundColor(int bgColor) {
+ }
+
+ public void setResizeBackgroundColor(@NonNull SurfaceControl.Transaction t, int bgColor) {
+ }
+
+ public SurfaceControl getSurfaceControl() {
+ return null;
+ }
+
+ public @Nullable IBinder getHostToken() {
+ return null;
+ }
+
+ public void setChildSurfacePackage(@NonNull SurfaceControlViewHost.SurfacePackage p) {
+ }
+
+ public void syncNextFrame(Consumer<Transaction> t) {
+ }
+
+ public void applyTransactionToFrame(@NonNull SurfaceControl.Transaction transaction) {
+ }
+
private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
@Override
diff --git a/bridge/src/android/view/ViewRootImpl_Accessor.java b/bridge/src/android/view/ViewRootImpl_Accessor.java
index 81ffe2e324..d15952ad3d 100644
--- a/bridge/src/android/view/ViewRootImpl_Accessor.java
+++ b/bridge/src/android/view/ViewRootImpl_Accessor.java
@@ -26,8 +26,17 @@ public class ViewRootImpl_Accessor {
public static void setChild(ViewRootImpl viewRoot, View child) {
viewRoot.mView = child;
- child.assignParent(viewRoot);
- viewRoot.mWidth = child.getWidth();
- viewRoot.mHeight = child.getHeight();
+ if (child != null) {
+ viewRoot.mWidth = child.getWidth();
+ viewRoot.mHeight = child.getHeight();
+ } else {
+ viewRoot.mWidth = -1;
+ viewRoot.mHeight = -1;
+ }
+ }
+
+ public static void detachFromWindow(ViewRootImpl viewRoot) {
+ viewRoot.mAccessibilityInteractionConnectionManager.ensureNoConnection();
+ viewRoot.mAccessibilityInteractionConnectionManager.ensureNoDirectConnection();
}
}
diff --git a/bridge/src/android/view/WindowManagerImpl.java b/bridge/src/android/view/WindowManagerImpl.java
index eb1e22c736..285ca9e5e4 100644
--- a/bridge/src/android/view/WindowManagerImpl.java
+++ b/bridge/src/android/view/WindowManagerImpl.java
@@ -41,6 +41,8 @@ import com.android.internal.R;
import com.android.internal.policy.DecorView;
import com.android.layoutlib.bridge.Bridge;
+import java.util.ArrayList;
+
public class WindowManagerImpl implements WindowManager {
private final Context mContext;
@@ -179,10 +181,12 @@ public class WindowManagerImpl implements WindowManager {
}
}
mCurrentRootView.addView(arg0, frameLayoutParams);
+ ViewRootImpl_Accessor.setChild(mBaseRootView.getViewRootImpl(), arg0);
}
@Override
public void removeView(View arg0) {
+ ViewRootImpl viewRootImpl = arg0.getViewRootImpl();
if (mCurrentRootView != null) {
mCurrentRootView.removeView(arg0);
if (mBaseRootView != null && mCurrentRootView.getChildCount() == 0) {
@@ -190,6 +194,20 @@ public class WindowManagerImpl implements WindowManager {
mCurrentRootView = null;
}
}
+ if (viewRootImpl != null && viewRootImpl.getView() == arg0) {
+ View newRoot = null;
+ if (mCurrentRootView != null && mCurrentRootView.getChildCount() > 0) {
+ ArrayList<View> childrenList = mCurrentRootView.buildOrderedChildList();
+ newRoot = childrenList.get(childrenList.size() - 1);
+ } else if (mBaseRootView != null) {
+ View root = mBaseRootView;
+ while (root.getParent() instanceof View) {
+ root = (View)root.getParent();
+ }
+ newRoot = root;
+ }
+ ViewRootImpl_Accessor.setChild(viewRootImpl, newRoot);
+ }
}
@Override
diff --git a/bridge/src/android/view/accessibility/AccessibilityInteractionClient_Accessor.java b/bridge/src/android/view/accessibility/AccessibilityInteractionClient_Accessor.java
new file mode 100644
index 0000000000..8c87c22e88
--- /dev/null
+++ b/bridge/src/android/view/accessibility/AccessibilityInteractionClient_Accessor.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2023 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 android.view.accessibility;
+
+public class AccessibilityInteractionClient_Accessor {
+ public static void clearCaches() {
+ AccessibilityInteractionClient.sCaches.clear();
+ AccessibilityInteractionClient.sClients.clear();
+ AccessibilityInteractionClient.sConnectionCache.clear();
+ AccessibilityInteractionClient.sScrollingWindows.clear();
+ AccessibilityInteractionClient.sDirectConnectionCount = 0;
+ }
+}
diff --git a/bridge/src/android/view/accessibility/AccessibilityManager_Delegate.java b/bridge/src/android/view/accessibility/AccessibilityManager_Delegate.java
new file mode 100644
index 0000000000..9408f79c66
--- /dev/null
+++ b/bridge/src/android/view/accessibility/AccessibilityManager_Delegate.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2023 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 android.view.accessibility;
+
+import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.tools.layoutlib.annotations.LayoutlibDelegate;
+
+import android.content.Context;
+import android.graphics.Matrix;
+import android.view.MagnificationSpec;
+import android.view.accessibility.IAccessibilityManager.WindowTransformationSpec;
+
+public class AccessibilityManager_Delegate {
+ private static WindowTransformationSpec sInstance;
+
+ @LayoutlibDelegate
+ public static IAccessibilityManager.WindowTransformationSpec getWindowTransformationSpec(
+ AccessibilityManager thisManager, int windowId) {
+ if (sInstance == null) {
+ WindowTransformationSpec spec = new WindowTransformationSpec();
+ spec.magnificationSpec = new MagnificationSpec();
+ float[] matrix = new float[9];
+ Matrix.IDENTITY_MATRIX.getValues(matrix);
+ spec.transformationMatrix = matrix;
+ sInstance = spec;
+ }
+ return sInstance;
+ }
+
+ @LayoutlibDelegate
+ public static AccessibilityManager getInstance(Context context) {
+ Context baseContext = BridgeContext.getBaseContext(context);
+ return ((BridgeContext)baseContext).getAccessibilityManager();
+ }
+}
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java
index 3c62193dad..ce0fb2ddd8 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -337,7 +337,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge {
}
/**
- * Tests if the field is pubic, static and one of int or int[].
+ * Tests if the field is public, static and one of int or int[].
*/
private static boolean isValidRField(Field field) {
int modifiers = field.getModifiers();
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
index a0cf2b23b2..9bffc23eb3 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java
@@ -40,6 +40,7 @@ import com.android.tools.layoutlib.annotations.NotNull;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;
+import android.animation.AnimationHandler;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager;
@@ -81,6 +82,8 @@ import android.os.Handler;
import android.os.IBinder;
import android.os.IInterface;
import android.os.Looper;
+import android.os.NullVibrator;
+import android.os.NullVibratorManager;
import android.os.Parcel;
import android.os.PowerManager;
import android.os.RemoteException;
@@ -170,7 +173,7 @@ public class BridgeContext extends Context {
private final LayoutlibCallback mLayoutlibCallback;
private final WindowManager mWindowManager;
private final DisplayManager mDisplayManager;
- private final AutofillManager mAutofillManager;
+ private AutofillManager mAutofillManager;
private final ClipboardManager mClipboardManager;
private final ActivityManager mActivityManager;
private final ConnectivityManager mConnectivityManager;
@@ -199,10 +202,12 @@ public class BridgeContext extends Context {
private Boolean mIsThemeAppCompat;
private boolean mUseThemedIcon;
private Context mApplicationContext;
+ private AccessibilityManager mAccessibilityManager;
private final ResourceNamespace mAppCompatNamespace;
private final Map<Key<?>, Object> mUserData = new HashMap<>();
private final SessionInteractiveData mSessionInteractiveData;
+ private final ThreadLocal<AnimationHandler> mAnimationHandlerThreadLocal = new ThreadLocal<>();
/**
* Some applications that target both pre API 17 and post API 17, set the newer attrs to
@@ -263,7 +268,6 @@ public class BridgeContext extends Context {
mWindowManager = new WindowManagerImpl(this, mMetrics);
mDisplayManager = new DisplayManager(this);
- mAutofillManager = new AutofillManager(this, new Default());
mClipboardManager = new ClipboardManager(this, null);
mActivityManager = ActivityManager_Accessor.getActivityManagerInstance(this);
mConnectivityManager = new ConnectivityManager(this, null);
@@ -462,9 +466,12 @@ public class BridgeContext extends Context {
try {
outValue.data = Integer.parseInt(stringValue);
outValue.type = TypedValue.TYPE_INT_DEC;
- } catch (NumberFormatException e) {
- outValue.type = TypedValue.TYPE_STRING;
- outValue.string = stringValue;
+ }
+ catch (NumberFormatException e) {
+ if (!ResourceHelper.parseFloatAttribute(null, stringValue, outValue, false)) {
+ outValue.type = TypedValue.TYPE_STRING;
+ outValue.string = stringValue;
+ }
}
}
}
@@ -601,6 +608,13 @@ public class BridgeContext extends Context {
return isThemeAppCompat;
}
+ public AccessibilityManager getAccessibilityManager() {
+ if (mAccessibilityManager == null) {
+ mAccessibilityManager = new AccessibilityManager(this, null, UserHandle.USER_CURRENT);
+ }
+ return mAccessibilityManager;
+ }
+
// ------------ Context methods
@Override
@@ -673,6 +687,9 @@ public class BridgeContext extends Context {
return InputMethodManager.forContext(this);
case AUTOFILL_MANAGER_SERVICE:
+ if (mAutofillManager == null) {
+ mAutofillManager = new AutofillManager(this, new Default());
+ }
return mAutofillManager;
case CLIPBOARD_SERVICE:
@@ -690,6 +707,12 @@ public class BridgeContext extends Context {
case INPUT_SERVICE:
return mInputManager;
+ case VIBRATOR_SERVICE:
+ return NullVibrator.getInstance();
+
+ case VIBRATOR_MANAGER_SERVICE:
+ return NullVibratorManager.getInstance();
+
case TEXT_CLASSIFICATION_SERVICE:
case CONTENT_CAPTURE_MANAGER_SERVICE:
case ALARM_SERVICE:
@@ -1525,7 +1548,7 @@ public class BridgeContext extends Context {
@Override
public ContentResolver getContentResolver() {
if (mContentResolver == null) {
- mContentResolver = new BridgeContentResolver(this);
+ mContentResolver = new BridgeContentResolver(getApplicationContext());
}
return mContentResolver;
}
@@ -2289,4 +2312,9 @@ public class BridgeContext extends Context {
public void applyWallpaper(String wallpaperPath) {
mRenderResources.setWallpaper(wallpaperPath, mConfig.isNightModeActive());
}
+
+ @NotNull
+ public ThreadLocal<AnimationHandler> getAnimationHandlerThreadLocal() {
+ return mAnimationHandlerThreadLocal;
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
index 85bf637d00..c376429186 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java
@@ -61,6 +61,11 @@ public class BridgePowerManager implements IPowerManager {
}
@Override
+ public boolean isBatterySaverSupported() throws RemoteException {
+ return true;
+ }
+
+ @Override
public BatterySaverPolicyConfig getFullPowerSavePolicy() {
return new BatterySaverPolicyConfig.Builder().build();
}
diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java
index 5fe116ea0c..0122da1818 100644
--- a/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java
+++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java
@@ -90,4 +90,9 @@ public class BridgeThermalService implements IThermalService {
public float getThermalHeadroom(int forecastSeconds) {
return Float.NaN;
}
+
+ @Override
+ public float[] getThermalHeadroomThresholds() {
+ return new float[]{};
+ }
}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
index d89960ea68..794990852a 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java
@@ -92,8 +92,8 @@ public class Config {
}
public static String getTime(int platformVersion) {
- if (isGreaterOrEqual(platformVersion, TIRAMISU)) {
- return "13:00";
+ if (isGreaterOrEqual(platformVersion, UPSIDE_DOWN_CAKE)) {
+ return "14:00";
}
if (platformVersion < GINGERBREAD) {
return "2:20";
@@ -143,6 +143,9 @@ public class Config {
if (platformVersion < TIRAMISU) {
return "12:00";
}
+ if (platformVersion < UPSIDE_DOWN_CAKE) {
+ return "13:00";
+ }
// Should never happen.
return "4:04";
}
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
index 63efec36f1..98378e63e1 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java
@@ -24,18 +24,13 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ResourceHelper;
-import com.android.layoutlib.bridge.resources.IconLoader;
import com.android.layoutlib.bridge.resources.SysUiResources;
import com.android.resources.Density;
-import com.android.resources.LayoutDirection;
import com.android.resources.ResourceType;
import android.annotation.NonNull;
import android.content.res.ColorStateList;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.graphics.BitmapFactory.Options;
-import android.graphics.drawable.BitmapDrawable;
+import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.TypedValue;
import android.view.Gravity;
@@ -45,12 +40,10 @@ import android.widget.ImageView;
import android.widget.LinearLayout;
import android.widget.TextView;
-import java.io.InputStream;
-
import static android.os._Original_Build.VERSION_CODES.LOLLIPOP;
/**
- * Base "bar" class for the window decor around the the edited layout.
+ * Base "bar" class for the window decor around the edited layout.
* This is basically an horizontal layout that loads a given layout on creation (it is read
* through {@link Class#getResourceAsStream(String)}).
* <p>
@@ -88,9 +81,9 @@ abstract class CustomBar extends LinearLayout {
layoutName);
}
- protected ImageView loadIcon(ImageView imageView, String iconName, Density density) {
+ protected ImageView loadIcon(ImageView imageView, String iconName, Density density, int color) {
return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName,
- density, false);
+ density, false, color);
}
protected ImageView loadIcon(int index, String iconName, Density density, boolean isRtl) {
@@ -98,39 +91,12 @@ abstract class CustomBar extends LinearLayout {
if (child instanceof ImageView) {
ImageView imageView = (ImageView) child;
return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName,
- density, isRtl);
+ density, isRtl, Color.WHITE);
}
return null;
}
- protected ImageView loadIcon(ImageView imageView, String iconName, Density density,
- boolean isRtl) {
- LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
- IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion, dir);
- InputStream stream = iconLoader.getIcon();
-
- if (stream != null) {
- density = iconLoader.getDensity();
- String path = iconLoader.getPath();
- // look for a cached bitmap
- Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/);
- if (bitmap == null) {
- Options options = new Options();
- options.inDensity = density.getDpiValue();
- bitmap = BitmapFactory.decodeStream(stream, null, options);
- Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/);
- }
-
- if (bitmap != null) {
- BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(), bitmap);
- imageView.setImageDrawable(drawable);
- }
- }
-
- return imageView;
- }
-
protected TextView setText(int index, String string) {
View child = getChildAt(index);
if (child instanceof TextView) {
@@ -247,9 +213,7 @@ abstract class CustomBar extends LinearLayout {
resource = renderResources.resolveResValue(resource);
if (resource != null) {
ResourceType type = resource.getResourceType();
- if (type == null || type == ResourceType.COLOR) {
- // if no type is specified, the value may have been specified directly in the style
- // file, rather than referencing a color resource value.
+ if (type == ResourceType.STYLE_ITEM || type == ResourceType.COLOR) {
try {
return ResourceHelper.getColor(resource.getValue());
} catch (NumberFormatException e) {
diff --git a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
index dc823f7e37..68423337a0 100644
--- a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
+++ b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java
@@ -17,11 +17,13 @@
package com.android.layoutlib.bridge.bars;
import com.android.ide.common.rendering.api.ILayoutLog;
+import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceNamespace;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.android.BridgeContext;
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
import com.android.layoutlib.bridge.impl.ParserFactory;
+import com.android.layoutlib.bridge.impl.ResourceHelper;
import com.android.layoutlib.bridge.resources.IconLoader;
import com.android.resources.Density;
@@ -42,9 +44,24 @@ import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;
+import static android.graphics.Color.WHITE;
+import static android.os._Original_Build.VERSION_CODES.M;
+import static com.android.layoutlib.bridge.bars.Config.getTimeColor;
+import static com.android.layoutlib.bridge.bars.Config.isGreaterOrEqual;
+
public class StatusBar extends CustomBar {
private final int mSimulatedPlatformVersion;
+ /**
+ * Color corresponding to light_mode_icon_color_single_tone
+ * from frameworks/base/packages/SettingsLib/res/values/colors.xml
+ */
+ private static final int LIGHT_ICON_COLOR = 0xffffffff;
+ /**
+ * Color corresponding to dark_mode_icon_color_single_tone
+ * from frameworks/base/packages/SettingsLib/res/values/colors.xml
+ */
+ private static final int DARK_ICON_COLOR = 0x99000000;
/** Status bar background color attribute name. */
private static final String ATTR_COLOR = "statusBarColor";
/** Attribute for translucency property. */
@@ -57,7 +74,7 @@ public class StatusBar extends CustomBar {
@SuppressWarnings("UnusedParameters")
public StatusBar(Context context, AttributeSet attrs) {
this((BridgeContext) context,
- Density.getEnum(((BridgeContext) context).getMetrics().densityDpi),
+ Density.create(((BridgeContext) context).getMetrics().densityDpi),
((BridgeContext) context).getConfiguration().getLayoutDirection() ==
View.LAYOUT_DIRECTION_RTL,
(context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0,
@@ -95,20 +112,45 @@ public class StatusBar extends CustomBar {
return;
}
+ int foregroundColor = getForegroundColor(simulatedPlatformVersion);
// Cannot access the inside items through id because no R.id values have been
// created for them.
// We do know the order though.
loadIcon(icons.get(0), "stat_sys_wifi_signal_4_fully."
- + Config.getWifiIconType(simulatedPlatformVersion), density);
- loadIcon(icons.get(1), "stat_sys_battery_100.png", density);
+ + Config.getWifiIconType(simulatedPlatformVersion), density,foregroundColor);
+ loadIcon(icons.get(1), "stat_sys_battery_100.png", density, foregroundColor);
clockView.setText(Config.getTime(simulatedPlatformVersion));
- clockView.setTextColor(Config.getTimeColor(simulatedPlatformVersion));
+ clockView.setTextColor(foregroundColor);
+ }
+
+ private int getForegroundColor(int platformVersion) {
+ if (isGreaterOrEqual(platformVersion, M)) {
+ RenderResources renderResources = getContext().getRenderResources();
+ boolean translucentBackground =
+ ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources,
+ ATTR_TRANSLUCENT, false);
+ if (translucentBackground) {
+ return WHITE;
+ }
+ boolean drawnByWindow =
+ ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources,
+ "windowDrawsSystemBarBackgrounds", false);
+ if (drawnByWindow) {
+ boolean lightStatusBar =
+ ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources,
+ "windowLightStatusBar", false);
+ return lightStatusBar ? DARK_ICON_COLOR : LIGHT_ICON_COLOR;
+ }
+ return WHITE;
+ } else {
+ return getTimeColor(platformVersion);
+ }
}
@Override
- protected ImageView loadIcon(ImageView imageView, String iconName, Density density) {
+ protected ImageView loadIcon(ImageView imageView, String iconName, Density density, int color) {
if (!iconName.endsWith(".xml")) {
- return super.loadIcon(imageView, iconName, density);
+ return super.loadIcon(imageView, iconName, density, color);
}
// The xml is stored only in xhdpi.
@@ -123,8 +165,9 @@ public class StatusBar extends CustomBar {
ParserFactory.create(stream, iconName),
(BridgeContext) mContext,
ResourceNamespace.ANDROID);
- imageView.setImageDrawable(
- Drawable.createFromXml(mContext.getResources(), parser));
+ Drawable drawable = Drawable.createFromXml(mContext.getResources(), parser);
+ drawable.setTint(color);
+ imageView.setImageDrawable(drawable);
} catch (XmlPullParserException e) {
Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e,
null, null);
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
index c8e5009888..1ca3b9c2cc 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java
@@ -38,6 +38,8 @@ import com.android.resources.ScreenOrientation;
import android.R.id;
import android.annotation.NonNull;
import android.graphics.Color;
+import android.graphics.Point;
+import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.util.DisplayMetrics;
import android.util.TypedValue;
@@ -196,6 +198,25 @@ class Layout extends FrameLayout {
mBuilder = null;
}
+ @Override
+ public boolean getChildVisibleRect(View child, Rect r, Point offset, boolean forceParentCheck) {
+ return r.intersect(0, 0, getWidth(), getHeight());
+ }
+
+ @Override
+ public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
+ int width = mRight - mLeft;
+ int height = mBottom - mTop;
+ if (width > 0 && height > 0) {
+ r.set(0, 0, width, height);
+ if (globalOffset != null) {
+ globalOffset.set(-mScrollX, -mScrollY);
+ }
+ return true;
+ }
+ return false;
+ }
+
@NonNull
private static View createSysUiOverlay(@NonNull BridgeContext context) {
SysUiOverlay overlay = new SysUiOverlay(context, 20, 10, 50, 40, 60);
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
index c2430a8959..98b59565f7 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java
@@ -32,6 +32,7 @@ import com.android.tools.layoutlib.annotations.NotNull;
import com.android.tools.layoutlib.annotations.Nullable;
import com.android.tools.layoutlib.annotations.VisibleForTesting;
+import android.animation.AnimationHandler;
import android.animation.PropertyValuesHolder_Accessor;
import android.content.res.Configuration;
import android.graphics.drawable.AdaptiveIconDrawable_Delegate;
@@ -42,6 +43,7 @@ import android.view.IWindowManagerImpl;
import android.view.Surface;
import android.view.ViewConfiguration_Accessor;
import android.view.WindowManagerGlobal_Delegate;
+import android.view.accessibility.AccessibilityInteractionClient_Accessor;
import android.view.inputmethod.InputMethodManager_Accessor;
import java.util.Collections;
@@ -51,6 +53,7 @@ import java.util.WeakHashMap;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;
+import static android.os._Original_Build.VERSION.SDK_INT;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTERRUPTED;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT;
import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
@@ -68,6 +71,12 @@ import static com.android.ide.common.rendering.api.Result.Status.SUCCESS;
*
*/
public abstract class RenderAction<T extends RenderParams> {
+ /**
+ * Static field to store an SDK version coming from the render configuration.
+ * This is to be accessed when wanting to know the simulated SDK version instead
+ * of Build.VERSION.SDK_INT.
+ */
+ public static int sSimulatedSdk;
private static final Set<String> COMPOSE_CLASS_FQNS =
Set.of("androidx.compose.ui.tooling.ComposeViewAdapter",
@@ -99,6 +108,7 @@ public abstract class RenderAction<T extends RenderParams> {
*/
protected RenderAction(T params) {
mParams = params;
+ sSimulatedSdk = SDK_INT;
}
/**
@@ -276,6 +286,7 @@ public abstract class RenderAction<T extends RenderParams> {
ILayoutLog currentLog = mParams.getLog();
Bridge.setLog(currentLog);
mContext.getRenderResources().setLogger(currentLog);
+ AnimationHandler.sAnimatorHandler = mContext.getAnimationHandlerThreadLocal();
}
/**
@@ -303,6 +314,7 @@ public abstract class RenderAction<T extends RenderParams> {
ParserFactory.setParserFactory(null);
PropertyValuesHolder_Accessor.clearClassCaches();
+ AccessibilityInteractionClient_Accessor.clearCaches();
}
public static BridgeContext getCurrentContext() {
@@ -467,6 +479,14 @@ public abstract class RenderAction<T extends RenderParams> {
if (sCurrentContext != null) {
// quit HandlerThread created during this session.
HandlerThread_Delegate.cleanUp(sCurrentContext);
+
+ AnimationHandler animationHandler =
+ sCurrentContext.getAnimationHandlerThreadLocal().get();
+ if (animationHandler != null) {
+ animationHandler.mDelayedCallbackStartTime.clear();
+ animationHandler.mAnimationCallbacks.clear();
+ animationHandler.mCommitCallbacks.clear();
+ }
}
sCurrentContext = null;
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
index 6a6e184617..0dd35ce055 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java
@@ -39,6 +39,7 @@ import java.awt.AlphaComposite;
import java.awt.Color;
import java.awt.Graphics2D;
import java.awt.image.BufferedImage;
+import java.awt.image.DataBufferInt;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
@@ -78,7 +79,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
return Status.ERROR_NOT_A_DRAWABLE.createResult();
}
- Drawable d = ResourceHelper.getDrawable(drawableResource, context);
+ Drawable d = ResourceHelper.getDrawable(drawableResource, context, context.getTheme());
if (d == null) {
return Status.ERROR_NOT_A_DRAWABLE.createResult();
}
@@ -128,15 +129,6 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
// Use screen size when either intrinsic width or height isn't available.
w = screenWidth;
h = screenHeight;
- } else if (w > screenWidth || h > screenHeight) {
- // If image wouldn't fit to the screen, resize it to avoid cropping.
-
- // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight.
- double scale = Math.min((double) screenWidth / w, (double) screenHeight / h);
-
- // scale * w / scale * h = w / h, so, proportions are preserved.
- w = (int) Math.floor(scale * w);
- h = (int) Math.floor(scale * h);
}
int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY);
@@ -149,46 +141,30 @@ public class RenderDrawable extends RenderAction<DrawableParams> {
// Pre-draw setup.
AttachInfo_Accessor.dispatchOnPreDraw(content);
- // Draw into a new image.
- BufferedImage image = getImage(w, h);
-
- // Create an Android bitmap around the BufferedImage.
- Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(),
- Config.ARGB_8888);
- bitmap.setPixels(image.getRGB(0, 0, image.getWidth(), image.getHeight(),
- null, 0, image.getWidth()), 0, image.getWidth(), 0, 0, image
- .getWidth(), image.getHeight());
-
- // Create a Canvas around the Android bitmap.
+ Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.setDensity(hardwareConfig.getDensity().getDpiValue());
// Draw.
content.draw(canvas);
- int[] pixels = new int[image.getWidth() * image.getHeight()];
- bitmap.getPixels(pixels, 0, image.getWidth(), 0, 0, image.getWidth(),
- image.getHeight());
- image.setRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth());
- // Detach root from window after draw.
- AttachInfo_Accessor.detachFromWindow(content);
+ if (w > screenWidth || h > screenHeight) {
+ // If image wouldn't fit to the screen, resize it to avoid cropping.
- return image;
- }
+ // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight.
+ double scale = Math.min((double) screenWidth / w, (double) screenHeight / h);
+ bitmap = Bitmap.createScaledBitmap(bitmap, (int) (w * scale), (int) (h * scale), true);
+ }
- @NonNull
- protected BufferedImage getImage(int w, int h) {
- BufferedImage image = new BufferedImage(w > 0 ? w : 1,
- h > 0 ? h : 1,
+ // Copy bitmap into BufferedImage.
+ BufferedImage image = new BufferedImage(bitmap.getWidth(), bitmap.getHeight(),
BufferedImage.TYPE_INT_ARGB);
- Graphics2D gc = image.createGraphics();
- gc.setComposite(AlphaComposite.Src);
-
- gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0, w, h);
+ int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ bitmap.getPixels(imageData, 0, image.getWidth(), 0, 0, image.getWidth(),
+ image.getHeight());
- // done
- gc.dispose();
+ // Detach root from window after draw.
+ AttachInfo_Accessor.detachFromWindow(content);
return image;
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
index ae5e401edc..9ed5d17531 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java
@@ -50,7 +50,6 @@ import com.android.tools.idea.validator.ValidatorHierarchy;
import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper;
import com.android.tools.layoutlib.annotations.NotNull;
-import android.animation.AnimationHandler;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
@@ -92,7 +91,10 @@ import java.util.ArrayList;
import java.util.IdentityHashMap;
import java.util.List;
import java.util.Map;
+import java.util.function.Consumer;
+import java.util.function.Function;
+import static android.os._Original_Build.VERSION.SDK_INT;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED;
import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN;
@@ -109,6 +111,10 @@ import static com.android.layoutlib.common.util.ReflectionUtils.isInstanceOf;
public class RenderSessionImpl extends RenderAction<SessionParams> {
private static final Canvas NOP_CANVAS = new NopCanvas();
+ private static final String SIMULATED_SDK_TOO_HIGH =
+ String.format("The current rendering only supports APIs up to %d. You may encounter " +
+ "crashes if using with higher APIs. To avoid, you can set a lower API for " +
+ "your previews.", SDK_INT);
// scene state
private RenderSession mScene;
@@ -198,7 +204,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
/**
- * Measures the the current layout if needed (see {@link #invalidateRenderingSize}).
+ * Measures the current layout if needed (see {@link #invalidateRenderingSize}).
*/
private void measureLayout(@NonNull SessionParams params) {
// only do the screen measure when needed.
@@ -310,6 +316,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
SessionParams params = getParams();
BridgeContext context = getContext();
+ int simulatedVersion = params.getSimulatedPlatformVersion();
+ sSimulatedSdk = simulatedVersion > 0 ? simulatedVersion : SDK_INT;
+ if (sSimulatedSdk > SDK_INT) {
+ Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, SIMULATED_SDK_TOO_HIGH,
+ null, null, null);
+ }
+
if (Bridge.isLocaleRtl(params.getLocale())) {
if (!params.isRtlSupported()) {
Bridge.getLog().warning(ILayoutLog.TAG_RTL_NOT_ENABLED,
@@ -362,14 +375,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mViewRoot.getViewRootImpl().mTmpFrames.displayFrame.set(mViewRoot.getLeft(),
mViewRoot.getTop(), mViewRoot.getRight(), mViewRoot.getBottom());
- ViewRootImpl rootImpl = AttachInfo_Accessor.getRootView(mViewRoot);
- if (rootImpl != null) {
- ViewRootImpl_Accessor.setChild(rootImpl, mViewRoot);
- }
-
mSystemViewInfoList =
- visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
- false);
+ visitAllChildren(mViewRoot, 0, 0, params, false);
return SUCCESS.createResult();
} catch (PostInflateException e) {
@@ -480,6 +487,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
SessionParams params = getParams();
+ int simulatedVersion = params.getSimulatedPlatformVersion();
+ sSimulatedSdk = simulatedVersion > 0 ? simulatedVersion : SDK_INT;
+ if (sSimulatedSdk > SDK_INT) {
+ Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, SIMULATED_SDK_TOO_HIGH,
+ null, null, null);
+ }
+
try {
if (mViewRoot == null) {
return ERROR_NOT_INFLATED.createResult();
@@ -570,8 +584,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
mSystemViewInfoList =
- visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(),
- false);
+ visitAllChildren(mViewRoot, 0, 0, params, false);
+
+ Consumer<BufferedImage> imageTransformation = getParams().getImageTransformation();
+ if (imageTransformation != null) {
+ imageTransformation.accept(mImage);
+ }
boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR));
boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals(
@@ -853,15 +871,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
*
* @return {@code ViewInfo} containing the bounds of the view and it children otherwise.
*/
- private ViewInfo visit(View view, int hOffset, int vOffset, boolean setExtendedInfo,
+ private ViewInfo visit(View view, int hOffset, int vOffset, SessionParams params,
boolean isContentFrame) {
- ViewInfo result = createViewInfo(view, hOffset, vOffset, setExtendedInfo, isContentFrame);
+ ViewInfo result = createViewInfo(view, hOffset, vOffset, params.getExtendedViewInfoMode(),
+ isContentFrame);
if (view instanceof ViewGroup) {
ViewGroup group = ((ViewGroup) view);
result.setChildren(visitAllChildren(group, isContentFrame ? 0 : hOffset,
isContentFrame ? 0 : vOffset,
- setExtendedInfo, isContentFrame));
+ params, isContentFrame));
}
return result;
}
@@ -880,7 +899,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* part of the system decor.
*/
private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int hOffset, int vOffset,
- boolean setExtendedInfo, boolean isContentFrame) {
+ SessionParams params, boolean isContentFrame) {
if (viewGroup == null) {
return null;
}
@@ -896,8 +915,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
List<ViewInfo> childrenWithOffset = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
ViewInfo[] childViewInfo =
- visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset,
- setExtendedInfo);
+ visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset, params);
childrenWithoutOffset.add(childViewInfo[0]);
childrenWithOffset.add(childViewInfo[1]);
}
@@ -906,7 +924,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
} else {
List<ViewInfo> children = new ArrayList<>(childCount);
for (int i = 0; i < childCount; i++) {
- children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, setExtendedInfo,
+ children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, params,
isContentFrame));
}
return children;
@@ -920,26 +938,32 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
* get the right bounds if the {@code ViewInfo} hierarchy is accessed from
* {@code mViewInfoList}. When the hierarchy is accessed via {@code mSystemViewInfoList}, the
* offset is not needed.
+ * If a custom parser was passed inside the {@link SessionParams} argument, this will be used
+ * to generate the {@link ViewInfo}s. Otherwise, {@link RenderSessionImpl#visitAllChildren}
+ * will be used.
*
* @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at
* index 1 is with the offset.
*/
@NonNull
- private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset,
- boolean setExtendedInfo) {
+ private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset, SessionParams params) {
ViewInfo[] result = new ViewInfo[2];
if (view == null) {
return result;
}
+ boolean setExtendedInfo = params.getExtendedViewInfoMode();
result[0] = createViewInfo(view, 0, 0, setExtendedInfo, true);
result[1] = createViewInfo(view, hOffset, vOffset, setExtendedInfo, true);
- if (view instanceof ViewGroup) {
- List<ViewInfo> children =
- visitAllChildren((ViewGroup) view, 0, 0, setExtendedInfo, true);
- result[0].setChildren(children);
- result[1].setChildren(children);
+ Function<Object, List<ViewInfo>> customParser = params.getCustomContentHierarchyParser();
+ List<ViewInfo> children = null;
+ if (customParser != null) {
+ children = customParser.apply(view);
+ } else if (view instanceof ViewGroup) {
+ children = visitAllChildren((ViewGroup) view, 0, 0, params, true);
}
+ result[0].setChildren(children);
+ result[1].setChildren(children);
return result;
}
@@ -975,13 +999,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
shiftY + view.getTop(),
shiftX + view.getRight(),
shiftY + view.getBottom(),
- view, view.getLayoutParams());
+ view, null, view.getLayoutParams());
} else {
// We are part of the system decor.
SystemViewInfo r = new SystemViewInfo(view.getClass().getName(),
getViewKey(view),
view.getLeft(), view.getTop(), view.getRight(),
- view.getBottom(), view, view.getLayoutParams());
+ view.getBottom(), view, null, view.getLayoutParams());
result = r;
// We currently mark three kinds of views:
// 1. Menus in the Action Bar
@@ -1184,6 +1208,19 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
}
}
+ @Override
+ public void release() {
+ super.release();
+ if (mViewRoot == null) {
+ return;
+ }
+ ViewRootImpl viewRootImpl = mViewRoot.getViewRootImpl();
+ if (viewRootImpl == null) {
+ return;
+ }
+ ViewRootImpl_Accessor.detachFromWindow(viewRootImpl);
+ }
+
private void disposeImageSurface() {
if (mCanvas != null) {
mCanvas.release();
@@ -1198,12 +1235,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
mImage = null;
// detachFromWindow might create Handler callbacks, thus before Handler_Delegate.dispose
AttachInfo_Accessor.detachFromWindow(mViewRoot);
- AnimationHandler animationHandler = AnimationHandler.sAnimatorHandler.get();
- if (animationHandler != null) {
- animationHandler.mDelayedCallbackStartTime.clear();
- animationHandler.mAnimationCallbacks.clear();
- animationHandler.mCommitCallbacks.clear();
- }
getContext().getSessionInteractiveData().dispose();
if (mViewInfoList != null) {
mViewInfoList.clear();
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
index 87bed3d967..358795f256 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java
@@ -111,7 +111,7 @@ import static android.content.res.AssetManager.ACCESS_STREAMING;
public final class ResourceHelper {
private static final Key<Set<ResourceValue>> KEY_GET_DRAWABLE =
Key.create("ResourceHelper.getDrawable");
- private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]+)?)(.*)");
+ private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]*)?)(.*)");
private static final float[] sFloatOut = new float[1];
private static final TypedValue mValue = new TypedValue();
@@ -368,7 +368,7 @@ public final class ResourceHelper {
if (value instanceof DensityBasedResourceValue) {
density = ((DensityBasedResourceValue) value).getResourceDensity();
if (density == Density.NODPI || density == Density.ANYDPI) {
- density = Density.getEnum(context.getConfiguration().densityDpi);
+ density = Density.create(context.getConfiguration().densityDpi);
}
}
diff --git a/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java b/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
index 9fea1677d5..6f9092cc12 100644
--- a/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
+++ b/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java
@@ -32,8 +32,9 @@ public class SystemViewInfo extends ViewInfo {
}
public SystemViewInfo(String name, Object cookie, int left, int top,
- int right, int bottom, Object viewObject, Object layoutParamsObject) {
- super(name, cookie, left, top, right, bottom, viewObject,
+ int right, int bottom, Object viewObject, Object accessibilityObject,
+ Object layoutParamsObject) {
+ super(name, cookie, left, top, right, bottom, viewObject, accessibilityObject,
layoutParamsObject);
}
diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
index 84ed6a04c4..f8884d4b2e 100644
--- a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
+++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java
@@ -36,7 +36,6 @@ import android.graphics.BitmapFactory.Options;
import android.graphics.drawable.BitmapDrawable;
import android.widget.ImageView;
-import java.io.IOException;
import java.io.InputStream;
public class SysUiResources {
@@ -67,10 +66,8 @@ public class SysUiResources {
return null;
}
- public static ImageView loadIcon(Context context, int api, ImageView imageView, String
- iconName,
- Density density, boolean
- isRtl) {
+ public static ImageView loadIcon(Context context, int api, ImageView imageView,
+ String iconName, Density density, boolean isRtl, int color) {
LayoutDirection dir = isRtl ? LayoutDirection.RTL : null;
IconLoader iconLoader = new IconLoader(iconName, density, api,
dir);
@@ -90,6 +87,7 @@ public class SysUiResources {
if (bitmap != null) {
BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap);
+ drawable.setTint(color);
imageView.setImageDrawable(drawable);
}
}
diff --git a/bridge/src/dalvik/system/VMRuntime_Delegate.java b/bridge/src/dalvik/system/VMRuntime_Delegate.java
index 2fe10154b8..23faf55518 100644
--- a/bridge/src/dalvik/system/VMRuntime_Delegate.java
+++ b/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -36,4 +36,9 @@ public class VMRuntime_Delegate {
/*package*/ static int getNotifyNativeInterval() {
return VMRuntimeCommonHelper.getNotifyNativeInterval();
}
+
+ @LayoutlibDelegate
+ public static boolean is64Bit(VMRuntime runtime) {
+ return true;
+ }
}
diff --git a/bridge/tests/res/testApp/MyApplication/golden/activity.png b/bridge/tests/res/testApp/MyApplication/golden/activity.png
index a05349d73c..4546682d06 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
index 2d6938aa28..b438464c6c 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
index 5570291230..e939a572ec 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_circle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_green.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_green.png
index 61f1f18af1..b70c65d05e 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_green.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_green.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_orange.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_orange.png
index dd1dd57027..31647c615d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_orange.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_dynamic_orange.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
index e0b60374cd..67deb6e4d4 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_rounded_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
index 3e41ccc2c1..6e63ef2286 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/adaptive_icon_squircle.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
index 48a40cd191..7ac28b683a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
index f8cec3261b..50b5f26443 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/allwidgets_tab.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
index c00823d326..3887e292e3 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
index db0b343eb3..9588148737 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/animated_vector_1.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/asset.png b/bridge/tests/res/testApp/MyApplication/golden/asset.png
index b6193f61e4..f4467d6c1d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/asset.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/asset.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
index 6a23995415..7cabc39941 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/auto-scale-image.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/bitmap_decoder.png b/bridge/tests/res/testApp/MyApplication/golden/bitmap_decoder.png
index 20f15a9ecd..5be2d1a800 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/bitmap_decoder.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/bitmap_decoder.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
index 323d51493e..9e08e22488 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/context_theme_wrapper.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/dark_status_bar.png b/bridge/tests/res/testApp/MyApplication/golden/dark_status_bar.png
new file mode 100644
index 0000000000..34ff1adccf
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/dark_status_bar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
index 5ed270620f..7b1f1f1e02 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_horz_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
index 80e72b0199..d6a4c5c586 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/expand_vert_layout.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
index 82e40ac58e..4e7feb443f 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/four_corners.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/light_status_bar.png b/bridge/tests/res/testApp/MyApplication/golden/light_status_bar.png
new file mode 100644
index 0000000000..a37d8dee26
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/light_status_bar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
index fefb3b7181..6403637078 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ninepatch_background.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
index d69667c6b7..4051b05ca2 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/ondraw_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
index 4d9f7769d9..e27ae9eb39 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/onmeasure_crash.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
index 118e926796..fab98dc2d4 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/rtl_ltr.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
index 19e65aa167..55a9982b5d 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_scrollview_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
index 84b7299a56..6093824921 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadow_sizes_test.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
index 7d9a51d793..f1437af29a 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/shadows_test_rounded_edge.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
index 2b2aab8a0e..1300dd0cca 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
index 38924a322c..7852c9e323 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/simple_activity_noactionbar.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/software_layer.png b/bridge/tests/res/testApp/MyApplication/golden/software_layer.png
new file mode 100644
index 0000000000..70465cf27b
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/golden/software_layer.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
index 5832c67a71..03da31efc0 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_91383.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
index 769406e5fb..67e502fccd 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
index 6882d79b87..4b1425ac89 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_radial_gradient.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
index 759d535f6e..055af89473 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_in_image_view.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
index 4396a51816..99e37aed57 100644
--- a/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
+++ b/bridge/tests/res/testApp/MyApplication/golden/vector_drawable_with_tint_itself.png
Binary files differ
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/SoftwareTextView.java b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/SoftwareTextView.java
new file mode 100644
index 0000000000..3d8cb0c75d
--- /dev/null
+++ b/bridge/tests/res/testApp/MyApplication/src/main/myapplication.widgets/SoftwareTextView.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2023 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.layoutlib.test.myapplication.widgets;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.widget.TextView;
+
+public class SoftwareTextView extends TextView {
+
+ public SoftwareTextView(Context context) {
+ super(context);
+ init();
+ }
+
+ public SoftwareTextView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public SoftwareTextView(Context context, AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ init();
+ }
+
+ public SoftwareTextView(Context context, AttributeSet attrs, int defStyleAttr,
+ int defStyleRes) {
+ super(context, attrs, defStyleAttr, defStyleRes);
+ init();
+ }
+
+ private void init() {
+ setLayerType(LAYER_TYPE_SOFTWARE, null);
+ setBackgroundColor(Color.RED);
+ setText("Software Layer");
+ }
+}
diff --git a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
index 7b338d1dc6..d75aab1c1c 100644
--- a/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
+++ b/bridge/tests/res/testApp/MyApplication/src/main/res/values/styles.xml
@@ -16,4 +16,13 @@
<item name="android:windowBackground">@drawable/theme_attribute_drawable</item>
</style>
+ <style name="LightStatusBarTheme" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="android:statusBarColor">#ffff0000</item>
+ <item name="android:windowLightStatusBar">true</item>
+ </style>
+
+ <style name="DarkStatusBarTheme" parent="android:Theme.Material.Light.DarkActionBar">
+ <item name="android:statusBarColor">#ffff0000</item>
+ </style>
+
</resources>
diff --git a/bridge/tests/run_tests.sh b/bridge/tests/run_tests.sh
index 91e88b6c94..bf338102bc 100755
--- a/bridge/tests/run_tests.sh
+++ b/bridge/tests/run_tests.sh
@@ -9,7 +9,7 @@ echo "BASE_DIR: $BASE_DIR"
readonly FAILURE_DIR=layoutlib-test-failures
readonly FAILURE_ZIP=layoutlib-test-failures.zip
-readonly CLEAN_TMP_FILES=0
+readonly CLEAN_TMP_FILES=1
readonly USE_SOONG=1
readonly APP_NAME="regression"
@@ -76,6 +76,7 @@ set +x
# Create zip of all failure screenshots
+rm -f ${OUT_DIR}/${FAILURE_ZIP}
if [[ -d "${OUT_DIR}/${FAILURE_DIR}" ]]; then
zip -q -j -r ${OUT_DIR}/${FAILURE_ZIP} ${OUT_DIR}/${FAILURE_DIR}
fi
diff --git a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
index 39759279a3..dba0f76ee8 100644
--- a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
+++ b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
@@ -52,6 +52,7 @@ public class BridgeTypedArrayTest {
assertEquals(TYPE_STRING, BridgeTypedArray.getType("#notacolor"));
assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType("16dp"));
assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType(".16dp"));
+ assertEquals(TYPE_DIMENSION, BridgeTypedArray.getType("9999.dp"));
assertEquals(TYPE_STRING, BridgeTypedArray.getType("16notaunit"));
assertEquals(TYPE_INT_DEC, BridgeTypedArray.getType("98543"));
assertEquals(TYPE_FLOAT, BridgeTypedArray.getType("43.364"));
diff --git a/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java b/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
index f995d1abaa..a40dfed432 100644
--- a/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
+++ b/bridge/tests/src/android/util/BridgeXmlPullAttributesTest.java
@@ -20,8 +20,10 @@ import com.android.ide.common.rendering.api.LayoutlibCallback;
import com.android.ide.common.rendering.api.RenderResources;
import com.android.ide.common.rendering.api.ResourceNamespace;
import com.android.ide.common.rendering.api.ResourceNamespace.Resolver;
+import com.android.ide.common.rendering.api.ResourceValue;
import com.android.layoutlib.bridge.BridgeConstants;
import com.android.layoutlib.bridge.android.BridgeContext;
+import com.android.tools.layoutlib.annotations.NotNull;
import org.junit.Test;
import org.xmlpull.v1.XmlPullParser;
@@ -34,12 +36,10 @@ import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
public class BridgeXmlPullAttributesTest {
-
- @Test
- public void testGetAttributeIntValueForEnums() {
- RenderResources renderResources = new RenderResources();
-
+ @NotNull
+ private static XmlPullParser prepareParser() {
XmlPullParser parser = mock(XmlPullParser.class);
+
when(parser.getAttributeValue(BridgeConstants.NS_RESOURCES, "layout_width"))
.thenReturn("match_parent");
when(parser.getAttributeName(0)).thenReturn("layout_width");
@@ -50,34 +50,53 @@ public class BridgeXmlPullAttributesTest {
when(parser.getAttributeName(1)).thenReturn("my_custom_attr");
when(parser.getAttributeNamespace(1)).thenReturn(BridgeConstants.NS_APP_RES_AUTO);
+ return parser;
+ }
+
+ @NotNull
+ private static BridgeContext prepareContext() {
BridgeContext context = mock(BridgeContext.class);
+ RenderResources renderResources = new RenderResources() {
+ @Override
+ public ResourceValue resolveResValue(ResourceValue value) {
+ // Simulate behaviour from the actual resolver where a failed resolution will
+ // return the passed value.
+ return value;
+ }
+ };
when(context.getRenderResources()).thenReturn(renderResources);
-
LayoutlibCallback callback = mock(LayoutlibCallback.class);
when(callback.getImplicitNamespaces()).thenReturn(Resolver.EMPTY_RESOLVER);
when(context.getLayoutlibCallback()).thenReturn(callback);
- BridgeXmlPullAttributes attributes = new BridgeXmlPullAttributes(
- parser,
- context,
- ResourceNamespace.RES_AUTO,
- attrName -> {
- if ("layout_width".equals(attrName)) {
- return ImmutableMap.of(
- "match_parent", 123);
- }
- return ImmutableMap.of();
- },
- (ns, attrName) -> {
- if ("my_custom_attr".equals(attrName)) {
- return ImmutableMap.of(
- "a", 1,
- "b", 2
- );
- }
- return ImmutableMap.of();
- });
+ return context;
+ }
+
+ private final XmlPullParser parser = prepareParser();
+ private final BridgeContext context = prepareContext();
+ private final BridgeXmlPullAttributes attributes = new BridgeXmlPullAttributes(
+ parser,
+ context,
+ ResourceNamespace.RES_AUTO,
+ attrName -> {
+ if ("layout_width".equals(attrName)) {
+ return ImmutableMap.of(
+ "match_parent", 123);
+ }
+ return ImmutableMap.of();
+ },
+ (ns, attrName) -> {
+ if ("my_custom_attr".equals(attrName)) {
+ return ImmutableMap.of(
+ "a", 1,
+ "b", 2
+ );
+ }
+ return ImmutableMap.of();
+ });
+ @Test
+ public void testGetAttributeIntValueForEnums() {
// Test a framework defined enum attribute
assertEquals(123, attributes.getAttributeIntValue(BridgeConstants.NS_RESOURCES,
"layout_width", 500));
@@ -115,4 +134,11 @@ public class BridgeXmlPullAttributesTest {
"my_other_attr", 500));
}
+ @Test
+ public void testNotExistingAttributes() {
+ assertEquals(501, attributes.getAttributeUnsignedIntValue(BridgeConstants.NS_APP_RES_AUTO,
+ "my_other_attr", 501));
+ assertEquals(502, attributes.getAttributeResourceValue(BridgeConstants.NS_APP_RES_AUTO,
+ "my_other_attr", 502));
+ }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/AccessibilityTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/AccessibilityTest.java
index ed90d9b60c..e1cad4ee2d 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/AccessibilityTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/AccessibilityTest.java
@@ -19,6 +19,8 @@ package com.android.layoutlib.bridge.android;
import com.android.ide.common.rendering.api.RenderSession;
import com.android.ide.common.rendering.api.Result;
import com.android.ide.common.rendering.api.SessionParams;
+import com.android.ide.common.rendering.api.SessionParams.RenderingMode;
+import com.android.ide.common.rendering.api.ViewInfo;
import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.intensive.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
@@ -28,9 +30,13 @@ import org.junit.BeforeClass;
import org.junit.Test;
import android.view.View;
+import android.view.ViewGroup;
+import android.view.accessibility.AccessibilityInteractionClient;
import android.view.accessibility.AccessibilityNodeInfo;
import java.io.FileNotFoundException;
+import java.util.ArrayList;
+import java.util.List;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
@@ -58,6 +64,7 @@ public class AccessibilityTest extends RenderTestBase {
try {
Result renderResult = session.render(50000);
assertTrue(renderResult.isSuccess());
+ assertEquals(0, AccessibilityInteractionClient.sConnectionCache.size());
View rootView = (View)session.getSystemRootViews().get(0).getViewObject();
AccessibilityNodeInfo rootNode = rootView.createAccessibilityNodeInfo();
assertNotNull(rootNode);
@@ -71,4 +78,101 @@ public class AccessibilityTest extends RenderTestBase {
session.dispose();
}
}
+
+ @Test
+ public void customHierarchyParserTest() throws FileNotFoundException,
+ ClassNotFoundException {
+ LayoutPullParser parser = createParserFromPath("allwidgets.xml");
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setConfigGenerator(ConfigGenerator.NEXUS_5)
+ .setCallback(layoutLibCallback)
+ .build();
+ params.setCustomContentHierarchyParser(viewObject -> {
+ List<ViewInfo> result = new ArrayList<>();
+ if (viewObject instanceof ViewGroup) {
+ ViewGroup view = (ViewGroup)viewObject;
+ for (int i = 0; i < view.getChildCount(); i++) {
+ View child = view.getChildAt(i);
+ ViewInfo childInfo =
+ new ViewInfo(child.toString(), null, child.getLeft(), child.getTop(),
+ child.getRight(), child.getBottom(), child,
+ child.createAccessibilityNodeInfo(), null);
+ childInfo.setChildren(null);
+ result.add(childInfo);
+ }
+ }
+ return result;
+ });
+ RenderSession session = sBridge.createSession(params);
+ try {
+ Result renderResult = session.render(50000);
+ assertTrue(renderResult.isSuccess());
+ ViewInfo contentRootViewInfo = session.getRootViews().get(0);
+ contentRootViewInfo.getChildren().forEach(child -> {
+ assertNotNull(child.getAccessibilityObject());
+ assertEquals(0, child.getChildren().size());
+ });
+ } finally {
+ session.dispose();
+ }
+ }
+
+ @Test
+ public void testDialogAccessibility() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <com.android.layoutlib.test.myapplication.widgets.DialogView\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_width=\"wrap_content\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+ RenderSession session = sBridge.createSession(params);
+ session.setElapsedFrameTimeNanos(1);
+ try {
+ Result renderResult = session.render(50000);
+ assertTrue(renderResult.isSuccess());
+ assertEquals(0, AccessibilityInteractionClient.sConnectionCache.size());
+ View rootView =
+ (View)((View) session.getSystemRootViews().get(1).getViewObject()).getParent();
+ int[] counter = {0};
+ session.execute(() -> {
+ AccessibilityNodeInfo rootNode = rootView.createAccessibilityNodeInfo();
+ assertNotNull(rootNode);
+ rootNode.setQueryFromAppProcessEnabled(rootView, true);
+ traverseAccessibilityTree(rootNode, counter);
+ });
+ assertEquals(0, AccessibilityInteractionClient.sConnectionCache.size());
+ assertEquals(17, counter[0]);
+ } finally {
+ session.dispose();
+ }
+ }
+
+ private void traverseAccessibilityTree(AccessibilityNodeInfo node, int[] counter) {
+ int childrenSize = node.getChildCount();
+ for (int i = 0; i < childrenSize; i++) {
+ AccessibilityNodeInfo child = node.getChild(i);
+ counter[0]++;
+ traverseAccessibilityTree(child, counter);
+ }
+ }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
index b12adc93e4..83a4f80111 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/BridgeContextTest.java
@@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.Bridge;
import com.android.layoutlib.bridge.impl.RenderAction;
import com.android.layoutlib.bridge.impl.RenderActionTestUtil;
import com.android.layoutlib.bridge.intensive.LayoutLibTestCallback;
+import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
import org.junit.BeforeClass;
@@ -33,10 +34,13 @@ import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.os.PowerManager;
import android.util.DisplayMetrics;
+import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertNotEquals;
+import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertTrue;
@@ -174,12 +178,12 @@ public class BridgeContextTest extends RenderTestBase {
((DynamicRenderResources) context.getRenderResources()).setWallpaper(
"/com/android/layoutlib/testdata/wallpaper1.webp",
configuration.isNightModeActive());
- assertEquals(-13226195, context.getResources().getColor(android.R.color.system_neutral1_800, null));
+ assertEquals(-13029845, context.getResources().getColor(android.R.color.system_neutral1_800, null));
((DynamicRenderResources) context.getRenderResources()).setWallpaper(
"/com/android/layoutlib/testdata/wallpaper2.webp",
configuration.isNightModeActive());
- assertEquals(-13749969, context.getResources().getColor(android.R.color.system_neutral1_800, null));
+ assertEquals(-13946321, context.getResources().getColor(android.R.color.system_neutral1_800, null));
} finally {
context.disposeResources();
}
@@ -204,4 +208,63 @@ public class BridgeContextTest extends RenderTestBase {
assertFalse(powerManager.isPowerSaveMode());
assertTrue(powerManager.isInteractive());
}
+
+ @Test
+ public void testTypedValue() throws Exception {
+ // Setup
+ // Create the layout pull parser for our resources (empty.xml can not be part of the test
+ // app as it won't compile).
+ LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+ SessionParams params = getSessionParamsBuilder()
+ .setConfigGenerator(ConfigGenerator.NEXUS_4)
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .build();
+ DisplayMetrics metrics = new DisplayMetrics();
+ Configuration configuration = RenderAction.getConfiguration(params);
+
+ BridgeContext mContext =
+ new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
+ params.getAssets(), params.getLayoutlibCallback(), configuration,
+ params.getTargetSdkVersion(), params.isRtlSupported());
+
+ TypedValue outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
+ assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
+ assertNotEquals(0, outValue.data);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.colorError, outValue, true);
+ assertEquals(TypedValue.TYPE_INT_COLOR_RGB4, outValue.type);
+ assertEquals(-65536, outValue.data);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(attr.colorActivatedHighlight, outValue, true);
+ assertEquals(TypedValue.TYPE_INT_COLOR_ARGB4, outValue.type);
+ assertEquals(-872349952, outValue.data);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.isLightTheme, outValue, true);
+ assertEquals(TypedValue.TYPE_INT_BOOLEAN, outValue.type);
+ assertEquals(1, outValue.data);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.scrollbarFadeDuration, outValue, true);
+ assertEquals(TypedValue.TYPE_INT_DEC, outValue.type);
+ assertEquals(250, outValue.data);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.scrollbarThumbHorizontal, outValue, true);
+ assertEquals(TypedValue.TYPE_STRING, outValue.type);
+ assertNotNull(outValue.string);
+
+ outValue = new TypedValue();
+ mContext.resolveThemeAttribute(android.R.attr.actionBarSize, outValue, true);
+ assertEquals(TypedValue.TYPE_DIMENSION, outValue.type);
+ assertEquals(14337, outValue.data);
+ }
}
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/android/DynamicRenderResourcesTest.java b/bridge/tests/src/com/android/layoutlib/bridge/android/DynamicRenderResourcesTest.java
index 16a809b3e7..ec7aa768fd 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/android/DynamicRenderResourcesTest.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/android/DynamicRenderResourcesTest.java
@@ -50,9 +50,9 @@ public class DynamicRenderResourcesTest extends RenderTestBase {
assertEquals(-4478092, (int)dynamicColorMap.get("system_accent3_300"));
assertEquals(-12963835, (int)dynamicColorMap.get("system_accent3_800"));
assertEquals(-1, (int)dynamicColorMap.get("system_neutral1_0"));
- assertEquals(-266518, (int)dynamicColorMap.get("system_neutral1_50"));
- assertEquals(-4937306, (int)dynamicColorMap.get("system_neutral1_300"));
- assertEquals(-13226195, (int)dynamicColorMap.get("system_neutral1_800"));
+ assertEquals(-4632, (int)dynamicColorMap.get("system_neutral1_50"));
+ assertEquals(-4675421, (int)dynamicColorMap.get("system_neutral1_300"));
+ assertEquals(-13029845, (int)dynamicColorMap.get("system_neutral1_800"));
assertEquals(-1, (int)dynamicColorMap.get("system_neutral2_0"));
assertEquals(-4632, (int)dynamicColorMap.get("system_neutral2_50"));
assertEquals(-4413535, (int)dynamicColorMap.get("system_neutral2_300"));
diff --git a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
index 7b9c163634..c5ef0e0488 100644
--- a/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
+++ b/bridge/tests/src/com/android/layoutlib/bridge/intensive/RenderTests.java
@@ -46,7 +46,6 @@ import org.junit.Test;
import org.kxml2.io.KXmlParser;
import org.xmlpull.v1.XmlPullParser;
-import android.R.attr;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.res.AssetManager;
@@ -57,7 +56,6 @@ import android.content.res.Resources_Delegate;
import android.graphics.Color;
import android.util.DisplayMetrics;
import android.util.StateSet;
-import android.util.TypedValue;
import android.widget.Button;
import android.widget.LinearLayout;
@@ -72,10 +70,10 @@ import java.io.PrintWriter;
import java.lang.reflect.Field;
import java.util.concurrent.TimeUnit;
+import static android.os._Original_Build.VERSION.SDK_INT;
import static org.junit.Assert.assertArrayEquals;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertFalse;
-import static org.junit.Assert.assertNotEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
@@ -974,61 +972,6 @@ public class RenderTests extends RenderTestBase {
}
@Test
- public void testTypedValue() throws Exception {
- // Setup
- // Create the layout pull parser for our resources (empty.xml can not be part of the test
- // app as it won't compile).
- LayoutPullParser parser = LayoutPullParser.createFromPath("/empty.xml");
- // Create LayoutLibCallback.
- LayoutLibTestCallback layoutLibCallback =
- new LayoutLibTestCallback(RenderTestBase.getLogger(), mDefaultClassLoader);
- layoutLibCallback.initResources();
- SessionParams params = getSessionParamsBuilder()
- .setConfigGenerator(ConfigGenerator.NEXUS_4)
- .setParser(parser)
- .setCallback(layoutLibCallback)
- .build();
- DisplayMetrics metrics = new DisplayMetrics();
- Configuration configuration = RenderAction.getConfiguration(params);
-
- BridgeContext mContext =
- new BridgeContext(params.getProjectKey(), metrics, params.getResources(),
- params.getAssets(), params.getLayoutlibCallback(), configuration,
- params.getTargetSdkVersion(), params.isRtlSupported());
-
- TypedValue outValue = new TypedValue();
- mContext.resolveThemeAttribute(android.R.attr.colorPrimary, outValue, true);
- assertEquals(TypedValue.TYPE_INT_COLOR_ARGB8, outValue.type);
- assertNotEquals(0, outValue.data);
-
- outValue = new TypedValue();
- mContext.resolveThemeAttribute(android.R.attr.colorError, outValue, true);
- assertEquals(TypedValue.TYPE_INT_COLOR_RGB4, outValue.type);
- assertEquals(-65536, outValue.data);
-
- outValue = new TypedValue();
- mContext.resolveThemeAttribute(attr.colorActivatedHighlight, outValue, true);
- assertEquals(TypedValue.TYPE_INT_COLOR_ARGB4, outValue.type);
- assertEquals(-872349952, outValue.data);
-
- outValue = new TypedValue();
- mContext.resolveThemeAttribute(android.R.attr.isLightTheme, outValue, true);
- assertEquals(TypedValue.TYPE_INT_BOOLEAN, outValue.type);
- assertEquals(1, outValue.data);
-
- outValue = new TypedValue();
- mContext.resolveThemeAttribute(android.R.attr.scrollbarFadeDuration, outValue, true);
- assertEquals(TypedValue.TYPE_INT_DEC, outValue.type);
- assertEquals(250, outValue.data);
-
- outValue = new TypedValue();
- mContext.resolveThemeAttribute(android.R.attr.scrollbarThumbHorizontal, outValue, true);
- assertEquals(TypedValue.TYPE_STRING, outValue.type);
- assertNotNull(outValue.string);
- assertTrue(sRenderMessages.isEmpty());
- }
-
- @Test
public void testColorStateList() throws Exception {
final String STATE_LIST =
"<selector xmlns:android=\"http://schemas.android.com/apk/res/android\">\n" +
@@ -2130,4 +2073,106 @@ public class RenderTests extends RenderTestBase {
renderAndVerify(params, "html.png", TimeUnit.SECONDS.toNanos(2));
}
+
+ @Test
+ public void testStatusBar() throws ClassNotFoundException {
+ final String layout =
+ "<FrameLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:layout_width=\"match_parent\"\n" +
+ " android:layout_height=\"match_parent\">\n" + "\n" +
+ " <TextView\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:text=\"Test status bar colour\"\n" +
+ " android:textSize=\"30sp\"/>\n" +
+ "</FrameLayout>";
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(LayoutPullParser.createFromString(layout))
+ .setCallback(layoutLibCallback)
+ .setTheme("DarkStatusBarTheme", true)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .build();
+
+ renderAndVerify(params, "dark_status_bar.png", TimeUnit.SECONDS.toNanos(2));
+
+ params = getSessionParamsBuilder()
+ .setParser(LayoutPullParser.createFromString(layout))
+ .setCallback(layoutLibCallback)
+ .setTheme("LightStatusBarTheme", true)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .build();
+
+ renderAndVerify(params, "light_status_bar.png", TimeUnit.SECONDS.toNanos(2));
+ }
+
+ @Test
+ public void testSoftwareLayer() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <com.android.layoutlib.test.myapplication.widgets.SoftwareTextView\n" +
+ " android:layout_height=\"200dp\"\n" +
+ " android:layout_width=\"wrap_content\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .disableDecoration()
+ .build();
+
+ renderAndVerify(params, "software_layer.png",
+ TimeUnit.SECONDS.toNanos(2));
+ }
+
+ @Test
+ public void testHighSimulatedSdk() throws Exception {
+ String layout =
+ "<LinearLayout xmlns:android=\"http://schemas.android.com/apk/res/android\"\n" +
+ " android:padding=\"16dp\"\n" +
+ " android:orientation=\"horizontal\"\n" +
+ " android:layout_width=\"fill_parent\"\n" +
+ " android:layout_height=\"fill_parent\">\n" +
+ " <TextView\n" +
+ " android:layout_height=\"wrap_content\"\n" +
+ " android:layout_width=\"wrap_content\"\n" +
+ " android:text=\"This is a TextView\" />\n" +
+ "</LinearLayout>\n";
+ LayoutPullParser parser = LayoutPullParser.createFromString(layout);
+ // Create LayoutLibCallback.
+ LayoutLibTestCallback layoutLibCallback =
+ new LayoutLibTestCallback(getLogger(), mDefaultClassLoader);
+ layoutLibCallback.initResources();
+
+ SessionParams params = getSessionParamsBuilder()
+ .setParser(parser)
+ .setCallback(layoutLibCallback)
+ .setTheme("Theme.Material.Light.NoActionBar.Fullscreen", false)
+ .setRenderingMode(RenderingMode.V_SCROLL)
+ .setSimulatedSdk(SDK_INT + 1)
+ .disableDecoration()
+ .build();
+
+ render(sBridge, params, -1);
+ boolean hasApiError = sRenderMessages.removeIf(message -> message.equals(String.format(
+ "The current rendering only supports APIs up to %d. You may encounter crashes if " +
+ "using with higher APIs. To avoid, you can set a lower API for your " +
+ "previews.", SDK_INT)));
+ assertTrue(hasApiError);
+ }
}
diff --git a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
index fd5e7f1780..44b42aef4c 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/LayoutValidatorTests.java
@@ -21,6 +21,7 @@ import com.android.layoutlib.bridge.android.RenderTestBase;
import com.android.layoutlib.bridge.intensive.LayoutLibTestCallback;
import com.android.layoutlib.bridge.intensive.setup.ConfigGenerator;
import com.android.layoutlib.bridge.intensive.setup.LayoutPullParser;
+import com.android.layoutlib.common.util.ReflectionUtils;
import com.android.tools.idea.validator.ValidatorData.CompoundFix;
import com.android.tools.idea.validator.ValidatorData.Issue;
import com.android.tools.idea.validator.ValidatorData.Level;
@@ -29,7 +30,9 @@ import com.android.tools.idea.validator.ValidatorData.Type;
import org.junit.Test;
+import android.util.SparseArray;
import android.view.View;
+import android.view.accessibility.AccessibilityInteractionClient;
import java.util.ArrayList;
import java.util.EnumSet;
@@ -73,6 +76,9 @@ public class LayoutValidatorTests extends RenderTestBase {
.build();
renderAndVerify(params, "a11y_test1.png");
+ Object connectionCache = ReflectionUtils.getFieldValue(AccessibilityInteractionClient.class,
+ AccessibilityInteractionClient.getInstance(), "sConnectionCache");
+ assertEquals(0, ((SparseArray)connectionCache).size());
}
@Test
@@ -83,6 +89,8 @@ public class LayoutValidatorTests extends RenderTestBase {
null,
SCALE_X_FOR_NEXUS_5,
SCALE_Y_FOR_NEXUS_5);
+ assertEquals(4, result.getSrcMap().size());
+ assertEquals(4, result.getNodeInfoMap().size());
assertEquals(31, result.getIssues().size());
ArrayList<Issue> errorIssues = new ArrayList<>();
for (Issue issue : result.getIssues()) {
diff --git a/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
index 618200c921..b7e0e135f4 100644
--- a/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
+++ b/bridge/tests/src/com/android/tools/idea/validator/ValidatorResultTests.java
@@ -39,6 +39,7 @@ public class ValidatorResultTests {
assertNotNull(result);
assertTrue(result.getIssues().isEmpty());
assertTrue(result.getSrcMap().isEmpty());
+ assertTrue(result.getNodeInfoMap().isEmpty());
assertNotNull(result.getMetric());
assertEquals("Result containing 0 issues:\n", result.toString());
}
diff --git a/common/src/com/android/tools/layoutlib/create/NativeConfig.java b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
index 60a65efd95..339ce786ee 100644
--- a/common/src/com/android/tools/layoutlib/create/NativeConfig.java
+++ b/common/src/com/android/tools/layoutlib/create/NativeConfig.java
@@ -109,6 +109,7 @@ public class NativeConfig {
"android.provider.DeviceConfig#getLong",
"android.provider.DeviceConfig#getProperty",
"android.provider.DeviceConfig#getString",
+ "android.provider.Settings$Config#getContentResolver",
"android.text.format.DateFormat#is24HourFormat",
"android.util.Xml#newPullParser",
"android.view.Choreographer#getFrameTimeNanos",
@@ -120,7 +121,6 @@ public class NativeConfig {
"android.view.DisplayEventReceiver#nativeGetDisplayEventReceiverFinalizer",
"android.view.DisplayEventReceiver#nativeInit",
"android.view.HandlerActionQueue#postDelayed",
- "android.view.LayoutInflater#initPrecompiledViews",
"android.view.LayoutInflater#parseInclude",
"android.view.LayoutInflater#rInflate",
"android.view.MenuInflater#registerMenu",
@@ -137,6 +137,8 @@ public class NativeConfig {
"android.view.View#measure",
"android.view.ViewRootImpl#performHapticFeedback",
"android.view.WindowManagerGlobal#getWindowManagerService",
+ "android.view.accessibility.AccessibilityManager#getInstance",
+ "android.view.accessibility.AccessibilityManager#getWindowTransformationSpec",
"android.view.inputmethod.InputMethodManager#hideSoftInputFromWindow",
"android.view.inputmethod.InputMethodManager#isInEditMode",
"android.view.inputmethod.InputMethodManager#showSoftInput",
@@ -146,6 +148,7 @@ public class NativeConfig {
"com.android.internal.util.XmlUtils#convertValueToInt",
"com.android.internal.view.menu.MenuBuilder#createNewMenuItem",
"dalvik.system.VMRuntime#getNotifyNativeInterval",
+ "dalvik.system.VMRuntime#is64Bit",
"dalvik.system.VMRuntime#newUnpaddedArray",
"libcore.io.MemoryMappedFile#bigEndianIterator",
"libcore.io.MemoryMappedFile#close",
diff --git a/create/Android.bp b/create/Android.bp
index 13a00a878f..a8dcdaa5eb 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -30,7 +30,7 @@ java_binary_host {
"guava",
"layoutlib-common",
"layoutlib_create-classpath",
- "atf-prebuilt-502584086",
+ "atf-prebuilt-557133692",
"libprotobuf-java-lite",
],
}
diff --git a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
index 98055e3a90..55de0afe0f 100644
--- a/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
+++ b/create/src/com/android/tools/layoutlib/create/AsmGenerator.java
@@ -94,6 +94,8 @@ public class AsmGenerator {
private final Set<MethodReplacer> mMethodReplacers;
private boolean mKeepAllNativeClasses;
+ /** A map { FQCN => set { field names } } which should have their final modifier removed */
+ private final Map<String, Set<String>> mRemoveFinalModifierFields;
/**
* Creates a new generator that can generate the output JAR with the stubbed classes.
@@ -218,6 +220,9 @@ public class AsmGenerator {
mRenameStaticInitializerClasses =
Arrays.stream(createInfo.getDeferredStaticInitializerClasses()).collect(Collectors.toSet());
+
+ mRemoveFinalModifierFields = new HashMap<>();
+ addToMap(createInfo.getRemovedFinalModifierFields(), mRemoveFinalModifierFields);
}
/**
@@ -427,6 +432,11 @@ public class AsmGenerator {
cv = new DeferStaticInitializerClassAdapter(cv);
}
+ Set<String> removeFinalModifierFields = mRemoveFinalModifierFields.get(className);
+ if (removeFinalModifierFields != null && !removeFinalModifierFields.isEmpty()) {
+ cv = new RemoveFinalModifierFieldClassAdapter(cv, removeFinalModifierFields);
+ }
+
// Make sure no class file has a version above 55 (corresponding to Java 11),
// so that layoutlib can be run with JDK 11.
cv = new ChangeFileVersionAdapter(mLog, 55, cv);
diff --git a/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index fb50c46ff1..fbfd66895f 100644
--- a/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -129,6 +129,11 @@ public final class CreateInfo implements ICreateInfo {
return DEFERRED_STATIC_INITIALIZER_CLASSES;
}
+ @Override
+ public String[] getRemovedFinalModifierFields() {
+ return REMOVED_FINAL_MODIFIER_FIELDS;
+ }
+
//-----
private static final MethodReplacer[] METHOD_REPLACERS = new MethodReplacer[] {
@@ -331,6 +336,11 @@ public final class CreateInfo implements ICreateInfo {
"android.view.Choreographer#mCallbackQueues", // required for tests only
"android.view.Choreographer$CallbackQueue#mHead", // required for tests only
"android.view.ViewRootImpl#mTmpFrames",
+ "android.view.accessibility.AccessibilityInteractionClient#sCaches",
+ "android.view.accessibility.AccessibilityInteractionClient#sClients",
+ "android.view.accessibility.AccessibilityInteractionClient#sConnectionCache",
+ "android.view.accessibility.AccessibilityInteractionClient#sDirectConnectionCount",
+ "android.view.accessibility.AccessibilityInteractionClient#sScrollingWindows",
"com.android.internal.util.ArrayUtils#sCache",
};
@@ -381,6 +391,12 @@ public final class CreateInfo implements ICreateInfo {
private final static Map<String, InjectMethodRunnable> INJECTED_METHODS = Map.of(
"android.content.Context", InjectMethodRunnables.CONTEXT_GET_FRAMEWORK_CLASS_LOADER);
+ /**
+ * List of fields for which we will remove the final modifier.
+ */
+ private final static String[] REMOVED_FINAL_MODIFIER_FIELDS =
+ new String[]{"android.animation.AnimationHandler#sAnimatorHandler"};
+
public static class LinkedHashMapEldestReplacer implements MethodReplacer {
private final String VOID_TO_MAP_ENTRY =
diff --git a/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
index b5c331d838..3ce901a10b 100644
--- a/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/DeferStaticInitializerClassAdapter.java
@@ -47,7 +47,7 @@ public class DeferStaticInitializerClassAdapter extends ClassVisitor {
// Java 9 does not allow static final field to be modified outside of <clinit>.
// So if a field is static, it has to be non-final.
if ((access & Opcodes.ACC_STATIC) != 0 ) {
- access = access & ~Opcodes.ACC_FINAL;;
+ access = access & ~Opcodes.ACC_FINAL;
}
return super.visitField(access, name, desc, signature, value);
}
diff --git a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
index 83c5b24523..e536bdc903 100644
--- a/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
+++ b/create/src/com/android/tools/layoutlib/create/ICreateInfo.java
@@ -129,6 +129,13 @@ public interface ICreateInfo {
String[] getDeferredStaticInitializerClasses();
+ /**
+ * Returns a list of fields which should have their final modifier removed.
+ * The array values are in the form of the binary FQCN of the class containing the field and
+ * the field name separated by a '#'.
+ */
+ String[] getRemovedFinalModifierFields();
+
interface MethodReplacer {
boolean isNeeded(String owner, String name, String desc, String sourceClass);
diff --git a/create/src/com/android/tools/layoutlib/create/RemoveFinalModifierFieldClassAdapter.java b/create/src/com/android/tools/layoutlib/create/RemoveFinalModifierFieldClassAdapter.java
new file mode 100644
index 0000000000..2f2d6007c7
--- /dev/null
+++ b/create/src/com/android/tools/layoutlib/create/RemoveFinalModifierFieldClassAdapter.java
@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2023 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.layoutlib.create;
+
+import org.objectweb.asm.ClassVisitor;
+import org.objectweb.asm.FieldVisitor;
+
+import java.util.Set;
+
+import static org.objectweb.asm.Opcodes.ACC_FINAL;
+
+/**
+ * Removes the final modifier from the given fields.
+ */
+public class RemoveFinalModifierFieldClassAdapter extends ClassVisitor {
+
+ private final Set<String> mFieldNames;
+
+ public RemoveFinalModifierFieldClassAdapter(ClassVisitor cv, Set<String> fieldNames) {
+ super(Main.ASM_VERSION, cv);
+ mFieldNames = fieldNames;
+ }
+
+ @Override
+ public FieldVisitor visitField(int access, String name, String desc, String signature,
+ Object value) {
+ if (mFieldNames.contains(name)) {
+ access = access & ~ACC_FINAL;
+ }
+ return super.visitField(access, name, desc, signature, value);
+ }
+}
diff --git a/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java b/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java
index 73a57a2190..322f0656a3 100644
--- a/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java
+++ b/create/src/com/android/tools/layoutlib/create/StubExceptionMethodAdapter.java
@@ -24,7 +24,7 @@ import org.objectweb.asm.Type;
/**
- * {@link MethodVisitor} that replaces the method the implementation of the method with
+ * {@link MethodVisitor} that replaces the implementation of the method with
* <code>
* throw new RuntimeException("Stub!");
* </code>
diff --git a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
index aadab22173..334bcd2aa1 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/AsmGeneratorTest.java
@@ -64,7 +64,7 @@ public class AsmGeneratorTest {
private String mOsDestJar;
private File mTempFile;
- // ASM internal name for the the class in java package that should be refactored.
+ // ASM internal name for the class in java package that should be refactored.
private static final String JAVA_CLASS_NAME = "notjava.lang.JavaClass";
@Before
diff --git a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
index 3b1cb06a1f..778a191aaf 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/CreateInfoAdapter.java
@@ -106,4 +106,9 @@ class CreateInfoAdapter implements ICreateInfo {
public String[] getDeferredStaticInitializerClasses() {
return EMPTY_STRING_ARRAY;
}
+
+ @Override
+ public String[] getRemovedFinalModifierFields() {
+ return EMPTY_STRING_ARRAY;
+ }
}
diff --git a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
index 3655ec23b7..0fa7ecb764 100644
--- a/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
+++ b/create/tests/src/com/android/tools/layoutlib/create/PromoteClassClassAdapterTest.java
@@ -155,7 +155,7 @@ public class PromoteClassClassAdapterTest {
PromoteClassClassAdapter adapter = new PromoteClassClassAdapter(log, Set.of(
PackageProtectedClass.class.getName()));
reader.accept(adapter, 0);
- assertTrue(log.mLog.contains("[visit] - version=61, access=[public], " +
+ assertTrue(log.mLog.contains("[visit] - version=55, access=[public], " +
"name=com/android/tools/layoutlib/create/PackageProtectedClass, signature=null, " +
"superName=java/lang/Object, interfaces=[]"));
diff --git a/split_universal_binary.sh b/split_universal_binary.sh
index bd57245a7a..55b5b3829b 100755
--- a/split_universal_binary.sh
+++ b/split_universal_binary.sh
@@ -26,6 +26,7 @@ done
# Put the single architecture binaries inside the DIST folder to be accessible on ab/
if [[ -d "${DIST_DIR}" ]]; then
+ mkdir -p ${DIST_DIR}/layoutlib_native/darwin
cp -r ${OUT_DIR}/${ARM} ${DIST_DIR}/layoutlib_native/darwin
cp -r ${OUT_DIR}/${X86} ${DIST_DIR}/layoutlib_native/darwin
fi
diff --git a/validator/resources/strings.properties b/validator/resources/strings.properties
index 27f310cfa3..3a07104c0c 100644
--- a/validator/resources/strings.properties
+++ b/validator/resources/strings.properties
@@ -16,6 +16,7 @@
#
result_message_not_important_for_accessibility = This item was not found to be important for accessibility.
result_message_no_content_desc = This item has no <tt>android:contentDescription</tt>.
+result_message_no_content_desc_generic = This item has no content description.
result_message_not_visible = This item is not visible.
result_message_not_enabled = This item isn\'t enabled.
result_message_not_text_view = This item is not a <tt>TextView</tt>.
@@ -38,6 +39,7 @@ non_clickable = non-clickable
long_clickable = long clickable
clickable_and_long_clickable = clickable and long clickable
actionable = actionable
+result_message_addendum_conflicting_elements_list = Conflicting element(s): <tt>%1$s</tt>.
check_title_duplicate_speakable_text = Item descriptions
result_message_brief_same_speakable_text = Multiple items have the same description.
result_message_same_speakable_text = This %1$s item\'s speakable text: \"<tt>%2$s</tt>\" is identical to that of %3$d other item(s).
@@ -55,10 +57,14 @@ result_message_image_customized_contrast_not_sufficient_confirmed = The image\'s
check_title_redundant_description = Item type label
result_message_english_locale_only = This check only runs on devices with locales set to English.
result_message_brief_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt> might contain unnecessary text.
+result_message_brief_content_desc_contains_redundant_word_generic = This item\'s content description might contain unnecessary text.
result_message_content_desc_ends_with_view_type = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" ends with the item\'s type.
result_message_content_desc_contains_redundant_word = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_redundant_word_generic = This item\'s content description, \"<tt>%1$s</tt>\" contains the item type \"<tt>%2$s</tt>\".
result_message_content_desc_contains_action = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_action_generic = This item\'s content description, \"<tt>%1$s</tt>\", contains the action \"<tt>%2$s</tt>\".
result_message_content_desc_contains_state = This item\'s <tt>android:contentDescription</tt>, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
+result_message_content_desc_contains_state_generic = This item\'s content description, \"<tt>%1$s</tt>\", contains the state \"<tt>%2$s</tt>\".
button_item_type = button
checkbox_item_type = checkbox
checkbox_item_type_separate_words = check box
@@ -73,6 +79,7 @@ check_title_speakable_text_present = Item label
result_message_should_not_focus = This item would not be focused by a screen reader.
result_message_web_content = Web content is not evaluated.
result_message_unsupported_compose_content = Composable content is not evaluated in this environment.
+result_message_unsupported_flutter_content = Flutter content is not evaluated in this environment.
result_message_missing_speakable_text = This item may not have a label readable by screen readers.
check_title_text_contrast = Text contrast
result_message_textview_empty = This <tt>TextView</tt> is empty.
@@ -158,12 +165,19 @@ result_message_item_type_with_text_size_unit = <tt>%1$s</tt> with text size unit
result_message_small_fixed_text_size = This text is small and may be difficult for some users to read. Consider using a larger size or specifying the text size in scaled pixels (<tt>sp</tt>).
result_message_fixed_text_size = Consider specifying the text size in scaled pixels (<tt>sp</tt>).
result_message_brief_fixed_width_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed width and scalable text.
+result_message_brief_fixed_width_text_view_with_scaled_text_compose = This <tt>Text</tt> has a fixed width and scalable text.
result_message_brief_fixed_height_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed height and scalable text.
+result_message_brief_fixed_height_text_with_scaled_text_compose = This <tt>Text</tt> has a fixed height and scalable text.
result_message_brief_fixed_size_text_view_with_scaled_text = This <tt>TextView</tt> has a fixed size and scalable text.
+result_message_brief_fixed_size_text_with_scaled_text_compose = This <tt>Text</tt> has a fixed size and scalable text.
result_message_brief_fixed_width_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed width and contains a <tt>TextView</tt> with scalable text.
+result_message_brief_fixed_width_parent_with_scaled_text_compose = This element has a fixed width and contains a <tt>Text</tt> element with scalable text.
result_message_brief_fixed_height_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed height and contains a <tt>TextView</tt> with scalable text.
+result_message_brief_fixed_height_parent_with_scaled_text_compose = This element has a fixed height and contains a <tt>Text</tt> element with scalable text.
result_message_brief_fixed_size_view_group_with_scaled_text = This <tt>ViewGroup</tt> has a fixed size and contains a <tt>TextView</tt> with scalable text.
+result_message_brief_fixed_size_parent_with_scaled_text_compose = This element has a fixed size and contains a <tt>Text</tt> element with scalable text.
result_message_fixed_size_text_view_with_scaled_text = Consider modifying the <tt>LayoutParams</tt> to allow for text expansion.
+result_message_fixed_size_text_with_scaled_text_compose = Consider modifying the size modifiers using <tt>sizeIn</tt> to allow for text expansion.
check_title_unexposed_text = Unexposed Text
result_message_unexposed_text = Ensure this item's accessibility label includes its visible text.
result_message_text_detected_in_image_view = OCR results were detected inside this ImageView.
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorResult.java b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
index 7f25d46489..9d708b5bdf 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorResult.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorResult.java
@@ -21,6 +21,7 @@ import com.android.tools.idea.validator.ValidatorData.Level;
import com.android.tools.layoutlib.annotations.NotNull;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
@@ -35,14 +36,21 @@ import com.google.common.collect.ImmutableBiMap;
public class ValidatorResult {
@NotNull private final ImmutableBiMap<Long, View> mSrcMap;
+ @NotNull private final ImmutableBiMap<Long, AccessibilityNodeInfo> mNodeInfoMap;
@NotNull private final ArrayList<Issue> mIssues;
@NotNull private final Metric mMetric;
/**
* Please use {@link Builder} for creating results.
*/
- private ValidatorResult(BiMap<Long, View> srcMap, ArrayList<Issue> issues, Metric metric) {
+ private ValidatorResult(BiMap<Long, View> srcMap,
+ BiMap<Long, AccessibilityNodeInfo> nodeInfoMap,
+ ArrayList<Issue> issues,
+ Metric metric) {
mSrcMap = ImmutableBiMap.<Long, View>builder().putAll(srcMap).build();
+ mNodeInfoMap = ImmutableBiMap.<Long, AccessibilityNodeInfo>builder()
+ .putAll(nodeInfoMap)
+ .build();
mIssues = issues;
mMetric = metric;
}
@@ -55,6 +63,13 @@ public class ValidatorResult {
}
/**
+ * @return the map from source ID to AccessibilityNodeInfo.
+ */
+ public ImmutableBiMap<Long, AccessibilityNodeInfo> getNodeInfoMap() {
+ return mNodeInfoMap;
+ }
+
+ /**
* @return list of issues.
*/
public List<Issue> getIssues() {
@@ -89,11 +104,12 @@ public class ValidatorResult {
public static class Builder {
@NotNull public final BiMap<Long, View> mSrcMap = HashBiMap.create();
+ @NotNull public final BiMap<Long, AccessibilityNodeInfo> mNodeInfoMap = HashBiMap.create();
@NotNull public final ArrayList<Issue> mIssues = new ArrayList<>();
@NotNull public final Metric mMetric = new Metric();
public ValidatorResult build() {
- return new ValidatorResult(mSrcMap, mIssues, mMetric);
+ return new ValidatorResult(mSrcMap, mNodeInfoMap, mIssues, mMetric);
}
}
diff --git a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
index 6078046de5..fa2862d191 100644
--- a/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
+++ b/validator/src/com/android/tools/idea/validator/ValidatorUtil.java
@@ -83,8 +83,6 @@ public class ValidatorUtil {
* uses be redirected.
*/
StringManager.setResourceBundleProvider(locale -> ResourceBundle.getBundle("strings"));
- // Enable using AccessibilityNodeInfo in addition to View for accessibility testing
- AccessibilityHierarchyAndroid.viewOverlayEnabled = true;
}
// Visible for testing.
@@ -130,7 +128,9 @@ public class ValidatorUtil {
try {
hierarchy.mView = AccessibilityHierarchyAndroid
.newBuilder(view)
+ .enableViewOverlay()
.setViewOriginMap(builder.mSrcMap)
+ .setNodeInfoOriginMap(builder.mNodeInfoMap)
.setObtainCharacterLocations(LayoutValidator.obtainCharacterLocations())
.setCharacterLocationArgMaxLength(CHARACTER_LOCATION_ARG_MAX_LENGTH)
.setCustomViewBuilder(new CustomViewBuilderAndroid() {
diff --git a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
index eee2f32c36..5c8b2e9a39 100644
--- a/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
+++ b/validator/src/com/android/tools/idea/validator/hierarchy/CustomHierarchyHelper.java
@@ -48,13 +48,29 @@ public class CustomHierarchyHelper {
// This is required as layoutlib does not know the support library such as
// MaterialButton. LayoutlibCallback calls for studio which understands all the maven
// pulled library.
- Class button = callback.findClass(
+ Class<?> button = callback.findClass(
"com.google.android.material.button.MaterialButton");
if (button.isInstance(fromView)) {
Method isCheckable = button.getMethod("isCheckable");
Object toReturn = isCheckable.invoke(fromView);
return (toReturn instanceof Boolean) && ((Boolean) toReturn);
}
+
+ Class<?> card = callback.findClass(
+ "com.google.android.material.card.MaterialCardView");
+ if (card.isInstance(fromView)) {
+ Method isCheckable = card.getMethod("isCheckable");
+ Object toReturn = isCheckable.invoke(fromView);
+ return (toReturn instanceof Boolean) && ((Boolean) toReturn);
+ }
+
+ Class<?> chip = callback.findClass(
+ "com.google.android.material.chip.Chip");
+ if (chip.isInstance(fromView)) {
+ Method isCheckable = chip.getMethod("isCheckable");
+ Object toReturn = isCheckable.invoke(fromView);
+ return (toReturn instanceof Boolean) && ((Boolean) toReturn);
+ }
} catch (ClassNotFoundException |
NoSuchMethodException |
IllegalAccessException |