diff options
Diffstat (limited to 'bridge/src/com/android')
14 files changed, 236 insertions, 141 deletions
diff --git a/bridge/src/com/android/layoutlib/bridge/Bridge.java b/bridge/src/com/android/layoutlib/bridge/Bridge.java index 3c62193dad..ce0fb2ddd8 100644 --- a/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -337,7 +337,7 @@ public final class Bridge extends com.android.ide.common.rendering.api.Bridge { } /** - * Tests if the field is pubic, static and one of int or int[]. + * Tests if the field is public, static and one of int or int[]. */ private static boolean isValidRField(Field field) { int modifiers = field.getModifiers(); diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index a0cf2b23b2..9bffc23eb3 100644 --- a/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java +++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -40,6 +40,7 @@ import com.android.tools.layoutlib.annotations.NotNull; import org.xmlpull.v1.XmlPullParser; import org.xmlpull.v1.XmlPullParserException; +import android.animation.AnimationHandler; import android.annotation.NonNull; import android.annotation.Nullable; import android.app.ActivityManager; @@ -81,6 +82,8 @@ import android.os.Handler; import android.os.IBinder; import android.os.IInterface; import android.os.Looper; +import android.os.NullVibrator; +import android.os.NullVibratorManager; import android.os.Parcel; import android.os.PowerManager; import android.os.RemoteException; @@ -170,7 +173,7 @@ public class BridgeContext extends Context { private final LayoutlibCallback mLayoutlibCallback; private final WindowManager mWindowManager; private final DisplayManager mDisplayManager; - private final AutofillManager mAutofillManager; + private AutofillManager mAutofillManager; private final ClipboardManager mClipboardManager; private final ActivityManager mActivityManager; private final ConnectivityManager mConnectivityManager; @@ -199,10 +202,12 @@ public class BridgeContext extends Context { private Boolean mIsThemeAppCompat; private boolean mUseThemedIcon; private Context mApplicationContext; + private AccessibilityManager mAccessibilityManager; private final ResourceNamespace mAppCompatNamespace; private final Map<Key<?>, Object> mUserData = new HashMap<>(); private final SessionInteractiveData mSessionInteractiveData; + private final ThreadLocal<AnimationHandler> mAnimationHandlerThreadLocal = new ThreadLocal<>(); /** * Some applications that target both pre API 17 and post API 17, set the newer attrs to @@ -263,7 +268,6 @@ public class BridgeContext extends Context { mWindowManager = new WindowManagerImpl(this, mMetrics); mDisplayManager = new DisplayManager(this); - mAutofillManager = new AutofillManager(this, new Default()); mClipboardManager = new ClipboardManager(this, null); mActivityManager = ActivityManager_Accessor.getActivityManagerInstance(this); mConnectivityManager = new ConnectivityManager(this, null); @@ -462,9 +466,12 @@ public class BridgeContext extends Context { try { outValue.data = Integer.parseInt(stringValue); outValue.type = TypedValue.TYPE_INT_DEC; - } catch (NumberFormatException e) { - outValue.type = TypedValue.TYPE_STRING; - outValue.string = stringValue; + } + catch (NumberFormatException e) { + if (!ResourceHelper.parseFloatAttribute(null, stringValue, outValue, false)) { + outValue.type = TypedValue.TYPE_STRING; + outValue.string = stringValue; + } } } } @@ -601,6 +608,13 @@ public class BridgeContext extends Context { return isThemeAppCompat; } + public AccessibilityManager getAccessibilityManager() { + if (mAccessibilityManager == null) { + mAccessibilityManager = new AccessibilityManager(this, null, UserHandle.USER_CURRENT); + } + return mAccessibilityManager; + } + // ------------ Context methods @Override @@ -673,6 +687,9 @@ public class BridgeContext extends Context { return InputMethodManager.forContext(this); case AUTOFILL_MANAGER_SERVICE: + if (mAutofillManager == null) { + mAutofillManager = new AutofillManager(this, new Default()); + } return mAutofillManager; case CLIPBOARD_SERVICE: @@ -690,6 +707,12 @@ public class BridgeContext extends Context { case INPUT_SERVICE: return mInputManager; + case VIBRATOR_SERVICE: + return NullVibrator.getInstance(); + + case VIBRATOR_MANAGER_SERVICE: + return NullVibratorManager.getInstance(); + case TEXT_CLASSIFICATION_SERVICE: case CONTENT_CAPTURE_MANAGER_SERVICE: case ALARM_SERVICE: @@ -1525,7 +1548,7 @@ public class BridgeContext extends Context { @Override public ContentResolver getContentResolver() { if (mContentResolver == null) { - mContentResolver = new BridgeContentResolver(this); + mContentResolver = new BridgeContentResolver(getApplicationContext()); } return mContentResolver; } @@ -2289,4 +2312,9 @@ public class BridgeContext extends Context { public void applyWallpaper(String wallpaperPath) { mRenderResources.setWallpaper(wallpaperPath, mConfig.isNightModeActive()); } + + @NotNull + public ThreadLocal<AnimationHandler> getAnimationHandlerThreadLocal() { + return mAnimationHandlerThreadLocal; + } } diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java index 85bf637d00..c376429186 100644 --- a/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java +++ b/bridge/src/com/android/layoutlib/bridge/android/BridgePowerManager.java @@ -61,6 +61,11 @@ public class BridgePowerManager implements IPowerManager { } @Override + public boolean isBatterySaverSupported() throws RemoteException { + return true; + } + + @Override public BatterySaverPolicyConfig getFullPowerSavePolicy() { return new BatterySaverPolicyConfig.Builder().build(); } diff --git a/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java index 5fe116ea0c..0122da1818 100644 --- a/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java +++ b/bridge/src/com/android/layoutlib/bridge/android/BridgeThermalService.java @@ -90,4 +90,9 @@ public class BridgeThermalService implements IThermalService { public float getThermalHeadroom(int forecastSeconds) { return Float.NaN; } + + @Override + public float[] getThermalHeadroomThresholds() { + return new float[]{}; + } } diff --git a/bridge/src/com/android/layoutlib/bridge/bars/Config.java b/bridge/src/com/android/layoutlib/bridge/bars/Config.java index d89960ea68..794990852a 100644 --- a/bridge/src/com/android/layoutlib/bridge/bars/Config.java +++ b/bridge/src/com/android/layoutlib/bridge/bars/Config.java @@ -92,8 +92,8 @@ public class Config { } public static String getTime(int platformVersion) { - if (isGreaterOrEqual(platformVersion, TIRAMISU)) { - return "13:00"; + if (isGreaterOrEqual(platformVersion, UPSIDE_DOWN_CAKE)) { + return "14:00"; } if (platformVersion < GINGERBREAD) { return "2:20"; @@ -143,6 +143,9 @@ public class Config { if (platformVersion < TIRAMISU) { return "12:00"; } + if (platformVersion < UPSIDE_DOWN_CAKE) { + return "13:00"; + } // Should never happen. return "4:04"; } diff --git a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java index 63efec36f1..98378e63e1 100644 --- a/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java +++ b/bridge/src/com/android/layoutlib/bridge/bars/CustomBar.java @@ -24,18 +24,13 @@ import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ResourceHelper; -import com.android.layoutlib.bridge.resources.IconLoader; import com.android.layoutlib.bridge.resources.SysUiResources; import com.android.resources.Density; -import com.android.resources.LayoutDirection; import com.android.resources.ResourceType; import android.annotation.NonNull; import android.content.res.ColorStateList; -import android.graphics.Bitmap; -import android.graphics.BitmapFactory; -import android.graphics.BitmapFactory.Options; -import android.graphics.drawable.BitmapDrawable; +import android.graphics.Color; import android.graphics.drawable.Drawable; import android.util.TypedValue; import android.view.Gravity; @@ -45,12 +40,10 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; -import java.io.InputStream; - import static android.os._Original_Build.VERSION_CODES.LOLLIPOP; /** - * Base "bar" class for the window decor around the the edited layout. + * Base "bar" class for the window decor around the edited layout. * This is basically an horizontal layout that loads a given layout on creation (it is read * through {@link Class#getResourceAsStream(String)}). * <p> @@ -88,9 +81,9 @@ abstract class CustomBar extends LinearLayout { layoutName); } - protected ImageView loadIcon(ImageView imageView, String iconName, Density density) { + protected ImageView loadIcon(ImageView imageView, String iconName, Density density, int color) { return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName, - density, false); + density, false, color); } protected ImageView loadIcon(int index, String iconName, Density density, boolean isRtl) { @@ -98,39 +91,12 @@ abstract class CustomBar extends LinearLayout { if (child instanceof ImageView) { ImageView imageView = (ImageView) child; return SysUiResources.loadIcon(mContext, mSimulatedPlatformVersion, imageView, iconName, - density, isRtl); + density, isRtl, Color.WHITE); } return null; } - protected ImageView loadIcon(ImageView imageView, String iconName, Density density, - boolean isRtl) { - LayoutDirection dir = isRtl ? LayoutDirection.RTL : null; - IconLoader iconLoader = new IconLoader(iconName, density, mSimulatedPlatformVersion, dir); - InputStream stream = iconLoader.getIcon(); - - if (stream != null) { - density = iconLoader.getDensity(); - String path = iconLoader.getPath(); - // look for a cached bitmap - Bitmap bitmap = Bridge.getCachedBitmap(path, Boolean.TRUE /*isFramework*/); - if (bitmap == null) { - Options options = new Options(); - options.inDensity = density.getDpiValue(); - bitmap = BitmapFactory.decodeStream(stream, null, options); - Bridge.setCachedBitmap(path, bitmap, Boolean.TRUE /*isFramework*/); - } - - if (bitmap != null) { - BitmapDrawable drawable = new BitmapDrawable(getContext().getResources(), bitmap); - imageView.setImageDrawable(drawable); - } - } - - return imageView; - } - protected TextView setText(int index, String string) { View child = getChildAt(index); if (child instanceof TextView) { @@ -247,9 +213,7 @@ abstract class CustomBar extends LinearLayout { resource = renderResources.resolveResValue(resource); if (resource != null) { ResourceType type = resource.getResourceType(); - if (type == null || type == ResourceType.COLOR) { - // if no type is specified, the value may have been specified directly in the style - // file, rather than referencing a color resource value. + if (type == ResourceType.STYLE_ITEM || type == ResourceType.COLOR) { try { return ResourceHelper.getColor(resource.getValue()); } catch (NumberFormatException e) { diff --git a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java index dc823f7e37..68423337a0 100644 --- a/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java +++ b/bridge/src/com/android/layoutlib/bridge/bars/StatusBar.java @@ -17,11 +17,13 @@ package com.android.layoutlib.bridge.bars; import com.android.ide.common.rendering.api.ILayoutLog; +import com.android.ide.common.rendering.api.RenderResources; import com.android.ide.common.rendering.api.ResourceNamespace; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.android.BridgeContext; import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import com.android.layoutlib.bridge.impl.ParserFactory; +import com.android.layoutlib.bridge.impl.ResourceHelper; import com.android.layoutlib.bridge.resources.IconLoader; import com.android.resources.Density; @@ -42,9 +44,24 @@ import java.io.InputStream; import java.util.ArrayList; import java.util.List; +import static android.graphics.Color.WHITE; +import static android.os._Original_Build.VERSION_CODES.M; +import static com.android.layoutlib.bridge.bars.Config.getTimeColor; +import static com.android.layoutlib.bridge.bars.Config.isGreaterOrEqual; + public class StatusBar extends CustomBar { private final int mSimulatedPlatformVersion; + /** + * Color corresponding to light_mode_icon_color_single_tone + * from frameworks/base/packages/SettingsLib/res/values/colors.xml + */ + private static final int LIGHT_ICON_COLOR = 0xffffffff; + /** + * Color corresponding to dark_mode_icon_color_single_tone + * from frameworks/base/packages/SettingsLib/res/values/colors.xml + */ + private static final int DARK_ICON_COLOR = 0x99000000; /** Status bar background color attribute name. */ private static final String ATTR_COLOR = "statusBarColor"; /** Attribute for translucency property. */ @@ -57,7 +74,7 @@ public class StatusBar extends CustomBar { @SuppressWarnings("UnusedParameters") public StatusBar(Context context, AttributeSet attrs) { this((BridgeContext) context, - Density.getEnum(((BridgeContext) context).getMetrics().densityDpi), + Density.create(((BridgeContext) context).getMetrics().densityDpi), ((BridgeContext) context).getConfiguration().getLayoutDirection() == View.LAYOUT_DIRECTION_RTL, (context.getApplicationInfo().flags & ApplicationInfo.FLAG_SUPPORTS_RTL) != 0, @@ -95,20 +112,45 @@ public class StatusBar extends CustomBar { return; } + int foregroundColor = getForegroundColor(simulatedPlatformVersion); // Cannot access the inside items through id because no R.id values have been // created for them. // We do know the order though. loadIcon(icons.get(0), "stat_sys_wifi_signal_4_fully." - + Config.getWifiIconType(simulatedPlatformVersion), density); - loadIcon(icons.get(1), "stat_sys_battery_100.png", density); + + Config.getWifiIconType(simulatedPlatformVersion), density,foregroundColor); + loadIcon(icons.get(1), "stat_sys_battery_100.png", density, foregroundColor); clockView.setText(Config.getTime(simulatedPlatformVersion)); - clockView.setTextColor(Config.getTimeColor(simulatedPlatformVersion)); + clockView.setTextColor(foregroundColor); + } + + private int getForegroundColor(int platformVersion) { + if (isGreaterOrEqual(platformVersion, M)) { + RenderResources renderResources = getContext().getRenderResources(); + boolean translucentBackground = + ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources, + ATTR_TRANSLUCENT, false); + if (translucentBackground) { + return WHITE; + } + boolean drawnByWindow = + ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources, + "windowDrawsSystemBarBackgrounds", false); + if (drawnByWindow) { + boolean lightStatusBar = + ResourceHelper.getBooleanThemeFrameworkAttrValue(renderResources, + "windowLightStatusBar", false); + return lightStatusBar ? DARK_ICON_COLOR : LIGHT_ICON_COLOR; + } + return WHITE; + } else { + return getTimeColor(platformVersion); + } } @Override - protected ImageView loadIcon(ImageView imageView, String iconName, Density density) { + protected ImageView loadIcon(ImageView imageView, String iconName, Density density, int color) { if (!iconName.endsWith(".xml")) { - return super.loadIcon(imageView, iconName, density); + return super.loadIcon(imageView, iconName, density, color); } // The xml is stored only in xhdpi. @@ -123,8 +165,9 @@ public class StatusBar extends CustomBar { ParserFactory.create(stream, iconName), (BridgeContext) mContext, ResourceNamespace.ANDROID); - imageView.setImageDrawable( - Drawable.createFromXml(mContext.getResources(), parser)); + Drawable drawable = Drawable.createFromXml(mContext.getResources(), parser); + drawable.setTint(color); + imageView.setImageDrawable(drawable); } catch (XmlPullParserException e) { Bridge.getLog().error(ILayoutLog.TAG_BROKEN, "Unable to draw wifi icon", e, null, null); diff --git a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java index c8e5009888..1ca3b9c2cc 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/Layout.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/Layout.java @@ -38,6 +38,8 @@ import com.android.resources.ScreenOrientation; import android.R.id; import android.annotation.NonNull; import android.graphics.Color; +import android.graphics.Point; +import android.graphics.Rect; import android.graphics.drawable.Drawable; import android.util.DisplayMetrics; import android.util.TypedValue; @@ -196,6 +198,25 @@ class Layout extends FrameLayout { mBuilder = null; } + @Override + public boolean getChildVisibleRect(View child, Rect r, Point offset, boolean forceParentCheck) { + return r.intersect(0, 0, getWidth(), getHeight()); + } + + @Override + public boolean getGlobalVisibleRect(Rect r, Point globalOffset) { + int width = mRight - mLeft; + int height = mBottom - mTop; + if (width > 0 && height > 0) { + r.set(0, 0, width, height); + if (globalOffset != null) { + globalOffset.set(-mScrollX, -mScrollY); + } + return true; + } + return false; + } + @NonNull private static View createSysUiOverlay(@NonNull BridgeContext context) { SysUiOverlay overlay = new SysUiOverlay(context, 20, 10, 50, 40, 60); diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java index c2430a8959..98b59565f7 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderAction.java @@ -32,6 +32,7 @@ import com.android.tools.layoutlib.annotations.NotNull; import com.android.tools.layoutlib.annotations.Nullable; import com.android.tools.layoutlib.annotations.VisibleForTesting; +import android.animation.AnimationHandler; import android.animation.PropertyValuesHolder_Accessor; import android.content.res.Configuration; import android.graphics.drawable.AdaptiveIconDrawable_Delegate; @@ -42,6 +43,7 @@ import android.view.IWindowManagerImpl; import android.view.Surface; import android.view.ViewConfiguration_Accessor; import android.view.WindowManagerGlobal_Delegate; +import android.view.accessibility.AccessibilityInteractionClient_Accessor; import android.view.inputmethod.InputMethodManager_Accessor; import java.util.Collections; @@ -51,6 +53,7 @@ import java.util.WeakHashMap; import java.util.concurrent.TimeUnit; import java.util.concurrent.locks.ReentrantLock; +import static android.os._Original_Build.VERSION.SDK_INT; import static com.android.ide.common.rendering.api.Result.Status.ERROR_LOCK_INTERRUPTED; import static com.android.ide.common.rendering.api.Result.Status.ERROR_TIMEOUT; import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; @@ -68,6 +71,12 @@ import static com.android.ide.common.rendering.api.Result.Status.SUCCESS; * */ public abstract class RenderAction<T extends RenderParams> { + /** + * Static field to store an SDK version coming from the render configuration. + * This is to be accessed when wanting to know the simulated SDK version instead + * of Build.VERSION.SDK_INT. + */ + public static int sSimulatedSdk; private static final Set<String> COMPOSE_CLASS_FQNS = Set.of("androidx.compose.ui.tooling.ComposeViewAdapter", @@ -99,6 +108,7 @@ public abstract class RenderAction<T extends RenderParams> { */ protected RenderAction(T params) { mParams = params; + sSimulatedSdk = SDK_INT; } /** @@ -276,6 +286,7 @@ public abstract class RenderAction<T extends RenderParams> { ILayoutLog currentLog = mParams.getLog(); Bridge.setLog(currentLog); mContext.getRenderResources().setLogger(currentLog); + AnimationHandler.sAnimatorHandler = mContext.getAnimationHandlerThreadLocal(); } /** @@ -303,6 +314,7 @@ public abstract class RenderAction<T extends RenderParams> { ParserFactory.setParserFactory(null); PropertyValuesHolder_Accessor.clearClassCaches(); + AccessibilityInteractionClient_Accessor.clearCaches(); } public static BridgeContext getCurrentContext() { @@ -467,6 +479,14 @@ public abstract class RenderAction<T extends RenderParams> { if (sCurrentContext != null) { // quit HandlerThread created during this session. HandlerThread_Delegate.cleanUp(sCurrentContext); + + AnimationHandler animationHandler = + sCurrentContext.getAnimationHandlerThreadLocal().get(); + if (animationHandler != null) { + animationHandler.mDelayedCallbackStartTime.clear(); + animationHandler.mAnimationCallbacks.clear(); + animationHandler.mCommitCallbacks.clear(); + } } sCurrentContext = null; diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java index 6a6e184617..0dd35ce055 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderDrawable.java @@ -39,6 +39,7 @@ import java.awt.AlphaComposite; import java.awt.Color; import java.awt.Graphics2D; import java.awt.image.BufferedImage; +import java.awt.image.DataBufferInt; import java.util.ArrayList; import java.util.Collections; import java.util.List; @@ -78,7 +79,7 @@ public class RenderDrawable extends RenderAction<DrawableParams> { return Status.ERROR_NOT_A_DRAWABLE.createResult(); } - Drawable d = ResourceHelper.getDrawable(drawableResource, context); + Drawable d = ResourceHelper.getDrawable(drawableResource, context, context.getTheme()); if (d == null) { return Status.ERROR_NOT_A_DRAWABLE.createResult(); } @@ -128,15 +129,6 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // Use screen size when either intrinsic width or height isn't available. w = screenWidth; h = screenHeight; - } else if (w > screenWidth || h > screenHeight) { - // If image wouldn't fit to the screen, resize it to avoid cropping. - - // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight. - double scale = Math.min((double) screenWidth / w, (double) screenHeight / h); - - // scale * w / scale * h = w / h, so, proportions are preserved. - w = (int) Math.floor(scale * w); - h = (int) Math.floor(scale * h); } int w_spec = MeasureSpec.makeMeasureSpec(w, MeasureSpec.EXACTLY); @@ -149,46 +141,30 @@ public class RenderDrawable extends RenderAction<DrawableParams> { // Pre-draw setup. AttachInfo_Accessor.dispatchOnPreDraw(content); - // Draw into a new image. - BufferedImage image = getImage(w, h); - - // Create an Android bitmap around the BufferedImage. - Bitmap bitmap = Bitmap.createBitmap(image.getWidth(), image.getHeight(), - Config.ARGB_8888); - bitmap.setPixels(image.getRGB(0, 0, image.getWidth(), image.getHeight(), - null, 0, image.getWidth()), 0, image.getWidth(), 0, 0, image - .getWidth(), image.getHeight()); - - // Create a Canvas around the Android bitmap. + Bitmap bitmap = Bitmap.createBitmap(w, h, Config.ARGB_8888); Canvas canvas = new Canvas(bitmap); canvas.setDensity(hardwareConfig.getDensity().getDpiValue()); // Draw. content.draw(canvas); - int[] pixels = new int[image.getWidth() * image.getHeight()]; - bitmap.getPixels(pixels, 0, image.getWidth(), 0, 0, image.getWidth(), - image.getHeight()); - image.setRGB(0, 0, image.getWidth(), image.getHeight(), pixels, 0, image.getWidth()); - // Detach root from window after draw. - AttachInfo_Accessor.detachFromWindow(content); + if (w > screenWidth || h > screenHeight) { + // If image wouldn't fit to the screen, resize it to avoid cropping. - return image; - } + // We need to find scale such that scale * w <= screenWidth, scale * h <= screenHeight. + double scale = Math.min((double) screenWidth / w, (double) screenHeight / h); + bitmap = Bitmap.createScaledBitmap(bitmap, (int) (w * scale), (int) (h * scale), true); + } - @NonNull - protected BufferedImage getImage(int w, int h) { - BufferedImage image = new BufferedImage(w > 0 ? w : 1, - h > 0 ? h : 1, + // Copy bitmap into BufferedImage. + BufferedImage image = new BufferedImage(bitmap.getWidth(), bitmap.getHeight(), BufferedImage.TYPE_INT_ARGB); - Graphics2D gc = image.createGraphics(); - gc.setComposite(AlphaComposite.Src); - - gc.setColor(new Color(0x00000000, true)); - gc.fillRect(0, 0, w, h); + int[] imageData = ((DataBufferInt) image.getRaster().getDataBuffer()).getData(); + bitmap.getPixels(imageData, 0, image.getWidth(), 0, 0, image.getWidth(), + image.getHeight()); - // done - gc.dispose(); + // Detach root from window after draw. + AttachInfo_Accessor.detachFromWindow(content); return image; } diff --git a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java index ae5e401edc..9ed5d17531 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/RenderSessionImpl.java @@ -50,7 +50,6 @@ import com.android.tools.idea.validator.ValidatorHierarchy; import com.android.tools.idea.validator.hierarchy.CustomHierarchyHelper; import com.android.tools.layoutlib.annotations.NotNull; -import android.animation.AnimationHandler; import android.annotation.NonNull; import android.annotation.Nullable; import android.content.Context; @@ -92,7 +91,10 @@ import java.util.ArrayList; import java.util.IdentityHashMap; import java.util.List; import java.util.Map; +import java.util.function.Consumer; +import java.util.function.Function; +import static android.os._Original_Build.VERSION.SDK_INT; import static com.android.ide.common.rendering.api.Result.Status.ERROR_INFLATION; import static com.android.ide.common.rendering.api.Result.Status.ERROR_NOT_INFLATED; import static com.android.ide.common.rendering.api.Result.Status.ERROR_UNKNOWN; @@ -109,6 +111,10 @@ import static com.android.layoutlib.common.util.ReflectionUtils.isInstanceOf; public class RenderSessionImpl extends RenderAction<SessionParams> { private static final Canvas NOP_CANVAS = new NopCanvas(); + private static final String SIMULATED_SDK_TOO_HIGH = + String.format("The current rendering only supports APIs up to %d. You may encounter " + + "crashes if using with higher APIs. To avoid, you can set a lower API for " + + "your previews.", SDK_INT); // scene state private RenderSession mScene; @@ -198,7 +204,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } /** - * Measures the the current layout if needed (see {@link #invalidateRenderingSize}). + * Measures the current layout if needed (see {@link #invalidateRenderingSize}). */ private void measureLayout(@NonNull SessionParams params) { // only do the screen measure when needed. @@ -310,6 +316,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); BridgeContext context = getContext(); + int simulatedVersion = params.getSimulatedPlatformVersion(); + sSimulatedSdk = simulatedVersion > 0 ? simulatedVersion : SDK_INT; + if (sSimulatedSdk > SDK_INT) { + Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, SIMULATED_SDK_TOO_HIGH, + null, null, null); + } + if (Bridge.isLocaleRtl(params.getLocale())) { if (!params.isRtlSupported()) { Bridge.getLog().warning(ILayoutLog.TAG_RTL_NOT_ENABLED, @@ -362,14 +375,8 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { mViewRoot.getViewRootImpl().mTmpFrames.displayFrame.set(mViewRoot.getLeft(), mViewRoot.getTop(), mViewRoot.getRight(), mViewRoot.getBottom()); - ViewRootImpl rootImpl = AttachInfo_Accessor.getRootView(mViewRoot); - if (rootImpl != null) { - ViewRootImpl_Accessor.setChild(rootImpl, mViewRoot); - } - mSystemViewInfoList = - visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(), - false); + visitAllChildren(mViewRoot, 0, 0, params, false); return SUCCESS.createResult(); } catch (PostInflateException e) { @@ -480,6 +487,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { SessionParams params = getParams(); + int simulatedVersion = params.getSimulatedPlatformVersion(); + sSimulatedSdk = simulatedVersion > 0 ? simulatedVersion : SDK_INT; + if (sSimulatedSdk > SDK_INT) { + Bridge.getLog().fidelityWarning(ILayoutLog.TAG_UNSUPPORTED, SIMULATED_SDK_TOO_HIGH, + null, null, null); + } + try { if (mViewRoot == null) { return ERROR_NOT_INFLATED.createResult(); @@ -570,8 +584,12 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } mSystemViewInfoList = - visitAllChildren(mViewRoot, 0, 0, params.getExtendedViewInfoMode(), - false); + visitAllChildren(mViewRoot, 0, 0, params, false); + + Consumer<BufferedImage> imageTransformation = getParams().getImageTransformation(); + if (imageTransformation != null) { + imageTransformation.accept(mImage); + } boolean enableLayoutValidation = Boolean.TRUE.equals(params.getFlag(RenderParamsFlags.FLAG_ENABLE_LAYOUT_VALIDATOR)); boolean enableLayoutValidationImageCheck = Boolean.TRUE.equals( @@ -853,15 +871,16 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * * @return {@code ViewInfo} containing the bounds of the view and it children otherwise. */ - private ViewInfo visit(View view, int hOffset, int vOffset, boolean setExtendedInfo, + private ViewInfo visit(View view, int hOffset, int vOffset, SessionParams params, boolean isContentFrame) { - ViewInfo result = createViewInfo(view, hOffset, vOffset, setExtendedInfo, isContentFrame); + ViewInfo result = createViewInfo(view, hOffset, vOffset, params.getExtendedViewInfoMode(), + isContentFrame); if (view instanceof ViewGroup) { ViewGroup group = ((ViewGroup) view); result.setChildren(visitAllChildren(group, isContentFrame ? 0 : hOffset, isContentFrame ? 0 : vOffset, - setExtendedInfo, isContentFrame)); + params, isContentFrame)); } return result; } @@ -880,7 +899,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * part of the system decor. */ private List<ViewInfo> visitAllChildren(ViewGroup viewGroup, int hOffset, int vOffset, - boolean setExtendedInfo, boolean isContentFrame) { + SessionParams params, boolean isContentFrame) { if (viewGroup == null) { return null; } @@ -896,8 +915,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { List<ViewInfo> childrenWithOffset = new ArrayList<>(childCount); for (int i = 0; i < childCount; i++) { ViewInfo[] childViewInfo = - visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset, - setExtendedInfo); + visitContentRoot(viewGroup.getChildAt(i), hOffset, vOffset, params); childrenWithoutOffset.add(childViewInfo[0]); childrenWithOffset.add(childViewInfo[1]); } @@ -906,7 +924,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } else { List<ViewInfo> children = new ArrayList<>(childCount); for (int i = 0; i < childCount; i++) { - children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, setExtendedInfo, + children.add(visit(viewGroup.getChildAt(i), hOffset, vOffset, params, isContentFrame)); } return children; @@ -920,26 +938,32 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { * get the right bounds if the {@code ViewInfo} hierarchy is accessed from * {@code mViewInfoList}. When the hierarchy is accessed via {@code mSystemViewInfoList}, the * offset is not needed. + * If a custom parser was passed inside the {@link SessionParams} argument, this will be used + * to generate the {@link ViewInfo}s. Otherwise, {@link RenderSessionImpl#visitAllChildren} + * will be used. * * @return an array of length two, with ViewInfo at index 0 is without offset and ViewInfo at * index 1 is with the offset. */ @NonNull - private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset, - boolean setExtendedInfo) { + private ViewInfo[] visitContentRoot(View view, int hOffset, int vOffset, SessionParams params) { ViewInfo[] result = new ViewInfo[2]; if (view == null) { return result; } + boolean setExtendedInfo = params.getExtendedViewInfoMode(); result[0] = createViewInfo(view, 0, 0, setExtendedInfo, true); result[1] = createViewInfo(view, hOffset, vOffset, setExtendedInfo, true); - if (view instanceof ViewGroup) { - List<ViewInfo> children = - visitAllChildren((ViewGroup) view, 0, 0, setExtendedInfo, true); - result[0].setChildren(children); - result[1].setChildren(children); + Function<Object, List<ViewInfo>> customParser = params.getCustomContentHierarchyParser(); + List<ViewInfo> children = null; + if (customParser != null) { + children = customParser.apply(view); + } else if (view instanceof ViewGroup) { + children = visitAllChildren((ViewGroup) view, 0, 0, params, true); } + result[0].setChildren(children); + result[1].setChildren(children); return result; } @@ -975,13 +999,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { shiftY + view.getTop(), shiftX + view.getRight(), shiftY + view.getBottom(), - view, view.getLayoutParams()); + view, null, view.getLayoutParams()); } else { // We are part of the system decor. SystemViewInfo r = new SystemViewInfo(view.getClass().getName(), getViewKey(view), view.getLeft(), view.getTop(), view.getRight(), - view.getBottom(), view, view.getLayoutParams()); + view.getBottom(), view, null, view.getLayoutParams()); result = r; // We currently mark three kinds of views: // 1. Menus in the Action Bar @@ -1184,6 +1208,19 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { } } + @Override + public void release() { + super.release(); + if (mViewRoot == null) { + return; + } + ViewRootImpl viewRootImpl = mViewRoot.getViewRootImpl(); + if (viewRootImpl == null) { + return; + } + ViewRootImpl_Accessor.detachFromWindow(viewRootImpl); + } + private void disposeImageSurface() { if (mCanvas != null) { mCanvas.release(); @@ -1198,12 +1235,6 @@ public class RenderSessionImpl extends RenderAction<SessionParams> { mImage = null; // detachFromWindow might create Handler callbacks, thus before Handler_Delegate.dispose AttachInfo_Accessor.detachFromWindow(mViewRoot); - AnimationHandler animationHandler = AnimationHandler.sAnimatorHandler.get(); - if (animationHandler != null) { - animationHandler.mDelayedCallbackStartTime.clear(); - animationHandler.mAnimationCallbacks.clear(); - animationHandler.mCommitCallbacks.clear(); - } getContext().getSessionInteractiveData().dispose(); if (mViewInfoList != null) { mViewInfoList.clear(); diff --git a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index 87bed3d967..358795f256 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -111,7 +111,7 @@ import static android.content.res.AssetManager.ACCESS_STREAMING; public final class ResourceHelper { private static final Key<Set<ResourceValue>> KEY_GET_DRAWABLE = Key.create("ResourceHelper.getDrawable"); - private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]+)?)(.*)"); + private static final Pattern sFloatPattern = Pattern.compile("(-?[0-9]*(?:\\.[0-9]*)?)(.*)"); private static final float[] sFloatOut = new float[1]; private static final TypedValue mValue = new TypedValue(); @@ -368,7 +368,7 @@ public final class ResourceHelper { if (value instanceof DensityBasedResourceValue) { density = ((DensityBasedResourceValue) value).getResourceDensity(); if (density == Density.NODPI || density == Density.ANYDPI) { - density = Density.getEnum(context.getConfiguration().densityDpi); + density = Density.create(context.getConfiguration().densityDpi); } } diff --git a/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java b/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java index 9fea1677d5..6f9092cc12 100644 --- a/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java +++ b/bridge/src/com/android/layoutlib/bridge/impl/SystemViewInfo.java @@ -32,8 +32,9 @@ public class SystemViewInfo extends ViewInfo { } public SystemViewInfo(String name, Object cookie, int left, int top, - int right, int bottom, Object viewObject, Object layoutParamsObject) { - super(name, cookie, left, top, right, bottom, viewObject, + int right, int bottom, Object viewObject, Object accessibilityObject, + Object layoutParamsObject) { + super(name, cookie, left, top, right, bottom, viewObject, accessibilityObject, layoutParamsObject); } diff --git a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java index 84ed6a04c4..f8884d4b2e 100644 --- a/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java +++ b/bridge/src/com/android/layoutlib/bridge/resources/SysUiResources.java @@ -36,7 +36,6 @@ import android.graphics.BitmapFactory.Options; import android.graphics.drawable.BitmapDrawable; import android.widget.ImageView; -import java.io.IOException; import java.io.InputStream; public class SysUiResources { @@ -67,10 +66,8 @@ public class SysUiResources { return null; } - public static ImageView loadIcon(Context context, int api, ImageView imageView, String - iconName, - Density density, boolean - isRtl) { + public static ImageView loadIcon(Context context, int api, ImageView imageView, + String iconName, Density density, boolean isRtl, int color) { LayoutDirection dir = isRtl ? LayoutDirection.RTL : null; IconLoader iconLoader = new IconLoader(iconName, density, api, dir); @@ -90,6 +87,7 @@ public class SysUiResources { if (bitmap != null) { BitmapDrawable drawable = new BitmapDrawable(context.getResources(), bitmap); + drawable.setTint(color); imageView.setImageDrawable(drawable); } } |