diff options
| author | Jean-Baptiste Queru <jbq@google.com> | 2009-09-03 15:18:14 -0700 |
|---|---|---|
| committer | Jean-Baptiste Queru <jbq@google.com> | 2009-09-03 15:19:21 -0700 |
| commit | be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2 (patch) | |
| tree | 34ae4ff9404e97d9c3b858903b0a414149671ac9 /tools | |
| parent | 44b283dc27ed8ba5a946d809489f29b4ae88cb15 (diff) | |
| parent | 8f4b5a561813ee8c22d2b8e73c33299471d4a3f3 (diff) | |
| download | frameworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.zip frameworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.tar.gz frameworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.tar.bz2 | |
merge from donut
Diffstat (limited to 'tools')
30 files changed, 1641 insertions, 716 deletions
diff --git a/tools/aapt/Command.cpp b/tools/aapt/Command.cpp index 79e6641..7e6a12c 100644 --- a/tools/aapt/Command.cpp +++ b/tools/aapt/Command.cpp @@ -321,6 +321,7 @@ enum { LABEL_ATTR = 0x01010001, ICON_ATTR = 0x01010002, MIN_SDK_VERSION_ATTR = 0x0101020c, + MAX_SDK_VERSION_ATTR = 0x01010271, REQ_TOUCH_SCREEN_ATTR = 0x01010227, REQ_KEYBOARD_TYPE_ATTR = 0x01010228, REQ_HARD_KEYBOARD_ATTR = 0x01010229, @@ -585,6 +586,10 @@ int doDump(Bundle* bundle) targetSdk = code; printf("sdkVersion:'%d'\n", code); } + code = getIntegerAttribute(tree, MAX_SDK_VERSION_ATTR, NULL, -1); + if (code != -1) { + printf("maxSdkVersion:'%d'\n", code); + } code = getIntegerAttribute(tree, TARGET_SDK_VERSION_ATTR, &error); if (error != "") { error = ""; diff --git a/tools/aidl/AST.cpp b/tools/aidl/AST.cpp index 1856cb9..752ef7c 100755 --- a/tools/aidl/AST.cpp +++ b/tools/aidl/AST.cpp @@ -6,6 +6,10 @@ WriteModifiers(FILE* to, int mod, int mask) { int m = mod & mask; + if (m & OVERRIDE) { + fprintf(to, "@Override "); + } + if ((m & SCOPE_MASK) == PUBLIC) { fprintf(to, "public "); } @@ -79,7 +83,7 @@ Field::Write(FILE* to) if (this->comment.length() != 0) { fprintf(to, "%s\n", this->comment.c_str()); } - WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL); + WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE); fprintf(to, "%s %s", this->variable->type->QualifiedName().c_str(), this->variable->name.c_str()); if (this->value.length() != 0) { @@ -674,7 +678,7 @@ Method::Write(FILE* to) fprintf(to, "%s\n", this->comment.c_str()); } - WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL); + WriteModifiers(to, this->modifiers, SCOPE_MASK | STATIC | FINAL | OVERRIDE); if (this->returnType != NULL) { string dim; diff --git a/tools/aidl/AST.h b/tools/aidl/AST.h index aec2164..3156356 100755 --- a/tools/aidl/AST.h +++ b/tools/aidl/AST.h @@ -22,6 +22,8 @@ enum { FINAL = 0x00000020, ABSTRACT = 0x00000040, + OVERRIDE = 0x00000100, + ALL_MODIFIERS = 0xffffffff }; diff --git a/tools/aidl/generate_java.cpp b/tools/aidl/generate_java.cpp index da20d1f..0f18132 100644 --- a/tools/aidl/generate_java.cpp +++ b/tools/aidl/generate_java.cpp @@ -104,7 +104,7 @@ StubClass::StubClass(Type* type, Type* interfaceType) this->transact_reply = new Variable(PARCEL_TYPE, "reply"); this->transact_flags = new Variable(INT_TYPE, "flags"); Method* onTransact = new Method; - onTransact->modifiers = PUBLIC; + onTransact->modifiers = PUBLIC | OVERRIDE; onTransact->returnType = BOOLEAN_TYPE; onTransact->name = "onTransact"; onTransact->parameters.push_back(this->transact_code); diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java index 6bc01b1..7dde634 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java +++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java @@ -16,7 +16,6 @@ package android.graphics; -import com.android.layoutlib.bridge.BridgeCanvas; import java.awt.image.BufferedImage; import java.io.File; @@ -25,15 +24,15 @@ import java.io.IOException; import javax.imageio.ImageIO; public final class Bitmap extends _Original_Bitmap { - + private BufferedImage mImage; public Bitmap(File input) throws IOException { super(1, true, null); - + mImage = ImageIO.read(input); } - + Bitmap(BufferedImage image) { super(1, true, null); mImage = image; @@ -42,9 +41,9 @@ public final class Bitmap extends _Original_Bitmap { public BufferedImage getImage() { return mImage; } - + // ----- overriden methods - + public enum Config { // these native values must match up with the enum in SkBitmap.h ALPHA_8 (2), @@ -56,27 +55,26 @@ public final class Bitmap extends _Original_Bitmap { this.nativeInt = ni; } final int nativeInt; - + /* package */ static Config nativeToConfig(int ni) { return sConfigs[ni]; } - + private static Config sConfigs[] = { null, null, ALPHA_8, null, RGB_565, ARGB_4444, ARGB_8888 }; } - @Override public int getWidth() { return mImage.getWidth(); } - + @Override public int getHeight() { return mImage.getHeight(); } - + /** * Returns an immutable bitmap from the source bitmap. The new bitmap may * be the same object as source, or a copy may have been made. @@ -100,7 +98,7 @@ public final class Bitmap extends _Original_Bitmap { int width, int height) { return new Bitmap(source.mImage.getSubimage(x, y, width, height)); } - + /** * Returns an immutable bitmap from subset of the source bitmap, * transformed by the optional matrix. @@ -158,7 +156,7 @@ public final class Bitmap extends _Original_Bitmap { neww = Math.round(deviceR.width()); newh = Math.round(deviceR.height()); - BridgeCanvas canvas = new BridgeCanvas(neww, newh); + Canvas canvas = new Canvas(neww, newh); canvas.translate(-deviceR.left, -deviceR.top); canvas.concat(m); @@ -169,10 +167,10 @@ public final class Bitmap extends _Original_Bitmap { } canvas.drawBitmap(source, srcR, dstR, paint); - + return new Bitmap(canvas.getImage()); } - + /** * Returns a mutable bitmap with the specified width and height. * @@ -184,7 +182,7 @@ public final class Bitmap extends _Original_Bitmap { public static Bitmap createBitmap(int width, int height, Config config) { return new Bitmap(new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB)); } - + /** * Returns a immutable bitmap with the specified width and height, with each * pixel value set to the corresponding value in the colors array. @@ -215,7 +213,7 @@ public final class Bitmap extends _Original_Bitmap { || (lastScanline + width > length)) { throw new ArrayIndexOutOfBoundsException(); } - + // TODO: create an immutable bitmap... throw new UnsupportedOperationException(); } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java index 4710691..3fa1d1d 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java +++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java @@ -14,24 +14,16 @@ * limitations under the License. */ -package com.android.layoutlib.bridge; +package android.graphics; import com.android.layoutlib.api.ILayoutLog; -import android.graphics.Bitmap; -import android.graphics.Canvas; import android.graphics.DrawFilter; -import android.graphics.LinearGradient; -import android.graphics.Matrix; -import android.graphics.Paint; -import android.graphics.Path; import android.graphics.Picture; import android.graphics.PorterDuff; -import android.graphics.PorterDuffXfermode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.Region; -import android.graphics.Shader; import android.graphics.Xfermode; import android.graphics.Paint.Align; import android.graphics.Paint.Style; @@ -43,6 +35,7 @@ import java.awt.Composite; import java.awt.Graphics2D; import java.awt.Rectangle; import java.awt.RenderingHints; +import java.awt.geom.AffineTransform; import java.awt.image.BufferedImage; import java.util.Stack; @@ -51,36 +44,59 @@ import javax.microedition.khronos.opengles.GL; /** * Re-implementation of the Canvas, 100% in java on top of a BufferedImage. */ -public class BridgeCanvas extends Canvas { - +public class Canvas extends _Original_Canvas { + private BufferedImage mBufferedImage; private final Stack<Graphics2D> mGraphicsStack = new Stack<Graphics2D>(); private final ILayoutLog mLogger; - public BridgeCanvas(int width, int height, ILayoutLog logger) { + public Canvas() { + mLogger = null; + // the mBufferedImage will be taken from a bitmap in #setBitmap() + } + + public Canvas(Bitmap bitmap) { + mLogger = null; + mBufferedImage = bitmap.getImage(); + mGraphicsStack.push(mBufferedImage.createGraphics()); + } + + public Canvas(int nativeCanvas) { + mLogger = null; + throw new UnsupportedOperationException("Can't create Canvas(int)"); + } + + public Canvas(javax.microedition.khronos.opengles.GL gl) { + mLogger = null; + throw new UnsupportedOperationException("Can't create Canvas(javax.microedition.khronos.opengles.GL)"); + } + + // custom constructors for our use. + public Canvas(int width, int height, ILayoutLog logger) { mLogger = logger; mBufferedImage = new BufferedImage(width, height, BufferedImage.TYPE_INT_ARGB); mGraphicsStack.push(mBufferedImage.createGraphics()); } - - public BridgeCanvas(int width, int height) { + + public Canvas(int width, int height) { this(width, height, null /* logger*/); } - + + // custom mehtods public BufferedImage getImage() { return mBufferedImage; } - - Graphics2D getGraphics2d() { + + public Graphics2D getGraphics2d() { return mGraphicsStack.peek(); } - - void dispose() { + + public void dispose() { while (mGraphicsStack.size() > 0) { mGraphicsStack.pop().dispose(); } } - + /** * Creates a new {@link Graphics2D} based on the {@link Paint} parameters. * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used. @@ -91,11 +107,11 @@ public class BridgeCanvas extends Canvas { g.setColor(new Color(paint.getColor())); int alpha = paint.getAlpha(); float falpha = alpha / 255.f; - + Xfermode xfermode = paint.getXfermode(); if (xfermode instanceof PorterDuffXfermode) { PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode(); - + setModeInGraphics(mode, g, falpha); } else { if (mLogger != null && xfermode != null) { @@ -105,7 +121,7 @@ public class BridgeCanvas extends Canvas { } g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, falpha)); } - + Shader shader = paint.getShader(); if (shader instanceof LinearGradient) { g.setPaint(((LinearGradient)shader).getPaint()); @@ -116,10 +132,10 @@ public class BridgeCanvas extends Canvas { shader.getClass().getCanonicalName())); } } - + return g; } - + private void setModeInGraphics(PorterDuff.Mode mode, Graphics2D g, float falpha) { switch (mode) { case CLEAR: @@ -168,14 +184,43 @@ public class BridgeCanvas extends Canvas { break; } } - + + + // -------------------- + // OVERRIDEN ENUMS + // This is needed since we rename Canvas into _Original_Canvas + // -------------------- + + public enum EdgeType { + BW(0), //!< treat edges by just rounding to nearest pixel boundary + AA(1); //!< treat edges by rounding-out, since they may be antialiased + + EdgeType(int nativeInt) { + this.nativeInt = nativeInt; + } + final int nativeInt; + } + + // -------------------- - + // OVERRIDEN METHODS + // -------------------- + @Override public void finalize() throws Throwable { // pass } - + + /* (non-Javadoc) + * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap) + */ + @Override + public void setBitmap(Bitmap bitmap) { + mBufferedImage = bitmap.getImage(); + mGraphicsStack.push(mBufferedImage.createGraphics()); + } + + /* (non-Javadoc) * @see android.graphics.Canvas#translate(float, float) */ @@ -183,7 +228,7 @@ public class BridgeCanvas extends Canvas { public void translate(float dx, float dy) { getGraphics2d().translate(dx, dy); } - + /* (non-Javadoc) * @see android.graphics.Canvas#save() */ @@ -191,7 +236,7 @@ public class BridgeCanvas extends Canvas { public int save() { Graphics2D g = (Graphics2D)getGraphics2d().create(); mGraphicsStack.push(g); - + return mGraphicsStack.size() - 1; } @@ -203,7 +248,7 @@ public class BridgeCanvas extends Canvas { // For now we ignore saveFlags return save(); } - + /* (non-Javadoc) * @see android.graphics.Canvas#restore() */ @@ -221,7 +266,7 @@ public class BridgeCanvas extends Canvas { mGraphicsStack.pop(); } } - + /* (non-Javadoc) * @see android.graphics.Canvas#getSaveCount() */ @@ -229,8 +274,8 @@ public class BridgeCanvas extends Canvas { public int getSaveCount() { return mGraphicsStack.size() - 1; } - - + + /* (non-Javadoc) * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op) */ @@ -288,23 +333,36 @@ public class BridgeCanvas extends Canvas { public boolean clipRect(RectF rect) { return clipRect(rect.left, rect.top, rect.right, rect.bottom); } - - @Override + public boolean quickReject(RectF rect, EdgeType type) { return false; } @Override + public boolean quickReject(RectF rect, _Original_Canvas.EdgeType type) { + throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); + } + public boolean quickReject(Path path, EdgeType type) { return false; } @Override + public boolean quickReject(Path path, _Original_Canvas.EdgeType type) { + throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); + } + public boolean quickReject(float left, float top, float right, float bottom, EdgeType type) { return false; } + @Override + public boolean quickReject(float left, float top, float right, float bottom, + _Original_Canvas.EdgeType type) { + throw new UnsupportedOperationException("CALL TO PARENT FORBIDDEN"); + } + /** * Retrieve the clip bounds, returning true if they are non-empty. * @@ -324,31 +382,31 @@ public class BridgeCanvas extends Canvas { } return false; } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawColor(int, android.graphics.PorterDuff.Mode) */ @Override public void drawColor(int color, PorterDuff.Mode mode) { Graphics2D g = getGraphics2d(); - + // save old color Color c = g.getColor(); - + Composite composite = g.getComposite(); - + // get the alpha from the color int alpha = color >>> 24; float falpha = alpha / 255.f; - + setModeInGraphics(mode, g, falpha); - + g.setColor(new Color(color)); - + getGraphics2d().fillRect(0, 0, getWidth(), getHeight()); - + g.setComposite(composite); - + // restore color g.setColor(c); } @@ -360,7 +418,7 @@ public class BridgeCanvas extends Canvas { public void drawColor(int color) { drawColor(color, PorterDuff.Mode.SRC_OVER); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawARGB(int, int, int, int) */ @@ -368,7 +426,7 @@ public class BridgeCanvas extends Canvas { public void drawARGB(int a, int r, int g, int b) { drawColor(a << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawRGB(int, int, int) */ @@ -377,7 +435,7 @@ public class BridgeCanvas extends Canvas { drawColor(0xFF << 24 | r << 16 | g << 8 | b, PorterDuff.Mode.SRC_OVER); } - + /* (non-Javadoc) * @see android.graphics.Canvas#getWidth() */ @@ -385,7 +443,7 @@ public class BridgeCanvas extends Canvas { public int getWidth() { return mBufferedImage.getWidth(); } - + /* (non-Javadoc) * @see android.graphics.Canvas#getHeight() */ @@ -401,7 +459,7 @@ public class BridgeCanvas extends Canvas { public void drawPaint(Paint paint) { drawColor(paint.getColor()); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawBitmap(android.graphics.Bitmap, float, float, android.graphics.Paint) */ @@ -417,7 +475,32 @@ public class BridgeCanvas extends Canvas { */ @Override public void drawBitmap(Bitmap bitmap, Matrix matrix, Paint paint) { - throw new UnsupportedOperationException(); + boolean needsRestore = false; + if (matrix.isIdentity() == false) { + // create a new graphics and apply the matrix to it + save(); // this creates a new Graphics2D, and stores it for children call to use + needsRestore = true; + Graphics2D g = getGraphics2d(); // get the newly create Graphics2D + + // get the Graphics2D current matrix + AffineTransform currentTx = g.getTransform(); + // get the AffineTransform from the matrix + AffineTransform matrixTx = matrix.getTransform(); + + // combine them so that the matrix is applied after. + currentTx.preConcatenate(matrixTx); + + // give it to the graphics as a new matrix replacing all previous transform + g.setTransform(currentTx); + } + + // draw the bitmap + drawBitmap(bitmap, 0, 0, paint); + + if (needsRestore) { + // remove the new graphics + restore(); + } } /* (non-Javadoc) @@ -456,39 +539,42 @@ public class BridgeCanvas extends Canvas { int height, boolean hasAlpha, Paint paint) { throw new UnsupportedOperationException(); } - + private void drawBitmap(Bitmap bitmap, int sleft, int stop, int sright, int sbottom, int dleft, int dtop, int dright, int dbottom, Paint paint) { BufferedImage image = bitmap.getImage(); - + Graphics2D g = getGraphics2d(); - + Composite c = null; - - if (paint.isFilterBitmap()) { - g = (Graphics2D)g.create(); - g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, - RenderingHints.VALUE_INTERPOLATION_BILINEAR); - } - - if (paint.getAlpha() != 0xFF) { - c = g.getComposite(); - g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, - paint.getAlpha()/255.f)); + + if (paint != null) { + if (paint.isFilterBitmap()) { + g = (Graphics2D)g.create(); + g.setRenderingHint(RenderingHints.KEY_INTERPOLATION, + RenderingHints.VALUE_INTERPOLATION_BILINEAR); + } + + if (paint.getAlpha() != 0xFF) { + c = g.getComposite(); + g.setComposite(AlphaComposite.getInstance(AlphaComposite.SRC_OVER, + paint.getAlpha()/255.f)); + } } - + g.drawImage(image, dleft, dtop, dright, dbottom, sleft, stop, sright, sbottom, null); - if (paint.isFilterBitmap()) { - g.dispose(); - } - - if (c != null) { - g.setComposite(c); + if (paint != null) { + if (paint.isFilterBitmap()) { + g.dispose(); + } + if (c != null) { + g.setComposite(c); + } } } - + /* (non-Javadoc) * @see android.graphics.Canvas#rotate(float, float, float) */ @@ -509,7 +595,7 @@ public class BridgeCanvas extends Canvas { public void rotate(float degrees) { getGraphics2d().rotate(Math.toRadians(degrees)); } - + /* (non-Javadoc) * @see android.graphics.Canvas#scale(float, float, float, float) */ @@ -528,19 +614,19 @@ public class BridgeCanvas extends Canvas { public void scale(float sx, float sy) { getGraphics2d().scale(sx, sy); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawText(char[], int, int, float, float, android.graphics.Paint) */ @Override public void drawText(char[] text, int index, int count, float x, float y, Paint paint) { Graphics2D g = getGraphics2d(); - + g = (Graphics2D)g.create(); g.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_ON); - + g.setFont(paint.getFont()); - + // set the color. because this only handles RGB we have to handle the alpha separately g.setColor(new Color(paint.getColor())); int alpha = paint.getAlpha(); @@ -557,9 +643,9 @@ public class BridgeCanvas extends Canvas { x -= m; } } - + g.drawChars(text, index, count, (int)x, (int)y); - + g.dispose(); } @@ -586,7 +672,7 @@ public class BridgeCanvas extends Canvas { public void drawText(String text, int start, int end, float x, float y, Paint paint) { drawText(text.toCharArray(), start, end - start, x, y, paint); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawRect(android.graphics.RectF, android.graphics.Paint) */ @@ -594,7 +680,7 @@ public class BridgeCanvas extends Canvas { public void drawRect(RectF rect, Paint paint) { doDrawRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), paint); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawRect(float, float, float, float, android.graphics.Paint) */ @@ -614,11 +700,11 @@ public class BridgeCanvas extends Canvas { private final void doDrawRect(int left, int top, int width, int height, Paint paint) { // get current graphisc Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); Style style = paint.getStyle(); - + // draw if (style == Style.FILL || style == Style.FILL_AND_STROKE) { g.fillRect(left, top, width, height); @@ -639,16 +725,16 @@ public class BridgeCanvas extends Canvas { public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) { // get current graphisc Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); Style style = paint.getStyle(); - + // draw - + int arcWidth = (int)(rx * 2); int arcHeight = (int)(ry * 2); - + if (style == Style.FILL || style == Style.FILL_AND_STROKE) { g.fillRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(), arcWidth, arcHeight); @@ -671,7 +757,7 @@ public class BridgeCanvas extends Canvas { public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) { // get current graphisc Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY); @@ -679,7 +765,7 @@ public class BridgeCanvas extends Canvas { // dispose Graphics2D object g.dispose(); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawLines(float[], int, int, android.graphics.Paint) */ @@ -687,7 +773,7 @@ public class BridgeCanvas extends Canvas { public void drawLines(float[] pts, int offset, int count, Paint paint) { // get current graphisc Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); for (int i = 0 ; i < count ; i += 4) { @@ -706,7 +792,7 @@ public class BridgeCanvas extends Canvas { public void drawLines(float[] pts, Paint paint) { drawLines(pts, 0, pts.length, paint); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawCircle(float, float, float, android.graphics.Paint) */ @@ -714,11 +800,11 @@ public class BridgeCanvas extends Canvas { public void drawCircle(float cx, float cy, float radius, Paint paint) { // get current graphisc Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); Style style = paint.getStyle(); - + int size = (int)(radius * 2); // draw @@ -741,11 +827,11 @@ public class BridgeCanvas extends Canvas { public void drawOval(RectF oval, Paint paint) { // get current graphics Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); Style style = paint.getStyle(); - + // draw if (style == Style.FILL || style == Style.FILL_AND_STROKE) { g.fillOval((int)oval.left, (int)oval.top, (int)oval.width(), (int)oval.height()); @@ -758,7 +844,7 @@ public class BridgeCanvas extends Canvas { // dispose Graphics2D object g.dispose(); } - + /* (non-Javadoc) * @see android.graphics.Canvas#drawPath(android.graphics.Path, android.graphics.Paint) */ @@ -766,11 +852,11 @@ public class BridgeCanvas extends Canvas { public void drawPath(Path path, Paint paint) { // get current graphics Graphics2D g = getGraphics2d(); - + g = getNewGraphics(paint, g); Style style = paint.getStyle(); - + // draw if (style == Style.FILL || style == Style.FILL_AND_STROKE) { g.fill(path.getAwtShape()); @@ -783,7 +869,7 @@ public class BridgeCanvas extends Canvas { // dispose Graphics2D object g.dispose(); } - + /* (non-Javadoc) * @see android.graphics.Canvas#setMatrix(android.graphics.Matrix) */ @@ -795,10 +881,10 @@ public class BridgeCanvas extends Canvas { // get the new current graphics Graphics2D g = getGraphics2d(); - + // and apply the matrix g.setTransform(matrix.getTransform()); - + if (mLogger != null && matrix.hasPerspective()) { mLogger.warning("android.graphics.Canvas#setMatrix(android.graphics.Matrix) only supports affine transformations in the Layout Editor."); } @@ -1059,15 +1145,6 @@ public class BridgeCanvas extends Canvas { } /* (non-Javadoc) - * @see android.graphics.Canvas#setBitmap(android.graphics.Bitmap) - */ - @Override - public void setBitmap(Bitmap bitmap) { - // TODO Auto-generated method stub - super.setBitmap(bitmap); - } - - /* (non-Javadoc) * @see android.graphics.Canvas#setDrawFilter(android.graphics.DrawFilter) */ @Override diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java index 18c0e17..3974e08 100644 --- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java +++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java @@ -17,6 +17,7 @@ package android.graphics; import java.awt.geom.AffineTransform; +import java.awt.geom.NoninvertibleTransformException; /** @@ -747,7 +748,24 @@ public class Matrix extends _Original_Matrix { * inverted, ignore inverse and return false. */ public boolean invert(Matrix inverse) { - throw new UnsupportedOperationException("STUB NEEDED"); + if (inverse == null) { + return false; + } + + try { + AffineTransform affineTransform = getTransform(); + AffineTransform inverseTransform = affineTransform.createInverse(); + inverse.mValues[0] = (float)inverseTransform.getScaleX(); + inverse.mValues[1] = (float)inverseTransform.getShearX(); + inverse.mValues[2] = (float)inverseTransform.getTranslateX(); + inverse.mValues[3] = (float)inverseTransform.getScaleX(); + inverse.mValues[4] = (float)inverseTransform.getShearY(); + inverse.mValues[5] = (float)inverseTransform.getTranslateY(); + + return true; + } catch (NoninvertibleTransformException e) { + return false; + } } @Override @@ -770,7 +788,19 @@ public class Matrix extends _Original_Matrix { public void mapPoints(float[] dst, int dstIndex, float[] src, int srcIndex, int pointCount) { checkPointArrays(src, srcIndex, dst, dstIndex, pointCount); - throw new UnsupportedOperationException("STUB NEEDED"); + + for (int i = 0 ; i < pointCount ; i++) { + // just in case we are doing in place, we better put this in temp vars + float x = mValues[0] * src[i + srcIndex] + + mValues[1] * src[i + srcIndex + 1] + + mValues[2]; + float y = mValues[3] * src[i + srcIndex] + + mValues[4] * src[i + srcIndex + 1] + + mValues[5]; + + dst[i + dstIndex] = x; + dst[i + dstIndex + 1] = y; + } } /** @@ -858,7 +888,26 @@ public class Matrix extends _Original_Matrix { if (dst == null || src == null) { throw new NullPointerException(); } - throw new UnsupportedOperationException("STUB NEEDED"); + + // array with 4 corners + float[] corners = new float[] { + src.left, src.top, + src.right, src.top, + src.right, src.bottom, + src.left, src.bottom, + }; + + // apply the transform to them. + mapPoints(corners); + + // now put the result in the rect. We take the min/max of Xs and min/max of Ys + dst.left = Math.min(Math.min(corners[0], corners[2]), Math.min(corners[4], corners[6])); + dst.right = Math.max(Math.max(corners[0], corners[2]), Math.max(corners[4], corners[6])); + + dst.top = Math.min(Math.min(corners[1], corners[3]), Math.min(corners[5], corners[7])); + dst.bottom = Math.max(Math.max(corners[1], corners[3]), Math.max(corners[5], corners[7])); + + return rectStaysRect(); } /** diff --git a/tools/layoutlib/bridge/src/android/os/ServiceManager.java b/tools/layoutlib/bridge/src/android/os/ServiceManager.java new file mode 100644 index 0000000..6a68ee2 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/os/ServiceManager.java @@ -0,0 +1,72 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.os; + +import java.util.Map; + +public final class ServiceManager { + + /** + * Returns a reference to a service with the given name. + * + * @param name the name of the service to get + * @return a reference to the service, or <code>null</code> if the service doesn't exist + */ + public static IBinder getService(String name) { + return null; + } + + /** + * Place a new @a service called @a name into the service + * manager. + * + * @param name the name of the new service + * @param service the service object + */ + public static void addService(String name, IBinder service) { + // pass + } + + /** + * Retrieve an existing service called @a name from the + * service manager. Non-blocking. + */ + public static IBinder checkService(String name) { + return null; + } + + /** + * Return a list of all currently running services. + */ + public static String[] listServices() throws RemoteException { + // actual implementation returns null sometimes, so it's ok + // to return null instead of an empty list. + return null; + } + + /** + * This is only intended to be called when the process is first being brought + * up and bound by the activity manager. There is only one thread in the process + * at that time, so no locking is done. + * + * @param cache the cache of service references + * @hide + */ + public static void initServiceCache(Map<String, IBinder> cache) { + // pass + } +} diff --git a/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java new file mode 100644 index 0000000..251c053 --- /dev/null +++ b/tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java @@ -0,0 +1,95 @@ +/* + * Copyright (C) 2009 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package android.view.accessibility; + +import android.content.Context; +import android.content.pm.ServiceInfo; + +import java.util.Collections; +import java.util.List; + +/** + * System level service that serves as an event dispatch for {@link AccessibilityEvent}s. + * Such events are generated when something notable happens in the user interface, + * for example an {@link android.app.Activity} starts, the focus or selection of a + * {@link android.view.View} changes etc. Parties interested in handling accessibility + * events implement and register an accessibility service which extends + * {@link android.accessibilityservice.AccessibilityService}. + * + * @see AccessibilityEvent + * @see android.accessibilityservice.AccessibilityService + * @see android.content.Context#getSystemService + */ +public final class AccessibilityManager { + private static AccessibilityManager sInstance = new AccessibilityManager(); + + /** + * Get an AccessibilityManager instance (create one if necessary). + * + * @hide + */ + public static AccessibilityManager getInstance(Context context) { + return sInstance; + } + + /** + * Create an instance. + * + * @param context A {@link Context}. + */ + private AccessibilityManager() { + } + + /** + * Returns if the {@link AccessibilityManager} is enabled. + * + * @return True if this {@link AccessibilityManager} is enabled, false otherwise. + */ + public boolean isEnabled() { + return false; + } + + /** + * Sends an {@link AccessibilityEvent}. If this {@link AccessibilityManager} is not + * enabled the call is a NOOP. + * + * @param event The {@link AccessibilityEvent}. + * + * @throws IllegalStateException if a client tries to send an {@link AccessibilityEvent} + * while accessibility is not enabled. + */ + public void sendAccessibilityEvent(AccessibilityEvent event) { + } + + /** + * Requests interruption of the accessibility feedback from all accessibility services. + */ + public void interrupt() { + } + + /** + * Returns the {@link ServiceInfo}s of the installed accessibility services. + * + * @return An unmodifiable list with {@link ServiceInfo}s. + */ + public List<ServiceInfo> getAccessibilityServiceList() { + // normal implementation does this in some case, so let's do the same + // (unmodifiableList wrapped around null). + List<ServiceInfo> services = null; + return Collections.unmodifiableList(services); + } +} 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 fd77d51..145a045 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java @@ -31,6 +31,7 @@ import com.android.tools.layoutlib.create.MethodAdapter; import com.android.tools.layoutlib.create.OverrideMethod; import android.graphics.Bitmap; +import android.graphics.Canvas; import android.graphics.Rect; import android.graphics.Region; import android.graphics.Typeface; @@ -358,7 +359,7 @@ public final class Bridge implements ILayoutBridge { windowBackground = context.findItemInStyle(currentTheme, "windowBackground"); windowBackground = context.resolveResValue(windowBackground); - screenOffset = getScreenOffset(currentTheme, context); + screenOffset = getScreenOffset(frameworkResources, currentTheme, context); } // we need to make sure the Looper has been initialized for this thread. @@ -401,8 +402,7 @@ public final class Bridge implements ILayoutBridge { view.layout(0, screenOffset, screenWidth, screenHeight); // draw them - BridgeCanvas canvas = new BridgeCanvas(screenWidth, screenHeight - screenOffset, - logger); + Canvas canvas = new Canvas(screenWidth, screenHeight - screenOffset, logger); root.draw(canvas); canvas.dispose(); @@ -673,9 +673,13 @@ public final class Bridge implements ILayoutBridge { /** * 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(IStyleResourceValue currentTheme, BridgeContext context) { + 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. @@ -687,22 +691,25 @@ public final class Bridge implements ILayoutBridge { // 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); - // default value - offset = DEFAULT_TITLE_BAR_HEIGHT; - - // get the real value; if (value != null) { + // get the numerical value, if available TypedValue typedValue = ResourceHelper.getValue(value.getValue()); if (typedValue != null) { - offset = (int)typedValue.getDimension(context.getResources().mMetrics); + // 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. @@ -713,8 +720,25 @@ public final class Bridge implements ILayoutBridge { if (value == null || value.getValue() == null || XmlUtils.convertValueToBoolean(value.getValue(), false /* defValue */) == false) { - // FIXME: Right now this is hard-coded in the platform, but once there's a constant, we'll need to use it. - offset += DEFAULT_STATUS_BAR_HEIGHT; + + // 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; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java index b426247..b5b7ceb 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java @@ -45,11 +45,12 @@ public class BridgeConstants { public final static String PREFIX_RESOURCE_REF = "@"; public final static String PREFIX_ANDROID_THEME_REF = "?android:"; public final static String PREFIX_THEME_REF = "?"; - + public final static String PREFIX_ANDROID = "android:"; - + public final static String RES_STYLE = "style"; public final static String RES_ATTR = "attr"; + public final static String RES_DIMEN = "dimen"; public final static String RES_DRAWABLE = "drawable"; public final static String RES_COLOR = "color"; public final static String RES_LAYOUT = "layout"; diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java index 69f3d9c..f48c8db 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java @@ -64,7 +64,7 @@ import java.util.Map.Entry; * Custom implementation of Context to handle non compiled resources. */ public final class BridgeContext extends Context { - + private Resources mResources; private Theme mTheme; private HashMap<View, Object> mViewKeyMap = new HashMap<View, Object>(); @@ -73,12 +73,12 @@ public final class BridgeContext extends Context { private Map<String, Map<String, IResourceValue>> mProjectResources; private Map<String, Map<String, IResourceValue>> mFrameworkResources; private Map<IStyleResourceValue, IStyleResourceValue> mStyleInheritanceMap; - + // maps for dynamically generated id representing style objects (IStyleResourceValue) private Map<Integer, IStyleResourceValue> mDynamicIdToStyleMap; private Map<IStyleResourceValue, Integer> mStyleToDynamicIdMap; private int mDynamicIdGenerator = 0x01030000; // Base id for framework R.style - + // cache for TypedArray generated from IStyleResourceValue object private Map<int[], Map<Integer, TypedArray>> mTypedArrayCache; private BridgeInflater mInflater; @@ -112,7 +112,7 @@ public final class BridgeContext extends Context { mProjectCallback = customViewLoader; mLogger = logger; Configuration config = new Configuration(); - + AssetManager assetManager = BridgeAssetManager.initSystem(); mResources = BridgeResources.initSystem( this, @@ -120,19 +120,19 @@ public final class BridgeContext extends Context { metrics, config, customViewLoader); - + mTheme = mResources.newTheme(); - + mThemeValues = currentTheme; mProjectResources = projectResources; mFrameworkResources = frameworkResources; mStyleInheritanceMap = styleInheritanceMap; } - + public void setBridgeInflater(BridgeInflater inflater) { mInflater = inflater; } - + public void addViewKey(View view, Object viewKey) { mViewKeyMap.put(view, viewKey); } @@ -140,19 +140,19 @@ public final class BridgeContext extends Context { public Object getViewKey(View view) { return mViewKeyMap.get(view); } - + public Object getProjectKey() { return mProjectKey; } - + public IProjectCallback getProjectCallback() { return mProjectCallback; } - + public ILayoutLog getLogger() { return mLogger; } - + // ------------ Context methods @Override @@ -169,14 +169,14 @@ public final class BridgeContext extends Context { public ClassLoader getClassLoader() { return this.getClass().getClassLoader(); } - + @Override public Object getSystemService(String service) { if (LAYOUT_INFLATER_SERVICE.equals(service)) { return mInflater; } - - // AutoCompleteTextView and MultiAutoCompleteTextView want a window + + // AutoCompleteTextView and MultiAutoCompleteTextView want a window // service. We don't have any but it's not worth an exception. if (WINDOW_SERVICE.equals(service)) { return null; @@ -196,38 +196,38 @@ public final class BridgeContext extends Context { throws Resources.NotFoundException { // get the IStyleResourceValue based on the resId; IStyleResourceValue style = getStyleByDynamicId(resid); - + if (style == null) { throw new Resources.NotFoundException(); } if (mTypedArrayCache == null) { mTypedArrayCache = new HashMap<int[], Map<Integer,TypedArray>>(); - + Map<Integer, TypedArray> map = new HashMap<Integer, TypedArray>(); mTypedArrayCache.put(attrs, map); BridgeTypedArray ta = createStyleBasedTypedArray(style, attrs); map.put(resid, ta); - + return ta; } - + // get the 2nd map Map<Integer, TypedArray> map = mTypedArrayCache.get(attrs); if (map == null) { map = new HashMap<Integer, TypedArray>(); mTypedArrayCache.put(attrs, map); } - + // get the array from the 2nd map TypedArray ta = map.get(resid); - + if (ta == null) { ta = createStyleBasedTypedArray(style, attrs); map.put(resid, ta); } - + return ta; } @@ -235,11 +235,11 @@ public final class BridgeContext extends Context { public final TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs) { return obtainStyledAttributes(set, attrs, 0, 0); } - + @Override public TypedArray obtainStyledAttributes(AttributeSet set, int[] attrs, int defStyleAttr, int defStyleRes) { - + // Hint: for XmlPullParser, attach source //DEVICE_SRC/dalvik/libcore/xml/src/java BridgeXmlBlockParser parser = null; if (set instanceof BridgeXmlBlockParser) { @@ -252,10 +252,10 @@ public final class BridgeContext extends Context { boolean[] frameworkAttributes = new boolean[1]; TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, frameworkAttributes); - + BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length, parser.isPlatformFile()); - + // resolve the defStyleAttr value into a IStyleResourceValue IStyleResourceValue defStyleValues = null; if (defStyleAttr != 0) { @@ -265,7 +265,7 @@ public final class BridgeContext extends Context { // look for the style in the current theme, and its parent: if (mThemeValues != null) { IResourceValue item = findItemInStyle(mThemeValues, defStyleName); - + if (item != null) { // item is a reference to a style entry. Search for it. item = findResValue(item.getValue()); @@ -279,12 +279,12 @@ public final class BridgeContext extends Context { } } } - + if (defStyleRes != 0) { // FIXME: See what we need to do with this. throw new UnsupportedOperationException(); } - + String namespace = BridgeConstants.NS_RESOURCES; if (frameworkAttributes[0] == false) { // need to use the application namespace @@ -294,32 +294,32 @@ public final class BridgeContext extends Context { if (styleNameMap != null) { for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) { int index = styleAttribute.getKey().intValue(); - + String name = styleAttribute.getValue(); String value = parser.getAttributeValue(namespace, name); - + // if there's no direct value for this attribute in the XML, we look for default // values in the widget defStyle, and then in the theme. if (value == null) { IResourceValue resValue = null; - + // look for the value in the defStyle first (and its parent if needed) if (defStyleValues != null) { resValue = findItemInStyle(defStyleValues, name); } - + // if the item is not present in the defStyle, we look in the main theme (and // its parent themes) if (resValue == null && mThemeValues != null) { resValue = findItemInStyle(mThemeValues, name); } - + // if we found a value, we make sure this doesn't reference another value. // So we resolve it. if (resValue != null) { resValue = resolveResValue(resValue); } - + ta.bridgeSetValue(index, name, resValue); } else { // there is a value in the XML, but we need to resolve it in case it's @@ -328,15 +328,20 @@ public final class BridgeContext extends Context { } } } - + ta.sealArray(); - + return ta; } - - + + @Override + public Looper getMainLooper() { + return Looper.myLooper(); + } + + // ------------- private new methods - + /** * Creates a {@link BridgeTypedArray} by filling the values defined by the int[] with the * values found in the given style. @@ -345,30 +350,30 @@ public final class BridgeContext extends Context { private BridgeTypedArray createStyleBasedTypedArray(IStyleResourceValue style, int[] attrs) throws Resources.NotFoundException { TreeMap<Integer, String> styleNameMap = searchAttrs(attrs, null); - + BridgeTypedArray ta = ((BridgeResources) mResources).newTypeArray(attrs.length, false /* platformResourceFlag */); - + // loop through all the values in the style map, and init the TypedArray with // the style we got from the dynamic id for (Entry<Integer, String> styleAttribute : styleNameMap.entrySet()) { int index = styleAttribute.getKey().intValue(); String name = styleAttribute.getValue(); - + // get the value from the style, or its parent styles. IResourceValue resValue = findItemInStyle(style, name); - + // resolve it to make sure there are no references left. ta.bridgeSetValue(index, name, resolveResValue(resValue)); } - + ta.sealArray(); return ta; } - + /** * Resolves the value of a resource, if the value references a theme or resource value. * <p/> @@ -391,13 +396,13 @@ public final class BridgeContext extends Context { // get the IResourceValue referenced by this value IResourceValue resValue = findResValue(value); - + // 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); } - + // we resolved a first reference, but we need to make sure this isn't a reference also. return resolveResValue(resValue); } @@ -411,7 +416,7 @@ public final class BridgeContext extends Context { * <p/> * If a value that does not need to be resolved is given, the method will return the input * value. - * + * * @param value the value containing the reference to resolve. * @return a {@link IResourceValue} object or <code>null</code> */ @@ -419,7 +424,7 @@ public final class BridgeContext extends Context { if (value == null) { return null; } - + // if the resource value is a style, we simply return it. if (value instanceof IStyleResourceValue) { return value; @@ -436,7 +441,7 @@ public final class BridgeContext extends Context { // otherwise, we attempt to resolve this new value as well return resolveResValue(resolvedValue); } - + /** * Searches for, and returns a {@link IResourceValue} by its reference. * <p/> @@ -451,7 +456,7 @@ public final class BridgeContext extends Context { * <p/> * The actual format of a reference is <pre>@[namespace:]resType/resName</pre> but this method * only support the android namespace. - * + * * @param reference the resource reference to search for. * @return a {@link IResourceValue} or <code>null</code>. */ @@ -481,7 +486,7 @@ public final class BridgeContext extends Context { // we look for the referenced item name. String referenceName = null; - + if (segments.length == 2) { // there was a resType in the reference. If it's attr, we ignore it // else, we assert for now. @@ -495,7 +500,7 @@ public final class BridgeContext extends Context { // it's just an item name. referenceName = segments[0]; } - + // now we look for android: in the referenceName in order to support format // such as: ?attr/android:name if (referenceName.startsWith(BridgeConstants.PREFIX_ANDROID)) { @@ -512,9 +517,9 @@ public final class BridgeContext extends Context { return findItemInStyle(mThemeValues, referenceName); } else if (reference.startsWith(BridgeConstants.PREFIX_RESOURCE_REF)) { boolean frameworkOnly = false; - + // check for the specific null reference value. - if (BridgeConstants.REFERENCE_NULL.equals(reference)) { + if (BridgeConstants.REFERENCE_NULL.equals(reference)) { return null; } @@ -526,20 +531,20 @@ public final class BridgeContext extends Context { } else { reference = reference.substring(BridgeConstants.PREFIX_RESOURCE_REF.length()); } - + // at this point, value contains type/[android:]name (drawable/foo for instance) String[] segments = reference.split("\\/"); - + // now we look for android: in the resource name in order to support format // such as: @drawable/android:name if (segments[1].startsWith(BridgeConstants.PREFIX_ANDROID)) { frameworkOnly = true; segments[1] = segments[1].substring(BridgeConstants.PREFIX_ANDROID.length()); } - + return findResValue(segments[0], segments[1], frameworkOnly); } - + // Looks like the value didn't reference anything. Return null. return null; } @@ -565,7 +570,7 @@ public final class BridgeContext extends Context { } } } - + // now search in the framework resources. typeMap = mFrameworkResources.get(resType); if (typeMap != null) { @@ -574,11 +579,11 @@ public final class BridgeContext extends Context { return item; } } - + // didn't find the resource anywhere. return null; } - + /** * Returns a framework resource by type and name. The returned resource is resolved. * @param resourceType the type of the resource @@ -587,7 +592,7 @@ public final class BridgeContext extends Context { public IResourceValue getFrameworkResource(String resourceType, String resourceName) { return getResource(resourceType, resourceName, mFrameworkResources); } - + /** * Returns a project resource by type and name. The returned resource is resolved. * @param resourceType the type of the resource @@ -596,7 +601,7 @@ public final class BridgeContext extends Context { public IResourceValue getProjectResource(String resourceType, String resourceName) { return getResource(resourceType, resourceName, mProjectResources); } - + IResourceValue getResource(String resourceType, String resourceName, Map<String, Map<String, IResourceValue>> resourceRepository) { Map<String, IResourceValue> typeMap = resourceRepository.get(resourceType); @@ -607,12 +612,12 @@ public final class BridgeContext extends Context { return item; } } - + // didn't find the resource anywhere. return null; - + } - + /** * Returns the {@link IResourceValue} matching a given name in a given style. If the * item is not directly available in the style, the method looks in its parent style. @@ -622,7 +627,7 @@ public final class BridgeContext extends Context { */ 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) if (item == null && mStyleInheritanceMap != null) { IStyleResourceValue parentStyle = mStyleInheritanceMap.get(style); @@ -630,7 +635,7 @@ public final class BridgeContext extends Context { return findItemInStyle(parentStyle, itemName); } } - + return item; } @@ -642,7 +647,7 @@ public final class BridgeContext extends Context { * attrs == com.android.internal.R.styleable.View, this returns the list of the "xyz" where * there's a field com.android.internal.R.styleable.View_xyz and the field value is the index * that is used to reference the attribute later in the TypedArray. - * + * * @param attrs An attribute array reference given to obtainStyledAttributes. * @return A sorted map Attribute-Value to Attribute-Name for all attributes declared by the * attribute array. Returns null if nothing is found. @@ -662,14 +667,14 @@ public final class BridgeContext extends Context { attributes.put(i, null); } } - + if (outFrameworkFlag != null) { outFrameworkFlag[0] = true; } - + return attributes; } - + // if the name was not found in the framework resources, look in the project // resources arrayName = mProjectCallback.resolveResourceValue(attrs); @@ -697,7 +702,7 @@ public final class BridgeContext extends Context { /** * Searches for the attribute referenced by its internal id. - * + * * @param attr An attribute reference given to obtainStyledAttributes such as defStyle. * @return The unique name of the attribute, if found, e.g. "buttonStyle". Returns null * if nothing is found. @@ -707,12 +712,12 @@ public final class BridgeContext extends Context { if (info != null) { return info[0]; } - + info = mProjectCallback.resolveResourceValue(attr); if (info != null) { return info[0]; } - + return null; } @@ -722,27 +727,27 @@ public final class BridgeContext extends Context { mDynamicIdToStyleMap = new HashMap<Integer, IStyleResourceValue>(); mStyleToDynamicIdMap = new HashMap<IStyleResourceValue, Integer>(); } - + // look for an existing id Integer id = mStyleToDynamicIdMap.get(resValue); - + if (id == null) { // generate a new id id = Integer.valueOf(++mDynamicIdGenerator); - + // and add it to the maps. mDynamicIdToStyleMap.put(id, resValue); mStyleToDynamicIdMap.put(resValue, id); } - + return id; } - + private IStyleResourceValue getStyleByDynamicId(int i) { if (mDynamicIdToStyleMap != null) { return mDynamicIdToStyleMap.get(i); } - + return null; } @@ -751,10 +756,10 @@ public final class BridgeContext extends Context { if (value != null) { return value.intValue(); } - + return defValue; } - + int getProjectIdValue(String idName, int defValue) { if (mProjectCallback != null) { Integer value = mProjectCallback.getResourceValue(BridgeConstants.RES_ID, idName); @@ -762,7 +767,7 @@ public final class BridgeContext extends Context { return value.intValue(); } } - + return defValue; } @@ -820,7 +825,7 @@ public final class BridgeContext extends Context { @Override public void clearWallpaper() { // TODO Auto-generated method stub - + } @Override @@ -850,46 +855,46 @@ public final class BridgeContext extends Context { @Override public void enforceCallingOrSelfPermission(String arg0, String arg1) { // TODO Auto-generated method stub - + } @Override public void enforceCallingOrSelfUriPermission(Uri arg0, int arg1, String arg2) { // TODO Auto-generated method stub - + } @Override public void enforceCallingPermission(String arg0, String arg1) { // TODO Auto-generated method stub - + } @Override public void enforceCallingUriPermission(Uri arg0, int arg1, String arg2) { // TODO Auto-generated method stub - + } @Override public void enforcePermission(String arg0, int arg1, int arg2, String arg3) { // TODO Auto-generated method stub - + } @Override public void enforceUriPermission(Uri arg0, int arg1, int arg2, int arg3, String arg4) { // TODO Auto-generated method stub - + } @Override public void enforceUriPermission(Uri arg0, String arg1, String arg2, int arg3, int arg4, int arg5, String arg6) { // TODO Auto-generated method stub - + } @Override @@ -965,7 +970,7 @@ public final class BridgeContext extends Context { // TODO Auto-generated method stub return null; } - + @Override public String getPackageResourcePath() { // TODO Auto-generated method stub @@ -1003,7 +1008,7 @@ public final class BridgeContext extends Context { @Override public void grantUriPermission(String arg0, Uri arg1, int arg2) { // TODO Auto-generated method stub - + } @SuppressWarnings("unused") @@ -1051,31 +1056,31 @@ public final class BridgeContext extends Context { @Override public void removeStickyBroadcast(Intent arg0) { // TODO Auto-generated method stub - + } @Override public void revokeUriPermission(Uri arg0, int arg1) { // TODO Auto-generated method stub - + } @Override public void sendBroadcast(Intent arg0) { // TODO Auto-generated method stub - + } @Override public void sendBroadcast(Intent arg0, String arg1) { // TODO Auto-generated method stub - + } @Override public void sendOrderedBroadcast(Intent arg0, String arg1) { // TODO Auto-generated method stub - + } @Override @@ -1083,39 +1088,39 @@ public final class BridgeContext extends Context { BroadcastReceiver arg2, Handler arg3, int arg4, String arg5, Bundle arg6) { // TODO Auto-generated method stub - + } @Override public void sendStickyBroadcast(Intent arg0) { // TODO Auto-generated method stub - + } @Override public void setTheme(int arg0) { // TODO Auto-generated method stub - + } @SuppressWarnings("unused") @Override public void setWallpaper(Bitmap arg0) throws IOException { // TODO Auto-generated method stub - + } @SuppressWarnings("unused") @Override public void setWallpaper(InputStream arg0) throws IOException { // TODO Auto-generated method stub - + } @Override public void startActivity(Intent arg0) { // TODO Auto-generated method stub - + } @Override @@ -1140,20 +1145,15 @@ public final class BridgeContext extends Context { @Override public void unbindService(ServiceConnection arg0) { // TODO Auto-generated method stub - + } @Override public void unregisterReceiver(BroadcastReceiver arg0) { // TODO Auto-generated method stub - - } - @Override - public Looper getMainLooper() { - throw new UnsupportedOperationException(); } - + @Override public Context getApplicationContext() { throw new UnsupportedOperationException(); diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java index 0bcc7fd..2b0100b 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java @@ -43,14 +43,14 @@ import java.io.FileReader; import java.io.InputStream; /** - * + * */ public final class BridgeResources extends Resources { private BridgeContext mContext; private IProjectCallback mProjectCallback; private boolean[] mPlatformResourceFlag = new boolean[1]; - + /** * This initializes the static field {@link Resources#mSystem} which is used * by methods who get global resources using {@link Resources#getSystem()}. @@ -59,7 +59,7 @@ public final class BridgeResources extends Resources { * <p/> * {@link Bridge} calls this method after setting up a new bridge. */ - /*package*/ static Resources initSystem(BridgeContext context, + /*package*/ static Resources initSystem(BridgeContext context, AssetManager assets, DisplayMetrics metrics, Configuration config, @@ -73,7 +73,7 @@ public final class BridgeResources extends Resources { } return Resources.mSystem; } - + /** * Clears the static {@link Resources#mSystem} to make sure we don't leave objects * around that would prevent us from unloading the library. @@ -92,15 +92,15 @@ public final class BridgeResources extends Resources { mContext = context; mProjectCallback = projectCallback; } - + public BridgeTypedArray newTypeArray(int numEntries, boolean platformFile) { return new BridgeTypedArray(this, mContext, numEntries, platformFile); } - + private IResourceValue getResourceValue(int id, boolean[] platformResFlag_out) { // first get the String related to this id in the framework String[] resourceInfo = Bridge.resolveResourceValue(id); - + if (resourceInfo != null) { platformResFlag_out[0] = true; return mContext.getFrameworkResource(resourceInfo[1], resourceInfo[0]); @@ -109,7 +109,7 @@ public final class BridgeResources extends Resources { // didn't find a match in the framework? look in the project. if (mProjectCallback != null) { resourceInfo = mProjectCallback.resolveResourceValue(id); - + if (resourceInfo != null) { platformResFlag_out[0] = false; return mContext.getProjectResource(resourceInfo[1], resourceInfo[0]); @@ -118,26 +118,26 @@ public final class BridgeResources extends Resources { return null; } - + @Override public Drawable getDrawable(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null) { return ResourceHelper.getDrawable(value.getValue(), mContext, value.isFramework()); } - + // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } - + @Override public int getColor(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null) { try { return ResourceHelper.getColor(value.getValue()); @@ -145,18 +145,18 @@ public final class BridgeResources extends Resources { return 0; } } - + // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return 0; } - + @Override public ColorStateList getColorStateList(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null) { try { int color = ResourceHelper.getColor(value.getValue()); @@ -165,33 +165,33 @@ public final class BridgeResources extends Resources { return null; } } - + // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } - + @Override public CharSequence getText(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null) { return value.getValue(); } - + // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } - + @Override public XmlResourceParser getLayout(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null) { File xml = new File(value.getValue()); if (xml.isFile()) { @@ -201,7 +201,7 @@ public final class BridgeResources extends Resources { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(new FileReader(xml)); - + return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { mContext.getLogger().error(e); @@ -215,22 +215,22 @@ public final class BridgeResources extends Resources { // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } - + @Override public TypedArray obtainAttributes(AttributeSet set, int[] attrs) { return mContext.obtainStyledAttributes(set, attrs); } - + @Override public TypedArray obtainTypedArray(int id) throws NotFoundException { throw new UnsupportedOperationException(); } - - + + @Override public float getDimension(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); @@ -244,7 +244,7 @@ public final class BridgeResources extends Resources { } else if (v.equals(BridgeConstants.WRAP_CONTENT)) { return LayoutParams.WRAP_CONTENT; } - + if (ResourceHelper.stringToFloat(v, mTmpValue) && mTmpValue.type == TypedValue.TYPE_DIMENSION) { return mTmpValue.getDimension(mMetrics); @@ -254,7 +254,7 @@ public final class BridgeResources extends Resources { // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return 0; } @@ -276,7 +276,7 @@ public final class BridgeResources extends Resources { // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return 0; } @@ -298,7 +298,7 @@ public final class BridgeResources extends Resources { // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return 0; } @@ -306,7 +306,7 @@ public final class BridgeResources extends Resources { @Override public int getInteger(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null && value.getValue() != null) { String v = value.getValue(); int radix = 10; @@ -320,10 +320,10 @@ public final class BridgeResources extends Resources { // return exception below } } - + // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return 0; } @@ -348,12 +348,12 @@ public final class BridgeResources extends Resources { String s = getString(id); if (s != null) { return String.format(s, formatArgs); - + } // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } @@ -361,14 +361,14 @@ public final class BridgeResources extends Resources { @Override public String getString(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); - + if (value != null && value.getValue() != null) { return value.getValue(); } // id was not found or not resolved. Throw a NotFoundException. throwException(id); - + // this is not used since the method above always throws return null; } @@ -385,6 +385,11 @@ public final class BridgeResources extends Resources { if (ResourceHelper.stringToFloat(v, outValue)) { return; } + + // else it's a string + outValue.type = TypedValue.TYPE_STRING; + outValue.string = v; + return; } } @@ -413,7 +418,7 @@ public final class BridgeResources extends Resources { KXmlParser parser = new KXmlParser(); parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); parser.setInput(new FileReader(f)); - + return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); } catch (XmlPullParserException e) { NotFoundException newE = new NotFoundException(); @@ -436,6 +441,33 @@ public final class BridgeResources extends Resources { } @Override + public XmlResourceParser loadXmlResourceParser(String file, int id, + int assetCookie, String type) throws NotFoundException { + // even though we know the XML file to load directly, we still need to resolve the + // id so that we can know if it's a platform or project resource. + // (mPlatformResouceFlag will get the result and will be used later). + getResourceValue(id, mPlatformResourceFlag); + + File f = new File(file); + try { + KXmlParser parser = new KXmlParser(); + parser.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, true); + parser.setInput(new FileReader(f)); + + return new BridgeXmlBlockParser(parser, mContext, mPlatformResourceFlag[0]); + } catch (XmlPullParserException e) { + NotFoundException newE = new NotFoundException(); + newE.initCause(e); + throw newE; + } catch (FileNotFoundException e) { + NotFoundException newE = new NotFoundException(); + newE.initCause(e); + throw newE; + } + } + + + @Override public InputStream openRawResource(int id) throws NotFoundException { IResourceValue value = getResourceValue(id, mPlatformResourceFlag); @@ -465,6 +497,24 @@ public final class BridgeResources extends Resources { } @Override + public InputStream openRawResource(int id, TypedValue value) throws NotFoundException { + getValue(id, value, true); + + File f = new File(value.string.toString()); + if (f.isFile()) { + try { + return new FileInputStream(f); + } catch (FileNotFoundException e) { + NotFoundException exception = new NotFoundException(); + exception.initCause(e); + throw exception; + } + } + + throw new NotFoundException(); + } + + @Override public AssetFileDescriptor openRawResourceFd(int id) throws NotFoundException { throw new UnsupportedOperationException(); } @@ -482,7 +532,7 @@ public final class BridgeResources extends Resources { if (resourceInfo == null && mProjectCallback != null) { resourceInfo = mProjectCallback.resolveResourceValue(id); } - + String message = null; if (resourceInfo != null) { message = String.format( @@ -492,7 +542,7 @@ public final class BridgeResources extends Resources { message = String.format( "Could not resolve resource value: 0x%1$X.", id); } - + throw new NotFoundException(message); } } diff --git a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java index 5f0852e..abbf2f0 100644 --- a/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java +++ b/tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java @@ -30,17 +30,17 @@ public class NinePatchDrawable extends Drawable { NinePatchDrawable(NinePatch ninePatch) { m9Patch = ninePatch; } - + @Override public int getMinimumWidth() { return m9Patch.getWidth(); } - + @Override public int getMinimumHeight() { return m9Patch.getHeight(); } - + /** * Return the intrinsic width of the underlying drawable object. Returns * -1 if it has no intrinsic width, such as with a solid color. @@ -58,7 +58,7 @@ public class NinePatchDrawable extends Drawable { public int getIntrinsicHeight() { return m9Patch.getHeight(); } - + /** * Return in padding the insets suggested by this Drawable for placing * content inside the drawable's bounds. Positive values move toward the @@ -76,24 +76,18 @@ public class NinePatchDrawable extends Drawable { padding.bottom = padd[3]; return true; } - + @Override public void draw(Canvas canvas) { - if (canvas instanceof BridgeCanvas) { - BridgeCanvas bridgeCanvas = (BridgeCanvas)canvas; - - Rect r = getBounds(); - m9Patch.draw(bridgeCanvas.getGraphics2d(), r.left, r.top, r.width(), r.height()); - - return; - } - - throw new UnsupportedOperationException(); + Rect r = getBounds(); + m9Patch.draw(canvas.getGraphics2d(), r.left, r.top, r.width(), r.height()); + + return; } - + // ----------- Not implemented methods --------------- - + @Override public int getOpacity() { diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java index 76bd8d4..47184f1 100644 --- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java +++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java @@ -40,7 +40,7 @@ public class Main { for (String path : osJarPath) { log.info("Input : %1$s", path); } - + try { AsmGenerator agen = new AsmGenerator(log, osDestJar[0], new Class<?>[] { // classes to inject in the final JAR @@ -54,20 +54,23 @@ public class Main { }, new String[] { // classes to rename (so that we can replace them in layoutlib) // original-platform-class-name ======> renamed-class-name + "android.graphics.Bitmap", "android.graphics._Original_Bitmap", + "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", + "android.graphics.Canvas", "android.graphics._Original_Canvas", + "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", + "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", "android.graphics.Matrix", "android.graphics._Original_Matrix", "android.graphics.Paint", "android.graphics._Original_Paint", - "android.graphics.Typeface", "android.graphics._Original_Typeface", - "android.graphics.Bitmap", "android.graphics._Original_Bitmap", "android.graphics.Path", "android.graphics._Original_Path", "android.graphics.PorterDuffXfermode", "android.graphics._Original_PorterDuffXfermode", - "android.graphics.Shader", "android.graphics._Original_Shader", - "android.graphics.LinearGradient", "android.graphics._Original_LinearGradient", - "android.graphics.BitmapShader", "android.graphics._Original_BitmapShader", - "android.graphics.ComposeShader", "android.graphics._Original_ComposeShader", "android.graphics.RadialGradient", "android.graphics._Original_RadialGradient", + "android.graphics.Shader", "android.graphics._Original_Shader", "android.graphics.SweepGradient", "android.graphics._Original_SweepGradient", + "android.graphics.Typeface", "android.graphics._Original_Typeface", + "android.os.ServiceManager", "android.os._Original_ServiceManager", "android.util.FloatMath", "android.util._Original_FloatMath", "android.view.SurfaceView", "android.view._Original_SurfaceView", + "android.view.accessibility.AccessibilityManager", "android.view.accessibility._Original_AccessibilityManager", }, new String[] { // methods deleted from their return type. "android.graphics.Paint", // class to delete method from @@ -101,7 +104,7 @@ public class Main { }); aa.analyze(); agen.generate(); - + // Throw an error if any class failed to get renamed by the generator // // IMPORTANT: if you're building the platform and you get this error message, @@ -123,7 +126,7 @@ public class Main { } System.exit(1); } - + System.exit(0); } catch (IOException e) { log.exception(e, "Failed to load jar"); @@ -158,7 +161,7 @@ public class Main { return false; } } - + if (osJarPath.isEmpty()) { log.error("Missing parameter: path to input jar"); return false; diff --git a/tools/preload/20090811.compiled b/tools/preload/20090811.compiled Binary files differnew file mode 100644 index 0000000..6dbeca0 --- /dev/null +++ b/tools/preload/20090811.compiled diff --git a/tools/preload/Android.mk b/tools/preload/Android.mk index e6fa103..f325870 100644 --- a/tools/preload/Android.mk +++ b/tools/preload/Android.mk @@ -3,13 +3,13 @@ LOCAL_PATH:= $(call my-dir) include $(CLEAR_VARS) LOCAL_SRC_FILES := \ - ClassRank.java \ Compile.java \ LoadedClass.java \ MemoryUsage.java \ Operation.java \ Policy.java \ PrintCsv.java \ + PrintHtmlDiff.java \ PrintPsTree.java \ Proc.java \ Record.java \ diff --git a/tools/preload/ClassRank.java b/tools/preload/ClassRank.java deleted file mode 100644 index 3699b89..0000000 --- a/tools/preload/ClassRank.java +++ /dev/null @@ -1,53 +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. - */ - -import java.util.Comparator; - -/** - * Ranks classes for preloading based on how long their operations took - * and how early the operations happened. Higher ranked classes come first. - */ -class ClassRank implements Comparator<Operation> { - - /** - * Increase this number to add more weight to classes which were loaded - * earlier. - */ - static final int SEQUENCE_WEIGHT = 500; // 5 ms - - static final int BUCKET_SIZE = 5; - - public int compare(Operation a, Operation b) { - // Higher ranked operations should come first. - int result = rankOf(b) - rankOf(a); - if (result != 0) { - return result; - } - - // Make sure we don't drop one of two classes w/ the same rank. - // If a load and an initialization have the same rank, it's OK - // to treat the operations equally. - return a.loadedClass.name.compareTo(b.loadedClass.name); - } - - /** Ranks the given operation. */ - private static int rankOf(Operation o) { - return o.medianExclusiveTimeMicros() - + SEQUENCE_WEIGHT / (o.index / BUCKET_SIZE + 1); - } -} - - diff --git a/tools/preload/LoadedClass.java b/tools/preload/LoadedClass.java index 5782807..86e5dfc 100644 --- a/tools/preload/LoadedClass.java +++ b/tools/preload/LoadedClass.java @@ -15,10 +15,7 @@ */ import java.io.Serializable; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Set; +import java.util.*; /** * A loaded class. @@ -54,7 +51,7 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> { } void measureMemoryUsage() { - this.memoryUsage = MemoryUsage.forClass(name); + this.memoryUsage = MemoryUsage.forClass(name); } int mlt = -1; @@ -79,6 +76,10 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> { return mit = calculateMedian(initializations); } + int medianTimeMicros() { + return medianInitTimeMicros() + medianLoadTimeMicros(); + } + /** Calculates the median duration for a list of operations. */ private static int calculateMedian(List<Operation> operations) { int size = operations.size(); @@ -102,31 +103,20 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> { } } - /** - * Counts loads by apps. - */ - int appLoads() { - return operationsByApps(loads); + /** Returns names of processes that loaded this class. */ + Set<String> processNames() { + Set<String> names = new HashSet<String>(); + addProcessNames(loads, names); + addProcessNames(initializations, names); + return names; } - /** - * Counts inits by apps. - */ - int appInits() { - return operationsByApps(initializations); - } - - /** - * Counts number of app operations in the given list. - */ - private static int operationsByApps(List<Operation> operations) { - int byApps = 0; - for (Operation operation : operations) { - if (operation.process.isApplication()) { - byApps++; + private void addProcessNames(List<Operation> ops, Set<String> names) { + for (Operation operation : ops) { + if (operation.process.fromZygote()) { + names.add(operation.process.name); } } - return byApps; } public int compareTo(LoadedClass o) { @@ -137,27 +127,4 @@ class LoadedClass implements Serializable, Comparable<LoadedClass> { public String toString() { return name; } - - /** - * Returns true if this class's initialization causes the given class to - * initialize. - */ - public boolean initializes(LoadedClass clazz, Set<LoadedClass> visited) { - // Avoid infinite recursion. - if (!visited.add(this)) { - return false; - } - - if (clazz == this) { - return true; - } - - for (Operation initialization : initializations) { - if (initialization.loadedClass.initializes(clazz, visited)) { - return true; - } - } - - return false; - } } diff --git a/tools/preload/MemoryUsage.java b/tools/preload/MemoryUsage.java index e5dfb2a..bc21b6f 100644 --- a/tools/preload/MemoryUsage.java +++ b/tools/preload/MemoryUsage.java @@ -34,8 +34,8 @@ class MemoryUsage implements Serializable { static final MemoryUsage NOT_AVAILABLE = new MemoryUsage(); static int errorCount = 0; - static final int MAXIMUM_ERRORS = 10; // give up after this many fails + // These values are in 1kB increments (not 4kB like you'd expect). final int nativeSharedPages; final int javaSharedPages; final int otherSharedPages; @@ -123,15 +123,24 @@ class MemoryUsage implements Serializable { return allocSize - freedSize; } + int totalHeap() { + return javaHeapSize() + (int) nativeHeapSize; + } + int javaPagesInK() { - return (javaSharedPages + javaPrivatePages) * 4; + return javaSharedPages + javaPrivatePages; } int nativePagesInK() { - return (nativeSharedPages + nativePrivatePages) * 4; + return nativeSharedPages + nativePrivatePages; } int otherPagesInK() { - return (otherSharedPages + otherPrivatePages) * 4; + return otherSharedPages + otherPrivatePages; + } + + int totalPages() { + return javaSharedPages + javaPrivatePages + nativeSharedPages + + nativePrivatePages + otherSharedPages + otherPrivatePages; } /** @@ -163,13 +172,6 @@ class MemoryUsage implements Serializable { * Measures memory usage for the given class. */ static MemoryUsage forClass(String className) { - - // This is a coarse approximation for determining that no device is connected, - // or that the communication protocol has changed, but we'll keep going and stop whining. - if (errorCount >= MAXIMUM_ERRORS) { - return NOT_AVAILABLE; - } - MeasureWithTimeout measurer = new MeasureWithTimeout(className); new Thread(measurer).start(); @@ -280,4 +282,17 @@ class MemoryUsage implements Serializable { e.printStackTrace(); } } + + /** Measures memory usage information and stores it in the model. */ + public static void main(String[] args) throws IOException, + ClassNotFoundException { + Root root = Root.fromFile(args[0]); + root.baseline = baseline(); + for (LoadedClass loadedClass : root.loadedClasses.values()) { + if (loadedClass.systemClass) { + loadedClass.measureMemoryUsage(); + } + } + root.toFile(args[0]); + } } diff --git a/tools/preload/Policy.java b/tools/preload/Policy.java index 554966b..7a190ac 100644 --- a/tools/preload/Policy.java +++ b/tools/preload/Policy.java @@ -19,52 +19,43 @@ import java.util.HashSet; import java.util.Set; /** - * This is not instantiated - we just provide data for other classes to use + * Policy that governs which classes are preloaded. */ public class Policy { - - /** - * This location (in the build system) of the preloaded-classes file. - */ - private static final String PRELOADED_CLASS_FILE = "frameworks/base/preloaded-classes"; - + /** - * The internal process name of the system process. Note, this also shows up as - * "system_process", e.g. in ddms. + * No constructor - use static methods only */ - private static final String SYSTEM_SERVER_PROCESS_NAME = "system_server"; + private Policy() {} - /** - * Names of non-application processes - these will not be checked for preloaded classes. - * - * TODO: Replace this hardcoded list with a walk up the parent chain looking for zygote. + /** + * This location (in the build system) of the preloaded-classes file. */ - private static final Set<String> NOT_FROM_ZYGOTE = new HashSet<String>(Arrays.asList( - "zygote", - "dexopt", - "unknown", - SYSTEM_SERVER_PROCESS_NAME, - "com.android.development", - "app_process" // am & other shell commands - )); + static final String PRELOADED_CLASS_FILE + = "frameworks/base/preloaded-classes"; - /** - * Long running services. These are restricted in their contribution to the preloader - * because their launch time is less critical. + /** + * Long running services. These are restricted in their contribution to the + * preloader because their launch time is less critical. */ + // TODO: Generate this automatically from package manager. private static final Set<String> SERVICES = new HashSet<String>(Arrays.asList( - SYSTEM_SERVER_PROCESS_NAME, - "com.android.acore", - // Commented out to make sure DefaultTimeZones gets preloaded. - // "com.android.phone", - "com.google.process.content", - "android.process.media" + "system_server", + "com.google.process.content", + "android.process.media", + "com.android.phone", + "com.google.android.apps.maps.FriendService", + "com.google.android.apps.maps.LocationFriendService", + "com.google.android.googleapps", + "com.google.process.gapps", + "android.tts" )); /** * Classes which we shouldn't load from the Zygote. */ - private static final Set<String> EXCLUDED_CLASSES = new HashSet<String>(Arrays.asList( + private static final Set<String> EXCLUDED_CLASSES + = new HashSet<String>(Arrays.asList( // Binders "android.app.AlarmManager", "android.app.SearchManager", @@ -75,40 +66,18 @@ public class Policy { "android.os.AsyncTask", "android.pim.ContactsAsyncHelper", "java.lang.ProcessManager" - )); /** - * No constructor - use static methods only - */ - private Policy() {} - - /** - * Returns the path/file name of the preloaded classes file that will be written - * by WritePreloadedClassFile. - */ - public static String getPreloadedClassFileName() { - return PRELOADED_CLASS_FILE; - } - - /** - * Reports if a given process name was created from zygote - */ - public static boolean isFromZygote(String processName) { - return !NOT_FROM_ZYGOTE.contains(processName); - } - - /** - * Reports if the given process name is a "long running" process or service + * Returns true if the given process name is a "long running" process or + * service. */ public static boolean isService(String processName) { return SERVICES.contains(processName); } - - /** - * Reports if the given class should never be preloaded - */ - public static boolean isPreloadableClass(String className) { - return !EXCLUDED_CLASSES.contains(className); + + /**Reports if the given class should be preloaded. */ + public static boolean isPreloadable(LoadedClass clazz) { + return clazz.systemClass && !EXCLUDED_CLASSES.contains(clazz.name); } } diff --git a/tools/preload/PrintCsv.java b/tools/preload/PrintCsv.java index 9f2a318..1820830 100644 --- a/tools/preload/PrintCsv.java +++ b/tools/preload/PrintCsv.java @@ -18,6 +18,12 @@ import java.io.IOException; import java.io.FileInputStream; import java.io.ObjectInputStream; import java.io.BufferedInputStream; +import java.io.Writer; +import java.io.PrintStream; +import java.util.Set; +import java.util.HashSet; +import java.util.TreeSet; +import java.util.Iterator; /** * Prints raw information in CSV format. @@ -33,10 +39,25 @@ public class PrintCsv { Root root = Root.fromFile(args[0]); - System.out.println("Name" + printHeaders(System.out); + + MemoryUsage baseline = MemoryUsage.baseline(); + + for (LoadedClass loadedClass : root.loadedClasses.values()) { + if (!loadedClass.systemClass) { + continue; + } + + printRow(System.out, baseline, loadedClass); + } + } + + static void printHeaders(PrintStream out) { + out.println("Name" + ",Preloaded" + ",Median Load Time (us)" + ",Median Init Time (us)" + + ",Process Names" + ",Load Count" + ",Init Count" + ",Managed Heap (B)" @@ -44,46 +65,63 @@ public class PrintCsv { + ",Managed Pages (kB)" + ",Native Pages (kB)" + ",Other Pages (kB)"); + } - MemoryUsage baseline = root.baseline; + static void printRow(PrintStream out, MemoryUsage baseline, + LoadedClass loadedClass) { + out.print(loadedClass.name); + out.print(','); + out.print(loadedClass.preloaded); + out.print(','); + out.print(loadedClass.medianLoadTimeMicros()); + out.print(','); + out.print(loadedClass.medianInitTimeMicros()); + out.print(','); + out.print('"'); - for (LoadedClass loadedClass : root.loadedClasses.values()) { - if (!loadedClass.systemClass) { - continue; - } + Set<String> procNames = new TreeSet<String>(); + for (Operation op : loadedClass.loads) + procNames.add(op.process.name); + for (Operation op : loadedClass.initializations) + procNames.add(op.process.name); - System.out.print(loadedClass.name); - System.out.print(','); - System.out.print(loadedClass.preloaded); - System.out.print(','); - System.out.print(loadedClass.medianLoadTimeMicros()); - System.out.print(','); - System.out.print(loadedClass.medianInitTimeMicros()); - System.out.print(','); - System.out.print(loadedClass.loads.size()); - System.out.print(','); - System.out.print(loadedClass.initializations.size()); - - if (loadedClass.memoryUsage.isAvailable()) { - MemoryUsage subtracted - = loadedClass.memoryUsage.subtract(baseline); - - System.out.print(','); - System.out.print(subtracted.javaHeapSize()); - System.out.print(','); - System.out.print(subtracted.nativeHeapSize); - System.out.print(','); - System.out.print(subtracted.javaPagesInK()); - System.out.print(','); - System.out.print(subtracted.nativePagesInK()); - System.out.print(','); - System.out.print(subtracted.otherPagesInK()); - - } else { - System.out.print(",n/a,n/a,n/a,n/a,n/a"); + if (procNames.size() <= 3) { + for (String name : procNames) { + out.print(name + "\n"); } + } else { + Iterator<String> i = procNames.iterator(); + out.print(i.next() + "\n"); + out.print(i.next() + "\n"); + out.print("...and " + (procNames.size() - 2) + + " others."); + } + + out.print('"'); + out.print(','); + out.print(loadedClass.loads.size()); + out.print(','); + out.print(loadedClass.initializations.size()); + + if (loadedClass.memoryUsage.isAvailable()) { + MemoryUsage subtracted + = loadedClass.memoryUsage.subtract(baseline); - System.out.println(); + out.print(','); + out.print(subtracted.javaHeapSize()); + out.print(','); + out.print(subtracted.nativeHeapSize); + out.print(','); + out.print(subtracted.javaPagesInK()); + out.print(','); + out.print(subtracted.nativePagesInK()); + out.print(','); + out.print(subtracted.otherPagesInK()); + + } else { + out.print(",n/a,n/a,n/a,n/a,n/a"); } + + out.println(); } } diff --git a/tools/preload/PrintHtmlDiff.java b/tools/preload/PrintHtmlDiff.java new file mode 100644 index 0000000..b101c85 --- /dev/null +++ b/tools/preload/PrintHtmlDiff.java @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2009 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. + */ + +import java.io.IOException; +import java.io.FileReader; +import java.io.BufferedReader; +import java.io.PrintStream; +import java.util.Set; +import java.util.TreeSet; +import java.util.HashSet; +import java.util.Iterator; + +/** + * Prints HTML containing removed and added files. + */ +public class PrintHtmlDiff { + + private static final String OLD_PRELOADED_CLASSES + = "old-preloaded-classes"; + + public static void main(String[] args) throws IOException, + ClassNotFoundException { + Root root = Root.fromFile(args[0]); + + BufferedReader oldClasses = new BufferedReader( + new FileReader(OLD_PRELOADED_CLASSES)); + + // Classes loaded implicitly by the zygote. + Set<LoadedClass> zygote = new HashSet<LoadedClass>(); + for (Proc proc : root.processes.values()) { + if (proc.name.equals("zygote")) { + for (Operation op : proc.operations) { + zygote.add(op.loadedClass); + } + break; + } + } + + Set<LoadedClass> removed = new TreeSet<LoadedClass>(); + Set<LoadedClass> added = new TreeSet<LoadedClass>(); + + for (LoadedClass loadedClass : root.loadedClasses.values()) { + if (loadedClass.preloaded && !zygote.contains(loadedClass)) { + added.add(loadedClass); + } + } + + String line; + while ((line = oldClasses.readLine()) != null) { + line = line.trim(); + LoadedClass clazz = root.loadedClasses.get(line); + if (clazz != null) { + added.remove(clazz); + if (!clazz.preloaded) removed.add(clazz); + } + } + + PrintStream out = System.out; + + out.println("<html><body>"); + out.println("<style>"); + out.println("a, th, td, h2 { font-family: arial }"); + out.println("th, td { font-size: small }"); + out.println("</style>"); + out.println("<script src=\"sorttable.js\"></script>"); + out.println("<p><a href=\"#removed\">Removed</a>"); + out.println("<a name=\"added\"/><h2>Added</h2>"); + printTable(out, root.baseline, added); + out.println("<a name=\"removed\"/><h2>Removed</h2>"); + printTable(out, root.baseline, removed); + out.println("</body></html>"); + } + + static void printTable(PrintStream out, MemoryUsage baseline, + Iterable<LoadedClass> classes) { + out.println("<table border=\"1\" cellpadding=\"5\"" + + " class=\"sortable\">"); + + out.println("<thead><tr>"); + out.println("<th>Name</th>"); + out.println("<th>Load Time (us)</th>"); + out.println("<th>Loaded By</th>"); + out.println("<th>Heap (B)</th>"); + out.println("<th>Pages</th>"); + out.println("</tr></thead>"); + + for (LoadedClass clazz : classes) { + out.println("<tr>"); + out.println("<td>" + clazz.name + "</td>"); + out.println("<td>" + clazz.medianTimeMicros() + "</td>"); + + out.println("<td>"); + Set<String> procNames = new TreeSet<String>(); + for (Operation op : clazz.loads) procNames.add(op.process.name); + for (Operation op : clazz.initializations) { + procNames.add(op.process.name); + } + if (procNames.size() <= 3) { + for (String name : procNames) { + out.print(name + "<br/>"); + } + } else { + Iterator<String> i = procNames.iterator(); + out.print(i.next() + "<br/>"); + out.print(i.next() + "<br/>"); + out.print("...and " + (procNames.size() - 2) + + " others."); + } + out.println("</td>"); + + if (clazz.memoryUsage.isAvailable()) { + MemoryUsage subtracted + = clazz.memoryUsage.subtract(baseline); + + out.println("<td>" + (subtracted.javaHeapSize() + + subtracted.nativeHeapSize) + "</td>"); + out.println("<td>" + subtracted.totalPages() + "</td>"); + } else { + for (int i = 0; i < 2; i++) { + out.println("<td>n/a</td>"); + } + } + + out.println("</tr>"); + } + + out.println("</table>"); + } +} diff --git a/tools/preload/Proc.java b/tools/preload/Proc.java index 22697f8..2105021 100644 --- a/tools/preload/Proc.java +++ b/tools/preload/Proc.java @@ -14,16 +14,11 @@ * limitations under the License. */ -import java.util.Set; -import java.util.HashSet; -import java.util.Arrays; import java.util.List; import java.util.ArrayList; import java.util.LinkedList; import java.util.Map; import java.util.HashMap; -import java.util.Collections; -import java.util.TreeSet; import java.io.Serializable; /** @@ -33,16 +28,6 @@ class Proc implements Serializable { private static final long serialVersionUID = 0; - /** - * Default percentage of time to cut off of app class loading times. - */ - static final int PERCENTAGE_TO_PRELOAD = 75; - - /** - * Maximum number of classes to preload for a given process. - */ - static final int MAX_TO_PRELOAD = 100; - /** Parent process. */ final Proc parent; @@ -88,89 +73,11 @@ class Proc implements Serializable { } /** - * Returns the percentage of time we should cut by preloading for this - * app. - */ - int percentageToPreload() { - return PERCENTAGE_TO_PRELOAD; - } - - /** - * Returns a list of classes which should be preloaded. - * - * @param takeAllClasses forces all classes to be taken (irrespective of ranking) - */ - List<LoadedClass> highestRankedClasses(boolean takeAllClasses) { - if (!isApplication()) { - return Collections.emptyList(); - } - - // Sort by rank. - Operation[] ranked = new Operation[operations.size()]; - ranked = operations.toArray(ranked); - Arrays.sort(ranked, new ClassRank()); - - // The percentage of time to save by preloading. - int timeToSave = totalTimeMicros() * percentageToPreload() / 100; - int timeSaved = 0; - - boolean service = Policy.isService(this.name); - - List<LoadedClass> highest = new ArrayList<LoadedClass>(); - for (Operation operation : ranked) { - - // These are actual ranking decisions, which can be overridden - if (!takeAllClasses) { - if (highest.size() >= MAX_TO_PRELOAD) { - System.out.println(name + " got " - + (timeSaved * 100 / timeToSave) + "% through"); - break; - } - - if (timeSaved >= timeToSave) { - break; - } - } - - // The remaining rules apply even to wired-down processes - if (!Policy.isPreloadableClass(operation.loadedClass.name)) { - continue; - } - - if (!operation.loadedClass.systemClass) { - continue; - } - - // Only load java.* class for services. - if (!service || operation.loadedClass.name.startsWith("java.")) { - highest.add(operation.loadedClass); - } - - // For services, still count the time even if it's not in java.* - timeSaved += operation.medianExclusiveTimeMicros(); - } - - return highest; - } - - /** - * Total time spent class loading and initializing. - */ - int totalTimeMicros() { - int totalTime = 0; - for (Operation operation : operations) { - totalTime += operation.medianExclusiveTimeMicros(); - } - return totalTime; - } - - /** - * Returns true if this process is an app. - * - * TODO: Replace the hardcoded list with a walk up the parent chain looking for zygote. + * Returns true if this process comes from the zygote. */ - public boolean isApplication() { - return Policy.isFromZygote(name); + public boolean fromZygote() { + return parent != null && parent.name.equals("zygote") + && !name.equals("com.android.development"); } /** diff --git a/tools/preload/Root.java b/tools/preload/Root.java index 949f9b7..0bc29bf 100644 --- a/tools/preload/Root.java +++ b/tools/preload/Root.java @@ -46,7 +46,7 @@ public class Root implements Serializable { final Map<String, LoadedClass> loadedClasses = new HashMap<String, LoadedClass>(); - final MemoryUsage baseline = MemoryUsage.baseline(); + MemoryUsage baseline = MemoryUsage.baseline(); /** * Records class loads and initializations. diff --git a/tools/preload/WritePreloadedClassFile.java b/tools/preload/WritePreloadedClassFile.java index d87b1f0..96c539b 100644 --- a/tools/preload/WritePreloadedClassFile.java +++ b/tools/preload/WritePreloadedClassFile.java @@ -20,83 +20,125 @@ import java.io.IOException; import java.io.OutputStreamWriter; import java.io.Writer; import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.List; import java.util.Set; import java.util.TreeSet; /** - * Writes /frameworks/base/preloaded-classes. Also updates LoadedClass.preloaded - * fields and writes over compiled log file. + * Writes /frameworks/base/preloaded-classes. Also updates + * {@link LoadedClass#preloaded} fields and writes over compiled log file. */ public class WritePreloadedClassFile { - public static void main(String[] args) throws IOException, ClassNotFoundException { - - // Process command-line arguments first - List<String> wiredProcesses = new ArrayList<String>(); - String inputFileName = null; - int argOffset = 0; - try { - while ("--preload-all-process".equals(args[argOffset])) { - argOffset++; - wiredProcesses.add(args[argOffset++]); - } - - inputFileName = args[argOffset++]; - } catch (RuntimeException e) { - System.err.println("Usage: WritePreloadedClassFile " + - "[--preload-all-process process-name] " + - "[compiled log file]"); - System.exit(0); - } + /** + * Preload any class that take longer to load than MIN_LOAD_TIME_MICROS us. + */ + static final int MIN_LOAD_TIME_MICROS = 1250; - Root root = Root.fromFile(inputFileName); + public static void main(String[] args) throws IOException, + ClassNotFoundException { + if (args.length != 1) { + System.err.println("Usage: WritePreloadedClassFile [compiled log]"); + System.exit(-1); + } + String rootFile = args[0]; + Root root = Root.fromFile(rootFile); + // No classes are preloaded to start. for (LoadedClass loadedClass : root.loadedClasses.values()) { loadedClass.preloaded = false; } + // Open preloaded-classes file for output. Writer out = new BufferedWriter(new OutputStreamWriter( - new FileOutputStream(Policy.getPreloadedClassFileName()), + new FileOutputStream(Policy.PRELOADED_CLASS_FILE), Charset.forName("US-ASCII"))); - out.write("# Classes which are preloaded by com.android.internal.os.ZygoteInit.\n"); - out.write("# Automatically generated by /frameworks/base/tools/preload.\n"); - out.write("# percent=" + Proc.PERCENTAGE_TO_PRELOAD + ", weight=" - + ClassRank.SEQUENCE_WEIGHT - + ", bucket_size=" + ClassRank.BUCKET_SIZE - + "\n"); - for (String wiredProcess : wiredProcesses) { - out.write("# forcing classes loaded by: " + wiredProcess + "\n"); - } + out.write("# Classes which are preloaded by" + + " com.android.internal.os.ZygoteInit.\n"); + out.write("# Automatically generated by frameworks/base/tools/preload/" + + WritePreloadedClassFile.class.getSimpleName() + ".java.\n"); + out.write("# MIN_LOAD_TIME_MICROS=" + MIN_LOAD_TIME_MICROS + "\n"); - Set<LoadedClass> highestRanked = new TreeSet<LoadedClass>(); - for (Proc proc : root.processes.values()) { - // test to see if this is one of the wired-down ("take all classes") processes - boolean isWired = wiredProcesses.contains(proc.name); - - List<LoadedClass> highestForProc = proc.highestRankedClasses(isWired); + /* + * The set of classes to preload. We preload a class if: + * + * a) it's loaded in the bootclasspath (i.e., is a system class) + * b) it takes > MIN_LOAD_TIME_MICROS us to load, and + * c) it's loaded by more than one process, or it's loaded by an + * application (i.e., not a long running service) + */ + Set<LoadedClass> toPreload = new TreeSet<LoadedClass>(); - System.out.println(proc.name + ": " + highestForProc.size()); + // Preload classes that were loaded by at least 2 processes. Hopefully, + // the memory associated with these classes will be shared. + for (LoadedClass loadedClass : root.loadedClasses.values()) { + Set<String> names = loadedClass.processNames(); + if (shouldPreload(loadedClass) && names.size() > 1) { + toPreload.add(loadedClass); + } + } + + int initialSize = toPreload.size(); + System.out.println(initialSize + + " classses were loaded by more than one app."); - for (LoadedClass loadedClass : highestForProc) { - loadedClass.preloaded = true; + // Preload eligable classes from applications (not long-running + // services). + for (Proc proc : root.processes.values()) { + if (proc.fromZygote() && !Policy.isService(proc.name)) { + for (Operation operation : proc.operations) { + LoadedClass loadedClass = operation.loadedClass; + if (shouldPreload(loadedClass)) { + toPreload.add(loadedClass); + } + } } - highestRanked.addAll(highestForProc); } - for (LoadedClass loadedClass : highestRanked) { - out.write(loadedClass.name); - out.write('\n'); + System.out.println("Added " + (toPreload.size() - initialSize) + + " more to speed up applications."); + + System.out.println(toPreload.size() + + " total classes will be preloaded."); + + // Make classes that were implicitly loaded by the zygote explicit. + // This adds minimal overhead but avoid confusion about classes not + // appearing in the list. + addAllClassesFrom("zygote", root, toPreload); + + for (LoadedClass loadedClass : toPreload) { + out.write(loadedClass.name + "\n"); } out.close(); - System.out.println(highestRanked.size() - + " classes will be preloaded."); - // Update data to reflect LoadedClass.preloaded changes. - root.toFile(inputFileName); + for (LoadedClass loadedClass : toPreload) { + loadedClass.preloaded = true; + } + root.toFile(rootFile); + } + + private static void addAllClassesFrom(String processName, Root root, + Set<LoadedClass> toPreload) { + for (Proc proc : root.processes.values()) { + if (proc.name.equals(processName)) { + for (Operation operation : proc.operations) { + boolean preloadable + = Policy.isPreloadable(operation.loadedClass); + if (preloadable) { + toPreload.add(operation.loadedClass); + } + } + } + } + } + + /** + * Returns true if the class should be preloaded. + */ + private static boolean shouldPreload(LoadedClass clazz) { + return Policy.isPreloadable(clazz) + && clazz.medianTimeMicros() > MIN_LOAD_TIME_MICROS; } } diff --git a/tools/preload/loadclass/LoadClass.java b/tools/preload/loadclass/LoadClass.java index 471cc84..a71b6a8 100644 --- a/tools/preload/loadclass/LoadClass.java +++ b/tools/preload/loadclass/LoadClass.java @@ -35,7 +35,11 @@ class LoadClass { if (args.length > 0) { try { + long start = System.currentTimeMillis(); Class.forName(args[0]); + long elapsed = System.currentTimeMillis() - start; + Log.i("LoadClass", "Loaded " + args[0] + " in " + elapsed + + "ms."); } catch (ClassNotFoundException e) { Log.w("LoadClass", e); return; diff --git a/tools/preload/preload.iml b/tools/preload/preload.iml index d1fab57..2d87c55 100644 --- a/tools/preload/preload.iml +++ b/tools/preload/preload.iml @@ -1,15 +1,14 @@ <?xml version="1.0" encoding="UTF-8"?> <module relativePaths="true" type="JAVA_MODULE" version="4"> <component name="NewModuleRootManager" inherit-compiler-output="false"> - <output url="file:///tmp/preload/" /> + <output url="file:///tmp/preload" /> + <output-test url="file:///tmp/preload" /> <exclude-output /> - <output-test url="file:///tmp/preload/" /> <content url="file://$MODULE_DIR$"> <sourceFolder url="file://$MODULE_DIR$" isTestSource="false" /> </content> <orderEntry type="inheritedJdk" /> <orderEntry type="sourceFolder" forTests="false" /> - <orderEntryProperties /> </component> </module> diff --git a/tools/preload/preload.ipr b/tools/preload/preload.ipr index c5613ad..0c9621c 100644 --- a/tools/preload/preload.ipr +++ b/tools/preload/preload.ipr @@ -25,7 +25,28 @@ <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> </component> <component name="CodeStyleSettingsManager"> - <option name="PER_PROJECT_SETTINGS" /> + <option name="PER_PROJECT_SETTINGS"> + <value> + <ADDITIONAL_INDENT_OPTIONS fileType="java"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + <ADDITIONAL_INDENT_OPTIONS fileType="xml"> + <option name="INDENT_SIZE" value="4" /> + <option name="CONTINUATION_INDENT_SIZE" value="8" /> + <option name="TAB_SIZE" value="4" /> + <option name="USE_TAB_CHARACTER" value="false" /> + <option name="SMART_TABS" value="false" /> + <option name="LABEL_INDENT_SIZE" value="0" /> + <option name="LABEL_INDENT_ABSOLUTE" value="false" /> + </ADDITIONAL_INDENT_OPTIONS> + </value> + </option> <option name="USE_PER_PROJECT_SETTINGS" value="false" /> </component> <component name="CompilerConfiguration"> @@ -114,6 +135,7 @@ <option name="ADDITIONAL_OPTIONS_STRING" value="" /> <option name="MAXIMUM_HEAP_SIZE" value="128" /> </component> + <component name="Encoding" useUTFGuessing="true" native2AsciiForPropertiesFiles="false" /> <component name="EntryPointsManager"> <entry_points version="2.0" /> </component> @@ -125,13 +147,13 @@ <component name="IdProvider" IDEtalkID="D171F99B9178C1675593DC9A76A5CC7E" /> <component name="InspectionProjectProfileManager"> <option name="PROJECT_PROFILE" value="Project Default" /> - <option name="USE_PROJECT_LEVEL_SETTINGS" value="false" /> - <scopes /> + <option name="USE_PROJECT_PROFILE" value="true" /> + <version value="1.0" /> <profiles> <profile version="1.0" is_locked="false"> <option name="myName" value="Project Default" /> <option name="myLocal" value="false" /> - <inspection_tool class="JavaDoc" level="WARNING" enabled="false"> + <inspection_tool class="JavaDoc" enabled="false" level="WARNING" enabled_by_default="false"> <option name="TOP_LEVEL_CLASS_OPTIONS"> <value> <option name="ACCESS_JAVADOC_REQUIRED_FOR" value="none" /> @@ -160,14 +182,19 @@ <option name="IGNORE_JAVADOC_PERIOD" value="true" /> <option name="myAdditionalJavadocTags" value="" /> </inspection_tool> - <inspection_tool class="OnDemandImport" level="WARNING" enabled="true" /> - <inspection_tool class="SamePackageImport" level="WARNING" enabled="true" /> - <inspection_tool class="JavaLangImport" level="WARNING" enabled="true" /> - <inspection_tool class="RedundantImport" level="WARNING" enabled="true" /> - <inspection_tool class="UnusedImport" level="WARNING" enabled="true" /> + <inspection_tool class="JavaLangImport" enabled="true" level="WARNING" enabled_by_default="true" /> + <inspection_tool class="OnDemandImport" enabled="true" level="WARNING" enabled_by_default="true" /> + <inspection_tool class="RedundantImport" enabled="true" level="WARNING" enabled_by_default="true" /> + <inspection_tool class="SamePackageImport" enabled="true" level="WARNING" enabled_by_default="true" /> + <inspection_tool class="UnusedImport" enabled="true" level="WARNING" enabled_by_default="true" /> </profile> </profiles> - <list size="0" /> + <list size="4"> + <item index="0" class="java.lang.String" itemvalue="WARNING" /> + <item index="1" class="java.lang.String" itemvalue="SERVER PROBLEM" /> + <item index="2" class="java.lang.String" itemvalue="INFO" /> + <item index="3" class="java.lang.String" itemvalue="ERROR" /> + </list> </component> <component name="JavacSettings"> <option name="DEBUGGING_INFO" value="true" /> @@ -332,13 +359,19 @@ <option name="USE_CLIENT_FILTER" value="true" /> <option name="CLIENT" value="" /> </component> + <component name="ProjectDetails"> + <option name="projectName" value="preload" /> + </component> <component name="ProjectFileVersion" converted="true" /> + <component name="ProjectKey"> + <option name="state" value="project:///Volumes/Android/donut/frameworks/base/tools/preload/preload.ipr" /> + </component> <component name="ProjectModuleManager"> <modules> <module fileurl="file://$PROJECT_DIR$/preload.iml" filepath="$PROJECT_DIR$/preload.iml" /> </modules> </component> - <component name="ProjectRootManager" version="2" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK"> + <component name="ProjectRootManager" version="2" languageLevel="JDK_1_5" assert-keyword="true" jdk-15="true" project-jdk-name="1.5" project-jdk-type="JavaSDK"> <output url="file:///tmp/preload" /> </component> <component name="RmicSettings"> @@ -374,6 +407,9 @@ <option name="myValidatorValidationEnabled" value="true" /> <option name="myReportErrorsAsWarnings" value="true" /> </component> + <component name="SvnBranchConfigurationManager"> + <option name="mySupportsUserInfoFilter" value="true" /> + </component> <component name="SvnChangesBrowserSettings"> <option name="USE_AUTHOR_FIELD" value="true" /> <option name="AUTHOR" value="" /> @@ -381,15 +417,6 @@ <option name="USE_PROJECT_SETTINGS" value="true" /> <option name="USE_ALTERNATE_LOCATION" value="false" /> </component> - <component name="SvnConfiguration"> - <option name="USER" value="" /> - <option name="PASSWORD" value="" /> - <option name="PROCESS_UNRESOLVED" value="false" /> - <option name="LAST_MERGED_REVISION" /> - <option name="UPDATE_RUN_STATUS" value="false" /> - <option name="UPDATE_RECURSIVELY" value="true" /> - <option name="MERGE_DRY_RUN" value="false" /> - </component> <component name="VCS.FileViewConfiguration"> <option name="SELECTED_STATUSES" value="DEFAULT" /> <option name="SELECTED_COLUMNS" value="DEFAULT" /> @@ -399,6 +426,7 @@ </component> <component name="VcsDirectoryMappings"> <mapping directory="" vcs="Perforce" /> + <mapping directory="/Volumes/Android/donut/frameworks/base" vcs="Git" /> </component> <component name="VssConfiguration"> <option name="CLIENT_PATH" value="" /> diff --git a/tools/preload/sorttable.js b/tools/preload/sorttable.js new file mode 100644 index 0000000..25bccb2 --- /dev/null +++ b/tools/preload/sorttable.js @@ -0,0 +1,493 @@ +/* + SortTable + version 2 + 7th April 2007 + Stuart Langridge, http://www.kryogenix.org/code/browser/sorttable/ + + Instructions: + Download this file + Add <script src="sorttable.js"></script> to your HTML + Add class="sortable" to any table you'd like to make sortable + Click on the headers to sort + + Thanks to many, many people for contributions and suggestions. + Licenced as X11: http://www.kryogenix.org/code/browser/licence.html + This basically means: do what you want with it. +*/ + + +var stIsIE = /*@cc_on!@*/false; + +sorttable = { + init: function() { + // quit if this function has already been called + if (arguments.callee.done) return; + // flag this function so we don't do the same thing twice + arguments.callee.done = true; + // kill the timer + if (_timer) clearInterval(_timer); + + if (!document.createElement || !document.getElementsByTagName) return; + + sorttable.DATE_RE = /^(\d\d?)[\/\.-](\d\d?)[\/\.-]((\d\d)?\d\d)$/; + + forEach(document.getElementsByTagName('table'), function(table) { + if (table.className.search(/\bsortable\b/) != -1) { + sorttable.makeSortable(table); + } + }); + + }, + + makeSortable: function(table) { + if (table.getElementsByTagName('thead').length == 0) { + // table doesn't have a tHead. Since it should have, create one and + // put the first table row in it. + the = document.createElement('thead'); + the.appendChild(table.rows[0]); + table.insertBefore(the,table.firstChild); + } + // Safari doesn't support table.tHead, sigh + if (table.tHead == null) table.tHead = table.getElementsByTagName('thead')[0]; + + if (table.tHead.rows.length != 1) return; // can't cope with two header rows + + // Sorttable v1 put rows with a class of "sortbottom" at the bottom (as + // "total" rows, for example). This is B&R, since what you're supposed + // to do is put them in a tfoot. So, if there are sortbottom rows, + // for backwards compatibility, move them to tfoot (creating it if needed). + sortbottomrows = []; + for (var i=0; i<table.rows.length; i++) { + if (table.rows[i].className.search(/\bsortbottom\b/) != -1) { + sortbottomrows[sortbottomrows.length] = table.rows[i]; + } + } + if (sortbottomrows) { + if (table.tFoot == null) { + // table doesn't have a tfoot. Create one. + tfo = document.createElement('tfoot'); + table.appendChild(tfo); + } + for (var i=0; i<sortbottomrows.length; i++) { + tfo.appendChild(sortbottomrows[i]); + } + delete sortbottomrows; + } + + // work through each column and calculate its type + headrow = table.tHead.rows[0].cells; + for (var i=0; i<headrow.length; i++) { + // manually override the type with a sorttable_type attribute + if (!headrow[i].className.match(/\bsorttable_nosort\b/)) { // skip this col + mtch = headrow[i].className.match(/\bsorttable_([a-z0-9]+)\b/); + if (mtch) { override = mtch[1]; } + if (mtch && typeof sorttable["sort_"+override] == 'function') { + headrow[i].sorttable_sortfunction = sorttable["sort_"+override]; + } else { + headrow[i].sorttable_sortfunction = sorttable.guessType(table,i); + } + // make it clickable to sort + headrow[i].sorttable_columnindex = i; + headrow[i].sorttable_tbody = table.tBodies[0]; + dean_addEvent(headrow[i],"click", function(e) { + + if (this.className.search(/\bsorttable_sorted\b/) != -1) { + // if we're already sorted by this column, just + // reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted', + 'sorttable_sorted_reverse'); + this.removeChild(document.getElementById('sorttable_sortfwdind')); + sortrevind = document.createElement('span'); + sortrevind.id = "sorttable_sortrevind"; + sortrevind.innerHTML = stIsIE ? ' <font face="webdings">5</font>' : ' ▴'; + this.appendChild(sortrevind); + return; + } + if (this.className.search(/\bsorttable_sorted_reverse\b/) != -1) { + // if we're already sorted by this column in reverse, just + // re-reverse the table, which is quicker + sorttable.reverse(this.sorttable_tbody); + this.className = this.className.replace('sorttable_sorted_reverse', + 'sorttable_sorted'); + this.removeChild(document.getElementById('sorttable_sortrevind')); + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + return; + } + + // remove sorttable_sorted classes + theadrow = this.parentNode; + forEach(theadrow.childNodes, function(cell) { + if (cell.nodeType == 1) { // an element + cell.className = cell.className.replace('sorttable_sorted_reverse',''); + cell.className = cell.className.replace('sorttable_sorted',''); + } + }); + sortfwdind = document.getElementById('sorttable_sortfwdind'); + if (sortfwdind) { sortfwdind.parentNode.removeChild(sortfwdind); } + sortrevind = document.getElementById('sorttable_sortrevind'); + if (sortrevind) { sortrevind.parentNode.removeChild(sortrevind); } + + this.className += ' sorttable_sorted'; + sortfwdind = document.createElement('span'); + sortfwdind.id = "sorttable_sortfwdind"; + sortfwdind.innerHTML = stIsIE ? ' <font face="webdings">6</font>' : ' ▾'; + this.appendChild(sortfwdind); + + // build an array to sort. This is a Schwartzian transform thing, + // i.e., we "decorate" each row with the actual sort key, + // sort based on the sort keys, and then put the rows back in order + // which is a lot faster because you only do getInnerText once per row + row_array = []; + col = this.sorttable_columnindex; + rows = this.sorttable_tbody.rows; + for (var j=0; j<rows.length; j++) { + row_array[row_array.length] = [sorttable.getInnerText(rows[j].cells[col]), rows[j]]; + } + /* If you want a stable sort, uncomment the following line */ + //sorttable.shaker_sort(row_array, this.sorttable_sortfunction); + /* and comment out this one */ + row_array.sort(this.sorttable_sortfunction); + + tb = this.sorttable_tbody; + for (var j=0; j<row_array.length; j++) { + tb.appendChild(row_array[j][1]); + } + + delete row_array; + }); + } + } + }, + + guessType: function(table, column) { + // guess the type of a column based on its first non-blank row + sortfn = sorttable.sort_alpha; + for (var i=0; i<table.tBodies[0].rows.length; i++) { + text = sorttable.getInnerText(table.tBodies[0].rows[i].cells[column]); + if (text != '') { + if (text.match(/^-?[£$¤]?[\d,.]+%?$/)) { + return sorttable.sort_numeric; + } + // check for a date: dd/mm/yyyy or dd/mm/yy + // can have / or . or - as separator + // can be mm/dd as well + possdate = text.match(sorttable.DATE_RE) + if (possdate) { + // looks like a date + first = parseInt(possdate[1]); + second = parseInt(possdate[2]); + if (first > 12) { + // definitely dd/mm + return sorttable.sort_ddmm; + } else if (second > 12) { + return sorttable.sort_mmdd; + } else { + // looks like a date, but we can't tell which, so assume + // that it's dd/mm (English imperialism!) and keep looking + sortfn = sorttable.sort_ddmm; + } + } + } + } + return sortfn; + }, + + getInnerText: function(node) { + // gets the text we want to use for sorting for a cell. + // strips leading and trailing whitespace. + // this is *not* a generic getInnerText function; it's special to sorttable. + // for example, you can override the cell text with a customkey attribute. + // it also gets .value for <input> fields. + + hasInputs = (typeof node.getElementsByTagName == 'function') && + node.getElementsByTagName('input').length; + + if (node.getAttribute("sorttable_customkey") != null) { + return node.getAttribute("sorttable_customkey"); + } + else if (typeof node.textContent != 'undefined' && !hasInputs) { + return node.textContent.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.innerText != 'undefined' && !hasInputs) { + return node.innerText.replace(/^\s+|\s+$/g, ''); + } + else if (typeof node.text != 'undefined' && !hasInputs) { + return node.text.replace(/^\s+|\s+$/g, ''); + } + else { + switch (node.nodeType) { + case 3: + if (node.nodeName.toLowerCase() == 'input') { + return node.value.replace(/^\s+|\s+$/g, ''); + } + case 4: + return node.nodeValue.replace(/^\s+|\s+$/g, ''); + break; + case 1: + case 11: + var innerText = ''; + for (var i = 0; i < node.childNodes.length; i++) { + innerText += sorttable.getInnerText(node.childNodes[i]); + } + return innerText.replace(/^\s+|\s+$/g, ''); + break; + default: + return ''; + } + } + }, + + reverse: function(tbody) { + // reverse the rows in a tbody + newrows = []; + for (var i=0; i<tbody.rows.length; i++) { + newrows[newrows.length] = tbody.rows[i]; + } + for (var i=newrows.length-1; i>=0; i--) { + tbody.appendChild(newrows[i]); + } + delete newrows; + }, + + /* sort functions + each sort function takes two parameters, a and b + you are comparing a[0] and b[0] */ + sort_numeric: function(a,b) { + aa = parseFloat(a[0].replace(/[^0-9.-]/g,'')); + if (isNaN(aa)) aa = 0; + bb = parseFloat(b[0].replace(/[^0-9.-]/g,'')); + if (isNaN(bb)) bb = 0; + return aa-bb; + }, + sort_alpha: function(a,b) { + if (a[0]==b[0]) return 0; + if (a[0]<b[0]) return -1; + return 1; + }, + sort_ddmm: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; m = mtch[2]; d = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + sort_mmdd: function(a,b) { + mtch = a[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt1 = y+m+d; + mtch = b[0].match(sorttable.DATE_RE); + y = mtch[3]; d = mtch[2]; m = mtch[1]; + if (m.length == 1) m = '0'+m; + if (d.length == 1) d = '0'+d; + dt2 = y+m+d; + if (dt1==dt2) return 0; + if (dt1<dt2) return -1; + return 1; + }, + + shaker_sort: function(list, comp_func) { + // A stable sort function to allow multi-level sorting of data + // see: http://en.wikipedia.org/wiki/Cocktail_sort + // thanks to Joseph Nahmias + var b = 0; + var t = list.length - 1; + var swap = true; + + while(swap) { + swap = false; + for(var i = b; i < t; ++i) { + if ( comp_func(list[i], list[i+1]) > 0 ) { + var q = list[i]; list[i] = list[i+1]; list[i+1] = q; + swap = true; + } + } // for + t--; + + if (!swap) break; + + for(var i = t; i > b; --i) { + if ( comp_func(list[i], list[i-1]) < 0 ) { + var q = list[i]; list[i] = list[i-1]; list[i-1] = q; + swap = true; + } + } // for + b++; + + } // while(swap) + } +} + +/* ****************************************************************** + Supporting functions: bundled here to avoid depending on a library + ****************************************************************** */ + +// Dean Edwards/Matthias Miller/John Resig + +/* for Mozilla/Opera9 */ +if (document.addEventListener) { + document.addEventListener("DOMContentLoaded", sorttable.init, false); +} + +/* for Internet Explorer */ +/*@cc_on @*/ +/*@if (@_win32) + document.write("<script id=__ie_onload defer src=javascript:void(0)><\/script>"); + var script = document.getElementById("__ie_onload"); + script.onreadystatechange = function() { + if (this.readyState == "complete") { + sorttable.init(); // call the onload handler + } + }; +/*@end @*/ + +/* for Safari */ +if (/WebKit/i.test(navigator.userAgent)) { // sniff + var _timer = setInterval(function() { + if (/loaded|complete/.test(document.readyState)) { + sorttable.init(); // call the onload handler + } + }, 10); +} + +/* for other browsers */ +window.onload = sorttable.init; + +// written by Dean Edwards, 2005 +// with input from Tino Zijdel, Matthias Miller, Diego Perini + +// http://dean.edwards.name/weblog/2005/10/add-event/ + +function dean_addEvent(element, type, handler) { + if (element.addEventListener) { + element.addEventListener(type, handler, false); + } else { + // assign each event handler a unique ID + if (!handler.$$guid) handler.$$guid = dean_addEvent.guid++; + // create a hash table of event types for the element + if (!element.events) element.events = {}; + // create a hash table of event handlers for each element/event pair + var handlers = element.events[type]; + if (!handlers) { + handlers = element.events[type] = {}; + // store the existing event handler (if there is one) + if (element["on" + type]) { + handlers[0] = element["on" + type]; + } + } + // store the event handler in the hash table + handlers[handler.$$guid] = handler; + // assign a global event handler to do all the work + element["on" + type] = handleEvent; + } +}; +// a counter used to create unique IDs +dean_addEvent.guid = 1; + +function removeEvent(element, type, handler) { + if (element.removeEventListener) { + element.removeEventListener(type, handler, false); + } else { + // delete the event handler from the hash table + if (element.events && element.events[type]) { + delete element.events[type][handler.$$guid]; + } + } +}; + +function handleEvent(event) { + var returnValue = true; + // grab the event object (IE uses a global event object) + event = event || fixEvent(((this.ownerDocument || this.document || this).parentWindow || window).event); + // get a reference to the hash table of event handlers + var handlers = this.events[event.type]; + // execute each event handler + for (var i in handlers) { + this.$$handleEvent = handlers[i]; + if (this.$$handleEvent(event) === false) { + returnValue = false; + } + } + return returnValue; +}; + +function fixEvent(event) { + // add W3C standard event methods + event.preventDefault = fixEvent.preventDefault; + event.stopPropagation = fixEvent.stopPropagation; + return event; +}; +fixEvent.preventDefault = function() { + this.returnValue = false; +}; +fixEvent.stopPropagation = function() { + this.cancelBubble = true; +} + +// Dean's forEach: http://dean.edwards.name/base/forEach.js +/* + forEach, version 1.0 + Copyright 2006, Dean Edwards + License: http://www.opensource.org/licenses/mit-license.php +*/ + +// array-like enumeration +if (!Array.forEach) { // mozilla already supports this + Array.forEach = function(array, block, context) { + for (var i = 0; i < array.length; i++) { + block.call(context, array[i], i, array); + } + }; +} + +// generic enumeration +Function.prototype.forEach = function(object, block, context) { + for (var key in object) { + if (typeof this.prototype[key] == "undefined") { + block.call(context, object[key], key, object); + } + } +}; + +// character enumeration +String.forEach = function(string, block, context) { + Array.forEach(string.split(""), function(chr, index) { + block.call(context, chr, index, string); + }); +}; + +// globally resolve forEach enumeration +var forEach = function(object, block, context) { + if (object) { + var resolve = Object; // default + if (object instanceof Function) { + // functions have a "length" property + resolve = Function; + } else if (object.forEach instanceof Function) { + // the object implements a custom forEach method so use that + object.forEach(block, context); + return; + } else if (typeof object == "string") { + // the object is a string + resolve = String; + } else if (typeof object.length == "number") { + // the object is array-like + resolve = Array; + } + resolve.forEach(object, block, context); + } +}; + |
