Snap for 11566117 from 079b068bc74a02f8f38fc9831337fe59827e7f48 to sdk-release
Change-Id: Icb5812ccd75fd2b0d8549255bb2135291bc31a7b
diff --git a/.idea/misc.xml b/.idea/misc.xml
index 0a1e4e0..d47ec03 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 76bc916..5fd342d 100644
--- a/Android.bp
+++ b/Android.bp
@@ -30,7 +30,7 @@
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 35ade34..c0e22d3 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 @@
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 @@
}
@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 @@
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 da59fb7..ae6f538 100644
--- a/bridge/src/android/content/res/BridgeTypedArray.java
+++ b/bridge/src/android/content/res/BridgeTypedArray.java
@@ -907,7 +907,7 @@
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 7aa02f9..4eb6bdd 100644
--- a/bridge/src/android/content/res/Resources_Delegate.java
+++ b/bridge/src/android/content/res/Resources_Delegate.java
@@ -87,6 +87,8 @@
"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 0000000..fe269c7
--- /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 1aad830..642b015 100644
--- a/bridge/src/android/permission/PermissionManager_Delegate.java
+++ b/bridge/src/android/permission/PermissionManager_Delegate.java
@@ -23,7 +23,7 @@
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 0000000..ae802dd
--- /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 8a78fa3..07bc437 100644
--- a/bridge/src/android/util/BridgeXmlPullAttributes.java
+++ b/bridge/src/android/util/BridgeXmlPullAttributes.java
@@ -151,6 +151,9 @@
@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 cd35899..b8c8f0e 100644
--- a/bridge/src/android/view/AttachInfo_Accessor.java
+++ b/bridge/src/android/view/AttachInfo_Accessor.java
@@ -37,6 +37,8 @@
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 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 ec8476d..ad9a442 100644
--- a/bridge/src/android/view/BridgeInflater.java
+++ b/bridge/src/android/view/BridgeInflater.java
@@ -250,6 +250,9 @@
*/
@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 f1d4e65..0ca568c 100644
--- a/bridge/src/android/view/DisplayEventReceiver_Delegate.java
+++ b/bridge/src/android/view/DisplayEventReceiver_Delegate.java
@@ -20,6 +20,7 @@
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 @@
@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 cb446e7..51c413d 100644
--- a/bridge/src/android/view/LayoutInflater_Delegate.java
+++ b/bridge/src/android/view/LayoutInflater_Delegate.java
@@ -233,15 +233,4 @@
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 ebb2af4..2c1d674 100644
--- a/bridge/src/android/view/SurfaceView.java
+++ b/bridge/src/android/view/SurfaceView.java
@@ -18,11 +18,17 @@
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 @@
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 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 @@
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 81ffe2e..d15952a 100644
--- a/bridge/src/android/view/ViewRootImpl_Accessor.java
+++ b/bridge/src/android/view/ViewRootImpl_Accessor.java
@@ -26,8 +26,17 @@
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 eb1e22c..285ca9e 100644
--- a/bridge/src/android/view/WindowManagerImpl.java
+++ b/bridge/src/android/view/WindowManagerImpl.java
@@ -41,6 +41,8 @@
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 @@
}
}
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 @@
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 0000000..8c87c22
--- /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 0000000..9408f79
--- /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 3c62193..ce0fb2d 100644
--- a/bridge/src/com/android/layoutlib/bridge/Bridge.java
+++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java
@@ -337,7 +337,7 @@
}
/**
- * 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 a0cf2b2..9bffc23 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 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.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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
@Override
public ContentResolver getContentResolver() {
if (mContentResolver == null) {
- mContentResolver = new BridgeContentResolver(this);
+ mContentResolver = new BridgeContentResolver(getApplicationContext());
}
return mContentResolver;
}
@@ -2289,4 +2312,9 @@
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 85bf637..c376429 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 @@
}
@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 5fe116e..0122da1 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 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 d89960e..7949908 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 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 @@
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 63efec3..98378e6 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.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.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 @@
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 @@
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 @@
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 dc823f7..6842333 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.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 @@
@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 @@
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 @@
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 c8e5009..1ca3b9c 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 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 @@
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 c2430a8..98b5956 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.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.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.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 @@
*
*/
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 @@
*/
protected RenderAction(T params) {
mParams = params;
+ sSimulatedSdk = SDK_INT;
}
/**
@@ -276,6 +286,7 @@
ILayoutLog currentLog = mParams.getLog();
Bridge.setLog(currentLog);
mContext.getRenderResources().setLogger(currentLog);
+ AnimationHandler.sAnimatorHandler = mContext.getAnimationHandlerThreadLocal();
}
/**
@@ -303,6 +314,7 @@
ParserFactory.setParserFactory(null);
PropertyValuesHolder_Accessor.clearClassCaches();
+ AccessibilityInteractionClient_Accessor.clearCaches();
}
public static BridgeContext getCurrentContext() {
@@ -467,6 +479,14 @@
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 6a6e184..0dd35ce 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.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 @@
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 @@
// 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,47 +141,31 @@
// 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(),
+
+ 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);
+ bitmap = Bitmap.createScaledBitmap(bitmap, (int) (w * scale), (int) (h * scale), true);
+ }
+
+ // Copy bitmap into BufferedImage.
+ BufferedImage image = new BufferedImage(bitmap.getWidth(), bitmap.getHeight(),
+ BufferedImage.TYPE_INT_ARGB);
+ int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData();
+ bitmap.getPixels(imageData, 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);
return image;
}
-
- @NonNull
- protected BufferedImage getImage(int w, int h) {
- BufferedImage image = new BufferedImage(w > 0 ? w : 1,
- h > 0 ? h : 1,
- BufferedImage.TYPE_INT_ARGB);
- Graphics2D gc = image.createGraphics();
- gc.setComposite(AlphaComposite.Src);
-
- gc.setColor(new Color(0x00000000, true));
- gc.fillRect(0, 0, w, h);
-
- // done
- gc.dispose();
-
- 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 ae5e401..9ed5d17 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.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.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 @@
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 @@
}
/**
- * 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 @@
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 @@
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 @@
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 @@
}
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 @@
*
* @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 @@
* 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 @@
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 @@
} 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 @@
* 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 @@
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 @@
}
}
+ @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 @@
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 87bed3d..358795f 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 @@
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 @@
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 9fea167..6f9092c 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 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 84ed6a0..f8884d4 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.drawable.BitmapDrawable;
import android.widget.ImageView;
-import java.io.IOException;
import java.io.InputStream;
public class SysUiResources {
@@ -67,10 +66,8 @@
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 @@
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 2fe1015..23faf55 100644
--- a/bridge/src/dalvik/system/VMRuntime_Delegate.java
+++ b/bridge/src/dalvik/system/VMRuntime_Delegate.java
@@ -36,4 +36,9 @@
/*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 a05349d..4546682 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 2d6938a..b438464 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 5570291..e939a57 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 61f1f18..b70c65d 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 dd1dd57..31647c6 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 e0b6037..67deb6e 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 3e41ccc..6e63ef2 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 48a40cd..7ac28b6 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 f8cec32..50b5f26 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 c00823d..3887e29 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 db0b343..9588148 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 b6193f6..f4467d6 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 6a23995..7cabc39 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 20f15a9..5be2d1a 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 323d514..9e08e224 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 0000000..34ff1ad
--- /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 5ed2706..7b1f1f1 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 80e72b0..d6a4c5c 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 82e40ac..4e7feb4 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 0000000..a37d8de
--- /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 fefb3b7..6403637 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 d69667c..4051b05 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 4d9f776..e27ae9e 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 118e926..fab98dc 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 19e65aa..55a9982 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 84b7299..6093824 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 7d9a51d..f1437af 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 2b2aab8..1300dd0 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 38924a3..7852c9e 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 0000000..70465cf
--- /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 5832c67..03da31e 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 769406e..67e502f 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 6882d79..4b1425a 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 759d535..055af89 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 4396a51..99e37ae 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 0000000..3d8cb0c
--- /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 7b338d1..d75aab1 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 91e88b6..bf33810 100755
--- a/bridge/tests/run_tests.sh
+++ b/bridge/tests/run_tests.sh
@@ -9,7 +9,7 @@
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 @@
# 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 3975927..dba0f76 100644
--- a/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
+++ b/bridge/tests/src/android/content/res/BridgeTypedArrayTest.java
@@ -52,6 +52,7 @@
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 f995d1a..a40dfed 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.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.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 @@
when(parser.getAttributeName(1)).thenReturn("my_custom_attr");
when(parser.getAttributeNamespace(1)).thenReturn(BridgeConstants.NS_APP_RES_AUTO);
- BridgeContext context = mock(BridgeContext.class);
- when(context.getRenderResources()).thenReturn(renderResources);
+ 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 @@
"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 ed90d9b..e1cad4e 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 @@
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.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 @@
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 @@
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 b12adc9..83a4f80 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.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.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 @@
((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 @@
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 16a809b..ec7aa76 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 @@
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 7b9c163..c5ef0e0 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.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.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.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 @@
}
@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 @@
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 fd5e7f1..44b42ae 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.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 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 @@
.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 @@
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 618200c..b7e0e13 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 @@
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 60a65ef..339ce78 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 @@
"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 @@
"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 @@
"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 @@
"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 13a00a8..a8dcdaa 100644
--- a/create/Android.bp
+++ b/create/Android.bp
@@ -30,7 +30,7 @@
"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 98055e3..55de0af 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 @@
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 @@
mRenameStaticInitializerClasses =
Arrays.stream(createInfo.getDeferredStaticInitializerClasses()).collect(Collectors.toSet());
+
+ mRemoveFinalModifierFields = new HashMap<>();
+ addToMap(createInfo.getRemovedFinalModifierFields(), mRemoveFinalModifierFields);
}
/**
@@ -427,6 +432,11 @@
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 fb50c46..fbfd668 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 @@
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 @@
"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 @@
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 b5c331d..3ce901a 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 @@
// 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 83c5b24..e536bdc 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 @@
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 0000000..2f2d600
--- /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 73a57a2..322f065 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 @@
/**
- * {@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 aadab22..334bcd2 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 @@
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 3b1cb06..778a191 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 @@
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 3655ec2..0fa7ecb 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 @@
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 bd57245..55b5b38 100755
--- a/split_universal_binary.sh
+++ b/split_universal_binary.sh
@@ -26,6 +26,7 @@
# 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 27f310c..3a07104 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 @@
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 @@
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 @@
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_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 7f25d46..9d708b5 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.layoutlib.annotations.NotNull;
import android.view.View;
+import android.view.accessibility.AccessibilityNodeInfo;
import java.util.ArrayList;
import java.util.List;
@@ -35,14 +36,21 @@
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 @@
}
/**
+ * @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 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 6078046..fa2862d 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 @@
* 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 @@
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 eee2f32..5c8b2e9 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 @@
// 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 |