diff options
48 files changed, 1563 insertions, 1143 deletions
diff --git a/core/java/android/os/StrictMode.java b/core/java/android/os/StrictMode.java index 25d868f..22876c0 100644 --- a/core/java/android/os/StrictMode.java +++ b/core/java/android/os/StrictMode.java @@ -112,6 +112,9 @@ public final class StrictMode { // Only show an annoying dialog at most every 30 seconds private static final long MIN_DIALOG_INTERVAL_MS = 30000; + // How many Span tags (e.g. animations) to report. + private static final int MAX_SPAN_TAGS = 20; + // How many offending stacks to keep track of (and time) per loop // of the Looper. private static final int MAX_OFFENSES_PER_LOOP = 10; @@ -1217,6 +1220,140 @@ public final class StrictMode { } /** + * A tracked, critical time span. (e.g. during an animation.) + * + * The object itself is a linked list node, to avoid any allocations + * during rapid span entries and exits. + * + * @hide + */ + public static class Span { + private String mName; + private long mCreateMillis; + private Span mNext; + private Span mPrev; // not used when in freeList, only active + private final ThreadSpanState mContainerState; + + Span(ThreadSpanState threadState) { + mContainerState = threadState; + } + + /** + * To be called when the critical span is complete (i.e. the + * animation is done animating). This can be called on any + * thread (even a different one from where the animation was + * taking place), but that's only a defensive implementation + * measure. It really makes no sense for you to call this on + * thread other than that where you created it. + * + * @hide + */ + public void finish() { + ThreadSpanState state = mContainerState; + synchronized (state) { + if (mName == null) { + // Duplicate finish call. Ignore. + return; + } + + // Remove ourselves from the active list. + if (mPrev != null) { + mPrev.mNext = mNext; + } + if (mNext != null) { + mNext.mPrev = mPrev; + } + if (state.mActiveHead == this) { + state.mActiveHead = mNext; + } + + this.mCreateMillis = -1; + this.mName = null; + this.mPrev = null; + this.mNext = null; + state.mActiveSize--; + + // Add ourselves to the freeList, if it's not already + // too big. + if (state.mFreeListSize < 5) { + this.mNext = state.mFreeListHead; + state.mFreeListHead = this; + state.mFreeListSize++; + } + } + } + } + + /** + * Linked lists of active spans and a freelist. + * + * Locking notes: there's one of these structures per thread and + * all members of this structure (as well as the Span nodes under + * it) are guarded by the ThreadSpanState object instance. While + * in theory there'd be no locking required because it's all local + * per-thread, the finish() method above is defensive against + * people calling it on a different thread from where they created + * the Span, hence the locking. + */ + private static class ThreadSpanState { + public Span mActiveHead; // doubly-linked list. + public int mActiveSize; + public Span mFreeListHead; // singly-linked list. only changes at head. + public int mFreeListSize; + } + + private static final ThreadLocal<ThreadSpanState> sThisThreadSpanState = + new ThreadLocal<ThreadSpanState>() { + @Override protected ThreadSpanState initialValue() { + return new ThreadSpanState(); + } + }; + + /** + * Enter a named critical span (e.g. an animation) + * + * <p>The name is an arbitary label (or tag) that will be applied + * to any strictmode violation that happens while this span is + * active. You must call finish() on the span when done. + * + * <p>This will never return null, but on devices without debugging + * enabled, this may return a dummy object on which the finish() + * method is a no-op. + * + * <p>TODO: add CloseGuard to this, verifying callers call finish. + * + * @hide + */ + public static Span enterCriticalSpan(String name) { + if (name == null || name.isEmpty()) { + throw new IllegalArgumentException("name must be non-null and non-empty"); + } + ThreadSpanState state = sThisThreadSpanState.get(); + Span span = null; + synchronized (state) { + if (state.mFreeListHead != null) { + span = state.mFreeListHead; + state.mFreeListHead = span.mNext; + state.mFreeListSize--; + } else { + // Shouldn't have to do this often. + span = new Span(state); + } + span.mName = name; + span.mCreateMillis = SystemClock.uptimeMillis(); + span.mNext = state.mActiveHead; + span.mPrev = null; + state.mActiveHead = span; + state.mActiveSize++; + if (span.mNext != null) { + span.mNext.mPrev = span; + } + } + return span; + } + + + /** * Parcelable that gets sent in Binder call headers back to callers * to report violations that happened during a cross-process call. * @@ -1245,6 +1382,12 @@ public final class StrictMode { public int numAnimationsRunning = 0; /** + * List of tags from active Span instances during this + * violation, or null for none. + */ + public String[] tags; + + /** * Which violation number this was (1-based) since the last Looper loop, * from the perspective of the root caller (if it crossed any processes * via Binder calls). The value is 0 if the root caller wasn't on a Looper @@ -1284,6 +1427,23 @@ public final class StrictMode { if (broadcastIntent != null) { broadcastIntentAction = broadcastIntent.getAction(); } + ThreadSpanState state = sThisThreadSpanState.get(); + synchronized (state) { + int spanActiveCount = state.mActiveSize; + if (spanActiveCount > MAX_SPAN_TAGS) { + spanActiveCount = MAX_SPAN_TAGS; + } + if (spanActiveCount != 0) { + this.tags = new String[spanActiveCount]; + Span iter = state.mActiveHead; + int index = 0; + while (iter != null && index < spanActiveCount) { + this.tags[index] = iter.mName; + index++; + iter = iter.mNext; + } + } + } } /** @@ -1312,6 +1472,7 @@ public final class StrictMode { numAnimationsRunning = in.readInt(); violationUptimeMillis = in.readLong(); broadcastIntentAction = in.readString(); + tags = in.readStringArray(); } /** @@ -1325,6 +1486,7 @@ public final class StrictMode { dest.writeInt(numAnimationsRunning); dest.writeLong(violationUptimeMillis); dest.writeString(broadcastIntentAction); + dest.writeStringArray(tags); } @@ -1347,6 +1509,12 @@ public final class StrictMode { if (broadcastIntentAction != null) { pw.println(prefix + "broadcastIntentAction: " + broadcastIntentAction); } + if (tags != null) { + int index = 0; + for (String tag : tags) { + pw.println(prefix + "tag[" + (index++) + "]: " + tag); + } + } } } diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java index fb760ac..f3f9a6d 100644 --- a/core/java/android/webkit/WebView.java +++ b/core/java/android/webkit/WebView.java @@ -6529,8 +6529,11 @@ public class WebView extends AbsoluteLayout // If the text entry has created more events, ignore // this one. } else if (msg.arg2 == mTextGeneration) { - mWebTextView.setTextAndKeepSelection( - (String) msg.obj); + String text = (String) msg.obj; + if (null == text) { + text = ""; + } + mWebTextView.setTextAndKeepSelection(text); } } break; diff --git a/libs/hwui/OpenGLRenderer.cpp b/libs/hwui/OpenGLRenderer.cpp index 540f115..acce1a2 100644 --- a/libs/hwui/OpenGLRenderer.cpp +++ b/libs/hwui/OpenGLRenderer.cpp @@ -1463,7 +1463,9 @@ void OpenGLRenderer::setupColorRect(float left, float top, float right, float bo dirtyLayer(left, top, right, bottom); } } - mCaches.currentProgram->setColor(r, g, b, a); + if (!mShader || (mShader && setColor)) { + mCaches.currentProgram->setColor(r, g, b, a); + } // Setup attributes and uniforms required by the shaders if (mShader) { diff --git a/libs/hwui/SkiaShader.cpp b/libs/hwui/SkiaShader.cpp index e7e1187..590a9d7 100644 --- a/libs/hwui/SkiaShader.cpp +++ b/libs/hwui/SkiaShader.cpp @@ -63,8 +63,7 @@ void SkiaShader::setupProgram(Program* program, const mat4& modelView, const Sna GLuint* textureUnit) { } -void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit) { - glActiveTexture(gTextureUnitsMap[textureUnit]); +void SkiaShader::bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT) { glBindTexture(GL_TEXTURE_2D, texture->id); if (wrapS != texture->wrapS) { glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, wrapS); @@ -132,7 +131,7 @@ void SkiaBitmapShader::setupProgram(Program* program, const mat4& modelView, computeScreenSpaceMatrix(textureTransform, modelView); // Uniforms - bindTexture(texture, mWrapS, mWrapT, textureSlot); + bindTexture(texture, mWrapS, mWrapT); glUniform1i(program->getUniform("bitmapSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("textureTransform"), 1, GL_FALSE, &textureTransform.data[0]); @@ -204,7 +203,7 @@ void SkiaLinearGradientShader::setupProgram(Program* program, const mat4& modelV computeScreenSpaceMatrix(screenSpace, modelView); // Uniforms - bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot); + bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } @@ -297,7 +296,7 @@ void SkiaSweepGradientShader::setupProgram(Program* program, const mat4& modelVi computeScreenSpaceMatrix(screenSpace, modelView); // Uniforms - bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY], textureSlot); + bindTexture(texture, gTileModes[mTileX], gTileModes[mTileY]); glUniform1i(program->getUniform("gradientSampler"), textureSlot); glUniformMatrix4fv(program->getUniform("screenSpace"), 1, GL_FALSE, &screenSpace.data[0]); } diff --git a/libs/hwui/SkiaShader.h b/libs/hwui/SkiaShader.h index 1d884ab..6702129 100644 --- a/libs/hwui/SkiaShader.h +++ b/libs/hwui/SkiaShader.h @@ -97,7 +97,11 @@ struct SkiaShader { void computeScreenSpaceMatrix(mat4& screenSpace, const mat4& modelView); protected: - inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT, GLuint textureUnit); + /** + * The appropriate texture unit must have been activated prior to invoking + * this method. + */ + inline void bindTexture(Texture* texture, GLenum wrapS, GLenum wrapT); Type mType; SkShader* mKey; diff --git a/media/java/android/media/MediaFile.java b/media/java/android/media/MediaFile.java index 532a2df..80c97a0 100644 --- a/media/java/android/media/MediaFile.java +++ b/media/java/android/media/MediaFile.java @@ -170,6 +170,7 @@ public class MediaFile { addFileType("OTA", FILE_TYPE_MID, "audio/midi"); addFileType("MPEG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG); + addFileType("MPG", FILE_TYPE_MP4, "video/mpeg", MtpConstants.FORMAT_MPEG); addFileType("MP4", FILE_TYPE_MP4, "video/mp4", MtpConstants.FORMAT_MPEG); addFileType("M4V", FILE_TYPE_M4V, "video/mp4", MtpConstants.FORMAT_MPEG); addFileType("3GP", FILE_TYPE_3GPP, "video/3gpp", MtpConstants.FORMAT_3GP_CONTAINER); diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java index ba8d7d2..1ec8a22 100644 --- a/services/java/com/android/server/am/ActivityManagerService.java +++ b/services/java/com/android/server/am/ActivityManagerService.java @@ -6702,9 +6702,14 @@ public final class ActivityManagerService extends ActivityManagerNative if (info.broadcastIntentAction != null) { sb.append("Broadcast-Intent-Action: ").append(info.broadcastIntentAction).append("\n"); } - if (info != null && info.durationMillis != -1) { + if (info.durationMillis != -1) { sb.append("Duration-Millis: ").append(info.durationMillis).append("\n"); } + if (info.tags != null) { + for (String tag : info.tags) { + sb.append("Span-Tag: ").append(tag).append("\n"); + } + } sb.append("\n"); if (info.crashInfo != null && info.crashInfo.stackTrace != null) { sb.append(info.crashInfo.stackTrace); diff --git a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java index 68bcf11..d425734 100644 --- a/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java +++ b/tests/DumpRenderTree2/src/com/android/dumprendertree2/EventSenderImpl.java @@ -56,21 +56,38 @@ public class EventSenderImpl { private static final int MSG_SET_TOUCH_MODIFIER = 16; private static final int MSG_CANCEL_TOUCH_POINT = 17; - public static class TouchPoint { - WebView mWebView; - private int mId; + private static class Point { private int mX; private int mY; + + public Point(int x, int y) { + mX = x; + mY = y; + } + public int x() { + return mX; + } + public int y() { + return mY; + } + } + + private Point createViewPointFromContentCoordinates(int x, int y) { + return new Point((int)(x * mWebView.getScale()) - mWebView.getScrollX(), + (int)(y * mWebView.getScale()) - mWebView.getScrollY()); + } + + public static class TouchPoint { + private int mId; + private Point mPoint; private long mDownTime; private boolean mReleased = false; private boolean mMoved = false; private boolean mCancelled = false; - public TouchPoint(WebView webView, int id, int x, int y) { - mWebView = webView; + public TouchPoint(int id, Point point) { mId = id; - mX = scaleX(x); - mY = scaleY(y); + mPoint = point; } public int getId() { @@ -78,20 +95,19 @@ public class EventSenderImpl { } public int getX() { - return mX; + return mPoint.x(); } public int getY() { - return mY; + return mPoint.y(); } public boolean hasMoved() { return mMoved; } - public void move(int newX, int newY) { - mX = scaleX(newX); - mY = scaleY(newY); + public void move(Point point) { + mPoint = point; mMoved = true; } @@ -122,20 +138,11 @@ public class EventSenderImpl { public void cancel() { mCancelled = true; } - - private int scaleX(int x) { - return (int)(x * mWebView.getScale()) - mWebView.getScrollX(); - } - - private int scaleY(int y) { - return (int)(y * mWebView.getScale()) - mWebView.getScrollY(); - } } private List<TouchPoint> mTouchPoints; private int mTouchMetaState; - private int mMouseX; - private int mMouseY; + private Point mMousePoint; private WebView mWebView; @@ -177,15 +184,19 @@ public class EventSenderImpl { /** MOUSE */ case MSG_MOUSE_DOWN: - ts = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mMouseX, mMouseY, 0); - mWebView.onTouchEvent(event); + if (mMousePoint != null) { + ts = SystemClock.uptimeMillis(); + event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_DOWN, mMousePoint.x(), mMousePoint.y(), 0); + mWebView.onTouchEvent(event); + } break; case MSG_MOUSE_UP: - ts = SystemClock.uptimeMillis(); - event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mMouseX, mMouseY, 0); - mWebView.onTouchEvent(event); + if (mMousePoint != null) { + ts = SystemClock.uptimeMillis(); + event = MotionEvent.obtain(ts, ts, MotionEvent.ACTION_UP, mMousePoint.x(), mMousePoint.y(), 0); + mWebView.onTouchEvent(event); + } break; case MSG_MOUSE_CLICK: @@ -194,8 +205,7 @@ public class EventSenderImpl { break; case MSG_MOUSE_MOVE_TO: - mMouseX = msg.arg1; - mMouseY = msg.arg2; + mMousePoint = createViewPointFromContentCoordinates(msg.arg1, msg.arg2); break; /** TOUCH */ @@ -208,8 +218,8 @@ public class EventSenderImpl { } else { id = getTouchPoints().get(numPoints - 1).getId() + 1; } - getTouchPoints().add(new TouchPoint(mWebView, id, - msg.arg1, msg.arg2)); + getTouchPoints().add( + new TouchPoint(id, createViewPointFromContentCoordinates(msg.arg1, msg.arg2))); break; case MSG_TOUCH_START: @@ -232,7 +242,8 @@ public class EventSenderImpl { break; } - getTouchPoints().get(index).move(bundle.getInt("x"), bundle.getInt("y")); + getTouchPoints().get(index).move( + createViewPointFromContentCoordinates(bundle.getInt("x"), bundle.getInt("y"))); break; case MSG_TOUCH_MOVE: @@ -333,8 +344,7 @@ public class EventSenderImpl { mWebView = webView; mTouchPoints = null; mTouchMetaState = 0; - mMouseX = 0; - mMouseY = 0; + mMousePoint = null; } public void enableDOMUIEventLogging(int domNode) { 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; |
