diff options
author | Xavier Ducrohet <xav@android.com> | 2010-11-11 12:25:01 -0800 |
---|---|---|
committer | Android (Google) Code Review <android-gerrit@google.com> | 2010-11-11 12:25:01 -0800 |
commit | f917f9accf924447b3afb27d09382851003c190f (patch) | |
tree | ef1e135d130e03fc2f54a11693da204d5c991ef3 /tools | |
parent | 9232f2e5a97383f266b615841cc8560b11919cb0 (diff) | |
parent | c2e9651bf386a1f7bf7fc706cf5424950570470c (diff) | |
download | frameworks_base-f917f9accf924447b3afb27d09382851003c190f.zip frameworks_base-f917f9accf924447b3afb27d09382851003c190f.tar.gz frameworks_base-f917f9accf924447b3afb27d09382851003c190f.tar.bz2 |
Merge "Layoutlib: New bridge implementation using the new API 5."
Diffstat (limited to 'tools')
40 files changed, 1326 insertions, 1098 deletions
diff --git a/tools/layoutlib/bridge/.classpath b/tools/layoutlib/bridge/.classpath index aeeffa6..7204ace 100644 --- a/tools/layoutlib/bridge/.classpath +++ b/tools/layoutlib/bridge/.classpath @@ -4,9 +4,9 @@ <classpathentry kind="src" path="tests"/> <classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/> <classpathentry kind="con" path="org.eclipse.jdt.junit.JUNIT_CONTAINER/3"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/> - <classpathentry kind="var" path="ANDROID_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_SRC/dalvik/libcore/xml/src/main/java"/> - <classpathentry kind="var" path="ANDROID_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_SRC/development/tools/ninepatch/src"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/layoutlib_api/layoutlib_api-prebuilt.jar"/> + <classpathentry kind="var" path="ANDROID_PLAT_SRC/prebuilt/common/kxml2/kxml2-2.3.0.jar" sourcepath="/ANDROID_PLAT_SRC/dalvik/libcore/xml/src/main/java"/> + <classpathentry kind="var" path="ANDROID_PLAT_OUT_FRAMEWORK/ninepatch.jar" sourcepath="/ANDROID_PLAT_SRC/development/tools/ninepatch/src"/> <classpathentry kind="var" path="ANDROID_PLAT_SRC/out/host/common/obj/JAVA_LIBRARIES/temp_layoutlib_intermediates/javalib.jar" sourcepath="/ANDROID_PLAT_SRC/frameworks/base"/> <classpathentry kind="output" path="bin"/> </classpath> diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java index e97b1e6..392462f 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap_Delegate.java @@ -17,7 +17,7 @@ package android.graphics; import com.android.layoutlib.api.IDensityBasedResourceValue.Density; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Bitmap.Config; import android.os.Parcel; diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java index ce8e960..374bbb4 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas_Delegate.java @@ -17,7 +17,7 @@ package android.graphics; import com.android.layoutlib.api.ILayoutLog; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Paint_Delegate.FontInfo; import android.text.TextUtils; diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java index 59b6a91..7ee72d8 100644 --- a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.graphics.DashPathEffect diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java index 405e537..7573dc1 100644 --- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Shader.TileMode; diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java index 0966f39..77de32d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix_Delegate.java @@ -17,7 +17,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Matrix.ScaleToFit; diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java index 6e90bdd..d83a33b 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Paint_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Paint.FontMetrics; import android.graphics.Paint.FontMetricsInt; diff --git a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java index 6827ae7..ce7eef0 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PathEffect_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.graphics.PathEffect diff --git a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java index c242e80..a5885ea 100644 --- a/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/PorterDuffXfermode_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.graphics.PorterDuffXfermode diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java index c4e764c..c36ce53 100644 --- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import android.graphics.Shader.TileMode; diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java index 4dcf144..646ac80 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Shader_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.graphics.Shader diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java index 0492e4f..358c3c7 100644 --- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; import java.awt.Paint; diff --git a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java index 7e90e7d..0b54a0e 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Typeface_Delegate.java @@ -16,8 +16,8 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; -import com.android.layoutlib.bridge.FontLoader; +import com.android.layoutlib.bridge.impl.DelegateManager; +import com.android.layoutlib.bridge.impl.FontLoader; import android.content.res.AssetManager; diff --git a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java index d4408cf..0c1170d 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java +++ b/tools/layoutlib/bridge/src/android/graphics/Xfermode_Delegate.java @@ -16,7 +16,7 @@ package android.graphics; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.graphics.Xfermode diff --git a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java index ed24e16..9ca1338 100644 --- a/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java +++ b/tools/layoutlib/bridge/src/android/util/FloatMath_Delegate.java @@ -16,7 +16,7 @@ package android.util; -import com.android.layoutlib.bridge.DelegateManager; +import com.android.layoutlib.bridge.impl.DelegateManager; /** * Delegate implementing the native methods of android.util.FloatMath diff --git a/tools/layoutlib/bridge/src/android/view/SurfaceView.java b/tools/layoutlib/bridge/src/android/view/SurfaceView.java index ce32da9..f7db98a 100644 --- a/tools/layoutlib/bridge/src/android/view/SurfaceView.java +++ b/tools/layoutlib/bridge/src/android/view/SurfaceView.java @@ -16,7 +16,7 @@ package android.view; -import com.android.layoutlib.bridge.MockView; +import com.android.layoutlib.bridge.android.MockView; import android.content.Context; import android.graphics.Canvas; diff --git a/tools/layoutlib/bridge/src/android/webkit/WebView.java b/tools/layoutlib/bridge/src/android/webkit/WebView.java index 3b66188..a20a9d1 100644 --- a/tools/layoutlib/bridge/src/android/webkit/WebView.java +++ b/tools/layoutlib/bridge/src/android/webkit/WebView.java @@ -16,7 +16,7 @@ package android.webkit; -import com.android.layoutlib.bridge.MockView; +import com.android.layoutlib.bridge.android.MockView; import android.content.Context; import android.graphics.Bitmap; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java index 996a942..d2092d1 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -16,63 +16,26 @@ package com.android.layoutlib.bridge; -import com.android.internal.util.XmlUtils; -import com.android.layoutlib.api.ILayoutBridge; import com.android.layoutlib.api.ILayoutLog; -import com.android.layoutlib.api.ILayoutResult; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; -import com.android.layoutlib.api.IStyleResourceValue; import com.android.layoutlib.api.IXmlPullParser; -import com.android.layoutlib.api.IDensityBasedResourceValue.Density; -import com.android.layoutlib.api.ILayoutResult.ILayoutViewInfo; -import com.android.layoutlib.bridge.LayoutResult.LayoutViewInfo; +import com.android.layoutlib.api.LayoutBridge; +import com.android.layoutlib.api.SceneParams; +import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.bridge.android.BridgeAssetManager; +import com.android.layoutlib.bridge.impl.FontLoader; +import com.android.layoutlib.bridge.impl.LayoutSceneImpl; import com.android.ninepatch.NinePatch; import com.android.tools.layoutlib.create.MethodAdapter; import com.android.tools.layoutlib.create.OverrideMethod; -import android.app.Fragment_Delegate; -import android.content.ClipData; -import android.content.res.Configuration; import android.graphics.Bitmap; -import android.graphics.Bitmap_Delegate; -import android.graphics.Canvas; -import android.graphics.Canvas_Delegate; -import android.graphics.Rect; -import android.graphics.Region; import android.graphics.Typeface_Delegate; -import android.graphics.drawable.Drawable; -import android.os.Bundle; -import android.os.Handler; -import android.os.IBinder; -import android.os.Looper; -import android.os.ParcelFileDescriptor; -import android.os.RemoteException; -import android.util.DisplayMetrics; -import android.util.TypedValue; -import android.view.BridgeInflater; -import android.view.DragEvent; -import android.view.IWindow; -import android.view.IWindowSession; -import android.view.InputChannel; -import android.view.KeyEvent; -import android.view.MotionEvent; -import android.view.Surface; -import android.view.SurfaceView; -import android.view.View; -import android.view.ViewGroup; -import android.view.View.AttachInfo; -import android.view.View.MeasureSpec; -import android.view.WindowManager.LayoutParams; -import android.widget.FrameLayout; -import android.widget.TabHost; -import android.widget.TabWidget; -import java.awt.image.BufferedImage; import java.lang.ref.SoftReference; import java.lang.reflect.Field; import java.lang.reflect.Modifier; -import java.util.Collection; import java.util.HashMap; import java.util.Map; @@ -81,10 +44,7 @@ import java.util.Map; * <p/>To use this bridge, simply instantiate an object of type {@link Bridge} and call * {@link #computeLayout(IXmlPullParser, Object, int, int, String, boolean, Map, Map, IProjectCallback, ILayoutLog)}. */ -public final class Bridge implements ILayoutBridge { - - private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; - private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; +public final class Bridge extends LayoutBridge { public static class StaticMethodNotImplementedException extends RuntimeException { private static final long serialVersionUID = 1L; @@ -143,34 +103,28 @@ public final class Bridge implements ILayoutBridge { } }; - /** - * Logger defined during a compute layout operation. - * <p/> - * This logger is generally set to {@link #sDefaultLogger} except during rendering - * operations when it might be set to a specific provided logger. - * <p/> - * To change this value, use a block synchronized on {@link #sDefaultLogger}. - */ - private static ILayoutLog sLogger = sDefaultLogger; - - /* - * (non-Javadoc) - * @see com.android.layoutlib.api.ILayoutBridge#getApiLevel() - */ + @Override public int getApiLevel() { - return API_CURRENT; + return LayoutBridge.API_CURRENT; } /* * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutLibBridge#init(java.lang.String, java.util.Map) */ - public boolean init( - String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { + @Override + public boolean init(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { + BridgeAssetManager.initSystem(); return sinit(fontOsLocation, enumValueMap); } + @Override + public boolean dispose() { + BridgeAssetManager.clearSystem(); + return true; + } + private static synchronized boolean sinit(String fontOsLocation, Map<String, Map<String, Integer>> enumValueMap) { @@ -189,12 +143,8 @@ public final class Bridge implements ILayoutBridge { OverrideMethod.setDefaultListener(new MethodAdapter() { @Override public void onInvokeV(String signature, boolean isNative, Object caller) { - if (sLogger != null) { - synchronized (sDefaultLogger) { - sLogger.error("Missing Stub: " + signature + - (isNative ? " (native)" : "")); - } - } + sDefaultLogger.error("Missing Stub: " + signature + + (isNative ? " (native)" : "")); if (debug.equalsIgnoreCase("throw")) { // Throwing this exception doesn't seem that useful. It breaks @@ -278,236 +228,82 @@ public final class Bridge implements ILayoutBridge { return true; } - /* - * For compatilibty purposes, we implement the old deprecated version of computeLayout. - * (non-Javadoc) - * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + /** + * Sets a 9 patch in a project cache or in the framework cache. + * @param value the path of the 9 patch + * @param ninePatch the 9 patch object + * @param projectKey the key of the project, or null to put the bitmap in the framework cache. */ - @Deprecated - public ILayoutResult computeLayout(IXmlPullParser layoutDescription, - Object projectKey, - int screenWidth, int screenHeight, String themeName, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback customViewLoader, ILayoutLog logger) { - boolean isProjectTheme = false; - if (themeName.charAt(0) == '*') { - themeName = themeName.substring(1); - isProjectTheme = true; - } - - return computeLayout(layoutDescription, projectKey, - screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT, - DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT, - themeName, isProjectTheme, - projectResources, frameworkResources, customViewLoader, logger); - } + public static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) { + if (projectKey != null) { + Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); - /* - * For compatilibty purposes, we implement the old deprecated version of computeLayout. - * (non-Javadoc) - * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) - */ - @Deprecated - public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, - int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback customViewLoader, ILayoutLog logger) { - return computeLayout(layoutDescription, projectKey, - screenWidth, screenHeight, DisplayMetrics.DENSITY_DEFAULT, - DisplayMetrics.DENSITY_DEFAULT, DisplayMetrics.DENSITY_DEFAULT, - themeName, isProjectTheme, - projectResources, frameworkResources, customViewLoader, logger); - } + if (map == null) { + map = new HashMap<String, SoftReference<NinePatch>>(); + sProject9PatchCache.put(projectKey, map); + } - /* - * For compatilibty purposes, we implement the old deprecated version of computeLayout. - * (non-Javadoc) - * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) - */ - public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, - int screenWidth, int screenHeight, int density, float xdpi, float ydpi, - String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback customViewLoader, ILayoutLog logger) { - return computeLayout(layoutDescription, projectKey, - screenWidth, screenHeight, false /* renderFullSize */, - density, xdpi, ydpi, themeName, isProjectTheme, - projectResources, frameworkResources, customViewLoader, logger); + map.put(value, new SoftReference<NinePatch>(ninePatch)); + } else { + sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch)); + } } - /* - * (non-Javadoc) - * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, boolean, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + /** + * Starts a layout session by inflating and rendering it. The method returns a + * {@link ILayoutScene} on which further actions can be taken. + * + * @param layoutDescription the {@link IXmlPullParser} letting the LayoutLib Bridge visit the + * layout file. + * @param projectKey An Object identifying the project. This is used for the cache mechanism. + * @param screenWidth the screen width + * @param screenHeight the screen height + * @param renderFullSize if true, the rendering will render the full size needed by the + * layout. This size is never smaller than <var>screenWidth</var> x <var>screenHeight</var>. + * @param density the density factor for the screen. + * @param xdpi the screen actual dpi in X + * @param ydpi the screen actual dpi in Y + * @param themeName The name of the theme to use. + * @param isProjectTheme true if the theme is a project theme, false if it is a framework theme. + * @param projectResources the resources of the project. The map contains (String, map) pairs + * where the string is the type of the resource reference used in the layout file, and the + * map contains (String, {@link IResourceValue}) pairs where the key is the resource name, + * and the value is the resource value. + * @param frameworkResources the framework resources. The map contains (String, map) pairs + * where the string is the type of the resource reference used in the layout file, and the map + * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the + * value is the resource value. + * @param projectCallback The {@link IProjectCallback} object to get information from + * the project. + * @param logger the object responsible for displaying warning/errors to the user. + * @return a new {@link ILayoutScene} object that contains the result of the layout. + * @since 5 */ - public ILayoutResult computeLayout(IXmlPullParser layoutDescription, Object projectKey, - int screenWidth, int screenHeight, boolean renderFullSize, - int density, float xdpi, float ydpi, - String themeName, boolean isProjectTheme, - Map<String, Map<String, IResourceValue>> projectResources, - Map<String, Map<String, IResourceValue>> frameworkResources, - IProjectCallback customViewLoader, ILayoutLog logger) { - if (logger == null) { - logger = sDefaultLogger; - } - - synchronized (sDefaultLogger) { - sLogger = logger; - } - - // find the current theme and compute the style inheritance map - Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = - new HashMap<IStyleResourceValue, IStyleResourceValue>(); - - IStyleResourceValue currentTheme = computeStyleMaps(themeName, isProjectTheme, - projectResources.get(BridgeConstants.RES_STYLE), - frameworkResources.get(BridgeConstants.RES_STYLE), styleParentMap); - - BridgeContext context = null; + @Override + public BridgeLayoutScene createScene(SceneParams params) { try { - // we need to make sure the Looper has been initialized for this thread. - // this is required for View that creates Handler objects. - if (Looper.myLooper() == null) { - Looper.prepare(); - } - - // setup the display Metrics. - DisplayMetrics metrics = new DisplayMetrics(); - metrics.densityDpi = density; - metrics.density = density / (float) DisplayMetrics.DENSITY_DEFAULT; - metrics.scaledDensity = metrics.density; - metrics.widthPixels = screenWidth; - metrics.heightPixels = screenHeight; - metrics.xdpi = xdpi; - metrics.ydpi = ydpi; - - context = new BridgeContext(projectKey, metrics, currentTheme, projectResources, - frameworkResources, styleParentMap, customViewLoader, logger); - BridgeInflater inflater = new BridgeInflater(context, customViewLoader); - context.setBridgeInflater(inflater); - inflater.setFactory2(context); - - IResourceValue windowBackground = null; - int screenOffset = 0; - if (currentTheme != null) { - windowBackground = context.findItemInStyle(currentTheme, "windowBackground"); - windowBackground = context.resolveResValue(windowBackground); - - screenOffset = getScreenOffset(frameworkResources, currentTheme, context); - } - - BridgeXmlBlockParser parser = new BridgeXmlBlockParser(layoutDescription, - context, false /* platformResourceFlag */); - - ViewGroup root = new FrameLayout(context); - - // Sets the project callback (custom view loader) to the fragment delegate so that - // it can instantiate the custom Fragment. - Fragment_Delegate.setProjectCallback(customViewLoader); - - View view = inflater.inflate(parser, root); - - // post-inflate process. For now this supports TabHost/TabWidget - postInflateProcess(view, customViewLoader); - - Fragment_Delegate.setProjectCallback(null); - - // set the AttachInfo on the root view. - AttachInfo info = new AttachInfo(new WindowSession(), new Window(), - new Handler(), null); - info.mHasWindowFocus = true; - info.mWindowVisibility = View.VISIBLE; - info.mInTouchMode = false; // this is so that we can display selections. - root.dispatchAttachedToWindow(info, 0); - - // get the background drawable - if (windowBackground != null) { - Drawable d = ResourceHelper.getDrawable(windowBackground, - context, true /* isFramework */); - root.setBackgroundDrawable(d); - } - - // measure the views - int w_spec, h_spec; - - if (renderFullSize) { - // measure the full size needed by the layout. - w_spec = MeasureSpec.makeMeasureSpec(screenWidth, - MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size - h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, - MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size - root.measure(w_spec, h_spec); - - int neededWidth = root.getChildAt(0).getMeasuredWidth(); - if (neededWidth > screenWidth) { - screenWidth = neededWidth; - } - - int neededHeight = root.getChildAt(0).getMeasuredHeight(); - if (neededHeight > screenHeight - screenOffset) { - screenHeight = neededHeight + screenOffset; + SceneResult lastResult = SceneResult.SUCCESS; + LayoutSceneImpl scene = null; + synchronized (this) { + try { + scene = new LayoutSceneImpl(params); + + scene.prepare(); + lastResult = scene.inflate(); + if (lastResult == SceneResult.SUCCESS) { + lastResult = scene.render(); + } + } finally { + if (scene != null) { + scene.cleanup(); + } } } - // remeasure with only the size we need - // This must always be done before the call to layout - w_spec = MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY); - h_spec = MeasureSpec.makeMeasureSpec(screenHeight - screenOffset, - MeasureSpec.EXACTLY); - root.measure(w_spec, h_spec); - - // now do the layout. - root.layout(0, screenOffset, screenWidth, screenHeight); - - // draw the views - // create the BufferedImage into which the layout will be rendered. - BufferedImage image = new BufferedImage(screenWidth, screenHeight - screenOffset, - BufferedImage.TYPE_INT_ARGB); - - // create an Android bitmap around the BufferedImage - Bitmap bitmap = Bitmap_Delegate.createBitmap(image, Density.getEnum(density)); - - // create a Canvas around the Android bitmap - Canvas canvas = new Canvas(bitmap); - - // to set the logger, get the native delegate - Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); - canvasDelegate.setLogger(logger); - - root.draw(canvas); - canvasDelegate.dispose(); - - return new LayoutResult( - visit(((ViewGroup)view).getChildAt(0), context), - image); - - } catch (PostInflateException e) { - return new LayoutResult(ILayoutResult.ERROR, "Error during post inflation process:\n" - + e.getMessage()); - } catch (Throwable e) { - // get the real cause of the exception. - Throwable t = e; - while (t.getCause() != null) { - t = t.getCause(); - } - - // log it - logger.error(t); - - // then return with an ERROR status and the message from the real exception - return new LayoutResult(ILayoutResult.ERROR, - t.getClass().getSimpleName() + ": " + t.getMessage()); - } finally { - // Make sure to remove static references, otherwise we could not unload the lib - BridgeResources.clearSystem(); - BridgeAssetManager.clearSystem(); - - // Remove the global logger - synchronized (sDefaultLogger) { - sLogger = sDefaultLogger; - } + return new BridgeLayoutScene(this, scene, lastResult); + } catch (Throwable t) { + t.printStackTrace(); + return new BridgeLayoutScene(this, null, new SceneResult("error!", t)); } } @@ -515,6 +311,7 @@ public final class Bridge implements ILayoutBridge { * (non-Javadoc) * @see com.android.layoutlib.api.ILayoutLibBridge#clearCaches(java.lang.Object) */ + @Override public void clearCaches(Object projectKey) { if (projectKey != null) { sProjectBitmapCache.remove(projectKey); @@ -556,367 +353,25 @@ public final class Bridge implements ILayoutBridge { return null; } - static Map<String, Integer> getEnumValues(String attributeName) { - if (sEnumValueMap != null) { - return sEnumValueMap.get(attributeName); - } - - return null; - } - - /** - * Visits a View and its children and generate a {@link ILayoutViewInfo} containing the - * bounds of all the views. - * @param view the root View - * @param context the context. - */ - private ILayoutViewInfo visit(View view, BridgeContext context) { - if (view == null) { - return null; - } - - LayoutViewInfo result = new LayoutViewInfo(view.getClass().getName(), - context.getViewKey(view), - view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); - - if (view instanceof ViewGroup) { - ViewGroup group = ((ViewGroup) view); - int n = group.getChildCount(); - ILayoutViewInfo[] children = new ILayoutViewInfo[n]; - for (int i = 0; i < group.getChildCount(); i++) { - children[i] = visit(group.getChildAt(i), context); - } - result.setChildren(children); - } - - return result; - } - - /** - * Compute style information from the given list of style for the project and framework. - * @param themeName the name of the current theme. In order to differentiate project and - * platform themes sharing the same name, all project themes must be prepended with - * a '*' character. - * @param isProjectTheme Is this a project theme - * @param inProjectStyleMap the project style map - * @param inFrameworkStyleMap the framework style map - * @param outInheritanceMap the map of style inheritance. This is filled by the method - * @return the {@link IStyleResourceValue} matching <var>themeName</var> - */ - private IStyleResourceValue computeStyleMaps( - String themeName, boolean isProjectTheme, Map<String, - IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, - Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { - - if (inProjectStyleMap != null && inFrameworkStyleMap != null) { - // first, get the theme - IResourceValue theme = null; - - // project theme names have been prepended with a * - if (isProjectTheme) { - theme = inProjectStyleMap.get(themeName); - } else { - theme = inFrameworkStyleMap.get(themeName); - } - - if (theme instanceof IStyleResourceValue) { - // compute the inheritance map for both the project and framework styles - computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap, - inFrameworkStyleMap, outInheritanceMap); - - // Compute the style inheritance for the framework styles/themes. - // Since, for those, the style parent values do not contain 'android:' - // we want to force looking in the framework style only to avoid using - // similarly named styles from the project. - // To do this, we pass null in lieu of the project style map. - computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */, - inFrameworkStyleMap, outInheritanceMap); - - return (IStyleResourceValue)theme; - } - } - - return null; - } - - /** - * Compute the parent style for all the styles in a given list. - * @param styles the styles for which we compute the parent. - * @param inProjectStyleMap the map of project styles. - * @param inFrameworkStyleMap the map of framework styles. - * @param outInheritanceMap the map of style inheritance. This is filled by the method. - */ - private void computeStyleInheritance(Collection<IResourceValue> styles, - Map<String, IResourceValue> inProjectStyleMap, - Map<String, IResourceValue> inFrameworkStyleMap, - Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { - for (IResourceValue value : styles) { - if (value instanceof IStyleResourceValue) { - IStyleResourceValue style = (IStyleResourceValue)value; - IStyleResourceValue parentStyle = null; - - // first look for a specified parent. - String parentName = style.getParentStyle(); - - // no specified parent? try to infer it from the name of the style. - if (parentName == null) { - parentName = getParentName(value.getName()); - } - - if (parentName != null) { - parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); - - if (parentStyle != null) { - outInheritanceMap.put(style, parentStyle); - } - } - } - } - } - /** - * Searches for and returns the {@link IStyleResourceValue} from a given name. - * <p/>The format of the name can be: - * <ul> - * <li>[android:]<name></li> - * <li>[android:]style/<name></li> - * <li>@[android:]style/<name></li> - * </ul> - * @param parentName the name of the style. - * @param inProjectStyleMap the project style map. Can be <code>null</code> - * @param inFrameworkStyleMap the framework style map. - * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found. + * Returns the list of possible enums for a given attribute name. */ - private IStyleResourceValue getStyle(String parentName, - Map<String, IResourceValue> inProjectStyleMap, - Map<String, IResourceValue> inFrameworkStyleMap) { - boolean frameworkOnly = false; - - String name = parentName; - - // remove the useless @ if it's there - if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { - name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); - } - - // check for framework identifier. - if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) { - frameworkOnly = true; - name = name.substring(BridgeConstants.PREFIX_ANDROID.length()); - } - - // at this point we could have the format <type>/<name>. we want only the name as long as - // the type is style. - if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) { - name = name.substring(BridgeConstants.REFERENCE_STYLE.length()); - } else if (name.indexOf('/') != -1) { - return null; - } - - IResourceValue parent = null; - - // if allowed, search in the project resources. - if (frameworkOnly == false && inProjectStyleMap != null) { - parent = inProjectStyleMap.get(name); - } - - // if not found, then look in the framework resources. - if (parent == null) { - parent = inFrameworkStyleMap.get(name); - } - - // make sure the result is the proper class type and return it. - if (parent instanceof IStyleResourceValue) { - return (IStyleResourceValue)parent; - } - - sLogger.error(String.format("Unable to resolve parent style name: %s", parentName)); - - return null; - } - - /** - * Computes the name of the parent style, or <code>null</code> if the style is a root style. - */ - private String getParentName(String styleName) { - int index = styleName.lastIndexOf('.'); - if (index != -1) { - return styleName.substring(0, index); + public static Map<String, Integer> getEnumValues(String attributeName) { + if (sEnumValueMap != null) { + return sEnumValueMap.get(attributeName); } return null; } /** - * Returns the top screen offset. This depends on whether the current theme defines the user - * of the title and status bars. - * @param frameworkResources The framework resources - * @param currentTheme The current theme - * @param context The context - * @return the pixel height offset - */ - private int getScreenOffset(Map<String, Map<String, IResourceValue>> frameworkResources, - IStyleResourceValue currentTheme, BridgeContext context) { - int offset = 0; - - // get the title bar flag from the current theme. - IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle"); - - // because it may reference something else, we resolve it. - value = context.resolveResValue(value); - - // if there's a value and it's true (default is false) - if (value == null || value.getValue() == null || - XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { - // default size of the window title bar - int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT; - - // get value from the theme. - value = context.findItemInStyle(currentTheme, "windowTitleSize"); - - // resolve it - value = context.resolveResValue(value); - - if (value != null) { - // get the numerical value, if available - TypedValue typedValue = ResourceHelper.getValue(value.getValue()); - if (typedValue != null) { - // compute the pixel value based on the display metrics - defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics); - } - } - - offset += defaultOffset; - } - - // get the fullscreen flag from the current theme. - value = context.findItemInStyle(currentTheme, "windowFullscreen"); - - // because it may reference something else, we resolve it. - value = context.resolveResValue(value); - - if (value == null || value.getValue() == null || - XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { - - // default value - int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT; - - // get the real value, first the list of Dimensions from the framework map - Map<String, IResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN); - - // now get the value - value = dimens.get("status_bar_height"); - if (value != null) { - TypedValue typedValue = ResourceHelper.getValue(value.getValue()); - if (typedValue != null) { - // compute the pixel value based on the display metrics - defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics); - } - } - - // add the computed offset. - offset += defaultOffset; - } - - return offset; - } - - /** - * Post process on a view hierachy that was just inflated. - * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the - * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically - * based on the content of the {@link FrameLayout}. - * @param view the root view to process. - * @param projectCallback callback to the project. - */ - private void postInflateProcess(View view, IProjectCallback projectCallback) - throws PostInflateException { - if (view instanceof TabHost) { - setupTabHost((TabHost)view, projectCallback); - } else if (view instanceof ViewGroup) { - ViewGroup group = (ViewGroup)view; - final int count = group.getChildCount(); - for (int c = 0 ; c < count ; c++) { - View child = group.getChildAt(c); - postInflateProcess(child, projectCallback); - } - } - } - - /** - * Sets up a {@link TabHost} object. - * @param tabHost the TabHost to setup. - * @param projectCallback The project callback object to access the project R class. - * @throws PostInflateException - */ - private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback) - throws PostInflateException { - // look for the TabWidget, and the FrameLayout. They have their own specific names - View v = tabHost.findViewById(android.R.id.tabs); - - if (v == null) { - throw new PostInflateException( - "TabHost requires a TabWidget with id \"android:id/tabs\".\n"); - } - - if ((v instanceof TabWidget) == false) { - throw new PostInflateException(String.format( - "TabHost requires a TabWidget with id \"android:id/tabs\".\n" + - "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName())); - } - - v = tabHost.findViewById(android.R.id.tabcontent); - - if (v == null) { - // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty) - throw new PostInflateException( - "TabHost requires a FrameLayout with id \"android:id/tabcontent\"."); - } - - if ((v instanceof FrameLayout) == false) { - throw new PostInflateException(String.format( - "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" + - "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName())); - } - - FrameLayout content = (FrameLayout)v; - - // now process the content of the framelayout and dynamically create tabs for it. - final int count = content.getChildCount(); - - if (count == 0) { - throw new PostInflateException( - "The FrameLayout for the TabHost has no content. Rendering failed.\n"); - } - - // this must be called before addTab() so that the TabHost searches its TabWidget - // and FrameLayout. - tabHost.setup(); - - // for each child of the framelayout, add a new TabSpec - for (int i = 0 ; i < count ; i++) { - View child = content.getChildAt(i); - String tabSpec = String.format("tab_spec%d", i+1); - int id = child.getId(); - String[] resource = projectCallback.resolveResourceValue(id); - String name; - if (resource != null) { - name = resource[0]; // 0 is resource name, 1 is resource type. - } else { - name = String.format("Tab %d", i+1); // default name if id is unresolved. - } - tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id)); - } - } - - /** * Returns the bitmap for a specific path, from a specific project cache, or from the * framework cache. * @param value the path of the bitmap * @param projectKey the key of the project, or null to query the framework cache. * @return the cached Bitmap or null if not found. */ - static Bitmap getCachedBitmap(String value, Object projectKey) { + public static Bitmap getCachedBitmap(String value, Object projectKey) { if (projectKey != null) { Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey); if (map != null) { @@ -941,7 +396,7 @@ public final class Bridge implements ILayoutBridge { * @param bmp the Bitmap object * @param projectKey the key of the project, or null to put the bitmap in the framework cache. */ - static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) { + public static void setCachedBitmap(String value, Bitmap bmp, Object projectKey) { if (projectKey != null) { Map<String, SoftReference<Bitmap>> map = sProjectBitmapCache.get(projectKey); @@ -963,7 +418,7 @@ public final class Bridge implements ILayoutBridge { * @param projectKey the key of the project, or null to query the framework cache. * @return the cached 9 patch or null if not found. */ - static NinePatch getCached9Patch(String value, Object projectKey) { + public static NinePatch getCached9Patch(String value, Object projectKey) { if (projectKey != null) { Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); @@ -983,262 +438,69 @@ public final class Bridge implements ILayoutBridge { return null; } - /** - * Sets a 9 patch in a project cache or in the framework cache. - * @param value the path of the 9 patch - * @param ninePatch the 9 patch object - * @param projectKey the key of the project, or null to put the bitmap in the framework cache. - */ - static void setCached9Patch(String value, NinePatch ninePatch, Object projectKey) { - if (projectKey != null) { - Map<String, SoftReference<NinePatch>> map = sProject9PatchCache.get(projectKey); - if (map == null) { - map = new HashMap<String, SoftReference<NinePatch>>(); - sProject9PatchCache.put(projectKey, map); - } + // ---------- OBSOLETE API METHODS ---------- - map.put(value, new SoftReference<NinePatch>(ninePatch)); - } else { - sFramework9PatchCache.put(value, new SoftReference<NinePatch>(ninePatch)); - } + /* + * For compatilibty purposes, we implement the old deprecated version of computeLayout. + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + */ + @Deprecated + public com.android.layoutlib.api.ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, String themeName, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { + throw new UnsupportedOperationException(); } - private static final class PostInflateException extends Exception { - private static final long serialVersionUID = 1L; - - public PostInflateException(String message) { - super(message); - } + /* + * For compatilibty purposes, we implement the old deprecated version of computeLayout. + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) + */ + @Deprecated + public com.android.layoutlib.api.ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { + throw new UnsupportedOperationException(); } - /** - * Implementation of {@link IWindowSession} so that mSession is not null in - * the {@link SurfaceView}. + /* + * For compatilibty purposes, we implement the old deprecated version of computeLayout. + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ - private static final class WindowSession implements IWindowSession { - - @SuppressWarnings("unused") - public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3, - InputChannel outInputchannel) - throws RemoteException { - // pass for now. - return 0; - } - - @SuppressWarnings("unused") - public int addWithoutInputChannel(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3) - throws RemoteException { - // pass for now. - return 0; - } - - @SuppressWarnings("unused") - public void finishDrawing(IWindow arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void finishKey(IWindow arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public boolean getInTouchMode() throws RemoteException { - // pass for now. - return false; - } - - @SuppressWarnings("unused") - public boolean performHapticFeedback(IWindow window, int effectId, boolean always) { - // pass for now. - return false; - } - - @SuppressWarnings("unused") - public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { - // pass for now. - return null; - } - - @SuppressWarnings("unused") - public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException { - // pass for now. - return null; - } - - @SuppressWarnings("unused") - public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4, - boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8) - throws RemoteException { - // pass for now. - return 0; - } - - public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { - // pass for now. - } - - @SuppressWarnings("unused") - public void remove(IWindow arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void setInTouchMode(boolean arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void setInsets(IWindow window, int touchable, Rect contentInsets, - Rect visibleInsets) { - // pass for now. - } - - @SuppressWarnings("unused") - public IBinder prepareDrag(IWindow window, boolean localOnly, - int thumbnailWidth, int thumbnailHeight, Surface outSurface) - throws RemoteException { - // pass for now - return null; - } - - @SuppressWarnings("unused") - public boolean performDrag(IWindow window, IBinder dragToken, - float touchX, float touchY, float thumbCenterX, float thumbCenterY, - ClipData data) - throws RemoteException { - // pass for now - return false; - } - - @SuppressWarnings("unused") - public void reportDropResult(IWindow window, boolean consumed) throws RemoteException { - // pass for now - } - - @SuppressWarnings("unused") - public void dragRecipientEntered(IWindow window) throws RemoteException { - // pass for now - } - - @SuppressWarnings("unused") - public void dragRecipientExited(IWindow window) throws RemoteException { - // pass for now - } - - @SuppressWarnings("unused") - public void setWallpaperPosition(IBinder window, float x, float y, - float xStep, float yStep) { - // pass for now. - } - - @SuppressWarnings("unused") - public void wallpaperOffsetsComplete(IBinder window) { - // pass for now. - } - - @SuppressWarnings("unused") - public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, - int z, Bundle extras, boolean sync) { - // pass for now. - return null; - } - - @SuppressWarnings("unused") - public void wallpaperCommandComplete(IBinder window, Bundle result) { - // pass for now. - } - - @SuppressWarnings("unused") - public void closeSystemDialogs(String reason) { - // pass for now. - } - - public IBinder asBinder() { - // pass for now. - return null; - } + @Deprecated + public com.android.layoutlib.api.ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, int density, float xdpi, float ydpi, + String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { + throw new UnsupportedOperationException(); } - /** - * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. + /* + * (non-Javadoc) + * @see com.android.layoutlib.api.ILayoutBridge#computeLayout(com.android.layoutlib.api.IXmlPullParser, java.lang.Object, int, int, boolean, int, float, float, java.lang.String, boolean, java.util.Map, java.util.Map, com.android.layoutlib.api.IProjectCallback, com.android.layoutlib.api.ILayoutLog) */ - private static final class Window implements IWindow { - - @SuppressWarnings("unused") - public void dispatchAppVisibility(boolean arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchGetNewSurface() throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchKey(KeyEvent arg0) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2) - throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5) - throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, - boolean sync) { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchWallpaperCommand(String action, int x, int y, - int z, Bundle extras, boolean sync) { - // pass for now. - } - - @SuppressWarnings("unused") - public void closeSystemDialogs(String reason) { - // pass for now. - } - - @SuppressWarnings("unused") - public void dispatchDragEvent(DragEvent event) { - // pass for now. - } - - public IBinder asBinder() { - // pass for now. - return null; - } + @Deprecated + public com.android.layoutlib.api.ILayoutResult computeLayout(IXmlPullParser layoutDescription, + Object projectKey, + int screenWidth, int screenHeight, boolean renderFullSize, + int density, float xdpi, float ydpi, + String themeName, boolean isProjectTheme, + Map<String, Map<String, IResourceValue>> projectResources, + Map<String, Map<String, IResourceValue>> frameworkResources, + IProjectCallback customViewLoader, ILayoutLog logger) { + throw new UnsupportedOperationException(); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java new file mode 100644 index 0000000..5fcb9ff --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeLayoutScene.java @@ -0,0 +1,80 @@ +/* + * Copyright (C) 2010 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.bridge; + +import com.android.layoutlib.api.LayoutScene; +import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.api.ViewInfo; +import com.android.layoutlib.bridge.impl.LayoutSceneImpl; + +import java.awt.image.BufferedImage; + +/** + * An implementation of {@link LayoutScene}. + * + * This is a pretty basic class that does almost nothing. All of the work is done in + * {@link LayoutSceneImpl}. + * + */ +public class BridgeLayoutScene extends LayoutScene { + + private final Bridge mBridge; + private final LayoutSceneImpl mScene; + private SceneResult mLastResult; + + @Override + public SceneResult getResult() { + return mLastResult; + } + + @Override + public BufferedImage getImage() { + return mScene.getImage(); + } + + @Override + public ViewInfo getRootView() { + return mScene.getViewInfo(); + } + + @Override + public SceneResult render() { + + synchronized (mBridge) { + try { + mScene.prepare(); + mLastResult = mScene.render(); + } finally { + mScene.cleanup(); + } + } + + return mLastResult; + } + + @Override + public void dispose() { + // TODO Auto-generated method stub + + } + + /*package*/ BridgeLayoutScene(Bridge bridge, LayoutSceneImpl scene, SceneResult lastResult) { + mBridge = bridge; + mScene = scene; + mLastResult = lastResult; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java deleted file mode 100644 index c4c5225..0000000 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/LayoutResult.java +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2008 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.bridge; - -import com.android.layoutlib.api.ILayoutResult; - -import java.awt.image.BufferedImage; - -/** - * Implementation of {@link ILayoutResult} - */ -public final class LayoutResult implements ILayoutResult { - - private final ILayoutViewInfo mRootView; - private final BufferedImage mImage; - private final int mSuccess; - private final String mErrorMessage; - - /** - * Creates a {@link #SUCCESS} {@link ILayoutResult} with the specified params - * @param rootView - * @param image - */ - public LayoutResult(ILayoutViewInfo rootView, BufferedImage image) { - mSuccess = SUCCESS; - mErrorMessage = null; - mRootView = rootView; - mImage = image; - } - - /** - * Creates a LayoutResult with a specific success code and associated message - * @param code - * @param message - */ - public LayoutResult(int code, String message) { - mSuccess = code; - mErrorMessage = message; - mRootView = null; - mImage = null; - } - - public int getSuccess() { - return mSuccess; - } - - public String getErrorMessage() { - return mErrorMessage; - } - - public BufferedImage getImage() { - return mImage; - } - - public ILayoutViewInfo getRootView() { - return mRootView; - } - - /** - * Implementation of {@link ILayoutResult.ILayoutViewInfo} - */ - public static final class LayoutViewInfo implements ILayoutViewInfo { - private final Object mKey; - private final String mName; - private final int mLeft; - private final int mRight; - private final int mTop; - private final int mBottom; - private ILayoutViewInfo[] mChildren; - - public LayoutViewInfo(String name, Object key, int left, int top, int right, int bottom) { - mName = name; - mKey = key; - mLeft = left; - mRight = right; - mTop = top; - mBottom = bottom; - } - - public void setChildren(ILayoutViewInfo[] children) { - mChildren = children; - } - - public ILayoutViewInfo[] getChildren() { - return mChildren; - } - - public Object getViewKey() { - return mKey; - } - - public String getName() { - return mName; - } - - public int getLeft() { - return mLeft; - } - - public int getTop() { - return mTop; - } - - public int getRight() { - return mRight; - } - - public int getBottom() { - return mBottom; - } - } -} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeAssetManager.java index 71803fc..a825060 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeAssetManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeAssetManager.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; + +import com.android.layoutlib.bridge.Bridge; import android.content.res.AssetManager; @@ -28,7 +30,7 @@ public class BridgeAssetManager extends AssetManager { * <p/> * {@link Bridge} calls this method after setting up a new bridge. */ - /*package*/ static AssetManager initSystem() { + /*package*/ public static AssetManager initSystem() { if (!(AssetManager.sSystem instanceof BridgeAssetManager)) { // Note that AssetManager() creates a system AssetManager and we override it // with our BridgeAssetManager. @@ -42,7 +44,7 @@ public class BridgeAssetManager extends AssetManager { * Clears the static {@link AssetManager#sSystem} to make sure we don't leave objects * around that would prevent us from unloading the library. */ - /*package*/ static void clearSystem() { + public static void clearSystem() { AssetManager.sSystem = null; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentProvider.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java index 9d6dd27..3835378 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentProvider.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentProvider.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import android.content.ContentProviderOperation; import android.content.ContentProviderResult; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java index e15cb69..0257686 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContentResolver.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContentResolver.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import android.content.ContentResolver; import android.content.Context; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java index b9899b2..2fa97a3 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeContext.java @@ -14,12 +14,15 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.layoutlib.api.ILayoutLog; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.impl.TempResourceValue; import android.app.Activity; import android.app.Fragment; @@ -50,7 +53,6 @@ import android.os.Handler; import android.os.Looper; import android.util.AttributeSet; import android.util.DisplayMetrics; -import android.view.BridgeInflater; import android.view.LayoutInflater; import android.view.View; @@ -66,15 +68,16 @@ import java.util.TreeMap; import java.util.Map.Entry; /** - * Custom implementation of Context to handle non compiled resources. + * Custom implementation of Context/Activity to handle non compiled resources. */ public final class BridgeContext extends Activity { - private final Resources mResources; - private final Theme mTheme; + private Resources mResources; + private Theme mTheme; private final HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>(); private final IStyleResourceValue mThemeValues; private final Object mProjectKey; + private final DisplayMetrics mMetrics; private final Map<String, Map<String, IResourceValue>> mProjectResources; private final Map<String, Map<String, IResourceValue>> mFrameworkResources; private final Map<IStyleResourceValue, IStyleResourceValue> mStyleInheritanceMap; @@ -105,28 +108,18 @@ public final class BridgeContext extends Activity { * contains (String, {@link IResourceValue}) pairs where the key is the resource name, and the * value is the resource value. * @param styleInheritanceMap - * @param customViewLoader + * @param projectCallback */ public BridgeContext(Object projectKey, DisplayMetrics metrics, IStyleResourceValue currentTheme, Map<String, Map<String, IResourceValue>> projectResources, Map<String, Map<String, IResourceValue>> frameworkResources, Map<IStyleResourceValue, IStyleResourceValue> styleInheritanceMap, - IProjectCallback customViewLoader, ILayoutLog logger) { + IProjectCallback projectCallback, ILayoutLog logger) { mProjectKey = projectKey; - mProjectCallback = customViewLoader; + mMetrics = metrics; + mProjectCallback = projectCallback; mLogger = logger; - Configuration config = new Configuration(); - - AssetManager assetManager = BridgeAssetManager.initSystem(); - mResources = BridgeResources.initSystem( - this, - assetManager, - metrics, - config, - customViewLoader); - - mTheme = mResources.newTheme(); mThemeValues = currentTheme; mProjectResources = projectResources; @@ -137,6 +130,32 @@ public final class BridgeContext extends Activity { mFragments.mActivity = this; } + /** + * Initializes the {@link Resources} singleton to be linked to this {@link Context}, its + * {@link DisplayMetrics}, {@link Configuration}, and {@link IProjectCallback}. + * + * @see #disposeResources() + */ + public void initResources() { + AssetManager assetManager = AssetManager.getSystem(); + Configuration config = new Configuration(); + + mResources = BridgeResources.initSystem( + this, + assetManager, + mMetrics, + config, + mProjectCallback); + mTheme = mResources.newTheme(); + } + + /** + * Disposes the {@link Resources} singleton. + */ + public void disposeResources() { + BridgeResources.disposeSystem(); + } + public void setBridgeInflater(BridgeInflater inflater) { mInflater = inflater; } @@ -266,6 +285,15 @@ public final class BridgeContext extends Activity { return null; } + Object key = null; + if (parser != null) { + key = parser.getViewKey(); + } + if (key != null) { + String attrs_name = Bridge.resolveResourceValue(attrs); + System.out.println("KEY: " + key.toString() + "(" + attrs_name + ")"); + } + boolean[] frameworkAttributes = new boolean[1]; TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes); @@ -281,6 +309,9 @@ public final class BridgeContext extends Activity { customStyle = parser.getAttributeValue(null /* namespace*/, "style"); } if (customStyle != null) { + if (key != null) { + print("style", customStyle, false); + } IResourceValue item = findResValue(customStyle, false /*forceFrameworkOnly*/); if (item instanceof IStyleResourceValue) { @@ -292,6 +323,10 @@ public final class BridgeContext extends Activity { // get the name from the int. String defStyleName = searchAttr(defStyleAttr); + if (key != null) { + print("style", defStyleName, true); + } + // look for the style in the current theme, and its parent: if (mThemeValues != null) { IResourceValue item = findItemInStyle(mThemeValues, defStyleName); @@ -350,11 +385,20 @@ public final class BridgeContext extends Activity { // if we found a value, we make sure this doesn't reference another value. // So we resolve it. if (resValue != null) { + if (key != null) { + print(name, resValue.getValue(), true); + } + resValue = resolveResValue(resValue); + } else if (key != null) { + print(name, "<unknown>", true); } ta.bridgeSetValue(index, name, resValue); } else { + if (key != null) { + print(name, value, false); + } // there is a value in the XML, but we need to resolve it in case it's // referencing another resource or a theme value. ta.bridgeSetValue(index, name, resolveValue(null, name, value)); @@ -367,6 +411,15 @@ public final class BridgeContext extends Activity { return ta; } + private void print(String name, String value, boolean isDefault) { + System.out.print("\t" + name + " : " + value); + if (isDefault) { + System.out.println(" (default)"); + } else { + System.out.println(""); + } + } + @Override public Looper getMainLooper() { return Looper.myLooper(); @@ -433,7 +486,7 @@ public final class BridgeContext extends Activity { // if resValue is null, but value is not null, this means it was not a reference. // we return the name/value wrapper in a IResourceValue if (resValue == null) { - return new ResourceValue(type, name, value); + return new TempResourceValue(type, name, value); } // we resolved a first reference, but we need to make sure this isn't a reference also. @@ -453,7 +506,7 @@ public final class BridgeContext extends Activity { * @param value the value containing the reference to resolve. * @return a {@link IResourceValue} object or <code>null</code> */ - IResourceValue resolveResValue(IResourceValue value) { + public IResourceValue resolveResValue(IResourceValue value) { if (value == null) { return null; } @@ -661,7 +714,7 @@ public final class BridgeContext extends Activity { * @param itemName the name of the item to search for. * @return the {@link IResourceValue} object or <code>null</code> */ - IResourceValue findItemInStyle(IStyleResourceValue style, String itemName) { + public IResourceValue findItemInStyle(IStyleResourceValue style, String itemName) { IResourceValue item = style.findItem(itemName); // if we didn't find it, we look in the parent style (if applicable) diff --git a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java index 4bc8855..b4a28a6 100644 --- a/tools/layoutlib/bridge/src/android/view/BridgeInflater.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeInflater.java @@ -14,20 +14,22 @@ * limitations under the License. */ -package android.view; +package com.android.layoutlib.bridge.android; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.bridge.Bridge; import com.android.layoutlib.bridge.BridgeConstants; -import com.android.layoutlib.bridge.BridgeContext; -import com.android.layoutlib.bridge.BridgeXmlBlockParser; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; import android.content.Context; import android.util.AttributeSet; +import android.view.InflateException; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; import java.io.File; import java.io.FileReader; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java index 6358abb..46eb776 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeResources.java @@ -14,10 +14,13 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.layoutlib.api.IProjectCallback; import com.android.layoutlib.api.IResourceValue; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.impl.ResourceHelper; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; @@ -64,21 +67,18 @@ public final class BridgeResources extends Resources { DisplayMetrics metrics, Configuration config, IProjectCallback projectCallback) { - if (!(Resources.mSystem instanceof BridgeResources)) { - Resources.mSystem = new BridgeResources(context, - assets, - metrics, - config, - projectCallback); - } - return Resources.mSystem; + return Resources.mSystem = new BridgeResources(context, + assets, + metrics, + config, + projectCallback); } /** - * Clears the static {@link Resources#mSystem} to make sure we don't leave objects + * Disposes the static {@link Resources#mSystem} to make sure we don't leave objects * around that would prevent us from unloading the library. */ - /*package*/ static void clearSystem() { + /*package*/ static void disposeSystem() { if (Resources.mSystem instanceof BridgeResources) { ((BridgeResources)(Resources.mSystem)).mContext = null; ((BridgeResources)(Resources.mSystem)).mProjectCallback = null; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java index 70c5bd7..c3ab461 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeTypedArray.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeTypedArray.java @@ -14,11 +14,14 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.internal.util.XmlUtils; import com.android.layoutlib.api.IResourceValue; import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.impl.ResourceHelper; import org.kxml2.io.KXmlParser; import org.xmlpull.v1.XmlPullParser; @@ -36,7 +39,7 @@ import java.io.FileReader; import java.util.Map; /** - * TODO: describe. + * Custom implementation of TypedArray to handle non compiled resources. */ public final class BridgeTypedArray extends TypedArray { diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java new file mode 100644 index 0000000..c04c9e8 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindow.java @@ -0,0 +1,92 @@ +/* + * Copyright (C) 2010 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.bridge.android; + +import android.content.res.Configuration; +import android.graphics.Rect; +import android.os.Bundle; +import android.os.IBinder; +import android.os.ParcelFileDescriptor; +import android.os.RemoteException; +import android.view.DragEvent; +import android.view.IWindow; +import android.view.KeyEvent; +import android.view.MotionEvent; +import android.view.View.AttachInfo; + +/** + * Implementation of {@link IWindow} to pass to the {@link AttachInfo}. + */ +public final class BridgeWindow implements IWindow { + + public void dispatchAppVisibility(boolean arg0) throws RemoteException { + // pass for now. + } + + public void dispatchGetNewSurface() throws RemoteException { + // pass for now. + } + + public void dispatchKey(KeyEvent arg0) throws RemoteException { + // pass for now. + } + + public void dispatchPointer(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { + // pass for now. + } + + public void dispatchTrackball(MotionEvent arg0, long arg1, boolean arg2) throws RemoteException { + // pass for now. + } + + public void executeCommand(String arg0, String arg1, ParcelFileDescriptor arg2) + throws RemoteException { + // pass for now. + } + + public void resized(int arg0, int arg1, Rect arg2, Rect arg3, boolean arg4, Configuration arg5) + throws RemoteException { + // pass for now. + } + + public void windowFocusChanged(boolean arg0, boolean arg1) throws RemoteException { + // pass for now. + } + + public void dispatchWallpaperOffsets(float x, float y, float xStep, float yStep, + boolean sync) { + // pass for now. + } + + public void dispatchWallpaperCommand(String action, int x, int y, + int z, Bundle extras, boolean sync) { + // pass for now. + } + + public void closeSystemDialogs(String reason) { + // pass for now. + } + + public void dispatchDragEvent(DragEvent event) { + // pass for now. + } + + public IBinder asBinder() { + // pass for now. + return null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java new file mode 100644 index 0000000..74e5a65 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeWindowSession.java @@ -0,0 +1,163 @@ +/* + * Copyright (C) 2010 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.bridge.android; + +import android.content.ClipData; +import android.content.res.Configuration; +import android.graphics.Rect; +import android.graphics.Region; +import android.os.Bundle; +import android.os.IBinder; +import android.os.RemoteException; +import android.view.IWindow; +import android.view.IWindowSession; +import android.view.InputChannel; +import android.view.MotionEvent; +import android.view.Surface; +import android.view.SurfaceView; +import android.view.WindowManager.LayoutParams; + +/** + * Implementation of {@link IWindowSession} so that mSession is not null in + * the {@link SurfaceView}. + */ +public final class BridgeWindowSession implements IWindowSession { + + public int add(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3, + InputChannel outInputchannel) + throws RemoteException { + // pass for now. + return 0; + } + + public int addWithoutInputChannel(IWindow arg0, LayoutParams arg1, int arg2, Rect arg3) + throws RemoteException { + // pass for now. + return 0; + } + + public void finishDrawing(IWindow arg0) throws RemoteException { + // pass for now. + } + + public void finishKey(IWindow arg0) throws RemoteException { + // pass for now. + } + + public boolean getInTouchMode() throws RemoteException { + // pass for now. + return false; + } + + public boolean performHapticFeedback(IWindow window, int effectId, boolean always) { + // pass for now. + return false; + } + + public MotionEvent getPendingPointerMove(IWindow arg0) throws RemoteException { + // pass for now. + return null; + } + + public MotionEvent getPendingTrackballMove(IWindow arg0) throws RemoteException { + // pass for now. + return null; + } + + public int relayout(IWindow arg0, LayoutParams arg1, int arg2, int arg3, int arg4, + boolean arg4_5, Rect arg5, Rect arg6, Rect arg7, Configuration arg7b, Surface arg8) + throws RemoteException { + // pass for now. + return 0; + } + + public void getDisplayFrame(IWindow window, Rect outDisplayFrame) { + // pass for now. + } + + public void remove(IWindow arg0) throws RemoteException { + // pass for now. + } + + public void setInTouchMode(boolean arg0) throws RemoteException { + // pass for now. + } + + public void setTransparentRegion(IWindow arg0, Region arg1) throws RemoteException { + // pass for now. + } + + public void setInsets(IWindow window, int touchable, Rect contentInsets, + Rect visibleInsets) { + // pass for now. + } + + public IBinder prepareDrag(IWindow window, boolean localOnly, + int thumbnailWidth, int thumbnailHeight, Surface outSurface) + throws RemoteException { + // pass for now + return null; + } + + public boolean performDrag(IWindow window, IBinder dragToken, + float touchX, float touchY, float thumbCenterX, float thumbCenterY, + ClipData data) + throws RemoteException { + // pass for now + return false; + } + + public void reportDropResult(IWindow window, boolean consumed) throws RemoteException { + // pass for now + } + + public void dragRecipientEntered(IWindow window) throws RemoteException { + // pass for now + } + + public void dragRecipientExited(IWindow window) throws RemoteException { + // pass for now + } + + public void setWallpaperPosition(IBinder window, float x, float y, + float xStep, float yStep) { + // pass for now. + } + + public void wallpaperOffsetsComplete(IBinder window) { + // pass for now. + } + + public Bundle sendWallpaperCommand(IBinder window, String action, int x, int y, + int z, Bundle extras, boolean sync) { + // pass for now. + return null; + } + + public void wallpaperCommandComplete(IBinder window, Bundle result) { + // pass for now. + } + + public void closeSystemDialogs(String reason) { + // pass for now. + } + + public IBinder asBinder() { + // pass for now. + return null; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java index d842a66..24f61c8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlBlockParser.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlBlockParser.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.layoutlib.api.IXmlPullParser; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java index d145ff6..c99b70b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeXmlPullAttributes.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/BridgeXmlPullAttributes.java @@ -14,9 +14,11 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.layoutlib.api.IResourceValue; +import com.android.layoutlib.bridge.Bridge; +import com.android.layoutlib.bridge.BridgeConstants; import org.xmlpull.v1.XmlPullParser; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/MockView.java index 1ca3182..e5bddcb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/MockView.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/MockView.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import android.content.Context; import android.graphics.Canvas; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java index 2c92567..4efa631 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/android/NinePatchDrawable.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; import com.android.ninepatch.NinePatch; @@ -28,7 +28,7 @@ public class NinePatchDrawable extends Drawable { private NinePatch m9Patch; - NinePatchDrawable(NinePatch ninePatch) { + public NinePatchDrawable(NinePatch ninePatch) { m9Patch = ninePatch; } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java index 3d9f960..169d751 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/DelegateManager.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/DelegateManager.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.impl; import android.util.SparseArray; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java index de89a81..5d56370 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/FontLoader.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/FontLoader.java @@ -14,7 +14,7 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.impl; import org.xml.sax.Attributes; import org.xml.sax.SAXException; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java new file mode 100644 index 0000000..2012229 --- /dev/null +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/LayoutSceneImpl.java @@ -0,0 +1,689 @@ +/* + * Copyright (C) 2010 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.bridge.impl; + +import com.android.internal.util.XmlUtils; +import com.android.layoutlib.api.IProjectCallback; +import com.android.layoutlib.api.IResourceValue; +import com.android.layoutlib.api.IStyleResourceValue; +import com.android.layoutlib.api.LayoutBridge; +import com.android.layoutlib.api.SceneParams; +import com.android.layoutlib.api.SceneResult; +import com.android.layoutlib.api.ViewInfo; +import com.android.layoutlib.api.IDensityBasedResourceValue.Density; +import com.android.layoutlib.bridge.BridgeConstants; +import com.android.layoutlib.bridge.android.BridgeContext; +import com.android.layoutlib.bridge.android.BridgeInflater; +import com.android.layoutlib.bridge.android.BridgeWindow; +import com.android.layoutlib.bridge.android.BridgeWindowSession; +import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; + +import android.app.Fragment_Delegate; +import android.graphics.Bitmap; +import android.graphics.Bitmap_Delegate; +import android.graphics.Canvas; +import android.graphics.Canvas_Delegate; +import android.graphics.drawable.Drawable; +import android.os.Handler; +import android.os.Looper; +import android.util.DisplayMetrics; +import android.util.TypedValue; +import android.view.View; +import android.view.ViewGroup; +import android.view.View.AttachInfo; +import android.view.View.MeasureSpec; +import android.widget.FrameLayout; +import android.widget.TabHost; +import android.widget.TabWidget; + +import java.awt.Color; +import java.awt.Graphics2D; +import java.awt.image.BufferedImage; +import java.util.ArrayList; +import java.util.Collection; +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +/** + * Class managing a layout "scene". + * + * A scene is a stateful representation of a layout file. It is initialized with data coming through + * the {@link LayoutBridge} API to inflate the layout. Further actions and rendering can then + * be done on the layout. + * + */ +public class LayoutSceneImpl { + + private static final int DEFAULT_TITLE_BAR_HEIGHT = 25; + private static final int DEFAULT_STATUS_BAR_HEIGHT = 25; + + private final SceneParams mParams; + + // scene state + private BridgeContext mContext; + private BridgeXmlBlockParser mBlockParser; + private BridgeInflater mInflater; + private IStyleResourceValue mCurrentTheme; + private int mScreenOffset; + private IResourceValue mWindowBackground; + private FrameLayout mViewRoot; + + // information being returned through the API + private BufferedImage mImage; + private ViewInfo mViewInfo; + + private static final class PostInflateException extends Exception { + private static final long serialVersionUID = 1L; + + public PostInflateException(String message) { + super(message); + } + } + + /** + * Creates a layout scene with all the information coming from the layout bridge API. + * + * This also calls {@link LayoutSceneImpl#prepare()}. + * <p> + * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + * + * @see LayoutBridge#createScene(com.android.layoutlib.api.SceneParams) + */ + public LayoutSceneImpl(SceneParams params) { + // we need to make sure the Looper has been initialized for this thread. + // this is required for View that creates Handler objects. + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + // copy the params. + mParams = new SceneParams(params); + + // setup the display Metrics. + DisplayMetrics metrics = new DisplayMetrics(); + metrics.densityDpi = mParams.getDensity(); + metrics.density = mParams.getDensity() / (float) DisplayMetrics.DENSITY_DEFAULT; + metrics.scaledDensity = metrics.density; + metrics.widthPixels = mParams.getScreenWidth(); + metrics.heightPixels = mParams.getScreenHeight(); + metrics.xdpi = mParams.getXdpi(); + metrics.ydpi = mParams.getYdpi(); + + // find the current theme and compute the style inheritance map + Map<IStyleResourceValue, IStyleResourceValue> styleParentMap = + new HashMap<IStyleResourceValue, IStyleResourceValue>(); + + mCurrentTheme = computeStyleMaps(mParams.getThemeName(), mParams.getIsProjectTheme(), + mParams.getProjectResources().get(BridgeConstants.RES_STYLE), + mParams.getFrameworkResources().get(BridgeConstants.RES_STYLE), styleParentMap); + + // build the context + mContext = new BridgeContext(mParams.getProjectKey(), metrics, mCurrentTheme, + mParams.getProjectResources(), mParams.getFrameworkResources(), + styleParentMap, mParams.getProjectCallback(), mParams.getLogger()); + + // make sure the Resources object references the context (and other objects) for this + // scene + mContext.initResources(); + + // get the screen offset and window-background resource + mWindowBackground = null; + mScreenOffset = 0; + if (mCurrentTheme != null && mParams.isCustomBackgroundEnabled() == false) { + mWindowBackground = mContext.findItemInStyle(mCurrentTheme, "windowBackground"); + mWindowBackground = mContext.resolveResValue(mWindowBackground); + + mScreenOffset = getScreenOffset(mParams.getFrameworkResources(), mCurrentTheme, mContext); + } + + // build the inflater and parser. + mInflater = new BridgeInflater(mContext, mParams.getProjectCallback()); + mContext.setBridgeInflater(mInflater); + mInflater.setFactory2(mContext); + + mBlockParser = new BridgeXmlBlockParser(mParams.getLayoutDescription(), + mContext, false /* platformResourceFlag */); + } + + /** + * Prepares the scene for action. + * <p> + * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + */ + public void prepare() { + // we need to make sure the Looper has been initialized for this thread. + // this is required for View that creates Handler objects. + if (Looper.myLooper() == null) { + Looper.prepare(); + } + + // make sure the Resources object references the context (and other objects) for this + // scene + mContext.initResources(); + } + + /** + * Cleans up the scene after an action. + * <p> + * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + */ + public void cleanup() { + // clean up the looper + Looper.sThreadLocal.remove(); + + // Make sure to remove static references, otherwise we could not unload the lib + mContext.disposeResources(); + } + + /** + * Inflates the layout. + * <p> + * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + */ + public SceneResult inflate() { + try { + + mViewRoot = new FrameLayout(mContext); + + // Sets the project callback (custom view loader) to the fragment delegate so that + // it can instantiate the custom Fragment. + Fragment_Delegate.setProjectCallback(mParams.getProjectCallback()); + + View view = mInflater.inflate(mBlockParser, mViewRoot); + + // post-inflate process. For now this supports TabHost/TabWidget + postInflateProcess(view, mParams.getProjectCallback()); + + Fragment_Delegate.setProjectCallback(null); + + // set the AttachInfo on the root view. + AttachInfo info = new AttachInfo(new BridgeWindowSession(), new BridgeWindow(), + new Handler(), null); + info.mHasWindowFocus = true; + info.mWindowVisibility = View.VISIBLE; + info.mInTouchMode = false; // this is so that we can display selections. + mViewRoot.dispatchAttachedToWindow(info, 0); + + // get the background drawable + if (mWindowBackground != null) { + Drawable d = ResourceHelper.getDrawable(mWindowBackground, + mContext, true /* isFramework */); + mViewRoot.setBackgroundDrawable(d); + } + + return SceneResult.SUCCESS; + } catch (PostInflateException e) { + return new SceneResult("Error during post inflation process:\n" + e.getMessage()); + } catch (Throwable e) { + // get the real cause of the exception. + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + + // log it + mParams.getLogger().error(t); + + return new SceneResult("Unknown error during inflation.", t); + } + } + + /** + * Renders the scene. + * <p> + * <b>THIS MUST BE INSIDE A SYNCHRONIZED BLOCK on the BRIDGE OBJECT.<b> + */ + public SceneResult render() { + try { + if (mViewRoot == null) { + return new SceneResult("Layout has not been inflated!"); + } + // measure the views + int w_spec, h_spec; + + int renderScreenWidth = mParams.getScreenWidth(); + int renderScreenHeight = mParams.getScreenHeight(); + + if (mParams.getRenderFullSize()) { + // measure the full size needed by the layout. + w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, + MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size + h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset, + MeasureSpec.UNSPECIFIED); // this lets us know the actual needed size + mViewRoot.measure(w_spec, h_spec); + + int neededWidth = mViewRoot.getChildAt(0).getMeasuredWidth(); + if (neededWidth > renderScreenWidth) { + renderScreenWidth = neededWidth; + } + + int neededHeight = mViewRoot.getChildAt(0).getMeasuredHeight(); + if (neededHeight > renderScreenHeight - mScreenOffset) { + renderScreenHeight = neededHeight + mScreenOffset; + } + } + + // remeasure with the size we need + // This must always be done before the call to layout + w_spec = MeasureSpec.makeMeasureSpec(renderScreenWidth, MeasureSpec.EXACTLY); + h_spec = MeasureSpec.makeMeasureSpec(renderScreenHeight - mScreenOffset, + MeasureSpec.EXACTLY); + mViewRoot.measure(w_spec, h_spec); + + // now do the layout. + mViewRoot.layout(0, mScreenOffset, renderScreenWidth, renderScreenHeight); + + // draw the views + // create the BufferedImage into which the layout will be rendered. + mImage = new BufferedImage(renderScreenWidth, renderScreenHeight - mScreenOffset, + BufferedImage.TYPE_INT_ARGB); + + if (mParams.isCustomBackgroundEnabled()) { + Graphics2D gc = mImage.createGraphics(); + gc.setColor(new Color(mParams.getCustomBackgroundColor())); + gc.fillRect(0, 0, renderScreenWidth, renderScreenHeight - mScreenOffset); + gc.dispose(); + } + + // create an Android bitmap around the BufferedImage + Bitmap bitmap = Bitmap_Delegate.createBitmap(mImage, + Density.getEnum(mParams.getDensity())); + + // create a Canvas around the Android bitmap + Canvas canvas = new Canvas(bitmap); + + // to set the logger, get the native delegate + Canvas_Delegate canvasDelegate = Canvas_Delegate.getDelegate(canvas); + canvasDelegate.setLogger(mParams.getLogger()); + + mViewRoot.draw(canvas); + canvasDelegate.dispose(); + + mViewInfo = visit(((ViewGroup)mViewRoot).getChildAt(0), mContext); + + // success! + return SceneResult.SUCCESS; + } catch (Throwable e) { + // get the real cause of the exception. + Throwable t = e; + while (t.getCause() != null) { + t = t.getCause(); + } + + // log it + mParams.getLogger().error(t); + + return new SceneResult("Unknown error during inflation.", t); + } + } + + /** + * Compute style information from the given list of style for the project and framework. + * @param themeName the name of the current theme. In order to differentiate project and + * platform themes sharing the same name, all project themes must be prepended with + * a '*' character. + * @param isProjectTheme Is this a project theme + * @param inProjectStyleMap the project style map + * @param inFrameworkStyleMap the framework style map + * @param outInheritanceMap the map of style inheritance. This is filled by the method + * @return the {@link IStyleResourceValue} matching <var>themeName</var> + */ + private IStyleResourceValue computeStyleMaps( + String themeName, boolean isProjectTheme, Map<String, + IResourceValue> inProjectStyleMap, Map<String, IResourceValue> inFrameworkStyleMap, + Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { + + if (inProjectStyleMap != null && inFrameworkStyleMap != null) { + // first, get the theme + IResourceValue theme = null; + + // project theme names have been prepended with a * + if (isProjectTheme) { + theme = inProjectStyleMap.get(themeName); + } else { + theme = inFrameworkStyleMap.get(themeName); + } + + if (theme instanceof IStyleResourceValue) { + // compute the inheritance map for both the project and framework styles + computeStyleInheritance(inProjectStyleMap.values(), inProjectStyleMap, + inFrameworkStyleMap, outInheritanceMap); + + // Compute the style inheritance for the framework styles/themes. + // Since, for those, the style parent values do not contain 'android:' + // we want to force looking in the framework style only to avoid using + // similarly named styles from the project. + // To do this, we pass null in lieu of the project style map. + computeStyleInheritance(inFrameworkStyleMap.values(), null /*inProjectStyleMap */, + inFrameworkStyleMap, outInheritanceMap); + + return (IStyleResourceValue)theme; + } + } + + return null; + } + + /** + * Compute the parent style for all the styles in a given list. + * @param styles the styles for which we compute the parent. + * @param inProjectStyleMap the map of project styles. + * @param inFrameworkStyleMap the map of framework styles. + * @param outInheritanceMap the map of style inheritance. This is filled by the method. + */ + private void computeStyleInheritance(Collection<IResourceValue> styles, + Map<String, IResourceValue> inProjectStyleMap, + Map<String, IResourceValue> inFrameworkStyleMap, + Map<IStyleResourceValue, IStyleResourceValue> outInheritanceMap) { + for (IResourceValue value : styles) { + if (value instanceof IStyleResourceValue) { + IStyleResourceValue style = (IStyleResourceValue)value; + IStyleResourceValue parentStyle = null; + + // first look for a specified parent. + String parentName = style.getParentStyle(); + + // no specified parent? try to infer it from the name of the style. + if (parentName == null) { + parentName = getParentName(value.getName()); + } + + if (parentName != null) { + parentStyle = getStyle(parentName, inProjectStyleMap, inFrameworkStyleMap); + + if (parentStyle != null) { + outInheritanceMap.put(style, parentStyle); + } + } + } + } + } + + /** + * Searches for and returns the {@link IStyleResourceValue} from a given name. + * <p/>The format of the name can be: + * <ul> + * <li>[android:]<name></li> + * <li>[android:]style/<name></li> + * <li>@[android:]style/<name></li> + * </ul> + * @param parentName the name of the style. + * @param inProjectStyleMap the project style map. Can be <code>null</code> + * @param inFrameworkStyleMap the framework style map. + * @return The matching {@link IStyleResourceValue} object or <code>null</code> if not found. + */ + private IStyleResourceValue getStyle(String parentName, + Map<String, IResourceValue> inProjectStyleMap, + Map<String, IResourceValue> inFrameworkStyleMap) { + boolean frameworkOnly = false; + + String name = parentName; + + // remove the useless @ if it's there + if (name.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { + name = name.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); + } + + // check for framework identifier. + if (name.startsWith(BridgeConstants.PREFIX_ANDROID)) { + frameworkOnly = true; + name = name.substring(BridgeConstants.PREFIX_ANDROID.length()); + } + + // at this point we could have the format <type>/<name>. we want only the name as long as + // the type is style. + if (name.startsWith(BridgeConstants.REFERENCE_STYLE)) { + name = name.substring(BridgeConstants.REFERENCE_STYLE.length()); + } else if (name.indexOf('/') != -1) { + return null; + } + + IResourceValue parent = null; + + // if allowed, search in the project resources. + if (frameworkOnly == false && inProjectStyleMap != null) { + parent = inProjectStyleMap.get(name); + } + + // if not found, then look in the framework resources. + if (parent == null) { + parent = inFrameworkStyleMap.get(name); + } + + // make sure the result is the proper class type and return it. + if (parent instanceof IStyleResourceValue) { + return (IStyleResourceValue)parent; + } + + mParams.getLogger().error( + String.format("Unable to resolve parent style name: %s", parentName)); + + return null; + } + + /** + * Computes the name of the parent style, or <code>null</code> if the style is a root style. + */ + private String getParentName(String styleName) { + int index = styleName.lastIndexOf('.'); + if (index != -1) { + return styleName.substring(0, index); + } + + return null; + } + + /** + * Returns the top screen offset. This depends on whether the current theme defines the user + * of the title and status bars. + * @param frameworkResources The framework resources + * @param currentTheme The current theme + * @param context The context + * @return the pixel height offset + */ + private int getScreenOffset(Map<String, Map<String, IResourceValue>> frameworkResources, + IStyleResourceValue currentTheme, BridgeContext context) { + int offset = 0; + + // get the title bar flag from the current theme. + IResourceValue value = context.findItemInStyle(currentTheme, "windowNoTitle"); + + // because it may reference something else, we resolve it. + value = context.resolveResValue(value); + + // if there's a value and it's true (default is false) + if (value == null || value.getValue() == null || + XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { + // default size of the window title bar + int defaultOffset = DEFAULT_TITLE_BAR_HEIGHT; + + // get value from the theme. + value = context.findItemInStyle(currentTheme, "windowTitleSize"); + + // resolve it + value = context.resolveResValue(value); + + if (value != null) { + // get the numerical value, if available + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics); + } + } + + offset += defaultOffset; + } + + // get the fullscreen flag from the current theme. + value = context.findItemInStyle(currentTheme, "windowFullscreen"); + + // because it may reference something else, we resolve it. + value = context.resolveResValue(value); + + if (value == null || value.getValue() == null || + XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { + + // default value + int defaultOffset = DEFAULT_STATUS_BAR_HEIGHT; + + // get the real value, first the list of Dimensions from the framework map + Map<String, IResourceValue> dimens = frameworkResources.get(BridgeConstants.RES_DIMEN); + + // now get the value + value = dimens.get("status_bar_height"); + if (value != null) { + TypedValue typedValue = ResourceHelper.getValue(value.getValue()); + if (typedValue != null) { + // compute the pixel value based on the display metrics + defaultOffset = (int)typedValue.getDimension(context.getResources().mMetrics); + } + } + + // add the computed offset. + offset += defaultOffset; + } + + return offset; + + } + + /** + * Post process on a view hierachy that was just inflated. + * <p/>At the moment this only support TabHost: If {@link TabHost} is detected, look for the + * {@link TabWidget}, and the corresponding {@link FrameLayout} and make new tabs automatically + * based on the content of the {@link FrameLayout}. + * @param view the root view to process. + * @param projectCallback callback to the project. + */ + private void postInflateProcess(View view, IProjectCallback projectCallback) + throws PostInflateException { + if (view instanceof TabHost) { + setupTabHost((TabHost)view, projectCallback); + } else if (view instanceof ViewGroup) { + ViewGroup group = (ViewGroup)view; + final int count = group.getChildCount(); + for (int c = 0 ; c < count ; c++) { + View child = group.getChildAt(c); + postInflateProcess(child, projectCallback); + } + } + } + + /** + * Sets up a {@link TabHost} object. + * @param tabHost the TabHost to setup. + * @param projectCallback The project callback object to access the project R class. + * @throws PostInflateException + */ + private void setupTabHost(TabHost tabHost, IProjectCallback projectCallback) + throws PostInflateException { + // look for the TabWidget, and the FrameLayout. They have their own specific names + View v = tabHost.findViewById(android.R.id.tabs); + + if (v == null) { + throw new PostInflateException( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n"); + } + + if ((v instanceof TabWidget) == false) { + throw new PostInflateException(String.format( + "TabHost requires a TabWidget with id \"android:id/tabs\".\n" + + "View found with id 'tabs' is '%s'", v.getClass().getCanonicalName())); + } + + v = tabHost.findViewById(android.R.id.tabcontent); + + if (v == null) { + // TODO: see if we can fake tabs even without the FrameLayout (same below when the framelayout is empty) + throw new PostInflateException( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\"."); + } + + if ((v instanceof FrameLayout) == false) { + throw new PostInflateException(String.format( + "TabHost requires a FrameLayout with id \"android:id/tabcontent\".\n" + + "View found with id 'tabcontent' is '%s'", v.getClass().getCanonicalName())); + } + + FrameLayout content = (FrameLayout)v; + + // now process the content of the framelayout and dynamically create tabs for it. + final int count = content.getChildCount(); + + if (count == 0) { + throw new PostInflateException( + "The FrameLayout for the TabHost has no content. Rendering failed.\n"); + } + + // this must be called before addTab() so that the TabHost searches its TabWidget + // and FrameLayout. + tabHost.setup(); + + // for each child of the framelayout, add a new TabSpec + for (int i = 0 ; i < count ; i++) { + View child = content.getChildAt(i); + String tabSpec = String.format("tab_spec%d", i+1); + int id = child.getId(); + String[] resource = projectCallback.resolveResourceValue(id); + String name; + if (resource != null) { + name = resource[0]; // 0 is resource name, 1 is resource type. + } else { + name = String.format("Tab %d", i+1); // default name if id is unresolved. + } + tabHost.addTab(tabHost.newTabSpec(tabSpec).setIndicator(name).setContent(id)); + } + } + + + /** + * Visits a View and its children and generate a {@link ViewInfo} containing the + * bounds of all the views. + * @param view the root View + * @param context the context. + */ + private ViewInfo visit(View view, BridgeContext context) { + if (view == null) { + return null; + } + + ViewInfo result = new ViewInfo(view.getClass().getName(), + context.getViewKey(view), + view.getLeft(), view.getTop(), view.getRight(), view.getBottom()); + + if (view instanceof ViewGroup) { + ViewGroup group = ((ViewGroup) view); + List<ViewInfo> children = new ArrayList<ViewInfo>(); + for (int i = 0; i < group.getChildCount(); i++) { + children.add(visit(group.getChildAt(i), context)); + } + result.setChildren(children); + } + + return result; + } + + public BufferedImage getImage() { + return mImage; + } + + public ViewInfo getViewInfo() { + return mViewInfo; + } +} diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java index f13ecdc..3e506b8 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceHelper.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/ResourceHelper.java @@ -14,11 +14,15 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.impl; import com.android.layoutlib.api.IDensityBasedResourceValue; import com.android.layoutlib.api.IDensityBasedResourceValue.Density; import com.android.layoutlib.api.IResourceValue; +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.android.NinePatchDrawable; import com.android.ninepatch.NinePatch; import org.kxml2.io.KXmlParser; @@ -56,7 +60,7 @@ public final class ResourceHelper { * @return the color as an int * @throw NumberFormatException if the conversion failed. */ - static int getColor(String value) { + public static int getColor(String value) { if (value != null) { if (value.startsWith("#") == false) { throw new NumberFormatException(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/TempResourceValue.java index 01a4871..4ab98ce 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/ResourceValue.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/impl/TempResourceValue.java @@ -14,24 +14,24 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.impl; +import com.android.layoutlib.api.ILayoutBridge; import com.android.layoutlib.api.IResourceValue; /** - * Basic implementation of IResourceValue. + * Basic implementation of IResourceValue for when it is needed to dynamically make a new + * {@link IResourceValue} object. + * + * Most of the time, implementations of IResourceValue come through the {@link ILayoutBridge} + * API. */ -class ResourceValue implements IResourceValue { +public class TempResourceValue implements IResourceValue { private final String mType; private final String mName; private String mValue = null; - - ResourceValue(String name) { - mType = null; - mName = name; - } - public ResourceValue(String type, String name, String value) { + public TempResourceValue(String type, String name, String value) { mType = type; mName = name; mValue = value; @@ -44,16 +44,16 @@ class ResourceValue implements IResourceValue { public final String getName() { return mName; } - + public final String getValue() { return mValue; } - + public final void setValue(String value) { mValue = value; } - - public void replaceWith(ResourceValue value) { + + public void replaceWith(TempResourceValue value) { mValue = value.mValue; } diff --git a/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java index 6d013bb..1ec6262 100644 --- a/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java +++ b/tools/layoutlib/bridge/src/com/google/android/maps/MapView.java @@ -16,7 +16,7 @@ package com.google.android.maps; -import com.android.layoutlib.bridge.MockView; +import com.android.layoutlib.bridge.android.MockView; import android.content.Context; import android.os.Bundle; diff --git a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java index db1262f..3252fb4 100644 --- a/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/BridgeXmlBlockParserTest.java +++ b/tools/layoutlib/bridge/tests/com/android/layoutlib/bridge/android/BridgeXmlBlockParserTest.java @@ -14,7 +14,9 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package com.android.layoutlib.bridge.android; + +import com.android.layoutlib.bridge.android.BridgeXmlBlockParser; import org.kxml2.io.KXmlParser; import org.w3c.dom.Node; |