summaryrefslogtreecommitdiffstats
path: root/tools
diff options
context:
space:
mode:
authorJean-Baptiste Queru <jbq@google.com>2009-09-03 15:18:14 -0700
committerJean-Baptiste Queru <jbq@google.com>2009-09-03 15:19:21 -0700
commitbe8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2 (patch)
tree34ae4ff9404e97d9c3b858903b0a414149671ac9 /tools
parent44b283dc27ed8ba5a946d809489f29b4ae88cb15 (diff)
parent8f4b5a561813ee8c22d2b8e73c33299471d4a3f3 (diff)
downloadframeworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.zip
frameworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.tar.gz
frameworks_base-be8e36e32b9cb2bb37e28e4c0d414ee0e46c84a2.tar.bz2
merge from donut
Diffstat (limited to 'tools')
-rw-r--r--tools/aapt/Command.cpp5
-rwxr-xr-xtools/aidl/AST.cpp8
-rwxr-xr-xtools/aidl/AST.h2
-rw-r--r--tools/aidl/generate_java.cpp2
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Bitmap.java32
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Canvas.java (renamed from tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeCanvas.java)299
-rw-r--r--tools/layoutlib/bridge/src/android/graphics/Matrix.java55
-rw-r--r--tools/layoutlib/bridge/src/android/os/ServiceManager.java72
-rw-r--r--tools/layoutlib/bridge/src/android/view/accessibility/AccessibilityManager.java95
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/Bridge.java46
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeConstants.java5
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeContext.java228
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/BridgeResources.java142
-rw-r--r--tools/layoutlib/bridge/src/com/android/layoutlib/bridge/NinePatchDrawable.java28
-rw-r--r--tools/layoutlib/create/src/com/android/tools/layoutlib/create/Main.java23
-rw-r--r--tools/preload/20090811.compiledbin0 -> 15943336 bytes
-rw-r--r--tools/preload/Android.mk2
-rw-r--r--tools/preload/ClassRank.java53
-rw-r--r--tools/preload/LoadedClass.java65
-rw-r--r--tools/preload/MemoryUsage.java37
-rw-r--r--tools/preload/Policy.java89
-rw-r--r--tools/preload/PrintCsv.java110
-rw-r--r--tools/preload/PrintHtmlDiff.java142
-rw-r--r--tools/preload/Proc.java101
-rw-r--r--tools/preload/Root.java2
-rw-r--r--tools/preload/WritePreloadedClassFile.java144
-rw-r--r--tools/preload/loadclass/LoadClass.java4
-rw-r--r--tools/preload/preload.iml5
-rw-r--r--tools/preload/preload.ipr68
-rw-r--r--tools/preload/sorttable.js493
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
new file mode 100644
index 0000000..6dbeca0
--- /dev/null
+++ b/tools/preload/20090811.compiled
Binary files differ
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 ? '&nbsp<font face="webdings">5</font>' : '&nbsp;&#x25B4;';
+ 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 ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
+ 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 ? '&nbsp<font face="webdings">6</font>' : '&nbsp;&#x25BE;';
+ 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);
+ }
+};
+