Merge "docs: add guide to copying and installing the Dev Tools app from the emulator to a device." into eclair
diff --git a/core/java/android/view/ScaleGestureDetector.java b/core/java/android/view/ScaleGestureDetector.java
new file mode 100644
index 0000000..50bfefc
--- /dev/null
+++ b/core/java/android/view/ScaleGestureDetector.java
@@ -0,0 +1,445 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.util.DisplayMetrics;
+import android.util.Log;
+
+/**
+ * Detects transformation gestures involving more than one pointer ("multitouch")
+ * using the supplied {@link MotionEvent}s. The {@link OnScaleGestureListener}
+ * callback will notify users when a particular gesture event has occurred.
+ * This class should only be used with {@link MotionEvent}s reported via touch.
+ * 
+ * To use this class:
+ * <ul>
+ *  <li>Create an instance of the {@code ScaleGestureDetector} for your
+ *      {@link View}
+ *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
+ *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
+ *          callback will be executed when the events occur.
+ * </ul>
+ * @hide Pending API approval
+ */
+public class ScaleGestureDetector {
+    /**
+     * The listener for receiving notifications when gestures occur.
+     * If you want to listen for all the different gestures then implement
+     * this interface. If you only want to listen for a subset it might
+     * be easier to extend {@link SimpleOnScaleGestureListener}.
+     * 
+     * An application will receive events in the following order:
+     * <ul>
+     *  <li>One {@link OnScaleGestureListener#onScaleBegin()}
+     *  <li>Zero or more {@link OnScaleGestureListener#onScale()}
+     *  <li>One {@link OnScaleGestureListener#onTransformEnd()}
+     * </ul>
+     */
+    public interface OnScaleGestureListener {
+        /**
+         * Responds to scaling events for a gesture in progress.
+         * Reported by pointer motion.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return Whether or not the detector should consider this event
+         *          as handled. If an event was not handled, the detector
+         *          will continue to accumulate movement until an event is
+         *          handled. This can be useful if an application, for example,
+         *          only wants to update scaling factors if the change is
+         *          greater than 0.01.
+         */
+        public boolean onScale(ScaleGestureDetector detector);
+
+        /**
+         * Responds to the beginning of a scaling gesture. Reported by
+         * new pointers going down.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         * @return Whether or not the detector should continue recognizing
+         *          this gesture. For example, if a gesture is beginning
+         *          with a focal point outside of a region where it makes
+         *          sense, onScaleBegin() may return false to ignore the
+         *          rest of the gesture.
+         */
+        public boolean onScaleBegin(ScaleGestureDetector detector);
+
+        /**
+         * Responds to the end of a scale gesture. Reported by existing
+         * pointers going up. If the end of a gesture would result in a fling,
+         * {@link onTransformFling()} is called instead.
+         * 
+         * Once a scale has ended, {@link ScaleGestureDetector#getFocusX()}
+         * and {@link ScaleGestureDetector#getFocusY()} will return the location
+         * of the pointer remaining on the screen.
+         * 
+         * @param detector The detector reporting the event - use this to
+         *          retrieve extended info about event state.
+         */
+        public void onScaleEnd(ScaleGestureDetector detector);
+    }
+    
+    /**
+     * A convenience class to extend when you only want to listen for a subset
+     * of scaling-related events. This implements all methods in
+     * {@link OnScaleGestureListener} but does nothing.
+     * {@link OnScaleGestureListener#onScale(ScaleGestureDetector)} and
+     * {@link OnScaleGestureListener#onScaleBegin(ScaleGestureDetector)} return
+     * {@code true}. 
+     */
+    public class SimpleOnScaleGestureListener implements OnScaleGestureListener {
+
+        public boolean onScale(ScaleGestureDetector detector) {
+            return true;
+        }
+
+        public boolean onScaleBegin(ScaleGestureDetector detector) {
+            return true;
+        }
+
+        public void onScaleEnd(ScaleGestureDetector detector) {
+            // Intentionally empty
+        }
+    }
+
+    private static final float PRESSURE_THRESHOLD = 0.67f;
+
+    private Context mContext;
+    private OnScaleGestureListener mListener;
+    private boolean mGestureInProgress;
+
+    private MotionEvent mPrevEvent;
+    private MotionEvent mCurrEvent;
+
+    private float mFocusX;
+    private float mFocusY;
+    private float mPrevFingerDiffX;
+    private float mPrevFingerDiffY;
+    private float mCurrFingerDiffX;
+    private float mCurrFingerDiffY;
+    private float mCurrLen;
+    private float mPrevLen;
+    private float mScaleFactor;
+    private float mCurrPressure;
+    private float mPrevPressure;
+    private long mTimeDelta;
+    
+    private float mEdgeSlop;
+    private float mRightSlopEdge;
+    private float mBottomSlopEdge;
+    private boolean mSloppyGesture;
+
+    public ScaleGestureDetector(Context context, OnScaleGestureListener listener) {
+        ViewConfiguration config = ViewConfiguration.get(context);
+        DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        mContext = context;
+        mListener = listener;
+        mEdgeSlop = config.getScaledEdgeSlop();
+        mRightSlopEdge = metrics.widthPixels - mEdgeSlop;
+        mBottomSlopEdge = metrics.heightPixels - mEdgeSlop;
+    }
+
+    public boolean onTouchEvent(MotionEvent event) {
+        final int action = event.getAction();
+        boolean handled = true;
+
+        if (!mGestureInProgress) {
+            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
+                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
+                    event.getPointerCount() >= 2) {
+                // We have a new multi-finger gesture
+
+                // Be paranoid in case we missed an event
+                reset();
+
+                mPrevEvent = MotionEvent.obtain(event);
+                mTimeDelta = 0;
+
+                setContext(event);
+
+                // Check if we have a sloppy gesture. If so, delay
+                // the beginning of the gesture until we're sure that's
+                // what the user wanted. Sloppy gestures can happen if the
+                // edge of the user's hand is touching the screen, for example.
+                final float edgeSlop = mEdgeSlop;
+                final float rightSlop = mRightSlopEdge;
+                final float bottomSlop = mBottomSlopEdge;
+                final float x0 = event.getRawX();
+                final float y0 = event.getRawY();
+                final float x1 = getRawX(event, 1);
+                final float y1 = getRawY(event, 1);
+
+                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+                        x1 < edgeSlop || y1 < edgeSlop;
+                boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+                        x1 > rightSlop || y1 > bottomSlop;
+
+                if (p0sloppy) {
+                    mFocusX = event.getX(1);
+                    mFocusY = event.getY(1);
+                    mSloppyGesture = true;
+                } else if (p1sloppy) {
+                    mFocusX = event.getX(0);
+                    mFocusY = event.getY(0);
+                    mSloppyGesture = true;
+                } else {
+                    mGestureInProgress = mListener.onScaleBegin(this);
+                }
+            } else if (action == MotionEvent.ACTION_MOVE && mSloppyGesture) {
+                // Initiate sloppy gestures if we've moved outside of the slop area.
+                final float edgeSlop = mEdgeSlop;
+                final float rightSlop = mRightSlopEdge;
+                final float bottomSlop = mBottomSlopEdge;
+                final float x0 = event.getRawX();
+                final float y0 = event.getRawY();
+                final float x1 = getRawX(event, 1);
+                final float y1 = getRawY(event, 1);
+
+                boolean p0sloppy = x0 < edgeSlop || y0 < edgeSlop ||
+                x1 < edgeSlop || y1 < edgeSlop;
+                boolean p1sloppy = x0 > rightSlop || y0 > bottomSlop ||
+                x1 > rightSlop || y1 > bottomSlop;
+
+                if (p0sloppy) {
+                    mFocusX = event.getX(1);
+                    mFocusY = event.getY(1);
+                } else if (p1sloppy) {
+                    mFocusX = event.getX(0);
+                    mFocusY = event.getY(0);
+                } else {
+                    mSloppyGesture = false;
+                    mGestureInProgress = mListener.onScaleBegin(this);
+                }
+            }
+        } else {
+            // Transform gesture in progress - attempt to handle it
+            switch (action) {
+                case MotionEvent.ACTION_POINTER_1_UP:
+                case MotionEvent.ACTION_POINTER_2_UP:
+                    // Gesture ended
+                    setContext(event);
+
+                    // Set focus point to the remaining finger
+                    int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
+                            >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
+                    mFocusX = event.getX(id);
+                    mFocusY = event.getY(id);
+
+                    if (!mSloppyGesture) {
+                        mListener.onScaleEnd(this);
+                    }
+
+                    reset();
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    if (!mSloppyGesture) {
+                        mListener.onScaleEnd(this);
+                    }
+
+                    reset();
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    setContext(event);
+
+                    // Only accept the event if our relative pressure is within
+                    // a certain limit - this can help filter shaky data as a
+                    // finger is lifted.
+                    if (mCurrPressure / mPrevPressure > PRESSURE_THRESHOLD) {
+                        final boolean updatePrevious = mListener.onScale(this);
+
+                        if (updatePrevious) {
+                            mPrevEvent.recycle();
+                            mPrevEvent = MotionEvent.obtain(event);
+                        }
+                    }
+                    break;
+            }
+        }
+        return handled;
+    }
+    
+    /**
+     * MotionEvent has no getRawX(int) method; simulate it pending future API approval. 
+     */
+    private static float getRawX(MotionEvent event, int pointerIndex) {
+        float offset = event.getX() - event.getRawX();
+        return event.getX(pointerIndex) + offset;
+    }
+    
+    /**
+     * MotionEvent has no getRawY(int) method; simulate it pending future API approval. 
+     */
+    private static float getRawY(MotionEvent event, int pointerIndex) {
+        float offset = event.getY() - event.getRawY();
+        return event.getY(pointerIndex) + offset;
+    }
+
+    private void setContext(MotionEvent curr) {
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+        }
+        mCurrEvent = MotionEvent.obtain(curr);
+
+        mCurrLen = -1;
+        mPrevLen = -1;
+        mScaleFactor = -1;
+
+        final MotionEvent prev = mPrevEvent;
+
+        final float px0 = prev.getX(0);
+        final float py0 = prev.getY(0);
+        final float px1 = prev.getX(1);
+        final float py1 = prev.getY(1);
+        final float cx0 = curr.getX(0);
+        final float cy0 = curr.getY(0);
+        final float cx1 = curr.getX(1);
+        final float cy1 = curr.getY(1);
+
+        final float pvx = px1 - px0;
+        final float pvy = py1 - py0;
+        final float cvx = cx1 - cx0;
+        final float cvy = cy1 - cy0;
+        mPrevFingerDiffX = pvx;
+        mPrevFingerDiffY = pvy;
+        mCurrFingerDiffX = cvx;
+        mCurrFingerDiffY = cvy;
+
+        mFocusX = cx0 + cvx * 0.5f;
+        mFocusY = cy0 + cvy * 0.5f;
+        mTimeDelta = curr.getEventTime() - prev.getEventTime();
+        mCurrPressure = curr.getPressure(0) + curr.getPressure(1);
+        mPrevPressure = prev.getPressure(0) + prev.getPressure(1);
+    }
+
+    private void reset() {
+        if (mPrevEvent != null) {
+            mPrevEvent.recycle();
+            mPrevEvent = null;
+        }
+        if (mCurrEvent != null) {
+            mCurrEvent.recycle();
+            mCurrEvent = null;
+        }
+        mSloppyGesture = false;
+        mGestureInProgress = false;
+    }
+
+    /**
+     * Returns {@code true} if a two-finger scale gesture is in progress.
+     * @return {@code true} if a scale gesture is in progress, {@code false} otherwise.
+     */
+    public boolean isInProgress() {
+        return mGestureInProgress;
+    }
+
+    /**
+     * Get the X coordinate of the current gesture's focal point.
+     * If a gesture is in progress, the focal point is directly between
+     * the two pointers forming the gesture.
+     * If a gesture is ending, the focal point is the location of the
+     * remaining pointer on the screen.
+     * If {@link isInProgress()} would return false, the result of this
+     * function is undefined.
+     * 
+     * @return X coordinate of the focal point in pixels.
+     */
+    public float getFocusX() {
+        return mFocusX;
+    }
+
+    /**
+     * Get the Y coordinate of the current gesture's focal point.
+     * If a gesture is in progress, the focal point is directly between
+     * the two pointers forming the gesture.
+     * If a gesture is ending, the focal point is the location of the
+     * remaining pointer on the screen.
+     * If {@link isInProgress()} would return false, the result of this
+     * function is undefined.
+     * 
+     * @return Y coordinate of the focal point in pixels.
+     */
+    public float getFocusY() {
+        return mFocusY;
+    }
+
+    /**
+     * Return the current distance between the two pointers forming the
+     * gesture in progress.
+     * 
+     * @return Distance between pointers in pixels.
+     */
+    public float getCurrentSpan() {
+        if (mCurrLen == -1) {
+            final float cvx = mCurrFingerDiffX;
+            final float cvy = mCurrFingerDiffY;
+            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
+        }
+        return mCurrLen;
+    }
+
+    /**
+     * Return the previous distance between the two pointers forming the
+     * gesture in progress.
+     * 
+     * @return Previous distance between pointers in pixels.
+     */
+    public float getPreviousSpan() {
+        if (mPrevLen == -1) {
+            final float pvx = mPrevFingerDiffX;
+            final float pvy = mPrevFingerDiffY;
+            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
+        }
+        return mPrevLen;
+    }
+
+    /**
+     * Return the scaling factor from the previous scale event to the current
+     * event. This value is defined as
+     * ({@link getCurrentSpan()} / {@link getPreviousSpan()}).
+     * 
+     * @return The current scaling factor.
+     */
+    public float getScaleFactor() {
+        if (mScaleFactor == -1) {
+            mScaleFactor = getCurrentSpan() / getPreviousSpan();
+        }
+        return mScaleFactor;
+    }
+    
+    /**
+     * Return the time difference in milliseconds between the previous
+     * accepted scaling event and the current scaling event.
+     * 
+     * @return Time difference since the last scaling event in milliseconds.
+     */
+    public long getTimeDelta() {
+        return mTimeDelta;
+    }
+    
+    /**
+     * Return the event time of the current event being processed.
+     * 
+     * @return Current event time in milliseconds.
+     */
+    public long getEventTime() {
+        return mCurrEvent.getEventTime();
+    }
+}
diff --git a/core/java/android/view/TransformGestureDetector.java b/core/java/android/view/TransformGestureDetector.java
deleted file mode 100644
index 196716a..0000000
--- a/core/java/android/view/TransformGestureDetector.java
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package android.view;
-
-import android.content.Context;
-import android.util.Log;
-import android.view.GestureDetector.SimpleOnGestureListener;
-
-/**
- * Detects transformation gestures involving more than one pointer ("multitouch")
- * using the supplied {@link MotionEvent}s. The {@link OnGestureListener} callback
- * will notify users when a particular gesture event has occurred. This class
- * should only be used with {@link MotionEvent}s reported via touch.
- * 
- * To use this class:
- * <ul>
- *  <li>Create an instance of the {@code TransformGestureDetector} for your
- *      {@link View}
- *  <li>In the {@link View#onTouchEvent(MotionEvent)} method ensure you call
- *          {@link #onTouchEvent(MotionEvent)}. The methods defined in your
- *          callback will be executed when the events occur.
- * </ul>
- * @hide Pending API approval
- */
-public class TransformGestureDetector {
-    /**
-     * The listener for receiving notifications when gestures occur.
-     * If you want to listen for all the different gestures then implement
-     * this interface. If you only want to listen for a subset it might
-     * be easier to extend {@link SimpleOnGestureListener}.
-     * 
-     * An application will receive events in the following order:
-     * One onTransformBegin()
-     * Zero or more onTransform()
-     * One onTransformEnd() or onTransformFling()
-     */
-    public interface OnTransformGestureListener {
-        /**
-         * Responds to transformation events for a gesture in progress.
-         * Reported by pointer motion.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransform(TransformGestureDetector detector);
-        
-        /**
-         * Responds to the beginning of a transformation gesture. Reported by
-         * new pointers going down.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformBegin(TransformGestureDetector detector);
- 
-        /**
-         * Responds to the end of a transformation gesture. Reported by existing
-         * pointers going up. If the end of a gesture would result in a fling,
-         * onTransformFling is called instead.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformEnd(TransformGestureDetector detector);
-
-        /**
-         * Responds to the end of a transformation gesture that begins a fling.
-         * Reported by existing pointers going up. If the end of a gesture 
-         * would not result in a fling, onTransformEnd is called instead.
-         * 
-         * @param detector The detector reporting the event - use this to
-         *          retrieve extended info about event state.
-         * @return true if the event was handled, false otherwise.
-         */
-        public boolean onTransformFling(TransformGestureDetector detector);
-    }
-    
-    private static final boolean DEBUG = false;
-    
-    private static final int INITIAL_EVENT_IGNORES = 2;
-    
-    private Context mContext;
-    private float mTouchSizeScale;
-    private OnTransformGestureListener mListener;
-    private int mVelocityTimeUnits;
-    private MotionEvent mInitialEvent;
-    
-    private MotionEvent mPrevEvent;
-    private MotionEvent mCurrEvent;
-    private VelocityTracker mVelocityTracker;
-
-    private float mCenterX;
-    private float mCenterY;
-    private float mTransX;
-    private float mTransY;
-    private float mPrevFingerDiffX;
-    private float mPrevFingerDiffY;
-    private float mCurrFingerDiffX;
-    private float mCurrFingerDiffY;
-    private float mRotateDegrees;
-    private float mCurrLen;
-    private float mPrevLen;
-    private float mScaleFactor;
-    
-    // Units in pixels. Current value is pulled out of thin air for debugging only.
-    private float mPointerJumpLimit = 30;
-    
-    private int mEventIgnoreCount;
-    
-   public TransformGestureDetector(Context context, OnTransformGestureListener listener,
-            int velocityTimeUnits) {
-        mContext = context;
-        mListener = listener;
-        mTouchSizeScale = context.getResources().getDisplayMetrics().widthPixels/3;
-        mVelocityTimeUnits = velocityTimeUnits;
-        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
-    }
-    
-    public TransformGestureDetector(Context context, OnTransformGestureListener listener) {
-        this(context, listener, 1000);
-    }
-    
-    public boolean onTouchEvent(MotionEvent event) {
-        final int action = event.getAction();
-        boolean handled = true;
-
-        if (mInitialEvent == null) {
-            // No transform gesture in progress
-            if ((action == MotionEvent.ACTION_POINTER_1_DOWN ||
-                    action == MotionEvent.ACTION_POINTER_2_DOWN) &&
-                    event.getPointerCount() >= 2) {
-                // We have a new multi-finger gesture
-                mInitialEvent = MotionEvent.obtain(event);
-                mPrevEvent = MotionEvent.obtain(event);
-                mVelocityTracker = VelocityTracker.obtain();
-                handled = mListener.onTransformBegin(this);
-            }
-        } else {
-            // Transform gesture in progress - attempt to handle it
-            switch (action) {
-                case MotionEvent.ACTION_POINTER_1_UP:
-                case MotionEvent.ACTION_POINTER_2_UP:
-                    // Gesture ended
-                    handled = mListener.onTransformEnd(this);
-
-                    reset();
-                    break;
-                    
-                case MotionEvent.ACTION_CANCEL:
-                    handled = mListener.onTransformEnd(this);
-                    
-                    reset();
-                    break;
-                    
-                case MotionEvent.ACTION_MOVE:
-                    setContext(event);
-
-                    // Our first few events can be crazy from some touchscreens - drop them.
-                    if (mEventIgnoreCount == 0) {
-                        mVelocityTracker.addMovement(event);
-                        handled = mListener.onTransform(this);
-                    } else {
-                        mEventIgnoreCount--;
-                    }
-                    
-                    mPrevEvent.recycle();
-                    mPrevEvent = MotionEvent.obtain(event);
-                    break;
-            }
-        }
-        return handled;
-    }
-    
-    private void setContext(MotionEvent curr) {
-        mCurrEvent = MotionEvent.obtain(curr);
-
-        mRotateDegrees = -1;
-        mCurrLen = -1;
-        mPrevLen = -1;
-        mScaleFactor = -1;
-
-        final MotionEvent prev = mPrevEvent;
-        
-        float px0 = prev.getX(0);
-        float py0 = prev.getY(0);
-        float px1 = prev.getX(1);
-        float py1 = prev.getY(1);
-        float cx0 = curr.getX(0);
-        float cy0 = curr.getY(0);
-        float cx1 = curr.getX(1);
-        float cy1 = curr.getY(1);
-
-        // Some touchscreens do weird things with pointer values where points are
-        // too close along one axis. Try to detect this here and smooth things out.
-        // The main indicator is that we get the X or Y value from the other pointer.
-        final float dx0 = cx0 - px0;
-        final float dy0 = cy0 - py0;
-        final float dx1 = cx1 - px1;
-        final float dy1 = cy1 - py1;
-
-        if (cx0 == cx1) {
-            if (Math.abs(dx0) > mPointerJumpLimit) {
-                 cx0 = px0;
-            } else if (Math.abs(dx1) > mPointerJumpLimit) {
-                cx1 = px1;
-            }
-        } else if (cy0 == cy1) {
-            if (Math.abs(dy0) > mPointerJumpLimit) {
-                cy0 = py0;
-            } else if (Math.abs(dy1) > mPointerJumpLimit) {
-                cy1 = py1;
-            }
-        }
-        
-        final float pvx = px1 - px0;
-        final float pvy = py1 - py0;
-        final float cvx = cx1 - cx0;
-        final float cvy = cy1 - cy0;
-        mPrevFingerDiffX = pvx;
-        mPrevFingerDiffY = pvy;
-        mCurrFingerDiffX = cvx;
-        mCurrFingerDiffY = cvy;
-
-        final float pmidx = px0 + pvx * 0.5f;
-        final float pmidy = py0 + pvy * 0.5f;
-        final float cmidx = cx0 + cvx * 0.5f;
-        final float cmidy = cy0 + cvy * 0.5f;
-
-        mCenterX = cmidx;
-        mCenterY = cmidy;
-        mTransX = cmidx - pmidx;
-        mTransY = cmidy - pmidy;
-    }
-    
-    private void reset() {
-        if (mInitialEvent != null) {
-            mInitialEvent.recycle();
-            mInitialEvent = null;
-        }
-        if (mPrevEvent != null) {
-            mPrevEvent.recycle();
-            mPrevEvent = null;
-        }
-        if (mCurrEvent != null) {
-            mCurrEvent.recycle();
-            mCurrEvent = null;
-        }
-        if (mVelocityTracker != null) {
-            mVelocityTracker.recycle();
-            mVelocityTracker = null;
-        }
-        mEventIgnoreCount = INITIAL_EVENT_IGNORES;
-    }
-    
-    public float getCenterX() {
-        return mCenterX;
-    }
-
-    public float getCenterY() {
-        return mCenterY;
-    }
-
-    public float getTranslateX() {
-        return mTransX;
-    }
-
-    public float getTranslateY() {
-        return mTransY;
-    }
-
-    public float getCurrentSpan() {
-        if (mCurrLen == -1) {
-            final float cvx = mCurrFingerDiffX;
-            final float cvy = mCurrFingerDiffY;
-            mCurrLen = (float)Math.sqrt(cvx*cvx + cvy*cvy);
-        }
-        return mCurrLen;
-    }
-
-    public float getPreviousSpan() {
-        if (mPrevLen == -1) {
-            final float pvx = mPrevFingerDiffX;
-            final float pvy = mPrevFingerDiffY;
-            mPrevLen = (float)Math.sqrt(pvx*pvx + pvy*pvy);
-        }
-        return mPrevLen;
-    }
-
-    public float getScaleFactor() {
-        if (mScaleFactor == -1) {
-            mScaleFactor = getCurrentSpan() / getPreviousSpan();
-        }
-        return mScaleFactor;
-    }
-
-    public float getRotation() {
-        throw new UnsupportedOperationException();
-    }
-}
diff --git a/core/java/android/webkit/WebSettings.java b/core/java/android/webkit/WebSettings.java
index 6f3262a..093756d 100644
--- a/core/java/android/webkit/WebSettings.java
+++ b/core/java/android/webkit/WebSettings.java
@@ -411,6 +411,7 @@
      */
     public void setSupportZoom(boolean support) {
         mSupportZoom = support;
+        mWebView.updateMultiTouchSupport(mContext);
     }
 
     /**
@@ -425,6 +426,7 @@
      */
     public void setBuiltInZoomControls(boolean enabled) {
         mBuiltInZoomControls = enabled;
+        mWebView.updateMultiTouchSupport(mContext);
     }
     
     /**
diff --git a/core/java/android/webkit/WebView.java b/core/java/android/webkit/WebView.java
index f6d6d22..5b4ec60 100644
--- a/core/java/android/webkit/WebView.java
+++ b/core/java/android/webkit/WebView.java
@@ -50,6 +50,7 @@
 import android.view.KeyEvent;
 import android.view.LayoutInflater;
 import android.view.MotionEvent;
+import android.view.ScaleGestureDetector;
 import android.view.SoundEffectConstants;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -430,6 +431,18 @@
 
     private boolean mWrapContent;
 
+    // whether support multi-touch
+    private static boolean mSupportMultiTouch;
+    // use the framework's ScaleGestureDetector to handle multi-touch
+    private ScaleGestureDetector mScaleDetector;
+    // minimum scale change during multi-touch zoom
+    private static float PREVIEW_SCALE_INCREMENT = 0.01f;
+
+    // the anchor point in the document space where VIEW_SIZE_CHANGED should
+    // apply to
+    private int mAnchorX;
+    private int mAnchorY;
+
     /**
      * Private message ids
      */
@@ -747,9 +760,20 @@
                     params;
             frameParams.gravity = Gravity.RIGHT;
         }
+        updateMultiTouchSupport(context);
+    }
 
+    void updateMultiTouchSupport(Context context) {
+        WebSettings settings = getSettings();
         mSupportMultiTouch = context.getPackageManager().hasSystemFeature(
-                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH);
+                PackageManager.FEATURE_TOUCHSCREEN_MULTITOUCH)
+                && settings.supportZoom() && settings.getBuiltInZoomControls();
+        if (mSupportMultiTouch && (mScaleDetector == null)) {
+            mScaleDetector = new ScaleGestureDetector(context,
+                    new ScaleDetectorListener());
+        } else if (!mSupportMultiTouch && (mScaleDetector != null)) {
+            mScaleDetector = null;
+        }
     }
 
     private void updateZoomButtonsEnabled() {
@@ -3699,23 +3723,15 @@
     private static final float MAX_SLOPE_FOR_DIAG = 1.5f;
     private static final int MIN_BREAK_SNAP_CROSS_DISTANCE = 80;
 
-    // MultiTouch handling
-    private static boolean mSupportMultiTouch;
+    private class ScaleDetectorListener implements
+            ScaleGestureDetector.OnScaleGestureListener {
 
-    private double mPinchDistance;
-    private float mLastPressure;
-    private int mAnchorX;
-    private int mAnchorY;
-
-    private static float SCALE_INCREMENT = 0.01f;
-    private static float PRESSURE_THRESHOLD = 0.67f;
-
-    private boolean doMultiTouch(MotionEvent ev) {
-        int action = ev.getAction();
-
-        if ((action & 0xff) == MotionEvent.ACTION_POINTER_DOWN) {
+        public boolean onScaleBegin(ScaleGestureDetector detector) {
             // cancel the single touch handling
             cancelTouch();
+            if (mZoomButtonsController.isVisible()) {
+                mZoomButtonsController.setVisible(false);
+            }
             // reset the zoom overview mode so that the page won't auto grow
             mInZoomOverview = false;
             // If it is in password mode, turn it off so it does not draw
@@ -3723,20 +3739,18 @@
             if (inEditingMode() && nativeFocusCandidateIsPassword()) {
                 mWebTextView.setInPassword(false);
             }
-            // start multi (2-pointer) touch
-            float x0 = ev.getX(0);
-            float y0 = ev.getY(0);
-            float x1 = ev.getX(1);
-            float y1 = ev.getY(1);
-            mPinchDistance = Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1)
-                    * (y0 - y1));
-        } else if ((action & 0xff) == MotionEvent.ACTION_POINTER_UP) {
+            return true;
+        }
+
+        public void onScaleEnd(ScaleGestureDetector detector) {
             if (mPreviewZoomOnly) {
                 mPreviewZoomOnly = false;
                 mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
                 mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
-                // for testing only, default don't reflow now
-                boolean reflowNow = !getSettings().getPluginsEnabled();
+                // don't reflow when zoom in; when zoom out, do reflow if the
+                // new scale is almost minimum scale;
+                boolean reflowNow = (mActualScale - mMinZoomScale <= 0.01f)
+                        || ((mActualScale <= 0.8 * mTextWrapScale));
                 // force zoom after mPreviewZoomOnly is set to false so that the
                 // new view size will be passed to the WebKit
                 setNewZoomScale(mActualScale, reflowNow, true);
@@ -3751,24 +3765,14 @@
             // may trigger the unwanted click, can't use TOUCH_DRAG_MODE as it
             // may trigger the unwanted fling.
             mTouchMode = TOUCH_PINCH_DRAG;
-            // action indicates which pointer is UP. Use the other one as drag's
-            // starting position.
-            int id = (((action & MotionEvent.ACTION_POINTER_ID_MASK)
-                    >> MotionEvent.ACTION_POINTER_ID_SHIFT) == 0) ? 1 : 0;
-            startTouch(ev.getX(id), ev.getY(id), ev.getEventTime());
-        } else if (action == MotionEvent.ACTION_MOVE) {
-            float x0 = ev.getX(0);
-            float y0 = ev.getY(0);
-            float x1 = ev.getX(1);
-            float y1 = ev.getY(1);
-            double distance = Math.sqrt((x0 - x1) * (x0 - x1) + (y0 - y1)
-                    * (y0 - y1));
-            float scale = (float) (Math.round(distance / mPinchDistance
+            startTouch(detector.getFocusX(), detector.getFocusY(),
+                    mLastTouchTime);
+        }
+
+        public boolean onScale(ScaleGestureDetector detector) {
+            float scale = (float) (Math.round(detector.getScaleFactor()
                     * mActualScale * 100) / 100.0);
-            float pressure = ev.getPressure(0) + ev.getPressure(1);
-            if (Math.abs(scale - mActualScale) >= SCALE_INCREMENT
-                    && (!mPreviewZoomOnly
-                    || (pressure / mLastPressure) > PRESSURE_THRESHOLD)) {
+            if (Math.abs(scale - mActualScale) >= PREVIEW_SCALE_INCREMENT) {
                 mPreviewZoomOnly = true;
                 // limit the scale change per step
                 if (scale > mActualScale) {
@@ -3776,18 +3780,14 @@
                 } else {
                     scale = Math.max(scale, mActualScale * 0.8f);
                 }
-                mZoomCenterX = (x0 + x1) / 2;
-                mZoomCenterY = (y0 + y1) / 2;
+                mZoomCenterX = detector.getFocusX();
+                mZoomCenterY = detector.getFocusY();
                 setNewZoomScale(scale, false, false);
                 invalidate();
-                mPinchDistance = distance;
-                mLastPressure = pressure;
+                return true;
             }
-        } else {
-            Log.w(LOGTAG, action + " should not happen during doMultiTouch");
             return false;
         }
-        return true;
     }
 
     @Override
@@ -3801,9 +3801,12 @@
                     + mTouchMode);
         }
 
-        if (mSupportMultiTouch && getSettings().supportZoom()
-                && mMinZoomScale < mMaxZoomScale && ev.getPointerCount() > 1) {
-            return doMultiTouch(ev);
+        // FIXME: we may consider to give WebKit an option to handle multi-touch
+        // events later.
+        if (mSupportMultiTouch && mMinZoomScale < mMaxZoomScale
+                && ev.getPointerCount() > 1) {
+            mLastTouchTime = ev.getEventTime();
+            return mScaleDetector.onTouchEvent(ev);
         }
 
         int action = ev.getAction();
@@ -3939,20 +3942,18 @@
                     if (!mDragFromTextInput) {
                         nativeHideCursor();
                     }
-                    if (!mSupportMultiTouch) {
-                        WebSettings settings = getSettings();
-                        if (settings.supportZoom()
-                                && settings.getBuiltInZoomControls()
-                                && !mZoomButtonsController.isVisible()
-                                && mMinZoomScale < mMaxZoomScale) {
-                            mZoomButtonsController.setVisible(true);
-                            int count = settings.getDoubleTapToastCount();
-                            if (mInZoomOverview && count > 0) {
-                                settings.setDoubleTapToastCount(--count);
-                                Toast.makeText(mContext,
-                                        com.android.internal.R.string.double_tap_toast,
-                                        Toast.LENGTH_LONG).show();
-                            }
+                    WebSettings settings = getSettings();
+                    if (settings.supportZoom()
+                            && settings.getBuiltInZoomControls()
+                            && !mZoomButtonsController.isVisible()
+                            && mMinZoomScale < mMaxZoomScale) {
+                        mZoomButtonsController.setVisible(true);
+                        int count = settings.getDoubleTapToastCount();
+                        if (mInZoomOverview && count > 0) {
+                            settings.setDoubleTapToastCount(--count);
+                            Toast.makeText(mContext,
+                                    com.android.internal.R.string.double_tap_toast,
+                                    Toast.LENGTH_LONG).show();
                         }
                     }
                 }
@@ -4031,8 +4032,7 @@
                     mUserScroll = true;
                 }
 
-                if (!mSupportMultiTouch
-                        && !getSettings().getBuiltInZoomControls()) {
+                if (!getSettings().getBuiltInZoomControls()) {
                     boolean showPlusMinus = mMinZoomScale < mMaxZoomScale;
                     if (mZoomControls != null && showPlusMinus) {
                         if (mZoomControls.getVisibility() == View.VISIBLE) {
@@ -4778,22 +4778,21 @@
         mAnchorX = viewToContentX((int) mZoomCenterX + mScrollX);
         mAnchorY = viewToContentY((int) mZoomCenterY + mScrollY);
         WebSettings settings = getSettings();
-        if (!mSupportMultiTouch) {
-            // remove the zoom control after double tap
-            if (settings.getBuiltInZoomControls()) {
-                if (mZoomButtonsController.isVisible()) {
-                    mZoomButtonsController.setVisible(false);
-                }
-            } else {
-                if (mZoomControlRunnable != null) {
-                    mPrivateHandler.removeCallbacks(mZoomControlRunnable);
-                }
-                if (mZoomControls != null) {
-                    mZoomControls.hide();
-                }
+        // remove the zoom control after double tap
+        if (settings.getBuiltInZoomControls()) {
+            if (mZoomButtonsController.isVisible()) {
+                mZoomButtonsController.setVisible(false);
             }
-            settings.setDoubleTapToastCount(0);
+        } else {
+            if (mZoomControlRunnable != null) {
+                mPrivateHandler.removeCallbacks(mZoomControlRunnable);
+            }
+            if (mZoomControls != null) {
+                mZoomControls.hide();
+            }
         }
+        settings.setDoubleTapToastCount(0);
+        boolean zoomToDefault = false;
         if ((settings.getLayoutAlgorithm() == WebSettings.LayoutAlgorithm.NARROW_COLUMNS)
                 && (Math.abs(mActualScale - mTextWrapScale) >= 0.01f)) {
             setNewZoomScale(mActualScale, true, true);
@@ -4809,9 +4808,12 @@
                 if (mScrollY < getTitleHeight()) mScrollY = 0;
                 zoomWithPreview(newScale);
             } else if (Math.abs(mActualScale - mDefaultScale) >= 0.01f) {
-                mInZoomOverview = true;
+                zoomToDefault = true;
             }
         } else {
+            zoomToDefault = true;
+        }
+        if (zoomToDefault) {
             mInZoomOverview = false;
             int left = nativeGetBlockLeftEdge(mAnchorX, mAnchorY, mActualScale);
             if (left != NO_LEFTEDGE) {
@@ -5150,7 +5152,8 @@
                         }
                         if (mInitialScaleInPercent > 0) {
                             setNewZoomScale(mInitialScaleInPercent / 100.0f,
-                                    true, false);
+                                    mInitialScaleInPercent != mTextWrapScale * 100,
+                                    false);
                         } else if (restoreState.mViewScale > 0) {
                             mTextWrapScale = restoreState.mTextWrapScale;
                             setNewZoomScale(restoreState.mViewScale, false,
@@ -5158,14 +5161,15 @@
                         } else {
                             mInZoomOverview = useWideViewport
                                     && settings.getLoadWithOverviewMode();
+                            float scale;
                             if (mInZoomOverview) {
-                                setNewZoomScale((float) viewWidth
-                                        / WebViewCore.DEFAULT_VIEWPORT_WIDTH,
-                                        true, false);
+                                scale = (float) viewWidth
+                                        / WebViewCore.DEFAULT_VIEWPORT_WIDTH;
                             } else {
-                                setNewZoomScale(restoreState.mTextWrapScale,
-                                        true, false);
+                                scale = restoreState.mTextWrapScale;
                             }
+                            setNewZoomScale(scale, Math.abs(scale
+                                    - mTextWrapScale) >= 0.01f, false);
                         }
                         setContentScrollTo(restoreState.mScrollX,
                                 restoreState.mScrollY);
@@ -5194,8 +5198,9 @@
                         mPictureListener.onNewPicture(WebView.this, capturePicture());
                     }
                     if (useWideViewport) {
-                        mZoomOverviewWidth = Math.max(draw.mMinPrefWidth,
-                                draw.mViewPoint.x);
+                        mZoomOverviewWidth = Math.max(
+                                (int) (viewWidth / mDefaultScale), Math.max(
+                                        draw.mMinPrefWidth, draw.mViewPoint.x));
                     }
                     if (!mMinZoomScaleFixed) {
                         mMinZoomScale = (float) viewWidth / mZoomOverviewWidth;
@@ -5206,7 +5211,8 @@
                         if (Math.abs((viewWidth * mInvActualScale)
                                 - mZoomOverviewWidth) > 1) {
                             setNewZoomScale((float) viewWidth
-                                    / mZoomOverviewWidth, true, false);
+                                    / mZoomOverviewWidth, Math.abs(mActualScale
+                                            - mTextWrapScale) < 0.01f, false);
                         }
                     }
                     break;
diff --git a/core/java/android/webkit/WebViewCore.java b/core/java/android/webkit/WebViewCore.java
index c3817fb..8ac915c 100644
--- a/core/java/android/webkit/WebViewCore.java
+++ b/core/java/android/webkit/WebViewCore.java
@@ -1518,7 +1518,7 @@
             } else if (mViewportWidth > 0) {
                 width = Math.max(w, mViewportWidth);
             } else {
-                width = Math.max(w, textwrapWidth);
+                width = textwrapWidth;
             }
         }
         nativeSetSize(width, width == w ? h : Math.round((float) width * h / w),
diff --git a/core/java/android/widget/TextView.java b/core/java/android/widget/TextView.java
index cdaba8a..876359d 100644
--- a/core/java/android/widget/TextView.java
+++ b/core/java/android/widget/TextView.java
@@ -6301,7 +6301,8 @@
             if (DEBUG_EXTRACT) Log.v(TAG, "beforeTextChanged start=" + start
                     + " before=" + before + " after=" + after + ": " + buffer);
 
-            if (AccessibilityManager.getInstance(mContext).isEnabled()) {
+            if (AccessibilityManager.getInstance(mContext).isEnabled()
+                    && !isPasswordInputType(mInputType)) {
                 mBeforeText = buffer.toString();
             }
 
@@ -6972,9 +6973,7 @@
 
     @Override
     public boolean dispatchPopulateAccessibilityEvent(AccessibilityEvent event) {
-        boolean isPassword =
-            (mInputType & (EditorInfo.TYPE_MASK_CLASS | EditorInfo.TYPE_MASK_VARIATION)) ==
-            (EditorInfo.TYPE_CLASS_TEXT | EditorInfo.TYPE_TEXT_VARIATION_PASSWORD);
+        final boolean isPassword = isPasswordInputType(mInputType);
 
         if (!isPassword) {
             CharSequence text = getText();
diff --git a/core/java/android/widget/ZoomButtonsController.java b/core/java/android/widget/ZoomButtonsController.java
index e55fbb8..c683e71 100644
--- a/core/java/android/widget/ZoomButtonsController.java
+++ b/core/java/android/widget/ZoomButtonsController.java
@@ -546,6 +546,11 @@
     public boolean onTouch(View v, MotionEvent event) {
         int action = event.getAction();
 
+        if (event.getPointerCount() > 1) {
+            // ZoomButtonsController doesn't handle mutitouch. Give up control.
+            return false;
+        }
+
         if (mReleaseTouchListenerOnUp) {
             // The controls were dismissed but we need to throw away all events until the up
             if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL) {
diff --git a/core/java/com/android/internal/widget/SlidingTab.java b/core/java/com/android/internal/widget/SlidingTab.java
index f07b2f1..940a2e5 100644
--- a/core/java/com/android/internal/widget/SlidingTab.java
+++ b/core/java/com/android/internal/widget/SlidingTab.java
@@ -148,20 +148,6 @@
         void onGrabbedStateChange(View v, int grabbedState);
     }
 
-    // TODO: For debugging; remove after glitches debugged.
-    @Override
-    protected void dispatchDraw(Canvas canvas) {
-        int orientation = getResources().getConfiguration().orientation;
-        if (mOrientation == HORIZONTAL && orientation != Configuration.ORIENTATION_PORTRAIT
-                || mOrientation == VERTICAL && orientation != Configuration.ORIENTATION_LANDSCAPE) {
-            // UBER HACK ALERT.  This is a workaround for a configuration race condition between
-            // orientation changed notification and the resize notification. This just prevents
-            // us from drawing under this circumstance, though the view will still be wrong.
-            return;
-        }
-        super.dispatchDraw(canvas);
-    }
-
     /**
      * Simple container class for all things pertinent to a slider.
      * A slider consists of 3 Views:
@@ -436,7 +422,7 @@
         /**
          * Start animating the slider. Note we need two animations since an Animator
          * keeps internal state of the invalidation region which is just the view being animated.
-         * 
+         *
          * @param anim1
          * @param anim2
          */
@@ -674,7 +660,7 @@
                     resetView();
                 }
                 anim.setAnimationListener(mAnimationDoneListener);
-                
+
                 /* Animation can be the same for these since the animation just holds */
                 mLeftSlider.startAnimation(anim, anim);
                 mRightSlider.startAnimation(anim, anim);
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 238ece1..7c7bfeb 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -46,7 +46,7 @@
 }
  
 static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) {
-    face->unref();
+    SkSafeUnref(face);
 }
 
 static int Typeface_getStyle(JNIEnv* env, jobject obj, SkTypeface* face) {
diff --git a/core/res/res/values-land/donottranslate.xml b/core/res/res/values-land/donottranslate.xml
new file mode 100644
index 0000000..75a7b06
--- /dev/null
+++ b/core/res/res/values-land/donottranslate.xml
@@ -0,0 +1,23 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+/* //device/apps/common/assets/res/any/strings.xml
+**
+** Copyright 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.
+*/
+-->
+<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
+    <!-- @hide DO NOT TRANSLATE. Workaround for resource race condition in lockscreen -->
+    <bool name="lockscreen_isPortrait">false</bool>
+</resources>
diff --git a/core/res/res/values/donottranslate.xml b/core/res/res/values/donottranslate.xml
index 6def3bf..78d4d36d 100644
--- a/core/res/res/values/donottranslate.xml
+++ b/core/res/res/values/donottranslate.xml
@@ -20,4 +20,6 @@
 <resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
     <!-- Default text encoding for WebSettings. -->
     <string name="default_text_encoding">Latin-1</string>
+    <!-- @hide DO NOT TRANSLATE. Workaround for resource race condition in lockscreen. -->
+    <bool name="lockscreen_isPortrait">true</bool>
 </resources>
diff --git a/docs/html/resources/community-groups.jd b/docs/html/resources/community-groups.jd
index 61fbcc8..a36a425 100644
--- a/docs/html/resources/community-groups.jd
+++ b/docs/html/resources/community-groups.jd
@@ -1,43 +1,65 @@
 community=true
-page.title=Android Developer Groups
+page.title=Developer Forums
 @jd:body
 
-<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in these discussions. Before posting, please read the <a href="http://source.android.com/discuss/android-discussion-groups-charter">Groups Charter</a> that covers the community guidelines.</p>
+<p>Welcome to the Android developers community! We're glad you're here and invite you to participate in discussions with other Android application developers on topics that interest you.</p>
 
-<p class="note"><strong>Note:</strong> If you are seeking discussion about Android source code (not application development),
-then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
+<p>The lists on this page are primarily for discussion about Android application development. If you are seeking discussion about Android source code (not application development), then please refer to the <a href="http://source.android.com/discuss">Open Source Project Mailing lists</a>.</p>
 
 <p style="margin-bottom:.5em"><strong>Contents</strong></p>
 <ol class="toc">
-  <li><a href="#BeforeYouPost">Before you post</a></li>
-  <li><a href="#ApplicationDeveloperLists">Application developer mailing lists</a></li>
-  <li><a href="#UsingEmail">Using email with the mailing lists</a></li>
+  <li><a href="#StackOverflow">Stack Overflow</a> <span class="new">new!</span></li>
+  <li><a href="#MailingLists">Mailing lists</a><ol>
+    <li><a href="#BeforeYouPost">Before you post</a></li>
+    <li><a href="#UsingEmail">Using email with the mailing lists</a></li>
+    <li><a href="#ApplicationDeveloperLists">Application developer mailing lists</a></li>
+  </ol></li>
+  <li><a href="#MarketHelp">Android Market Help Forum</a></li>
 </ol>
 
-<h2 id="BeforeYouPost">Before you post</h2>
+
+<h2 id="StackOverflow">Stack Overflow</h2>
+
+<p><a href="http://stackoverflow.com">Stack Overflow</a> is a collaboratively edited question and answer site for programmers. It's a great place to ask technical questions about developing and maintaining Android applications. The site is especially useful for asking questions with definite answers, but can also be used for discussing best practices.</p>
+
+<p>On the site, questions and answers relating to Android use the <a href="http://stackoverflow.com/questions/tagged/android">'android' tag</a>. You can look for Android topics by adding '<code>[android]</code>' to your search query, or by visiting the tag page at:</p>
+
+<p style="margin-left: 2em"><a href="http://stackoverflow.com/questions/tagged/android">http://stackoverflow.com/questions/tagged/android</a></p>
+
+<p>If you want to ask a question on Stack Overflow, you can use <a href="http://stackoverflow.com/questions/ask">this form</a>. Before submitting the form, make sure to add the 'android' tag so that other Android developers will be able to find your question. As always, before submitting a new question, take a look at the existing topics to see whether another developer has already asked or answered the question.</p>
+
+<p>If you are getting started with Android development, Stack Overflow may be a great location to ask questions about general Java programming or setting up the Eclipse development environment. Simply tag your questions with the <a href="http://stackoverflow.com/questions/tagged/java">Java</a> or <a href="http://stackoverflow.com/questions/tagged/eclipse">Eclipse</a> tags in these cases.</p>
+
+
+<h2 id="MailingLists">Mailing lists</h2>
+
+<p>There are a number of mailing lists, powered by <a href="http://groups.google.com">Google Groups</a>, available for discussing Android application development.</p>
+
+
+<h3 id="BeforeYouPost">Before you post</h3>
 <p>Before writing a post, please try the following:</p>
 
 <ol>
-<li><a href="{@docRoot}resources/faq/index.html">Read the FAQs</a> The most common questions about developing Android applications are addressed in this frequently updated list.</li>
-<li><strong>Type in keywords of your questions in the main Android site's search bar</strong> (such as the one above). This search encompasses all previous discussions, across all groups, as well as the full contents of the site, documentation, and blogs. Chances are good that somebody has run into the same issue before.</li>
-<li><b>Search the mailing list archives</b> to see whether your questions have already been discussed.
+
+<li>Look through the support information available in the 'More' section of this tab. You may find the answer to your question in the <a href="{@docRoot}resources/faq/commontasks.html">Common Tasks</a>, <a href="{@docRoot}resources/faq/troubleshooting.html">Troubleshooting Tips</a>, or <a href="{@docRoot}resources/faq/index.html">FAQs</a> sections.</li>
+<li>Type in keywords of your questions in the main Android site's search bar (such as the one above). This search encompasses all previous discussions, across all groups, as well as the full contents of the site, documentation, and blogs. Chances are good that somebody has run into the same issue before.</li>
   </li>
 </ol>
 
 <p>If you can't find your answer, then we encourage you to address the community.
 As you write your post, please do the following:
 <ol>
-<li><b>Read
-the <a href="http://sites.google.com/a/android.com/opensource/discuss/android-discussion-groups-charter">mailing list charter</a></b> that covers the community guidelines. 
+<li><strong>Read
+the <a href="http://source.android.com/discuss/android-discussion-groups-charter">mailing list charter</a></strong> that covers the community guidelines.
 </li>
-<li><b>Select the most appropriate mailing list for your question</b>. There are several different lists for 
+<li><strong>Select the most appropriate mailing list for your question</strong>. There are several different lists for
 developers, described below.</li>
 <li>
-    <b>Be very clear</b> about your question
+    <strong>Be very clear</strong> about your question
 in the subject -- it helps everyone, both those trying to answer your
 question as well as those who may be looking for information in the
 future.</li>
-<li><b>Give plenty of details</b> in your post to
+<li><strong>Give plenty of details</strong> in your post to
 help others understand your problem. Code or log snippets, as well as
 pointers to screenshots, may also be helpful. For a great guide to
 phrasing your questions, read <a href="http://www.catb.org/%7Eesr/faqs/smart-questions.html">How To Ask Questions The Smart Way</a>.
@@ -45,77 +67,48 @@
 </ol>
 
 
-<h2 id="ApplicationDeveloperLists">Application developer mailing lists</h2>
-<ul>
-<li><b>Android beginners</b> - You're new to Android application development. You want to figure out how to get started with the Android SDK and the basic Android APIs? Start here. This list is open to any discussion around beginner-type questions for developers using the SDK; this is a great way to get up and running with your new application on the Android platform. Ask about getting your development environment set up, get help with the first steps of Android development (your first User Interface, your first permission, your first file on the Android filesystem, your first app on the Android Market...). Be sure to check the archives first before asking new questions. Please avoid advanced subjects, which belong on android-developers, and user questions, which will get a better reception on android-discuss.
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-beginners">android-beginners</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-beginners-subscribe@googlegroups.com">android-beginners-subscribe@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android developers</b> - You're now an experienced Android application developer. You've grasped the basics of Android app development, you're comfortable using the SDK, now you want to move to advanced topics. Get help here with troubleshooting applications, advice on implementation, and strategies for improving your application's performance and user experience. This is the not the right place to discuss user issues (use android-discuss for that) or beginner questions with the Android SDK (use android-beginners for that).
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-developers">android-developers</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-developers-subscribe@googlegroups.com">android-developers-subscribe@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android discuss</b> - The "water cooler" of Android discussion. You can discuss just about anything Android-related here, ideas for the Android platform, announcements about your applications, discussions about Android devices, community resources... As long as your discussion is related to Android, it's on-topic here. However, if you have a discussion here that could belong on another list, you are probably not reaching all of your target audience here and may want to consider shifting to a more targeted list.
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-discuss">android-discuss</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-discuss-subscribe@googlegroups.com">android-discuss-subscribe@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android ndk</b> - A place for discussing the Android NDK and topics related to using native code in Android applications. 
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-ndk">android-ndk</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-ndk-subscribe@googlegroups.com">android-ndk-subscribe@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android security discuss</b> - A place for open discussion on secure development, emerging security concerns, and best practices for and by android developers. Please don't disclose vulnerabilities directly on this list, you'd be putting all Android users at risk.
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-security-discuss@googlegroups.com">android-security-discuss@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android security announce</b> - A low-volume group for security-related announcements by the Android Security Team.
-<ul>
-<li>Subscribe using Google Groups:&nbsp;<a href="http://groups.google.com/group/android-security-announce">android-security-announce</a></li>
-<li>Subscribe via email:&nbsp;<a href="mailto:android-security-announce-subscribe@googlegroups.com">android-security-announce-subscribe@googlegroups.com</a></li>
-</ul>
-</li>
-
-<li><b>Android Market Help Forum</b> - A web-based discussion forum where you can ask questions or report issues relating to Android Market.
-<ul>
-<li>URL:&nbsp;<a href="http://www.google.com/support/forum/p/Android+Market?hl=en">http://www.google.com/support/forum/p/Android+Market?hl=en</a></li>
-</ul>
-</li>
-
-</ul>
-
-
-
-<h2 id="UsingEmail">Using email with the mailing lists</h2>
+<h3 id="UsingEmail">Using email with the mailing lists</h3>
 <p>Instead of using the <a href="http://groups.google.com/">Google Groups</a> site, you can use your email client of choice to participate in the mailing lists.</p>
 <p>To subscribe to a group without using the Google Groups site, use the link under "subscribe via email" in the lists above.</p>
 <p>To set up how you receive mailing list postings by email:</p>
 
 <ol><li>Sign into the group via the Google Groups site. For example, for the android-framework group you would visit <a href="http://groups.google.com/group/android-framework">http://groups.google.com/group/android-framework</a>.</li>
-<li>Click "Edit
-my membership" on the right side.</li>
-<li>Under "How do
-you want to read this group?" select one of the email options. </li>
+<li>Click "Edit my membership" on the right side.</li>
+<li>Under "How do you want to read this group?" select one of the email options.</li>
 </ol>
 
 
+<h3 id="ApplicationDeveloperLists">Application developer mailing lists</h3>
+<ul>
+<li><strong><a href="http://groups.google.com/group/android-developers">android-developers</a></strong>
+(<a href="mailto:android-developers-subscribe@googlegroups.com">subscribe via email</a>)<br>
+You're now an experienced Android application developer. You've grasped the basics of Android app development, you're comfortable using the SDK, now you want to move to advanced topics. Get help here with troubleshooting applications, advice on implementation, and strategies for improving your application's performance and user experience. This is the not the right place to discuss user issues (use android-discuss for that) or beginner questions with the Android SDK (use android-beginners for that).
+</li>
+
+<li><strong><a href="http://groups.google.com/group/android-discuss">android-discuss</a></strong>
+(<a href="mailto:android-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+The "water cooler" of Android discussion. You can discuss just about anything Android-related here, ideas for the Android platform, announcements about your applications, discussions about Android devices, community resources... As long as your discussion is related to Android, it's on-topic here. However, if you have a discussion here that could belong on another list, you are probably not reaching all of your target audience here and may want to consider shifting to a more targeted list.
+</li>
+
+<li><strong><a href="http://groups.google.com/group/android-ndk">android-ndk</a></strong>
+(<a href="mailto:android-ndk-subscribe@googlegroups.com">subscribe via email</a>)<br>
+A place for discussing the Android NDK and topics related to using native code in Android applications.
+</li>
+
+<li><strong><a href="http://groups.google.com/group/android-security-discuss">android-security-discuss</a></strong>
+(<a href="mailto:android-security-discuss-subscribe@googlegroups.com">subscribe via email</a>)<br>
+A place for open discussion on secure development, emerging security concerns, and best practices for and by android developers. Please don't disclose vulnerabilities directly on this list, you'd be putting all Android users at risk.
+</li>
+
+<li><strong><a href="http://groups.google.com/group/android-security-announce">android-security-announce</a></strong>
+(<a href="mailto:android-security-announce-subscribe@googlegroups.com">subscribe via email</a>)<br>
+A low-volume group for security-related announcements by the Android Security Team.
+</li>
+</ul>
 
 
+<h2 id="MarketHelp">Android Market Help Forum</h2>
 
+<p>The <a href="http://www.google.com/support/forum/p/Android+Market">Android Market Help Forum</a> is a web-based discussion forum where you can ask questions or report issues relating to Android Market.</p>
 
-
-
-</div>
+<p style="margin-left: 2em"><a href="http://www.google.com/support/forum/p/Android+Market">http://www.google.com/support/forum/p/Android+Market</a></p>
diff --git a/docs/html/resources/resources_toc.cs b/docs/html/resources/resources_toc.cs
index f5c573e..e337e38 100644
--- a/docs/html/resources/resources_toc.cs
+++ b/docs/html/resources/resources_toc.cs
@@ -11,7 +11,7 @@
     </h2>
     <ul>
       <li><a href="<?cs var:toroot ?>resources/community-groups.html">
-            <span class="en">Android Developer Groups</span>
+            <span class="en">Developer Forums</span>
           </a></li>
       <li><a href="<?cs var:toroot ?>resources/community-more.html">
             <span class="en">IRC, Twitter</span>
diff --git a/docs/html/resources/tutorials/views/hello-autocomplete.jd b/docs/html/resources/tutorials/views/hello-autocomplete.jd
index fba1ad8..e26683d 100644
--- a/docs/html/resources/tutorials/views/hello-autocomplete.jd
+++ b/docs/html/resources/tutorials/views/hello-autocomplete.jd
@@ -1,56 +1,82 @@
-page.title=Hello, AutoCompleteTextView
+page.title=Auto Complete
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>{@link android.widget.AutoCompleteTextView} is an implementation of the EditText widget that will provide 
-auto-complete suggestions as the user types. The suggestions are extracted from a collection of strings.</p>
+<p>To create a text entry widget that provides auto-complete suggestions, use
+the {@link android.widget.AutoCompleteTextView} widget. Suggestions are received from a
+collection of strings associated with the widget through an {@link
+android.widget.ArrayAdapter}.</p>
+
+<p>In this tutorial, you will create a {@link android.widget.AutoCompleteTextView} widget that
+provides suggestions for a country name.</p>
 
 
 <ol>
-  <li>Start a new project/Activity called HelloAutoComplete.</li>
-  <li>Open the layout file.
-    Make it like so:
+  <li>Start a new project named <em>HelloAutoComplete</em>.</li>
+  <li>Create an XML file named <code>list_item.xml</code> and save it inside the
+<code>res/layout/</code> folder. Edit the file to look like this:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp"
+    android:textSize="16sp"
+    android:textColor="#000">
+&lt;/TextView>
+</pre>
+  <p>This file defines a simple {@link android.widget.TextView} that will be used for each
+item that appears in the list of suggestions.</p>
+  </li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" 
     android:orientation="horizontal"
     android:layout_width="fill_parent" 
-    android:layout_height="wrap_content">
-
+    android:layout_height="wrap_content"
+    android:padding="5dp">
     &lt;TextView
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Country" />
-
-    &lt;AutoCompleteTextView android:id="@+id/edit"
+    &lt;AutoCompleteTextView android:id="@+id/autocomplete_country"
         android:layout_width="fill_parent"
-        android:layout_height="wrap_content"/>
-
+        android:layout_height="wrap_content"
+        android:layout_marginLeft="5dp"/>
 &lt;/LinearLayout>
 </pre>
+  <p>The {@link android.widget.TextView} is a label that introduces the {@link
+android.widget.AutoCompleteTextView} widget.
 </li>
 
-<li>Open HelloAutoComplete.java and insert the following as the <code>onCreate</code> method:
+<li>Open <code>HelloAutoComplete.java</code> and insert the following code for the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 &#64;Override
 protected void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 
-    AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.edit);
-    ArrayAdapter<String> adapter = new ArrayAdapter<String>(this,
-            android.R.layout.simple_dropdown_item_1line, COUNTRIES);
+    AutoCompleteTextView textView = (AutoCompleteTextView) findViewById(R.id.autocomplete_country);
+    ArrayAdapter&lt;String> adapter = new ArrayAdapter&lt;String>(this, R.layout.list_item, COUNTRIES);
     textView.setAdapter(adapter);
 }
 </pre>
-	<p>Here, we create an AutoComplteteTextView from our layout. We then 
-	create an {@link android.widget.ArrayAdapter} that binds a <code>simple_dropdown_item_1line</code>
-	layout item to each entry in the <code>COUNTRIES</code> array (which we'll add next).
-	The last part sets the ArrayAdapter to associate with our AutoCompleteTextView.</p>
+
+<p>After the content view is set to the <code>main.xml</code> layout, the {@link
+android.widget.AutoCompleteTextView} widget is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. A new {@link
+android.widget.ArrayAdapter} is then initialized to bind the <code>list_item.xml</code> layout
+to each list item in the <code>COUNTRIES</code> string array (defined in the next step).
+Finally, {@link android.widget.AutoCompleteTextView#setAdapter(T) setAdapter()} is called to
+associate the {@link android.widget.ArrayAdapter} with the
+{@link android.widget.AutoCompleteTextView} widget so that the string array will populate
+the list of suggestions.</p>
 </li>
 
-<li>After the <code>onCreate()</code> method, add the String array:
+<li>Inside the <code>HelloAutoComplete</code> class, add the string array:
 <pre>
 static final String[] COUNTRIES = new String[] {
   "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
@@ -96,19 +122,50 @@
   "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
 };
 </pre>
-	<p>This is the list of suggestions that will be offered as the user types into the 
-	AutoCompleteTextView.</p>
+<p>This is the list of suggestions that will be provided in a drop-down list when the user types into
+the {@link android.widget.AutoCompleteTextView} widget.</p>
 </li>
 
-<li>Now run it.</li>
+<li>Run the application.</li>
 </ol>
 <p>As you type, you should see something like this:</p>
 <img src="images/hello-autocomplete.png" width="150px" />
 
 
+<h2>More Information</h2>
+
+<p>Note that using a hard-coded string array is not a recommended design practice because your
+application code should focus on behavior, not content. Application content such as strings
+should be externalized from the code in order to make modifications to the content easier and
+facilitate localization of the content. The hard-coded strings are used in this tutorial only to
+make it simple and focus on the {@link android.widget.AutoCompleteTextView} widget.
+Instead, your application should declare such string arrays in an XML file. This can be done
+with a {@code &lt;string-array&lt;} resource in your project {@code res/values/strings.xml} file.
+For example:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+    &lt;string-array name="countries_array">
+        &lt;item>Bahrain&lt;/item>
+        &lt;item>Bangladesh&lt;/item>
+        &lt;item>Barbados&lt;/item>
+        &lt;item>Belarus&lt;/item>
+        &lt;item>Belgium&lt;/item>
+        &lt;item>Belize&lt;/item>
+        &lt;item>Benin&lt;/item>
+    &lt;/string-array>
+&lt;/resources>
+</pre>
+<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
+{@link android.widget.ArrayAdapter} constructor line with the following:</p>
+<pre>
+String[] countries = getResources().getStringArray(R.array.countries_array);
+ArrayAdapter&lt;String> adapter = new ArrayAdapter&lt;String>(this, R.layout.list_item, countries);
+</pre>
+
+
 <h3>References</h3>
 <ul>
-	<li>{@link android.R.layout}</li>
 	<li>{@link android.widget.ArrayAdapter}</li>
 	<li>{@link android.widget.AutoCompleteTextView}</li>
 </ul>
diff --git a/docs/html/resources/tutorials/views/hello-datepicker.jd b/docs/html/resources/tutorials/views/hello-datepicker.jd
index fcd43f3..c50d650 100644
--- a/docs/html/resources/tutorials/views/hello-datepicker.jd
+++ b/docs/html/resources/tutorials/views/hello-datepicker.jd
@@ -1,52 +1,57 @@
-page.title=Hello, DatePicker
+page.title=Date Picker
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.DatePicker} is a widget that allows the user to select a month, day and year.</p>
+<p>To provide a widget for selecting a date, use the {@link android.widget.DatePicker}
+widget, which allows the user to select the month, day, and year, in a familiar interface.</p>
 
+<p>In this tutorial, you'll create a {@link android.app.DatePickerDialog}, which presents the
+date picker in a floating dialog box at the press of a button. When the date is set by
+the user, a {@link android.widget.TextView} will update with the new date.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloDatePicker.</li>
-  <li>Open the layout file and make it like so:
-    <pre>
+  <li>Start a new project named <em>HelloDatePicker</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
+<pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="vertical">
-
     &lt;TextView android:id="@+id/dateDisplay"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text=""/>
-
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text=""/>
     &lt;Button android:id="@+id/pickDate"
-            android:layout_width="wrap_content"
-            android:layout_height="wrap_content"
-            android:text="Change the date"/>
-
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="Change the date"/>
 &lt;/LinearLayout>
 </pre>
-	<p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
-	will display the date and a {@link android.widget.Button} that will initiate the DatePicker dialog.
-	With this layout, the TextView will sit above the Button.
-	The text value in the TextView is set empty, as it will be filled 
-	with the current date when our Activity runs.</p>
-    </li> 
+  <p>This creates a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
+  that will display the date and a {@link android.widget.Button} that will open the {@link
+  android.app.DatePickerDialog}.</p>
+  </li>
 
-  <li>Open HelloDatePicker.java. Insert the following to the HelloDatePicker class:
+  <li>Open <code>HelloDatePicker.java</code> and add the following members to the class:
 <pre>
     private TextView mDateDisplay;
     private Button mPickDate;
-
     private int mYear;
     private int mMonth;
     private int mDay;
 
     static final int DATE_DIALOG_ID = 0;
+</pre>
+  <p>The first group of members define variables for the layout {@link android.view.View}s and the
+date items. The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the {@link
+android.app.Dialog} that will display the date picker.</p>
+  </li>
 
-    &#64;Override
+  <li>Now add the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
+<pre>
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
         setContentView(R.layout.main);
@@ -68,43 +73,30 @@
         mMonth = c.get(Calendar.MONTH);
         mDay = c.get(Calendar.DAY_OF_MONTH);
 
-        // display the current date
+        // display the current date (this method is below)
         updateDisplay();
     }
 </pre>
-<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
-	<p>We start by instantiating variables for our Views and date fields.
-	The <code>DATE_DIALOG_ID</code> is a static integer that uniquely identifies the Dialog. In the
-	<code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements. 
-	Then we create an on-click listener for the Button, so that when it is clicked it will
-	show our DatePicker dialog. The <code>showDialog()</code> method  will pop-up the date picker dialog
-        by calling the <code>onCreateDialog()</code> callback method 
-        (which we'll define in the next section). We then create an
-	instance of {@link java.util.Calendar} and get the current year, month and day. Finally, we call 
-	<code>updateDisplay()</code>&mdash;our own method (defined later) that will fill the TextView.</p>
+
+<p>First, the content is set to the <code>main.xml</code> layout. Then the {@link
+android.widget.TextView} and {@link android.widget.Button} elements are captured from the layout
+with {@link android.app.Activity#findViewById(int)}. A
+new {@link android.view.View.OnClickListener} is created for the
+{@link android.widget.Button}, so that when it is clicked, it
+will call {@link android.app.Activity#showDialog(int)}, passing the unique integer ID for
+the date picker dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
+android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
+android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
+that should be displayed (which you'll
+define later). After the on-click listener is set, a new {@link java.util.Calendar} is created
+and the current year, month and day are acquired. Finally, the private
+<code>updateDisplay()</code> method is called in order to fill the {@link android.widget.TextView}
+with the current date.</p>
 </li>
 
-<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method 
-(which is called by <code>showDialog()</code>)
+<li>Add the <code>updateDisplay()</code> method:
 <pre>
-&#64;Override
-protected Dialog onCreateDialog(int id) {
-    switch (id) {
-    case DATE_DIALOG_ID:
-        return new DatePickerDialog(this,
-                    mDateSetListener,
-                    mYear, mMonth, mDay);
-    }
-    return null;
-}
-</pre>
-	<p>This method is passed the identifier we gave <code>showDialog()</code> and initializes
-	the DatePicker to the date we retrieved from our Calendar instance.</p>
-</li>
-
-<li>Following that, add the <code>updateDisplay()</code> method:
-<pre>
-    // updates the date we display in the TextView
+    // updates the date in the TextView
     private void updateDisplay() {
         mDateDisplay.setText(
             new StringBuilder()
@@ -114,9 +106,13 @@
                     .append(mYear).append(" "));
     }
 </pre>
-<p>This uses the member date values to write the date to our TextView.</p>
+<p>This method uses the member date values declared for the class to write the date to the layout's
+{@link android.widget.TextView}, {@code mDateDisplay}, which was also declared and initialized
+above.</p>
 </li>
-<li>Finally, add a listener that will be called when the user sets a new date:
+
+<li>Initialize a new {@link android.app.DatePickerDialog.OnDateSetListener} as a member of the
+<code>HelloDatePicker</code> class:
 <pre>
     // the callback received when the user "sets" the date in the dialog
     private DatePickerDialog.OnDateSetListener mDateSetListener =
@@ -131,19 +127,45 @@
                 }
             };
 </pre>
-	<p>This <code>OnDateSetListener</code> method listens for when the user is done setting the date
-        (clicks the "Set" button). At that time, this fires and we update our member fields with
-	the new date defined by the user and update our TextView by calling <code>updateDisplay()</code>.</p>
+<p>The {@link android.app.DatePickerDialog.OnDateSetListener} listens for when the user
+has set the date (by clicking the "Set" button). At that time, the {@link
+android.app.DatePickerDialog.OnDateSetListener#onDateSet(DatePicker,int,int,int) onDateSet()}
+callback method is called, which is defined to update the {@code mYear}, {@code mMonth}, and
+{@code mDay} member fields with the new date then call the private <code>updateDisplay()</code>
+method to update the {@link android.widget.TextView}.</p>
 </li>
 
-<li>Now run it.</li>
+<li>Now add the {@link android.app.Activity#onCreateDialog(int)} callback method to the {@code
+HelloDatePicker} class:
+<pre>
+&#64;Override
+protected Dialog onCreateDialog(int id) {
+    switch (id) {
+    case DATE_DIALOG_ID:
+        return new DatePickerDialog(this,
+                    mDateSetListener,
+                    mYear, mMonth, mDay);
+    }
+    return null;
+}
+</pre>
+<p>This is an {@link android.app.Activity} callback method that is passed the integer ID given to
+{@link android.app.Activity#showDialog(int)} (which is called by the button's {@link
+android.view.View.OnClickListener}). When the ID matches the switch case defined here, a {@link
+android.app.DatePickerDialog} is instantiated with the {@link
+android.app.DatePickerDialog.OnDateSetListener} created in the previous
+step, along with the date variables to initialize the widget date.</p>
+</li>
+
+<li>Run the application.</li>
 </ol>
 <p>When you press the "Change the date" button, you should see the following:</p>
 <img src="images/hello-datepicker.png" width="150px" />
 
 <h3>References</h3>
 <ul>
-<li>{@link android.widget.DatePicker}</li>
+<li>{@link android.app.DatePickerDialog}</li>
+<li>{@link android.app.DatePickerDialog.OnDateSetListener}</li>
 <li>{@link android.widget.Button}</li>
 <li>{@link android.widget.TextView}</li>
 <li>{@link java.util.Calendar}</li>
diff --git a/docs/html/resources/tutorials/views/hello-formstuff.jd b/docs/html/resources/tutorials/views/hello-formstuff.jd
index da4289c..3dd5f21 100644
--- a/docs/html/resources/tutorials/views/hello-formstuff.jd
+++ b/docs/html/resources/tutorials/views/hello-formstuff.jd
@@ -1,58 +1,99 @@
-page.title=Hello, Form Stuff
+page.title=Form Stuff
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>This page introduces a variety of widgets, like image buttons,
-text fields, checkboxes and radio buttons.</p>
+<p>This tutorial introduces a variety of widgets that are useful when creating forms, such as
+image buttons, text fields, checkboxes and radio buttons.</p>
 
 
 <ol>
-  <li>Start a new project/Activity called HelloFormStuff.</li>
-  <li>Your layout file should have a basic LinearLayout:
-    <pre>
+  <li>Start a new project named <em>HelloFormStuff</em>.</li>
+  <li>Your <code>res/layout/main.xml</code> file should already have a basic {@link
+android.widget.LinearLayout}:
+<pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:orientation="vertical"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent" >
-    	
 &lt;/LinearLayout>
 </pre>
-    <p>For each widget you want to add, just put the respective View inside here.</p>
-</li>
+    <p>For each widget you want to add, just put the respective View inside this {@link
+android.widget.LinearLayout}.</p>
+  </li>
 </ol>
-<p class="note"><strong>Tip:</strong> As you add new Android code, press Ctrl(or Cmd) + Shift + O 
-to import all needed packages.</p>
-
-
-<h2>ImageButton</h2>
-<p>A button with a custom image on it. 
-We'll make it display a message when pressed.</p>
-<ol>
-  <li><img src="images/android.png" align="right"/>
-	Drag the Android image on the right (or your own image) into the
-	res/drawable/ directory of your project. 
-        We'll use this for the button.</li>
-  <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ImageButton} element:
+<p>Each section below also assumes that your <code>HelloFormStuff</code> Activity has the following
+default implementation of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
 <pre>
-&lt;ImageButton
-    android:id="@+id/android_button"
-    android:layout_width="100dip"
-    android:layout_height="wrap_content"
-    android:src="@drawable/android" />	
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main);
+}
 </pre>
-	<p>The source of the button
-	is from the res/drawable/ directory, where we've placed the android.png.</p>
-        <p class="note"><strong>Tip:</strong> You can also reference some of the many built-in
-        images from the Android {@link android.R.drawable} resources, 
-        like <code>ic_media_play</code>, for a "play" button image. To do so, change the source
-        attribute to <code>android:src="@android:drawable/ic_media_play"</code>.</p>
-</li>
-<li>To make the button to actually do something, add the following
-code at the end of the <code>onCreate()</code> method:
+
+
+
+<h2>Custom Button</h2>
+
+<p>In this section, you will create a button with a custom image instead of text, using the {@link
+android.widget.Button} widget and an XML file that defines three different images to use for the
+different button states. When the button is pressed, a short message will be displayed.</p>
+
+<img src="images/android_pressed.png" style="float:right;" title="android_pressed.png"/>
+<img src="images/android_focused.png" style="float:right;clear:right;" title="android_focused.png"/>
+<img src="images/android_normal.png" style="float:right;clear:right;" title="android_normal.png"/>
+<ol>
+  <li>Copy the images on the right into the <code>res/drawable/</code> directory of
+your project. These will be used for the different button states.</li>
+  <li>Create a new file in the <code>res/drawable/</code> directory named
+<code>android_button.xml</code>.
+Insert the following XML:
 <pre>
-final ImageButton button = (ImageButton) findViewById(R.id.android_button);
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android">
+    &lt;item android:drawable="@drawable/android_pressed"
+          android:state_pressed="true" />
+    &lt;item android:drawable="@drawable/android_focused"
+          android:state_focused="true" />
+    &lt;item android:drawable="@drawable/android_normal" />
+&lt;/selector>
+</pre>
+  <p>This defines a single drawable resource, which will change its image based on the current
+state of the button. The first <code>&lt;item></code> defines
+<code>android_pressed.png</code> as the image when the button is pressed (it's been
+activated); the second <code>&lt;item></code> defines <code>android_focused.png</code> as the image
+when the button is focused (when the button is highlighted using the trackball or directional
+pad); and the third <code>&lt;item></code> defines <code>android_normal.png</code> as the image
+for the normal state (when neither pressed nor focused). This XML file now represents a single
+drawable resource and when referenced by a {@link android.widget.Button} for its background,
+the image displayed will change based on these three states.</p>
+  <p class="note"><strong>Note:</strong> The order of the <code>&lt;item></code> elements is
+important. When this drawable is referenced, the  <code>&lt;item></code>s are traversed in-order to
+determine which one is appropriate for the current button state. Because the "normal" image is last,
+it is only applied when the conditions <code>android:state_pressed</code> and
+<code>android:state_focused</code> have both evaluated false.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and add the {@link
+android.widget.Button} element:
+<pre>
+    &lt;Button
+        android:id="@+id/button"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="10dp"
+        android:background="@drawable/android_button" />
+</pre>
+	<p>The <code>android:background</code> attribute specifies the drawable resource to use for the
+button background (which, when saved at <code>res/drawable/android.xml</code>, is
+referenced as <code>@drawable/android</code>). This replaces the normal background image
+used for buttons throughout the system. In order for the drawable to change its image based on
+the button state, the image must be applied to the background.</p>
+</li>
+
+<li>To make the button do something when pressed, add the following
+code at the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
+<pre>
+final Button button = (Button) findViewById(R.id.button);
 button.setOnClickListener(new OnClickListener() {
     public void onClick(View v) {
         // Perform action on clicks
@@ -60,34 +101,41 @@
     }
 });
 </pre>
-<p>This captures our ImageButton from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the button is clicked. Here, we show a 
-{@link android.widget.Toast} message when clicked.</p>
+<p>This captures the {@link android.widget.Button} from the layout, then adds an {@link
+android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener}
+must implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to be made when the button is clicked. In this example, a
+{@link android.widget.Toast} message will be displayed.</p>
 </li>
-<li>Run it.</li>
+<li>Now run the application.</li>
 </ol>
 
 
 <h2>EditText</h2>
-<p>A text field for user input. We'll make it display the text entered so far when the "Enter" key is pressed.</p>
+
+<p>In this section, you will create a text field for user input, using the {@link
+android.widget.EditText} widget. Once text has been entered into the field, the "Enter" key will
+display the text in a toast message.</p>
 
 <ol>
-  <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.EditText} element:
+  <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.EditText}
+element (inside the {@link android.widget.LinearLayout}):
 <pre>
-&lt;EditText
-    android:id="@+id/edittext"
-    android:layout_width="fill_parent"
-    android:layout_height="wrap_content"/>
+    &lt;EditText
+        android:id="@+id/edittext"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"/>
 </pre>
 </li>
-<li>To do something with the text that the user enters, add the following code
-to the end of the <code>onCreate()</code> method:
+<li>To do something with the text that the user types, add the following code
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 final EditText edittext = (EditText) findViewById(R.id.edittext);
 edittext.setOnKeyListener(new OnKeyListener() {
     public boolean onKey(View v, int keyCode, KeyEvent event) {
-        if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
+        // If the event is a key-down event on the "enter" button
+        if ((event.getAction() == KeyEvent.ACTION_DOWN) &amp;&amp;
+            (keyCode == KeyEvent.KEYCODE_ENTER)) {
           // Perform action on key press
           Toast.makeText(HelloFormStuff.this, edittext.getText(), Toast.LENGTH_SHORT).show();
           return true;
@@ -96,37 +144,44 @@
     }
 });
 </pre>
-<p>This captures our EditText element from the layout, then adds an on-key listener to it.
-The {@link android.view.View.OnKeyListener} must define the <code>onKey()</code> method, which
-defines the action to be made when a key is pressed. In this case, we want to listen for the
-Enter key (when pressed down), then pop up a {@link android.widget.Toast} message with the 
-text from the EditText field. Be sure to return <var>true</var> after the event is handled, 
-so that the event doesn't bubble-up and get handled by the View (which would result in a
-carriage return in the text field).</p>
-<li>Run it.</li>
+<p>This captures the {@link android.widget.EditText} element from the layout and adds an {@link
+android.view.View.OnKeyListener}. The {@link android.view.View.OnKeyListener} must implement the
+{@link android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method, which
+defines the action to be made when a key is pressed while the widget has focus. In this case, the
+method is defined to listen for the Enter key (when pressed down), then pop up a {@link
+android.widget.Toast} message with the text that has been entered. The {@link
+android.view.View.OnKeyListener#onKey(View,int,KeyEvent)} method should always return
+<code>true</code> if the event has been handled, so that the event doesn't bubble-up (which would
+result in a carriage return in the text field).</p>
+</li>
+<li>Run the application.</li>
 </ol>
 
 
 <h2>CheckBox</h2>
-<p>A checkbox for selecting items. We'll make it display the the current state when pressed.</p>
+
+<p>In this section, you will create a checkbox for selecting items, using the {@link
+android.widget.CheckBox} widget. When the checkbox is pressed, a toast message will
+indicate the current state of the checkbox.</p>
 
 <ol>
-  <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.CheckBox} element:
+  <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.CheckBox}
+element (inside the {@link android.widget.LinearLayout}):
 <pre>
-&lt;CheckBox android:id="@+id/checkbox"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:text="check it out" />
+    &lt;CheckBox android:id="@+id/checkbox"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:text="check it out" />
 </pre>
 </li>
 <li>To do something when the state is changed, add the following code
-to the end of the <code>onCreate()</code> method:
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 final CheckBox checkbox = (CheckBox) findViewById(R.id.checkbox);
 checkbox.setOnClickListener(new OnClickListener() {
     public void onClick(View v) {
-        // Perform action on clicks
-        if (checkbox.isChecked()) {
+        // Perform action on clicks, depending on whether it's now checked
+        if (((CheckBox) v).isChecked()) {
             Toast.makeText(HelloFormStuff.this, "Selected", Toast.LENGTH_SHORT).show();
         } else {
             Toast.makeText(HelloFormStuff.this, "Not selected", Toast.LENGTH_SHORT).show();
@@ -134,52 +189,62 @@
     }
 });
 </pre>
-<p>This captures our CheckBox element from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the checkbox is clicked. Here, we query the current state of the
-checkbox, then pop up a {@link android.widget.Toast} message that displays the current state. 
-Notice that the CheckBox handles its own state change between checked and un-checked, so we just
-ask which it currently is.</p>
+<p>This captures the {@link android.widget.CheckBox} element from the layout, then adds an {@link
+android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must implement the
+{@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to be made when the checkbox is clicked. When clicked, {@link
+android.widget.CompoundButton#isChecked()} is called to check the new state of the check box. If it
+has been checked, then a {@link android.widget.Toast} displays the message "Selected", otherwise it
+displays "Not selected". Note that the {@link android.view.View} object that is passed in the {@link
+android.view.View.OnClickListener#onClick(View)} callback must be cast to a {@link
+android.widget.CheckBox} because the {@link android.widget.CompoundButton#isChecked()} method is
+not defined by the parent {@link android.view.View} class. The {@link android.widget.CheckBox}
+handles its own state changes, so you only need to query the current state.</p>
+</li>
 <li>Run it.</li>
 </ol>
-<p class="note"><strong>Tip:</strong> If you find that you need to change the state
-in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
 
 
 <h2>RadioButton</h2>
-<p>Two mutually-exclusive radio buttons&mdash;enabling one disables the other. 
-When each is pressed, we'll pop up a message.</p>
+
+<p>In this section, you will create two mutually-exclusive radio buttons (enabling one disables
+the other), using the {@link android.widget.RadioGroup} and {@link android.widget.RadioButton}
+widgets. When either radio button is pressed, a toast message will be displayed.</p>
 
 <ol>
-  <li>Open the layout file and, inside the LinearLayout, add two {@link android.widget.RadioButton}s,
-inside a {@link android.widget.RadioGroup}:
+  <li>Open the <code>res/layout/main.xml</code> file and add two {@link
+android.widget.RadioButton}s, nested in a {@link android.widget.RadioGroup} (inside the {@link
+android.widget.LinearLayout}):
 <pre>
-&lt;RadioGroup
-  android:layout_width="fill_parent"
-  android:layout_height="wrap_content"
-  android:orientation="vertical">
-  
-  &lt;RadioButton android:id="@+id/radio_red"
-      android:layout_width="wrap_content"
+    &lt;RadioGroup
+      android:layout_width="fill_parent"
       android:layout_height="wrap_content"
-      android:text="Red" />
-  
-  &lt;RadioButton android:id="@+id/radio_blue"
-      android:layout_width="wrap_content"
-      android:layout_height="wrap_content"
-      android:text="Blue" />
-  
-&lt;/RadioGroup>
+      android:orientation="vertical">
+      &lt;RadioButton android:id="@+id/radio_red"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="Red" />
+      &lt;RadioButton android:id="@+id/radio_blue"
+          android:layout_width="wrap_content"
+          android:layout_height="wrap_content"
+          android:text="Blue" />
+    &lt;/RadioGroup>
 </pre>
+<p>It's important that the {@link android.widget.RadioButton}s are grouped together by the {@link
+android.widget.RadioGroup} element so that no more than one can be selected at a time. This logic
+is automatically handled by the Android system. When one {@link android.widget.RadioButton} within
+a group is selected, all others are automatically deselected.</p>
 </li>
-<li>To do something when each is selected, we'll need an OnClickListener. Unlike the other 
-listeners we've created, instead of creating this one as an anonymous inner class, 
-we'll create it as a new object. This way, we can re-use the OnClickLIstener for 
-both RadioButtons. So, add the following code in the HelloFormStuff Activity
-(<em>outside</em> the <code>onCreate()</code> method):
+
+<li>To do something when each {@link android.widget.RadioButton} is selected, you need an
+{@link android.view.View.OnClickListener}. In this case, you want the listener to be re-usable, so
+add the following code to create a new member in the <code>HelloFormStuff</code> Activity:
 <pre>
-OnClickListener radio_listener = new OnClickListener() {
+private OnClickListener radio_listener = new OnClickListener() {
     public void onClick(View v) {
         // Perform action on clicks
         RadioButton rb = (RadioButton) v;
@@ -187,68 +252,124 @@
     }
 };
 </pre>
-<p>Our <code>onClick()</code> method will be handed the View clicked, so the first thing to do
-is cast it into a RadioButton. Then we pop up a 
-{@link android.widget.Toast} message that displays the selection.</p>
-<li>Now, at the bottom of the <code>onCreate()</code> method, add the following:
+<p>First, the {@link android.view.View} that is passed to the {@link
+android.view.View.OnClickListener#onClick(View)} method is cast into a RadioButton. Then a
+{@link android.widget.Toast} message displays the selected radio button's text.</p>
+<li>Now, at the bottom of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add
+the following:
 <pre>
   final RadioButton radio_red = (RadioButton) findViewById(R.id.radio_red);
   final RadioButton radio_blue = (RadioButton) findViewById(R.id.radio_blue);
   radio_red.setOnClickListener(radio_listener);
   radio_blue.setOnClickListener(radio_listener);
 </pre>
-<p>This captures each of the RadioButtons from our layout and adds the newly-created 
-OnClickListener to each.</p>
-<li>Run it.</li>
+<p>This captures each of the {@link android.widget.RadioButton}s from the layout and adds the
+newly-created {@link android.view.View.OnClickListener} to each.</p>
+<li>Run the application.</li>
 </ol>
-<p class="note"><strong>Tip:</strong> If you find that you need to change the state of a
-RadioButton in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>.</p>
+
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
 
 
 <h2>ToggleButton</h2>
-<p>A button used specifically for toggling something on and off.</p>
+
+<p>In this section, you'll create a button used specifically for toggling between two
+states, using the {@link android.widget.ToggleButton} widget. This widget is an excellent
+alternative to radio buttons if you have two simple states that are mutually exclusive ("on" and
+"off", for example).</p>
 
 <ol>
-  <li>Open the layout file and, inside the LinearLayout, add the {@link android.widget.ToggleButton} element:
+  <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.ToggleButton}
+element (inside the {@link android.widget.LinearLayout}):
 <pre>
-&lt;ToggleButton android:id="@+id/togglebutton"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content" />
+    &lt;ToggleButton android:id="@+id/togglebutton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:textOn="Vibrate on"
+        android:textOff="Vibrate off"/>
 </pre>
+  <p>The attributes <code>android:textOn</code> and <code>android:textOff</code> specify the text
+for the button when the button has been toggled on or off. The default values are "ON" and
+"OFF".</p>
 </li>
 <li>To do something when the state is changed, add the following code
-to the end of the <code>onCreate()</code> method:
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 final ToggleButton togglebutton = (ToggleButton) findViewById(R.id.togglebutton);
 togglebutton.setOnClickListener(new OnClickListener() {
     public void onClick(View v) {
         // Perform action on clicks
         if (togglebutton.isChecked()) {
-            Toast.makeText(HelloFormStuff.this, "ON", Toast.LENGTH_SHORT).show();
+            Toast.makeText(HelloFormStuff.this, "Checked", Toast.LENGTH_SHORT).show();
         } else {
-            Toast.makeText(HelloFormStuff.this, "OFF", Toast.LENGTH_SHORT).show();
+            Toast.makeText(HelloFormStuff.this, "Not checked", Toast.LENGTH_SHORT).show();
         }
     }
 });
 </pre>
-<p>This captures our ToggleButton element from the layout, then adds an on-click listener to it.
-The {@link android.view.View.OnClickListener} must define the <code>onClick()</code> method, which
-defines the action to be made when the button is clicked. Here, we query the current state of the
-ToggleButton, then pop up a {@link android.widget.Toast} message that displays the current state. 
-Notice that the ToggleButton handles its own state change between checked and un-checked, so we just
-ask which it is.</p>
-<li>Run it.</li>
+<p>This captures the {@link android.widget.ToggleButton} element from the layout, then adds an
+{@link android.view.View.OnClickListener}. The {@link android.view.View.OnClickListener} must
+implement the {@link android.view.View.OnClickListener#onClick(View)} callback method, which
+defines the action to perform when the button is clicked. In this example, the callback
+method checks the new state of the button, then shows a {@link android.widget.Toast} message that
+indicates the current state.</p>
+
+<p>Notice that the {@link android.widget.ToggleButton} handles its own state change between checked
+and unchecked, so you just ask which it is.</p>
+<li>Run the application.</li>
 </ol>
 
-<p class="note"><strong>Tip:</strong> By default, the text on the button is "ON" and "OFF", but 
-you can change each of these with <code>setTextOn(<var>CharSequence</var>)</code> and 
-<code>setTextOff(<var>CharSequence</var>)</code>. And, if you find that you need to change the state
-in another way (such as when loading a saved {@link android.preference.CheckBoxPreference}),
-use <code>setChecked(true)</code> or <code>toggle()</code>. </p>
+<p class="note"><strong>Tip:</strong> If you need to change the state
+yourself (such as when loading a saved {@link android.preference.CheckBoxPreference}),
+use the {@link android.widget.CompoundButton#setChecked(boolean)} or {@link
+android.widget.CompoundButton#toggle()} method.</p>
 
 
-<p>If you've added all the form items above, your application should look something like this:</p>
+
+
+<h2>RatingBar</h2>
+
+<p>In this section, you'll create a widget that allows the user to provide a rating,
+with the {@link android.widget.RatingBar} widget.</p>
+
+<ol>
+  <li>Open the <code>res/layout/main.xml</code> file and add the {@link android.widget.RatingBar}
+element (inside the {@link android.widget.LinearLayout}):
+<pre>
+    &lt;RatingBar android:id="@+id/ratingbar"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:numStars="5"
+        android:stepSize="1.0"/>
+</pre>
+  <p>The <code>android:numStars</code> attribute defines how many stars to display for the rating
+bar. The <code>android:stepSize</code> attribute defines the granularity for each
+star (for example, a value of <code>0.5</code> would allow half-star ratings). </p>
+</li>
+<li>To do something when a new rating has been set, add the following code
+to the end of the {@link android.app.Activity#onCreate(Bundle) onCreate()} method:
+<pre>
+final RatingBar ratingbar = (RatingBar) findViewById(R.id.ratingbar);
+ratingbar.setOnRatingBarChangeListener(new OnRatingBarChangeListener() {
+    public void onRatingChanged(RatingBar ratingBar, float rating, boolean fromUser) {
+        Toast.makeText(HelloFormStuff.this, "New Rating: " + rating, Toast.LENGTH_SHORT).show();
+    }
+});
+</pre>
+<p>This captures the {@link android.widget.RatingBar} widget from the layout with {@link
+android.app.Activity#findViewById(int)} and then sets an {@link
+android.widget.RatingBar.OnRatingBarChangeListener}. The {@link
+android.widget.RatingBar.OnRatingBarChangeListener#onRatingChanged(RatingBar,float,boolean)
+onRatingChanged()} callback method then defines the action to perform when the user sets a rating.
+In this case, a simple {@link android.widget.Toast} message displays the new rating.</p>
+
+<li>Run the application.</li>
+</ol>
+
+<p>If you've added all the form widgets above, your application should look like this:</p>
 <img src="images/hello-formstuff.png" width="150px" />
 
 <h3>References</h3>
@@ -258,5 +379,6 @@
 	<li>{@link android.widget.CheckBox}</li>
 	<li>{@link android.widget.RadioButton}</li>
 	<li>{@link android.widget.ToggleButton}</li>
+  <li>{@link android.widget.RatingBar}</li>
 </ul>
 
diff --git a/docs/html/resources/tutorials/views/hello-gallery.jd b/docs/html/resources/tutorials/views/hello-gallery.jd
index 084f912..12d5a91 100644
--- a/docs/html/resources/tutorials/views/hello-gallery.jd
+++ b/docs/html/resources/tutorials/views/hello-gallery.jd
@@ -1,16 +1,21 @@
-page.title=Hello, Gallery
+page.title=Gallery
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.Gallery} is a View commonly used to display items in a horizontally scrolling list
-that locks the current selection at the center. When one is selected, we'll show a message.</p>
+<p>{@link android.widget.Gallery} is a layout widget used to display items in a
+horizontally scrolling list and positions the current selection at the center of the view.</p>
+
+<p>In this tutorial, you'll create a gallery of photos and then display a toast message each time a
+gallery item is selected.</p>
 
 
 <ol>
-  <li>Start a new project/Activity called HelloGallery.</li>
-  <li>Add some images to your res/drawable/ directory.</li>
-  <li>Open the layout file and make it like so:
+  <li>Start a new project named <em>HelloGallery</em>.</li>
+  <li>Find some photos you'd like to use, or use these <a
+href="{@docRoot}shareables/sample_images.zip">sample images</a>. Save the images into the project's
+<code>res/drawable/</code> directory.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;Gallery xmlns:android="http://schemas.android.com/apk/res/android" 
@@ -19,10 +24,10 @@
     android:layout_height="wrap_content"
 />
 </pre>
-</li>
+  </li>
 
-
-<li>Open the HelloGallery.java file. Insert the following for the <code>onCreate()</code> method:
+<li>Open the <code>HelloGallery.java</code> file and insert the following code for the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 &#64;Override
 public void onCreate(Bundle savedInstanceState) {
@@ -39,19 +44,46 @@
     });
 }
 </pre>
-	<p>We start as usual: set the layout and capture the View we want (our Gallery). 
-We then set an Adapter, called ImageAdapter for the Gallery&mdash;this is a new class that
-we'll create next. Then we create an item click listener for the Gallery. This is like a normal
-on-click listener (which you might be familiar with for buttons), but it listens to each item
-that we've added to the Gallery. The <code>onItemClick()</code> callback method 
-receives the AdapterView where the click occurred, the specific View that received the click, the 
-position of the View clicked (zero-based), and the row id of the item clicked (if applicable). All
-that we care about is the position, so that we can pop up a {@link android.widget.Toast} message that 
-tells us the index position of the item clicked. We do this with <code>Toast.makeText().show()</code>.
+  <p>This starts by setting the {@code main.xml} layout as the content view and then capturing the
+{@link android.widget.Gallery} from
+the layout with {@link
+android.app.Activity#findViewById(int)}. A custom {@link android.widget.BaseAdapter} called
+<code>ImageAdapter</code> is
+instantiated and applied to the {@link android.widget.Gallery} with {@link
+android.widget.AdapterView#setAdapter(T) setAdapter()}. (The <code>ImageAdapter</code> class is
+defined next.)
+Then an anonymous {@link android.widget.AdapterView.OnItemClickListener} is instantiated. The
+{@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)}
+callback method receives the {@link android.widget.AdapterView} where the click occurred, the
+specific {@link android.view.View} that received the click, the
+position of the {@link android.view.View} clicked (zero-based), and the row ID of the item clicked
+(if applicable). In this example, all that's needed is the position of the click to show a {@link
+android.widget.Toast} message that says the position of the item, using 
+{@link android.widget.Toast#makeText(Context,CharSequence,int)} and {@link
+android.widget.Toast#show()} (in a real world scenario, this ID could be used to get the full sized
+image for some other task).
 </p>
 </li>
+  <li>Create a new XML file in the <code>res/values/</code> directory named <code>attrs.xml</code>.
+Insert the following:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+    &lt;declare-styleable name="HelloGallery">
+        &lt;attr name="android:galleryItemBackground" />
+    &lt;/declare-styleable>
+&lt;/resources>
+</pre>
+  <p>This is a custom styleable resource that can be applied to a layout. In this case, it will be
+applied to the individual items placed into the {@link android.widget.Gallery} widget. The
+<code>&lt;attr></code> element defines a specific attribute for the styleable, and in this case, it
+refers to an existing platform attribute, {@link android.R.attr#galleryItemBackground}, which
+defines a border styling for gallery items. In the next step, you'll
+see how this attribute is referenced and then later applied to each item in the gallery.</p>
+  </li>
 
-<li>After the <code>onCreate()</code> method, add the <code>ImageAdapter</code> class:
+  <li>Go back to the <code>HelloGallery.java</code> file. After the {@link
+  android.app.Activity#onCreate(Bundle)} method, define the custom <code>ImageAdapter</code> class:
 <pre>
 public class ImageAdapter extends BaseAdapter {
     int mGalleryItemBackground;
@@ -100,26 +132,30 @@
 }
 </pre>
 <p>First, there are a few member variables, including an array of IDs that reference
-the images we placed in our drawable resources directory.</p>
-<p>Next is the constructor, where we define the member Context. The rest of the constructor
-sets up a reference for our Gallery them, which adds the nice framing for each Gallery item.
-Once we have our <code>mGalleryItemBackground</code>, it's important to recycle the 
-StyledAttribute for later re-use.</p>
-<p>The next three methods are required for basic member queries. 
-But then we have the <code>getView()</code> method, which is called
-for each item read by our ImageAdapter, when the Gallery is being built. Here, we 
-use our member Context to create a new {@link android.widget.ImageView}. We then define
-the image resource with the current position of the Gallery items (corresponding to our
-array of drawables), set the dimensions for the ImageView, 
-set the image scaling to fit the ImageView dimensions, then finally set the 
-background theme for the ImageView.</p>
+the images saved in the drawable resources directory ({@code res/drawable/}).</p>
+<p>Next is the class constructor, where the {@link android.content.Context} for an {@code
+ImageAdapter} instance is defined and the styleable
+resource defined in the last step is acquired and saved to a local field. At the end of the
+constructor, {@link android.content.res.TypedArray#recycle()} is called on the {@link
+android.content.res.TypedArray} so it can be re-used by the system.</p>
+<p>The methods {@link android.widget.Adapter#getCount()}, {@link
+android.widget.Adapter#getItem(int)}, and {@link android.widget.Adapter#getItemId(int)} are methods
+that must be implemented for simple queries on the {@link android.widget.Adapter}.
+The {@link android.widget.Adapter#getView(int,View,ViewGroup) method does the
+work to apply an image to an {@link android.widget.ImageView} that will be embedded in the
+{@link android.widget.Gallery}. In this method, the member {@link android.content.Context} is used
+to create a new {@link android.widget.ImageView}. The {@link android.widget.ImageView} is prepared
+by applying an image from the local array of drawable resources, setting the {@link
+android.widget.Gallery.LayoutParams} height and width for the image, setting the scale to fit the
+{@link android.widget.ImageView} dimensions, and then finally setting the background to use the
+styleable attribute acquired in the constructor.</p>
 
-<p>See {@link android.widget.ImageView.ScaleType}
-for other image scaling options, in case you want to avoid stretching images that don't 
-exactly match the ImageView dimensions.</p>
+<p>See {@link android.widget.ImageView.ScaleType} for other image scaling options.</p>
+</li>
 
-<li>Now run it.</li>
+<li>Run the application.</li>
 </ol>
+
 <p>You should see something like this:</p>
 <img src="images/hello-gallery.png" width="150px" />
 
@@ -129,7 +165,7 @@
 	<li>{@link android.widget.BaseAdapter}</li>
 	<li>{@link android.widget.Gallery}</li>
 	<li>{@link android.widget.ImageView}</li>
-	<li>{@link android.widget.Toast}</li>
+  <li>{@link android.widget.AdapterView.OnItemClickListener}</li>
 </ul>
 
 
diff --git a/docs/html/resources/tutorials/views/hello-gridview.jd b/docs/html/resources/tutorials/views/hello-gridview.jd
index ffb6c93..03bfc54 100644
--- a/docs/html/resources/tutorials/views/hello-gridview.jd
+++ b/docs/html/resources/tutorials/views/hello-gridview.jd
@@ -1,33 +1,44 @@
-page.title=Hello, GridView
+page.title=Grid View
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.GridView} displays items in a two-dimensional, scrolling grid. The items
-are acquired from a {@link android.widget.ListAdapter}.</p>
+<p>{@link android.widget.GridView} is a {@link android.view.ViewGroup} that displays items in a
+two-dimensional,
+scrollable grid. The grid items are automatically inserted to the layout using a {@link
+android.widget.ListAdapter}.</p>
+
+<p>In this tutorial, you'll create a grid of image thumbnails. When an item is selected, a
+toast message will display the position of the image.</p>
 
 
 <ol>
-  <li>Start a new project/Activity called HelloGridView.</li>
-  <li>Find some photos you'd like to use, or copy some from the SDK samples res/drawable/ 
-    folder of your project.</li>
-  <li>Open the layout and make it like so:
+  <li>Start a new project named <em>HelloGridView</em>.</li>
+  <li>Find some photos you'd like to use, or <a
+href="{@docRoot}shareables/sample_images.zip">download these sample images</a>. Save the image files
+into the project's
+<code>res/drawable/</code> directory.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;GridView xmlns:android="http://schemas.android.com/apk/res/android" 
     android:id="@+id/gridview"
     android:layout_width="fill_parent" 
     android:layout_height="fill_parent"
+    android:columnWidth="90dp"
     android:numColumns="auto_fit"
     android:verticalSpacing="10dp"
     android:horizontalSpacing="10dp"
-    android:columnWidth="90dp"
     android:stretchMode="columnWidth"
     android:gravity="center"
 />
 </pre>
+  <p>This {@link android.widget.GridView} will fill the entire screen. The attributes are rather
+self explanatory. For more information about valid attributes, see the {@link
+android.widget.GridView} reference.</p>
 </li>
-  <li>Open the HelloGridView Java file. Insert the following for the <code>onCreate()</code> method:
+  <li>Open <code>HelloGridView.java</code> and insert the following code for the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
@@ -35,12 +46,33 @@
 
     GridView gridview = (GridView) findViewById(R.id.gridview);
     gridview.setAdapter(new ImageAdapter(this));
+
+    gridview.setOnItemClickListener(new OnItemClickListener() {
+        public void onItemClick(AdapterView&lt;?> parent, View v, int position, long id) {
+            Toast.makeText(HelloGridView.this, "" + position, Toast.LENGTH_SHORT).show();
+        }
+    });
 }
 </pre>
-    <p>Here, we get a handle on our GridView, from the layout, and give it an Adapter.
-    We're actually going to create our own Adapter called ImageAdapter.</p>
+  <p>After the {@code main.xml} layout is set for the content view, the
+{@link android.widget.GridView} is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. The {@link
+android.widget.GridView#setAdapter(T) setAdapter()} method then sets a custom adapter ({@code
+ImageAdapter}) as the source for all items to be displayed in the grid. The {@code ImageAdapter} is
+created in the next step.</p>
+<p>To do something when an item in the grid is clicked, the {@link
+android.widget.AdapterView#setOnItemClickListener(OnItemClickListener) setOnItemClickListener()}
+method is passed a new {@link android.widget.AdapterView.OnItemClickListener}. This anonymous
+instance defines the {@link
+android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
+onItemClick()} callback method to show a {@link android.widget.Toast} that displays the index
+position (zero-based) of the selected item (in a real world scenario, the position could be used to
+get the full sized
+image for some other task).</p>
+
 </li>
-<li>Create a new class (nested or otherwise), called ImageAdapter, which extends {@link android.widget.BaseAdapter}:
+<li>Create a new class called <code>ImageAdapter</code> that extends {@link
+android.widget.BaseAdapter}:
 <pre>
 public class ImageAdapter extends BaseAdapter {
     private Context mContext;
@@ -93,37 +125,58 @@
     };
 }
 </pre>
-    <p>First we take care of some required methods inherited from BaseAdapter.
-    The constructor and <code>getCount()</code> are self-explanitory. Normally, <code>getItem()</code>
-    should return the actual object at the specified position in our Adapter, but for this Hello World,
-    we're not going to bother. Likewise, <code>getItemId()</code> should return the row id of
-    the item, but right now we don't care.</p>
-    <p>However, <code>getView()</code> is the method we care about. This one creates a new View for each image that we
-    put in our ImageAdapter. So we're going to create an ImageView each time. When this is called, we're
-    going to receive a View, which is likely a recycled View object (at least after the first call), so we
-    check for this&mdash;if it's null, we initialize the ImageView and setup all the properties we want.
-    The <code>LayoutParams()</code> initialization sets the height and width of the View&mdash;this ensures
-    that no matter the drawable size, each image is resized and cropped to fit in the ImageView (if necessary).
-    With <code>setScaleType()</code>, we say that images should be cropped toward the center (if necessary).
-    And finally, we set the padding within the ImageView. (Note that, if the images have various aspect-ratios, 
-    as they do in this demo, then less padding will cause for more cropping of the image, if it does not match
-    the dimensions given to the ImageView.) At the end of <code>getView()</code> we set the image resource and 
-    return the ImageView.</p>
-    <p>All that's left is our array or drawable resources at the bottom.</p>
+<p>First, this implements some required methods inherited from {@link
+android.widget.BaseAdapter}. The constructor and {@link
+android.widget.Adapter#getCount()} are self-explanatory. Normally, {@link
+android.widget.Adapter#getItem(int)} should return the actual object at the specified position in
+the adapter, but it's ignored for this example. Likewise, {@link
+android.widget.Adapter#getItemId(int)} should return the row id of the item, but it's not
+needed here.</p>
+
+<p>The first method necessary is {@link android.widget.Adapter#getView(int,View,ViewGroup)
+getView()}. This method creates a new {@link android.view.View} for each image added to the {@code
+ImageAdapter}. When this is called, a {@link android.view.View} is passed in, which is normally a
+recycled object (at least after this has been called once), so there's a check to see if the
+object is null. If it <em>is</em> null, an {@link android.widget.ImageView} is instantiated and
+configured with desired properties for the image presentation:</p>
+<ul>
+  <li>{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)} sets
+the height and width for the View&mdash;this ensures that, no matter the size of the drawable, each
+image is resized and cropped to fit in these dimensions, as appropriate.</li>
+  <li>{@link android.widget.ImageView#setScaleType(ImageView.ScaleType)} declares that images should
+be cropped toward the center (if necessary).</li>
+  <li>{@link android.widget.ImageView#setPadding(int,int,int,int)} defines the padding for all
+sides. (Note that, if the images have different aspect-ratios, then less
+padding will cause for more cropping of the image if it does not match
+the dimensions given to the ImageView.)</li>
+</ul>
+
+<p>If the {@link android.view.View} passed to {@link
+android.widget.Adapter#getView(int,View,ViewGroup) getView()} is <em>not</em> null, then the local
+{@link android.widget.ImageView} is initialized with the recycled {@link android.view.View}
+object.</p>
+
+<p>At the end of the {@link android.widget.Adapter#getView(int,View,ViewGroup) getView()} method,
+the {@code
+position} integer passed into the method is used to select an image from the {@code mThumbIds}
+array, which is set as the image resource for the {@link android.widget.ImageView}.</p>
+<p>All that's left is to define the {@code mThumbIds} array of drawable resources.</p>
 </li>
-<li>Run it.</li>
+<li>Run the application.</li>
 </ol>
 <p>Your grid layout should look something like this:</p>
 <img src="images/hello-gridview.png" width="150px" />
 
-<p>Try experimenting with the behaviors of the GridView and ImageView by adjusting their properties. For example,
-  instead of setting the ImageView LayoutParams, you can try using
-  {@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
+<p>Try experimenting with the behaviors of the {@link android.widget.GridView} and {@link
+android.widget.ImageView} elements by adjusting their properties. For example, instead of using
+{@link android.view.View#setLayoutParams(ViewGroup.LayoutParams)}, try using
+{@link android.widget.ImageView#setAdjustViewBounds(boolean)}. </p>
 
 <h3>References</h3>
 <ul>
 	<li>{@link android.widget.GridView}</li>
 	<li>{@link android.widget.ImageView}</li>
 	<li>{@link android.widget.BaseAdapter}</li>
+  <li>{@link android.widget.AdapterView.OnItemClickListener}</li>
 </ul>
 
diff --git a/docs/html/resources/tutorials/views/hello-linearlayout.jd b/docs/html/resources/tutorials/views/hello-linearlayout.jd
index 0e8947c..8e072b1 100644
--- a/docs/html/resources/tutorials/views/hello-linearlayout.jd
+++ b/docs/html/resources/tutorials/views/hello-linearlayout.jd
@@ -1,16 +1,18 @@
-page.title=Hello, LinearLayout
+page.title=Linear Layout
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.LinearLayout} is a GroupView that will lay child View elements
-vertically or horizontally.</p>
+<p>{@link android.widget.LinearLayout} is a {@link android.view.ViewGroup} that displays child
+{@link android.view.View} elements in a linear direction, either vertically or horizontally.</p>
 
+<p>You should be careful about over-using the {@link android.widget.LinearLayout}. If you begin
+nesting multiple {@link android.widget.LinearLayout}s, you may want to consider using a {@link
+android.widget.RelativeLayout} instead.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloLinearLayout.</li>
-  <li>Open the layout file.
-    Make it like so:
+  <li>Start a new project named <em>HelloLinearLayout</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -18,113 +20,114 @@
     android:layout_width="fill_parent"
     android:layout_height="fill_parent">
 
-    &lt;LinearLayout
-	android:orientation="horizontal"
-	android:layout_width="fill_parent"
-	android:layout_height="fill_parent"
-	android:layout_weight="1">
+  &lt;LinearLayout
+      android:orientation="horizontal"
+      android:layout_width="fill_parent"
+      android:layout_height="fill_parent"
+      android:layout_weight="1">
+      &lt;TextView
+          android:text="red"
+          android:gravity="center_horizontal"
+          android:background="#aa0000"
+          android:layout_width="wrap_content"
+          android:layout_height="fill_parent"
+          android:layout_weight="1"/>
+      &lt;TextView
+          android:text="green"
+          android:gravity="center_horizontal"
+          android:background="#00aa00"
+          android:layout_width="wrap_content"
+          android:layout_height="fill_parent"
+          android:layout_weight="1"/>
+      &lt;TextView
+          android:text="blue"
+          android:gravity="center_horizontal"
+          android:background="#0000aa"
+          android:layout_width="wrap_content"
+          android:layout_height="fill_parent"
+          android:layout_weight="1"/>
+      &lt;TextView
+          android:text="yellow"
+          android:gravity="center_horizontal"
+          android:background="#aaaa00"
+          android:layout_width="wrap_content"
+          android:layout_height="fill_parent"
+          android:layout_weight="1"/>
+  &lt;/LinearLayout>
 	
-	&lt;TextView
-	    android:text="red"
-	    android:gravity="center_horizontal"
-	    android:background="#aa0000"
-	    android:layout_width="wrap_content"
-	    android:layout_height="fill_parent"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="green"
-	    android:gravity="center_horizontal"
-	    android:background="#00aa00"
-	    android:layout_width="wrap_content"
-	    android:layout_height="fill_parent"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="blue"
-	    android:gravity="center_horizontal"
-	    android:background="#0000aa"
-	    android:layout_width="wrap_content"
-	    android:layout_height="fill_parent"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="yellow"
-	    android:gravity="center_horizontal"
-	    android:background="#aaaa00"
-	    android:layout_width="wrap_content"
-	    android:layout_height="fill_parent"
-	    android:layout_weight="1"/>
-		
-    &lt;/LinearLayout>
-	
-    &lt;LinearLayout
-	android:orientation="vertical"
-	android:layout_width="fill_parent"
-	android:layout_height="fill_parent"
-	android:layout_weight="1">
-	
-	&lt;TextView
-	    android:text="row one"
-	    android:textSize="15pt"
-	    android:layout_width="fill_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="row two"
-	    android:textSize="15pt"
-	    android:layout_width="fill_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="row three"
-	    android:textSize="15pt"
-	    android:layout_width="fill_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_weight="1"/>
-	
-	&lt;TextView
-	    android:text="row four"
-	    android:textSize="15pt"
-	    android:layout_width="fill_parent"
-	    android:layout_height="wrap_content"
-	    android:layout_weight="1"/>
-        
-    &lt;/LinearLayout>
-        
+  &lt;LinearLayout
+    android:orientation="vertical"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:layout_weight="1">
+    &lt;TextView
+        android:text="row one"
+        android:textSize="15pt"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+    &lt;TextView
+        android:text="row two"
+        android:textSize="15pt"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+    &lt;TextView
+        android:text="row three"
+        android:textSize="15pt"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+    &lt;TextView
+        android:text="row four"
+        android:textSize="15pt"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_weight="1"/>
+  &lt;/LinearLayout>
+
 &lt;/LinearLayout>
 </pre>
-        <p>Carefully inspect the XML. You'll notice how this layout works a lot like
-        an HTML layout. There is one parent LinearLayout that is defined to lay
-        its child elements vertically. The first child is another LinearLayout that uses a horizontal layout
-        and the second uses a vertical layout. Each LinearLayout contains several {@link android.widget.TextView}
-        elements.</p>
+
+<p>Carefully inspect this XML. There is a root {@link android.widget.LinearLayout} that defines
+its orientation to be vertical&mdash;all child {@link android.view.View}s (of which it has two) will
+be stacked vertically. The first child is
+another {@link android.widget.LinearLayout} that uses a horizontal orientation and the second child
+is a {@link android.widget.LinearLayout} that uses a vertical orientation. Each of these nested
+{@link android.widget.LinearLayout}s contain several {@link android.widget.TextView} elements, which
+are oriented with each other in the manner defined by their parent {@link
+android.widget.LinearLayout}.</p>
 </li>
-<li>Now open the HelloLinearLayout Activity and be sure it loads this layout in the <code>onCreate()</code> method:</p>
+<li>Now open <code>HelloLinearLayout.java</code> and be sure it loads the
+<code>res/layout/main.xml</code> layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
 <pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 }
 </pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
 </li>
-<li>Run it.</li>
+<li>Run the application.</li>
 </ol>
 <p>You should see the following:</p>
 <img src="images/hello-linearlayout.png" width="150px" />
 
-<p>Notice how the various XML attributes define the View's behavior.
-Pay attention to the effect of the <code>layout_weight</code>. Try 
-	experimenting with different values to see how the screen real estate is 
-	distributed based on the weight of each element.</p>
+<p>Notice how the XML attributes define each View's behavior. Try
+experimenting with different values for <code>android:layout_weight</code> to see how the screen
+real estate is distributed based on the weight of each element. See the <a
+href="{@docRoot}guide/topics/ui/layout-objects.html#linearlayout">Common Layout Objects</a>
+document for more about how {@link android.widget.LinearLayout} handles the
+<code>android:layout_weight</code> attribute.</p>
 
 <h3>References</h3>
 <ul>
 	<li>{@link android.widget.LinearLayout}</li>
-<li>{@link android.widget.TextView}</li>
+  <li>{@link android.widget.TextView}</li>
 </ul>
 
 
diff --git a/docs/html/resources/tutorials/views/hello-listview.jd b/docs/html/resources/tutorials/views/hello-listview.jd
index d90005b..194258e 100644
--- a/docs/html/resources/tutorials/views/hello-listview.jd
+++ b/docs/html/resources/tutorials/views/hello-listview.jd
@@ -1,36 +1,88 @@
-page.title=Hello, ListView
+page.title=List View
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.ListView} is a View that shows items in a vertically scrolling list. The items are
- acquired from a {@link android.widget.ListAdapter}.</p>
+<p>{@link android.widget.ListView} is a {@link android.view.ViewGroup} that creates a list of
+scrollable items. The list items are automatically inserted to the list using a {@link
+android.widget.ListAdapter}.</p>
 
+<p>In this tutorial, you'll create a scrollable list of country names that are read from a string array.
+When a list item is selected, a toast message will display the position of the item in the list.</p>
 
 <ol>
-  <li>Start a new project/ListActivity called HelloListView.</li>
-  <li>Open the HelloListView Java file. Make the class extend ListActivity (instead of Activity).
+  <li>Start a new project named <em>HelloListView</em>.</li>
+  <li>Create an XML file named <code>list_item.xml</code> and save it inside the
+<code>res/layout/</code> folder. Insert the following:
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;TextView xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:padding="10dp"
+    android:textSize="16sp" >
+&lt;/TextView>
+</pre>
+  <p>This file defines the layout for each item that will be placed in the {@link
+android.widget.ListView}.</p>
+  </li>
+
+  <li>Open the <code>HelloListView.java</code> and make the class extend {@link
+android.app.ListActivity} (instead of {@link android.app.Activity}):
 	<pre>public class HelloListView extends ListActivity {</pre>
   </li>
-  <li>Insert the following for the <code>onCreate()</code> method:
+  <li>Insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
 <pre>
 &#64;Override
 public void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
-  
-  setListAdapter(new ArrayAdapter&lt;String>(this,
-          android.R.layout.simple_list_item_1, COUNTRIES));
-  getListView().setTextFilterEnabled(true);
+
+  setListAdapter(new ArrayAdapter&lt;String>(this, R.layout.list_item, COUNTRIES));
+
+  ListView lv = getListView();
+  lv.setTextFilterEnabled(true);
+
+  lv.setOnItemClickListener(new OnItemClickListener() {
+    public void onItemClick(AdapterView&lt;?> parent, View view,
+        int position, long id) {
+      // When clicked, show a toast with the TextView text
+      Toast.makeText(getApplicationContext(), ((TextView) view).getText(),
+          Toast.LENGTH_SHORT).show();
+    }
+  });
 }
 </pre>
-	<p>Notice that we don't need to load a layout (at least, not in this case, because we're using
-	the whole screen for our list). Instead, we just call <code>setListAdapter()</code> (which automatically
-	adds a ListView to the ListActivity), and provide it with an ArrayAdapter that binds a 
-	<code>simple_list_item_1</code> layout item to each entry in the <code>COUNTRIES</code> 
-	array (added next). The next line of code adds a text filter to the ListView, so that when the user
-	begins typing, the list will filter the entire view to display only the items that match the entry.</p>
-  </li>
-  <li>Following the <code>onCreate()</code> method, add the String array:
+<p>Notice that this does not load a layout file for the Activity (which you usually do with {@link
+android.app.Activity#setContentView(int)}). Instead, {@link
+android.app.ListActivity#setListAdapter(ListAdapter)} automatically
+adds a {@link android.widget.ListView} to fill the entire screen of the {@link
+android.app.ListActivity}. This method takes an {@link android.widget.ArrayAdapter}, which
+manages the array of list items that will be placed into the {@link android.widget.ListView}. The
+{@link android.widget.ArrayAdapter} constructor takes the application {@link
+android.content.Context}, the
+layout description for each list item (created in
+the previous step), and a {@link java.util.List} of objects to insert in the {@link
+android.widget.ListView} (defined next).</p>
+
+<p>The {@link android.widget.ListView#setTextFilterEnabled(boolean)} method turns on text filtering
+for the {@link android.widget.ListView}, so that when the user begins typing, the list will be
+filtered.</p>
+
+<p>The {@link android.widget.ListView#setOnItemClickListener(OnItemClickListener)} method defines
+the on-click listener for each item. When an item in the {@link android.widget.ListView} is clicked,
+the {@link android.widget.AdapterView.OnItemClickListener#onItemClick(AdapterView,View,int,long)
+onItemClick()} method is called and a {@link android.widget.Toast} message is displayed, using the
+text from the clicked item.</p>
+
+<p class="note"><strong>Tip:</strong> You can use list item designs provided by the platform
+instead of defining your own layout file for the {@link android.widget.ListAdapter}. For example,
+try using <code>android.R.layout.simple_list_item_1</code> instead of
+<code>R.layout.list_item</code>.</p>
+</li>
+
+<li>After the {@link android.app.Activity#onCreate(Bundle) onCreate()} method, add the string
+array:
 <pre>
   static final String[] COUNTRIES = new String[] {
     "Afghanistan", "Albania", "Algeria", "American Samoa", "Andorra",
@@ -76,12 +128,40 @@
     "Yemen", "Yugoslavia", "Zambia", "Zimbabwe"
   };
 </pre>
+  <p>This is the array of strings that will be placed into the {@link android.widget.ListView}.</p>
 </li>
-<li> Run it.</li>
+<li>Run the application.</li>
 </ol>
-<p>You can scroll the list, or type to filter it. You should see something like this:</p>
+<p>You can scroll the list, or type to filter it, then click an item to see a message. You
+should see something like this:</p>
 <img src="images/hello-listview.png" width="150px" />
 
+<p>Note that using a hard-coded string array is not the best design practice. One is
+used in this tutorial for simplicity, in order to demonstrate the {@link
+android.widget.ListView} widget. The better practice is to reference a string array
+defined by an external resource, such as with a {@code &lt;string-array&gt;}
+resource in your project {@code res/values/strings.xml} file. For example:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;resources>
+    &lt;string-array name="countries_array">
+        &lt;item>Bahrain&lt;/item>
+        &lt;item>Bangladesh&lt;/item>
+        &lt;item>Barbados&lt;/item>
+        &lt;item>Belarus&lt;/item>
+        &lt;item>Belgium&lt;/item>
+        &lt;item>Belize&lt;/item>
+        &lt;item>Benin&lt;/item>
+    &lt;/string-array>
+&lt;/resources>
+</pre>
+<p>To use these resource strings for the {@link android.widget.ArrayAdapter}, replace the original
+{@link android.app.ListActivity#setListAdapter(ListAdapter)} line with the following:</p>
+<pre>
+String[] countries = getResources().getStringArray(R.array.countries_array);
+setListAdapter(new ArrayAdapter&lt;String>(this, R.layout.list_item, countries));
+</pre>
+
 <h3>References</h3>
 <ul>
 	<li>{@link android.widget.ListView}</li>
diff --git a/docs/html/resources/tutorials/views/hello-mapview.jd b/docs/html/resources/tutorials/views/hello-mapview.jd
index 531300f..a3fa548 100644
--- a/docs/html/resources/tutorials/views/hello-mapview.jd
+++ b/docs/html/resources/tutorials/views/hello-mapview.jd
@@ -1,273 +1,303 @@
-page.title=Hello, MapView 
+page.title=Google Map View
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
+<p>Using the Google Maps library, you can create your own map-viewing Activity. In this
+tutorial, you'll create a simple map application in two parts. In Part 1, you'll create an app that
+shows a map the user can pan and zoom. In Part 2, you'll add overlay items that mark
+points of interest.</p>
+
 <div class="special">
-<p>This tutorial requires that you have the Google Maps external library
-installed in your SDK environment. By default the Android SDK includes the
-Google APIs add-on, which in turn includes the Maps external library. If you
-don't have the Google APIs SDK add-on, you can download it from this
-location:</p>
+<p>This tutorial requires that you have the external Google Maps library
+installed in your SDK environment. The Maps library is included with the Google APIs
+add-on, which you can install using the Android SDK and
+AVD Manager. To learn how, see <a href="{@docRoot}sdk/adding-components.html">Adding SDK
+Components</a>.</p>
 
-<p style="margin-left:2em;"><a
-href="http://code.google.com/android/add-ons/google-apis">http://code.google.com/android/add-ons/google-apis</a></p>
-
-<p>The Google APIs add-on requires Android 1.5 SDK or later release. After
-installing the add-on in your SDK, set your project properties to use the build
-target called "Google APIs Add-on". See the instructions for setting a build
+<p>After installing the Google APIs add-on in your SDK, set your project properties to use the build
+target called "Google APIs by Google Inc.". See the instructions for setting a build
 target in <a href="{@docRoot}guide/developing/eclipse-adt.html">Developing in
 Eclipse with ADT</a> or <a
 href="{@docRoot}guide/developing/other-ide.html">Developing in Other IDEs</a>,
 as appropriate for your environment. </p>
 
-<p>You will also need to use the android tool to set up an AVD that uses the
-Google APIs deployment target. See <a
+<p>You will also need to set up a new AVD that uses the same Google APIs deployment target. See <a
 href="{@docRoot}guide/developing/tools/avd.html">Android Virtual Devices</a> for
-more information. Once you have set up your environment, you will be able to
-build and run the project described in this tutorial</a></p>
+more information.</p>
+
+<p>For reference material, see the <a
+href="http://code.google.com/android/add-ons/google-apis/reference/index.html">Google Maps
+library documentation</a>.</p>
 
 </div>
 
-<p>A MapView allows you to create your own map-viewing Activity. 
-First, we'll create a simple Activity that can view and navigate a map. Then we will add some overlay items.</p>
+<h2>Part 1: Creating a Map Activity</h2>
 
 <ol>
-  <li>Start a new project/Activity called HelloMapView.
+  <li>Start a new project named <em>HelloGoogleMaps</em>.</li>
 
-   <li>Because we're using the Google Maps library, 
-   which is not a part of the standard Android library, we need to 
-   declare it in the Android Manifest. Open the AndroidManifest.xml 
-   file and add the following as a child of the <code>&lt;application></code> element:
+  <li>Because the Maps library is not a part of the standard Android library, you must
+  declare it in the Android Manifest. Open the <code>AndroidManifest.xml</code>
+  file and add the following as a child of the <code>&lt;application></code> element:
+  <pre>&lt;uses-library android:name="com.google.android.maps" /></pre>
+  </li>
 
-    <pre>&lt;uses-library android:name="com.google.android.maps" /></pre>
-      </li>
-   <li>We also need access to the internet in order to retrieve the Google Maps tiles,
-    so the application must request the {@link android.Manifest.permission#INTERNET INTERNET} permissions.
-    In the manifest file, add the following as a child of the <code>&lt;manifest></code> element:
-    <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
-   </li>
-   <li>Now open the main layout file for your project. Define a layout with a com.google.android.maps.MapView 
-    inside a android.widget.RelativeLayout:
+  <li>You also need access to the Internet in order to retrieve map tiles,
+  so you must also request the {@link android.Manifest.permission#INTERNET} permission.
+  In the manifest file, add the following as a child of the <code>&lt;manifest></code> element:
+  <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre>
+  </li>
 
+  <li>While you're in the manifest, give the map some more space by getting rid of the title bar
+with the "NoTitleBar" theme:
+<pre>
+&lt;activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
+     <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>&gt;
+</pre>
+  </li>
+
+
+  <li>Open the <code>res/layout/main.xml</code> file and add a single
+  {@code com.google.android.maps.MapView} as the root node:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
-&lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:id="@+id/mainlayout"
-    android:orientation="vertical"
+&lt;com.google.android.maps.MapView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:id="@+id/mapview"
     android:layout_width="fill_parent"
-    android:layout_height="fill_parent" >
-
-    &lt;com.google.android.maps.MapView
-        android:id="@+id/mapview"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-        android:clickable="true"
-        android:apiKey="<em>Your Maps API Key</em>"
-    />
-
-&lt;/RelativeLayout>
+    android:layout_height="fill_parent"
+    android:clickable="true"
+    android:apiKey="<em>Your Maps API Key goes here</em>"
+/>
 </pre>
-      <p>The <code>clickable</code> attribute defines whether you want to allow user-interaction with the map.
-      In this case, we set it "true" so that the user can navigate.</p>
+    <p>The <code>android:clickable</code> attribute defines whether you want to allow
+    user-interaction with the map. If this is "false" then touching the map does nothing.</p>
 
-      <p>The <code>apiKey</code> attribute holds the Google Maps API Key that proves your application and signer
-      certificate has been registered with the Google Maps service. Because MapView uses Google Maps data, this key is required
-      in order to receive the map data, even while you are developing. Registration is free and it only takes a couple
-      minutes to register your certificate and receive a Maps API Key. For instructions on getting a key, read
-      <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API Key</a>.
-      (For the purpose of this tutorial, you should register with the fingerprint of the SDK debug certificate.)
-      Once you've acquired the Maps API Key, insert it for the <code>apiKey</code> value.</p></li>
+    <p>The <code>android:apiKey</code> attribute holds the Maps API Key for your
+    application, which proves your application and signer certificate has been registered with the
+    Maps service. This is required in order to receive the map data, even while you are
+    developing. Registration to the service is free and it only takes a couple
+    minutes to register your certificate and get a Maps API Key.</p>
+    <p>Go now to get a key. For instructions, read
+    <a href="http://code.google.com/android/add-ons/google-apis/mapkey.html">Obtaining a Maps API
+    Key</a>. For the purpose of this tutorial, you should <a
+  href="http://code.google.com/android/add-ons/google-apis/mapkey.html#getdebugfingerprint">register
+    with the SDK debug certificate</a>, which will only be valid while your application is signed
+    with the debug key (once you sign with your private key, you will need a new API key).
+    When you get your key, insert it for the value of <code>android:apiKey</code>.</p>
+  </li>
 
-   <li>Now open the HelloMapView.java file. For this Activity, we're going to extend the special sub-class of 
-      Activity called MapActivity, so change the class declaration to extend 
-      MapActicity, instead of Activity:</p>
+  <li>Now open the <code>HelloGoogleMaps.java</code> file. For this Activity, extend
+  {@code MapActivity} (instead of {@code android.app.Activity}):</p>
 
-      <pre>public class HelloMapView extends MapActivity {</pre>
+    <pre>public class HelloGoogleMaps extends MapActivity {</pre>
+    <p>This is a special sub-class of {@link android.app.Activity}, provided by the Maps
+    library, which provides important map capabilities.</p>
 
-   <li>The <code>isRouteDisplayed()</code> method is required, so add it inside the class:
+   <li>Inside every {@code MapActivity}, the <code>isRouteDisplayed()</code> method is required, so
+    override this method:
 <pre>
 &#64;Override
 protected boolean isRouteDisplayed() {
     return false;
 }
 </pre>
-<p>You can actually run this now, but all it does is allow you to pan around the map.</p>
-<p>Android provides a handy {@link android.widget.ZoomControls} widget for zooming in and out of a View. 
-MapView can automatically hook one for us by requesting it with the <code>getZoomControls()</code>
-method. Let's do this.</p>
+<p>This method is required for some accounting from the Maps service to see if you're currently
+displaying any route information. In this case, you're not, so return false.</p>
+</li>
 
-<li>Go back to the layout file. We need a new ViewGroup element, in which we'll 
-   place the ZoomControls. Just below the MapView element (but inside the RelativeLayout), add this element:
+<li>Now add the standard {@link android.app.Activity#onCreate(Bundle) onCreate()} callback method
+to the class:
 <pre>
-&lt;LinearLayout
-    android:id="@+id/zoomview"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:layout_alignBottom="@id/mapview"
-    android:layout_centerHorizontal="true"
-/></pre>
-
-      <p>It doesn't really matter what kind of ViewGroup we use, because we just want a 
-      container that we can position within our root RelativeLayout.</p>
-
-      <p>The last two attributes are available only to an element that's a child of a 
-      RelativeLayout. <code>layout_alignBottom</code> aligns the bottom of this element to the bottom of 
-      the element identified with a resource tag (which must be a sibling to this element). 
-      <code>layout_centerHorizontal</code> centers this on the horizontal plane.</p></li>
-
-   <li>Now go back to the HelloMapView class. We'll now retrieve the ZoomControls object from 
-   the MapView and add it to our new layout element. First, at the top of the HelloMapView, 
-   instantiate handles for the MapView and LinearLayout, plus a ZoomControl object:
+&#64;Override
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main);
+}
+</pre>
+<p>This loads the layout file created above. In fact, this is now a workable application that will
+display map tiles and allow the user to pan around the map. But there's no ability to zoom.
+Fortunately, there's a very simple zoom feature built into the {@code MapView} class, which you can
+summon with {@code setBuiltInZoomControls(boolean)}. Do this at the end of the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
 <pre>
-LinearLayout linearLayout;
-MapView mapView;
-ZoomControls mZoom;</pre></li>
-
-   <li>Then initialize each of these in <code>onCreate()</code>. We'll capture the LinearLayout and 
-   MapView through their layout resources. Then get the ZoomControls from the MapView::
-<pre>
-linearLayout = (LinearLayout) findViewById(R.id.zoomview);
-mapView = (MapView) findViewById(R.id.mapview);
-mZoom = (ZoomControls) mapView.getZoomControls();</pre>
-
-      <p>By using the ZoomControls object provided by MapView, we don't have to do any of the work
-      required to actually perform the zoom operations. The ZoomControls widget that MapView 
-      returns for us is already hooked into the MapView and works as soon as we add it to the 
-      layout. The controls will appear whenever the user touches the map, then dissapear after 
-      a few moments of inactivity.</p></li>
-
-   <li>Now just plug our ZoomControls into the LinearLayout we added:
-
-      <pre>linearLayout.addView(mZoom);</pre></li>
-
-   <li>Run it.</li>
+    MapView mapView = (MapView) findViewById(R.id.mapview);
+    mapView.setBuiltInZoomControls(true);
+</pre>
+</li>
+<li>That's all there is to it. Run the application. (Remember, you must have an <a
+href="{@docRoot}guide/developing/tools/avd.html">AVD</a> configured to use the Google APIs
+target, or be using a development device that includes the Maps library.)</li>
 </ol>
 
-<hr/>
+<h2>Part 2: Adding Overlay Items</h2>
 
-<p>So, we now have full interaction controls. All well and good, but what we really want our map 
-for is custom markers and layovers. Let's add some Overlay 
-objects to our map. To do this, we're going to 
-implement the ItemizedOverlay
-class, which can manage a whole set of Overlay items for us.</p>
+<p>So, now you have a map, but in many cases you'll also want to create your own map
+markers and lay-overs. That's what you'll do now. In order to do so, you must implement the
+{@code ItemizedOverlay} class, which can manage a whole set of {@code Overlay} (which are the
+individual items placed on the map).</p>
 
 <ol>   
-  <li>Create a new Java class named HelloItemizedOverlay that implements ItemizedOverlay.
+  <li>Create a new Java class named <code>HelloItemizedOverlay</code> that implements
+  {@code ItemizedOverlay}.
 
-      <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and select New > Class. Fill-in 
-      the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter 
-      <em>com.google.android.maps.ItemizedOverlay</em>. Click the checkbox for <em>Constructors from 
-      superclass</em>. Click Finish.</p></li>
+    <p>When using Eclipse, right-click the package name in the Eclipse Package Explorer, and
+    select <strong>New > Class</strong>. Fill-in
+    the Name field as <em>HelloItemizedOverlay</em>. For the Superclass, enter
+    "com.google.android.maps.ItemizedOverlay". Click the checkbox for <em>Constructors from
+    superclass</em>. Click Finish.</p></li>
 
-  <li> First thing, we need an OverlayItem ArrayList, in which we'll put each of the OverlayItem 
-   objects we want on our map. Add this at the top of the HelloItemizedOverlay class:
+  <li>First, you need an <code>OverlayItem</code> {@link java.util.ArrayList}, in which you'll put
+  each of the <code>OverlayItem</code> objects you want on the map. Add this at the top of the
+  <code>HelloItemizedOverlay</code> class:
 
-      <pre>private ArrayList&lt;OverlayItem> mOverlays = new ArrayList&lt;OverlayItem>();</pre></li>
+      <pre>private ArrayList&lt;OverlayItem> mOverlays = new ArrayList&lt;OverlayItem>();</pre>
+  </li>
 
-   <li>All the constructor does is define the default marker to be used on each of the OverlayItems. 
-   In order for the Drawable to actually get drawn, it must have its bounds defined. And we want the 
-   center-point at the bottom of the image to be the point at which it's attached to the map 
-   coordinates. We handle all this with the boundCenterBottom() method. Wrap this around our 
-   defaultMarker, so the super constructor call looks like this:
+  <li>Now define the <code>HelloItemizedOverlay</code> constructors. The constructor must
+  define the default marker for each of the {@code OverlayItem}s. In order for the {@link
+  android.graphics.drawable.Drawable} to actually get drawn, it must have its bounds  defined. Most
+  commonly, you want the center-point at the bottom of the image to be the point at which it's
+  attached to the map coordinates. This is handled for  you with the {@code boundCenterBottom()}
+  method. Wrap this  around our defaultMarker, so the super  constructor call looks like this:
+<pre>
+public HelloItemizedOverlay(Drawable defaultMarker) {
+  super(boundCenterBottom(defaultMarker));
+}
+</pre>
+  </li>
 
-      <pre>super(boundCenterBottom(defaultMarker));</pre></li>
-
-   <li>In order to add new OverlayItems to our ArrayList, we need a new public method. We'll handle 
-   this with the following method:
-
+  <li>In order to add new {@code OverlayItem}s to the ArrayList, you need a new method:
 <pre>
 public void addOverlay(OverlayItem overlay) {
     mOverlays.add(overlay);
     populate();
 }</pre>
+    <p>Each time you add a new {@code OverlayItem} to the ArrayList, you must call
+    <code>populate()</code> for the {@code ItemizedOverlay}, which will read each of the
+    {@code OverlayItem}s and prepare them to be drawn.</p>
+  </li>
 
-      <p>Each time we add a new OverlayItem, we must call <code>populate()</code>, which will read each of out 
-      OverlayItems and prepare them to be drawn.</p></li>
-
-   <li>In order for the <code>populate()</code> method to read each OverlayItem, it will make a request to 
-   <code>createItem(int)</code>. We must define this method to properly read from our ArrayList. Replace the 
-   existing contents of the createItem method with a <code>get()</code> call to our ArrayList:
+  <li>When the <code>populate()</code> method executes, it will call <code>createItem(int)</code> in
+  the {@code ItemizedOverlay} to retrieve each {@code OverlayItem}. You must override this method to
+  properly read from the ArrayList and return the {@code OverlayItem} from the position specified
+  by the given integer. Your override method should look like this:
 
 <pre>
 &#64;Override
 protected OverlayItem createItem(int i) {
   return mOverlays.get(i);
 }
-</pre></li>
+</pre>
+  </li>
 
-   <li>We're also required to override the <code>size()</code> method. Replace the existing contents of the 
-   method with a size request to our ArrayList:
+  <li>You must also override the <code>size()</code> method to return the current number of
+  items in the ArrayList:
+<pre>
+&#64;Override
+public int size() {
+  return mOverlays.size();
+}
+</pre>
+  </li>
 
-      <pre>return mOverlays.size();</pre></li>
+  <li>Now set up the ability to handle touch events on the overlay items. First, you're
+  going to need a reference to the application {@link android.content.Context} as a member of
+  this class. So add {@code Context mContext} as a class member, then initialize it with a
+  new class constructor:
+<pre>
+public HelloItemizedOverlay(Drawable defaultMarker, Context context) {
+  super(defaultMarker);
+  mContext = context;
+}
+</pre>
+    <p>This passes the {@code defaultMarker} up to the default constructor to bound its coordinates
+    and then initialize {@code mContext} with the given {@link android.content.Context}.</p>
+
+    <p>Then override the {@code onTap(int)} callback method, which will handle the event
+    when an item is tapped by the user:</p>
+<pre>
+&#64;Override
+protected boolean onTap(int index) {
+  OverlayItem item = mOverlays.get(index);
+  AlertDialog.Builder dialog = new AlertDialog.Builder(mContext);
+  dialog.setTitle(item.getTitle());
+  dialog.setMessage(item.getSnippet())
+  dialog.show();
+  return true;
+}
+</pre>
+    <p>This uses the member {@code android.content.Context} to create a new {@link
+android.app.AlertDialog.Builder} and uses the tapped {@code OverlayItem}'s title and snippet for
+the dialog's title and message text. (You'll see the {@code OverlayItem} title and snippet defined
+when you create it below.)</p>
+  </li>
+
 </ol>
 
+<p>You're now done with the <code>HelloItemizedOverlay</code> class and can start using it
+to add items on the map.</p>
 
-<p>That's it for the HelloItemizedOverlay class. We're now ready to use it.</p>
-
-<hr/>
-<p>Go back to the HelloMapView 
-class. We'll start by creating one OverlayItem, adding to an instance of our HelloItemizedOverlay, 
-and then adding this to the MapView.</p>
+<p>Go back to the <code>HelloGoogleMaps</code> class. In the following procedure, you'll create an
+{@code OverlayItem} and add it to an instance of the <code>HelloItemizedOverlay</code> class, then
+add the <code>HelloItemizedOverlay</code> to the <code>MapView</code> using a {@code GeoPoint}
+to define its coordinates on the map.</p>
 
 <img src="images/androidmarker.png" align="right" />
-<p>First, we need the image that we'll use for our map overlay. Here, we'll use the Android on the 
-right as our marker. Drag this image (or your own) to the res/drawable/ directory of your project workspace.</p>
-
-<p>Now we're ready to work in the HelloMapView:</p>
-
 <ol>
-   <li>First we need some more types. Add the following at the top of the HelloMapView class:
+  <li>First, you need the image for the map overlay. If you don't have one handy, use the Android on
+  the right. Drag this image (or your own) into the <code>res/drawable/</code> directory of your
+  project.</li>
+
+  <li>At the end of your existing {@code onCreate()} method, instantiate :
 
 <pre>
-List&lt;Overlay> mapOverlays;
-Drawable drawable;
-HelloItemizedOverlay itemizedOverlay;</pre></li>
+List&lt;Overlay> mapOverlays = mapView.getOverlays();
+Drawable drawable = this.getResources().getDrawable(R.drawable.androidmarker);
+HelloItemizedOverlay itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
 
-   <li>Now pick up where we left off in the <code>onCreate()</code> method. Instantiate the 
-   new fields:
+    <p>All overlay elements on a map are held by the {@code MapView}, so when you want to add some,
+    you have to get a list from the <code>getOverlays()</code> method. Then instantiate the {@link
+    android.graphics.drawable.Drawable} used for the map marker, which was saved in the {@code
+    res/drawable/} directory. The constructor for {@code HelloItemizedOverlay} (your custom {@code
+    ItemizedOverlay}) takes the Drawable in order to set the default marker for all overlay
+    items.</p>
+  </li>
 
-<pre>
-mapOverlays = mapView.getOverlays();
-drawable = this.getResources().getDrawable(R.drawable.androidmarker);
-itemizedoverlay = new HelloItemizedOverlay(drawable);</pre>
-
-      <p>All overlay elements on a map are held by the MapView, so when we want to add some, we must 
-      first retrieve the List with <code>getOverlays()</code> methods. We instantiate the Drawable, which will 
-      be used as our map marker, by using our Context resources to get the Drawable we placed in 
-      the res/drawable/ directory (androidmarker.png). Our HelloItemizedOverlay takes the Drawable in order to set the 
-      default marker.</p></li>
-
-   <li>Now let's make our first OverlayItem by creating a GeoPoint
-    that defines our map coordinates, then pass it to a new OverlayItem:
-
+  <li>Now create a {@code GeoPoint} that defines the map coordinates for the first overlay item,
+  and pass it to a new {@code OverlayItem}:
 <pre>
 GeoPoint point = new GeoPoint(19240000,-99120000);
-OverlayItem overlayitem = new OverlayItem(point, "", "");</pre>
+OverlayItem overlayitem = new OverlayItem(point, "Hola, Mundo!", "I'm in Mexico City!");
+</pre>
 
-      <p>GeoPoint coordinates are based in microdegrees (degrees * 1e6). The OverlayItem takes this 
-      GeoPoint and two strings. Here, we won't concern ourselves with the strings, which can display 
-      text when we click our marker, because we haven't yet written the click handler for the OverlayItem.</p></li>
+    <p>{@code GeoPoint} coordinates are specified in microdegrees (<code>degrees * 1e6</code>). The
+    {@code OverlayItem} constructor accepts the {@code GeoPoint} location, a string for the
+    item's title, and a string for the item's snippet text, respectively.</p>
+  </li>
 
-   <li>All that's left is for us to add this OverlayItem to our collection in the HelloItemizedOverlay, 
-   and add this to the List of Overlay objects retrieved from the MapView:
-
+  <li>All that's left is to add this {@code OverlayItem} to your collection in the
+  {@code HelloItemizedOverlay} instance, then add the {@code HelloItemizedOverlay} to the MapView:
 <pre>
 itemizedoverlay.addOverlay(overlayitem);
-mapOverlays.add(itemizedoverlay);</pre></li>
+mapOverlays.add(itemizedoverlay);
+</pre>
+  </li>
 
-   <li>Run it!</li>
+  <li>Now run the application.</li>
 </ol>
 
-<p>We've sent our droid to Mexico City. Hola, Mundo!</p>
 <p>You should see the following:</p>
 <img src="images/hello-mapview.png" width="150px" />
+<p>When you tap the overlay item, you'll see the dialog appear.</p>
 
-<p>Because we created our ItemizedOverlay class with an ArrayList, we can continue adding new
-OverlayItems. Try adding another one. Before the <code>addOverlay()</code> method is called, add these lines:</p>
+<p>Because the {@code ItemizedOverlay} class uses an {@code java.util.ArrayList} for all of the
+{@code OverlayItem}s, it's easy to add more. Try adding another one. Before the
+<code>addOverlay()</code> method is called, add these lines:</p>
 <pre>
 GeoPoint point2 = new GeoPoint(35410000, 139460000);
-OverlayItem overlayitem2 = new OverlayItem(point2, "", "");
+OverlayItem overlayitem2 = new OverlayItem(point2, "Sekai, konichiwa!", "I'm in Japan!");
 </pre>
-<p>Run it again... We've sent a new droid to Tokyo. Sekai, konichiwa!</p>
+<p>Run the application again. (You probably need to move the map to find the new overlay item.)</p>
 
diff --git a/docs/html/resources/tutorials/views/hello-relativelayout.jd b/docs/html/resources/tutorials/views/hello-relativelayout.jd
index 1b91537..adc1179 100644
--- a/docs/html/resources/tutorials/views/hello-relativelayout.jd
+++ b/docs/html/resources/tutorials/views/hello-relativelayout.jd
@@ -1,33 +1,38 @@
-page.title=Hello, RelativeLayout
+page.title=Relative Layout
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.RelativeLayout} is a ViewGroup that allows you to layout child elements
-in positions relative to the parent or siblings elements.</p>
+<p>{@link android.widget.RelativeLayout} is a {@link android.view.ViewGroup} that displays
+child {@link android.view.View} elements in relative positions. The position of a {@link
+android.view.View} can be specified as relative to sibling elements (such as to the left-of or below
+a given element) or in positions relative to the {@link android.widget.RelativeLayout} area (such as
+aligned to the bottom, left of center).</p>
+
+<p>A {@link android.widget.RelativeLayout} is a very powerful utility for designing a user
+interface because it can eliminate nested {@link android.view.ViewGroup}s. If you find
+yourself using several nested {@link android.widget.LinearLayout} groups, you may be able to
+replace them with a single {@link android.widget.RelativeLayout}.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloRelativeLayout.</li>
-  <li>Open the layout file. Make it like so:
+  <li>Start a new project named <em>HelloRelativeLayout</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="fill_parent"
     android:layout_height="fill_parent">
-
     &lt;TextView
         android:id="@+id/label"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:text="Type here:"/>
-
     &lt;EditText
         android:id="@+id/entry"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:background="@android:drawable/editbox_background"
         android:layout_below="@id/label"/>
-
     &lt;Button
         android:id="@+id/ok"
         android:layout_width="wrap_content"
@@ -36,39 +41,48 @@
         android:layout_alignParentRight="true"
         android:layout_marginLeft="10dip"
         android:text="OK" />
-
     &lt;Button
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:layout_toLeftOf="@id/ok"
         android:layout_alignTop="@id/ok"
         android:text="Cancel" />
-
 &lt;/RelativeLayout>
 </pre>
-<p>Pay attention to each of the additional <code>layout_*</code> attributes (besides the 
-usual width and height, which are required for all elements). When using relative layout,
-we use attributes like <code>layout_below</code> and <code>layout_toLeftOf</code> to describe
-how we'd like to position each View. Naturally, these are different relative positions, and the
-value of the attribute is the id of the element we want the position relative to.</p>
+<p>Notice each of the <code>android:layout_*</code> attributes, such as <code>layout_below</code>,
+<code>layout_alignParentRight</code>, and <code>layout_toLeftOf</code>. When using a {@link
+android.widget.RelativeLayout}, you can use these attributes to describe
+how you want to position each {@link android.view.View}. Each one of these attributes define a
+different kind
+of relative position. Some attributes use the resource ID of a sibling {@link android.view.View}
+to define its own relative position. For example, the last {@link android.widget.Button} is
+defined to lie to the left-of and aligned-with-the-top-of the {@link android.view.View}
+identified by the ID <code>ok</code> (which is the previous {@link android.widget.Button}).</p>
+<p>All of the available layout attributes are defined in {@link
+android.widget.RelativeLayout.LayoutParams}.</p>
 </li>
-<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:</p>
+<li>Make sure you load this layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:</p>
 <pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 }
 </pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
 </li>
-<li>Run it.</li>
+<li>Run the application.</li>
 </ol>
-<p>You should see the following:</p>
+<p>You should see the following layout:</p>
 <img src="images/hello-relativelayout.png" width="150px" />
 
 <h3>Resources</h3>
 <ul>
   <li>{@link android.widget.RelativeLayout}</li>
+  <li>{@link android.widget.RelativeLayout.LayoutParams}</li>
   <li>{@link android.widget.TextView}</li>
   <li>{@link android.widget.EditText}</li>
   <li>{@link android.widget.Button}</li>
diff --git a/docs/html/resources/tutorials/views/hello-spinner.jd b/docs/html/resources/tutorials/views/hello-spinner.jd
index 3a04214..7a3a9c3 100644
--- a/docs/html/resources/tutorials/views/hello-spinner.jd
+++ b/docs/html/resources/tutorials/views/hello-spinner.jd
@@ -1,17 +1,17 @@
-page.title=Hello, Spinner
+page.title=Spinner
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.Spinner} is a widget that allows the user to select an item from a group.
-It is similar to a dropdown list and will allow scrolling when the 
-list exceeds the available vertical space on the screen.</p>
+<p>{@link android.widget.Spinner} is a widget similar to a drop-down list for selecting items.</p>
 
+<p>In this tutorial, you'll create
+a simple spinner widget that displays a list of planets. When one is selected, a toast message
+will display the selected item.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloSpinner.</li>
-  <li>Open the layout file.
-    Make it like so:
+  <li>Start a new project named <em>HelloSpinner</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -19,41 +19,34 @@
     android:padding="10dip"
     android:layout_width="fill_parent"
     android:layout_height="wrap_content">
-
     &lt;TextView
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
         android:layout_marginTop="10dip"
-        android:text="Please select a planet:"
+        android:text="@string/planet_prompt"
     />
-
     &lt;Spinner 
         android:id="@+id/spinner"
         android:layout_width="fill_parent"
         android:layout_height="wrap_content"
-        android:drawSelectorOnTop="true"
         android:prompt="@string/planet_prompt"
     />
-
 &lt;/LinearLayout>
 </pre>
-	<p>Notice that the Spinner's <code>android:prompt</code> is a string resource. In
-        this case, Android does not allow it to be a string, it must be a reference to a resource. 
-        So...</p>
+  <p>Notice that the {@link android.widget.TextView}'s <code>android:text</code> attribute and the
+{@link android.widget.Spinner}'s <code>android:prompt</code> attribute both reference the same
+string resource. This text behaves as a title for the widget. When applied to the {@link
+android.widget.Spinner}, the title text will appear
+in the selection dialog that appears upon selecting the widget.</p>
 </li>
 
-<li>Open the strings.xml file in res/values/ and add the following <code>&lt;string></code> 
-element inside the <code>&lt;resources></code> element:
+<li>Create a <code>strings.xml</code> file in <code>res/values/</code> and edit the file to look
+like this:
 <pre>
-&lt;string name="planet_prompt">Choose a planet&lt;/string>
-</pre>
-</li>
-
-<li>Create a new XML file in res/values/ called arrays.xml. Insert the following:
-<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
 &lt;resources>
-
-    &lt;string-array name="planets">
+    &lt;string name="planet_prompt">Choose a planet&lt;/string>
+    &lt;string-array name="planets_array">
         &lt;item>Mercury&lt;/item>
         &lt;item>Venus&lt;/item>
         &lt;item>Earth&lt;/item>
@@ -63,35 +56,85 @@
         &lt;item>Uranus&lt;/item>
         &lt;item>Neptune&lt;/item>
     &lt;/string-array>
-    
 &lt;/resources>
 </pre>
-	<p>This is the list of items (planets) that the user can select from in the Spinner widget.</p>
+  <p>The {@code &lt;string>} element defines the title string referenced by the {@link
+android.widget.TextView} and {@link android.widget.Spinner} in the layout above. The {@code
+&lt;string-array} element defines the list of strings that will be displayed as the list in
+the {@link android.widget.Spinner} widget.</p>
 </li>
 
-<li>Now open the HelloSpinner.java file. Insert the following code into the HelloSpinner class:
+<li>Now open the <code>HelloSpinner.java</code> file and insert the following code for the {@link
+android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 &#64;Override
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 
-    Spinner s = (Spinner) findViewById(R.id.spinner);
-    ArrayAdapter<CharSequence> adapter = ArrayAdapter.createFromResource(
-            this, R.array.planets, android.R.layout.simple_spinner_item);
+    Spinner spinner = (Spinner) findViewById(R.id.spinner);
+    ArrayAdapter&lt;CharSequence> adapter = ArrayAdapter.createFromResource(
+            this, R.array.planets_array, android.R.layout.simple_spinner_item);
     adapter.setDropDownViewResource(android.R.layout.simple_spinner_dropdown_item);
-    s.setAdapter(adapter);
+    spinner.setAdapter(adapter);
 }
 </pre>
-	<p>That's it. We start by creating a Spinner from our layout. We then create an {@link android.widget.ArrayAdapter} 
-	that binds each element of our string array to a layout view&mdash;we pass <code>createFromResource</code> our Context, 
-	the array of selectable items and the type of layout we'd like each one bound to. We then call
-	<code>setDropDownViewResource()</code> to define the type of layout in which to present the 
-	entire collection. Finally, we set this Adapter to associate with our Spinner, 
-        so the string items have a place to go.</p>
+  <p>After the {@code main.xml} layout is set as the content view, the  {@link
+android.widget.Spinner} widget is captured from the layout with {@link
+android.app.Activity#findViewById(int)}. The {@link
+android.widget.ArrayAdapter#createFromResource(Context,int,int) createFromResource()} method then
+creates a new {@link  android.widget.ArrayAdapter}, which binds each item in the string array
+to the initial appearance for the {@link android.widget.Spinner} (which is how each item will
+appear in the spinner when selected). The {@code
+R.array.planets_array} ID references the {@code string-array} defined above and the
+{@code android.R.layout.simple_spinner_item} ID references a layout for the standard spinner
+appearance, defined by the platform. Then {@link
+android.widget.ArrayAdapter#setDropDownViewResource(int)} is called to define the appearance for
+each item when the widget is opened ({@code simple_spinner_dropdown_item} is
+another standard layout defined by the platform). Finally, the {@link android.widget.ArrayAdapter}
+is set to associate all of its items with the {@link android.widget.Spinner} by calling {@link
+android.widget.AdapterView#setAdapter(T)}.</p>
 </li>
 
-<li>Now run it.</li>
+<li>Now create a nested class that implements {@link
+android.widget.AdapterView.OnItemSelectedListener}. This will provide a callback method that will
+notify your application when an item has been selected from the {@link
+android.widget.Spinner}. Here's what this class should look like:
+<pre>
+public class MyOnItemSelectedListener implements OnItemSelectedListener {
+
+    public void onItemSelected(AdapterView&lt;?> parent,
+        View view, int pos, long id) {
+      Toast.makeText(parent.getContext()), "The planet is " +
+          parent.getItemAtPosition(pos).toString(), Toast.LENGTH_LONG).show();
+    }
+
+    public void onNothingSelected(AdapterView<?> parent) {
+      // Do nothing.
+    }
+}
+</pre>
+<p>The {@link android.widget.AdapterView.OnItemSelectedListener} requires the {@link
+android.widget.AdapterView.OnItemSelectedListener#onItemSelected(AdapterView,View,int,long)
+onItemSelected()} and {@link
+android.widget.AdapterView.OnItemSelectedListener#onNothingSelected(AdapterView)
+onNothingSelected()} callback methods. The former is called when an item from the {@link
+android.widget.AdapterView} is selected, in which case, a short {@link android.widget.Toast}
+message displays the selected text; and the latter is called when a selection disappears from the
+{@link android.widget.AdapterView}, which doesn't happen in this case, so it's ignored.</p>
+</li>
+
+<li>Now the {@code MyOnItemSelectedListener} needs to be applied to the {@link
+android.widget.Spinner}. Go back to the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method and add the following line to the end:
+<pre>
+    spinner.setOnItemSelectedListener(new MyOnItemSelectedListener());
+</pre>
+<p>This creates a new anonymous instance of the {@code MyOnItemSelectedListener} and sets it as the
+listener for the {@link android.widget.Spinner}.</p>
+</li>
+
+<li>Run the application.</li>
 </ol>
 <p>It should look like this:</p>
 <img src="images/hello-spinner.png" width="150px" />
diff --git a/docs/html/resources/tutorials/views/hello-tablelayout.jd b/docs/html/resources/tutorials/views/hello-tablelayout.jd
index 83d6f5d..c8c5982 100644
--- a/docs/html/resources/tutorials/views/hello-tablelayout.jd
+++ b/docs/html/resources/tutorials/views/hello-tablelayout.jd
@@ -1,16 +1,15 @@
-page.title=Hello, TableLayout
+page.title=Table Layout
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.TableLayout} is a ViewGroup that 
-will lay child View elements into rows and columns.</p>
 
+<p>{@link android.widget.TableLayout} is a {@link android.view.ViewGroup} that
+displays child {@link android.view.View} elements in rows and columns.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloTableLayout.</li>
-  <li>Open the layout file.
-    Make it like so:
+  <li>Start a new project named <em>HelloTableLayout</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;TableLayout xmlns:android="http://schemas.android.com/apk/res/android"
@@ -89,21 +88,28 @@
     &lt;/TableRow>
 &lt;/TableLayout>
 </pre>
-<p>Notice how this resembles the structure of an HTML table. <code>TableLayout</code> is like the
-<code>table</code> element; <code>TableRow</code> is like a <code>tr</code> element; but for our cells like
-the html <code>td</code> element, we can use any kind of View. Here, we use <code>TextView</code> for the cells.</p>
+<p>Notice how this resembles the structure of an HTML table. The {@link android.widget.TableLayout}
+element is like the HTML <code>&lt;table&gt;</code> element; {@link android.widget.TableRow} is like
+a <code>>&lt;tr>&gt;</code> element;
+but for the cells, you can use any kind of {@link android.view.View} element. In this example, a
+{@link android.widget.TextView} is used for each cell. In between some of the rows, there is also a
+basic {@link android.view.View}, which is used to draw a horizontal line.</p>
 
 </li>
-<li>Make sure your Activity loads this layout in the <code>onCreate()</code> method:
+<li>Make sure your <em>HelloTableLayout</em> Activity loads this layout in the
+{@link android.app.Activity#onCreate(Bundle) onCreate()} method:
 <pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 }
 </pre>
-<p><code>R.layout.main</code> refers to the <code>main.xml</code> layout file.</p>
+<p>The {@link android.app.Activity#setContentView(int)} method loads the
+layout file for the {@link android.app.Activity}, specified by the resource
+ID &mdash; <code>R.layout.main</code> refers to the <code>res/layout/main.xml</code> layout
+file.</p>
 </li>
-<li>Run it.</li>
+<li>Run the application.</li>
 </ol>
 <p>You should see the following:</p>
 <img src="images/hello-tablelayout.png" width="150px" />
diff --git a/docs/html/resources/tutorials/views/hello-tabwidget.jd b/docs/html/resources/tutorials/views/hello-tabwidget.jd
index 8424616..199ceef 100644
--- a/docs/html/resources/tutorials/views/hello-tabwidget.jd
+++ b/docs/html/resources/tutorials/views/hello-tabwidget.jd
@@ -1,14 +1,72 @@
-page.title=Hello, TabWidget
+page.title=Tab Layout
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.TabWidget} offers the ability to easily draw an interface that uses
-tabs to navigate between different views.</p>
+<p>To create a tabbed UI, you need to use a {@link android.widget.TabHost} and a {@link
+android.widget.TabWidget}. The {@link android.widget.TabHost} must be the root node for the layout,
+which contains both the {@link android.widget.TabWidget} for displaying the tabs and a {@link
+android.widget.FrameLayout} for displaying the tab content.</p>
+
+<p>You can implement your tab content in one of two ways: use the tabs to swap
+{@link android.view.View}s within the same {@link android.app.Activity}, or use the tabs to change
+between entirely separate activities. Which method you want for your application will depend on your
+demands, but if each tab provides a distinct user activity, then it probably makes sense to use
+a separate {@link android.app.Activity} for each tab, so that you can better manage the application
+in discrete groups, rather than one massive application and layout.</p>
+
+<p>In this tutorial, you'll create a tabbed UI that uses a separate {@link
+android.app.Activity} for each tab.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloTabWidget.</li>
-  <li>Open the layout file and make it like so:</li>
+  <li>Start a new project named <em>HelloTabWidget</em>.</li>
+  <li>First, create three separate {@link android.app.Activity} classes in your project:
+<code>ArtistsActivity</code>, <code>AlbumsActivity</code>, and <code>SongsActivity</code>. These
+will each represent a separate tab. For now, make each one display a simple message using a {@link
+android.widget.TextView}. For example:
+<pre>
+public class ArtistsActivity extends Activity {
+    public void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        TextView textview = new TextView(this);
+        textview.setText("This is the Artists tab");
+        setContentView(textview);
+    }
+}
+</pre>
+  <p>Notice that this doesn't use a layout file. Just create a {@link
+android.widget.TextView}, give it some text and set that as the content. Duplicate this for
+each of the three activities.</p>
+
+  <li>You're going to need an icon for each of your tabs. And for each one, you should create an 
+image for two different states: one for when the tab is selected, and one for when it is not. The
+general design recommendation is for the selected tab icon to be a darker color (grey), and the
+non-selected icon to be lighter (white). For example:
+  <p>
+  <img src="images/ic_tab_artists_white.png" title="ic_tab_artists_white.png"  alt="" />
+  <img src="images/ic_tab_artists_grey.png" title="ic_tab_artists_grey.png" alt="" />
+  </p>
+  <p>Copy these images for use in this tutorial. Save them into your project
+<code>res/drawable/</code> directory. You now need to create a {@link
+android.graphics.drawable.Drawable} with XML that specifies which image
+to use for each state. Create a new file in <code>res/drawable/</code> named
+<code>ic_tab_artists.xml</code> and insert the following:</p>
+<pre>
+&lt;?xml version="1.0" encoding="utf-8"?>
+&lt;selector xmlns:android="http://schemas.android.com/apk/res/android">
+    &lt;!-- When selected, use grey -->
+    &lt;item android:drawable="@drawable/ic_tab_artists_grey"
+          android:state_selected="true" />
+    &lt;!-- When not selected, use white-->
+    &lt;item android:drawable="@drawable/ic_tab_artists_white" />
+&lt;/selector>
+</pre>
+  <p>This is an XML definition for a {@link android.graphics.drawable.Drawable}, which you will
+reference as the image for a tab. When the image state changes, the image will automatically
+switch between the images defined here.</p>
+
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
   <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;TabHost xmlns:android="http://schemas.android.com/apk/res/android"
@@ -18,7 +76,8 @@
     &lt;LinearLayout
         android:orientation="vertical"
         android:layout_width="fill_parent"
-        android:layout_height="fill_parent">
+        android:layout_height="fill_parent"
+        android:padding="5dp">
         &lt;TabWidget
             android:id="@android:id/tabs"
             android:layout_width="fill_parent"
@@ -26,94 +85,111 @@
         &lt;FrameLayout
             android:id="@android:id/tabcontent"
             android:layout_width="fill_parent"
-            android:layout_height="fill_parent">
-            &lt;TextView 
-                android:id="@+id/textview1"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent" 
-                android:text="this is a tab" />
-            &lt;TextView 
-                android:id="@+id/textview2"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent" 
-                android:text="this is another tab" />
-            &lt;TextView 
-                android:id="@+id/textview3"
-                android:layout_width="fill_parent"
-                android:layout_height="fill_parent" 
-                android:text="this is a third tab" />
-    	&lt;/FrameLayout>
+            android:layout_height="fill_parent"
+            android;padding="5dp" />
     &lt;/LinearLayout>
 &lt;/TabHost>
 </pre>
-    <p>Here, we've created a {@link android.widget.TabHost} that contains the entire layout of the Activity.
-    A TabHost requires two descendant elements: a {@link android.widget.TabWidget} and a {@link android.widget.FrameLayout}.
-    In order to properly layout these elements, we've put them inside a vertical {@link android.widget.LinearLayout}.
-    The FrameLayout is where we keep the content that will change with each tab. Each child in the FrameLayout will
-    be associated with a different tab.
-    In this case, each tab simply shows a different {@link android.widget.TextView} with some text. </p>
-    <p>Notice that the TabWidget and the FrameLayout elements have specific <code>android</code> namespace IDs. These are necessary
-    so that the TabHost can automatically retireve references to them, populate the TabWidget with the tabs that we'll define
-    in our code, and swap the views in the FrameLayout. We've also defined our own IDs for each TextView, which we'll use to 
-    associate each tab with the view that it should reveal.</p>
-    <p>Of course, you can 
-    make these child views as large as complex as you'd like &mdash; instead of the TextView elements, 
-    you could start with other layout views and build a unique layout hierarchy for each tab.</p>
+    <p>This is the layout that will display the tabs and provide navigation between each {@link
+    android.app.Activity} created above.</p>
+    <p>The {@link android.widget.TabHost} requires that a {@link android.widget.TabWidget} and a
+{@link android.widget.FrameLayout} both live somewhere within it. To position the {@link
+android.widget.TabWidget} and {@link android.widget.FrameLayout} vertically, a {@link
+android.widget.LinearLayout} is used. The {@link android.widget.FrameLayout} is where the content
+for each tab goes, which is empty now because the {@link android.widget.TabHost} will automatically
+embed each {@link android.app.Activity} within it.</p>
+    <p>Notice that the {@link android.widget.TabWidget} and the {@link android.widget.FrameLayout}
+    elements have the IDs {@code tabs} and {@code tabcontent}, respectively. These names
+    must be used so that the {@link android.widget.TabHost} can retrieve references to each of
+    them. It expects exactly these names.</p>
   </li>
-  <li>Now we'll add our code. Open HelloTabWidget.java and make it a <code>TabActivity</code>.
-    <p>By default, Eclipse creates a class that extends <code>Activity</code>. Change it to 
-    extend <code>TabActivity</code>:</p>
-    <pre>
+
+  <li>Now open <code>HelloTabWidget.java</code> and make it extend {@link
+  android.app.TabActivity}:</p>
+<pre>
 public class HelloTabWidget extends TabActivity {
-</pre>  
+</pre>
   </li>
-  <li>Now fill in the the <code>onCreate</code> method like this:
-  <pre>
+  <li>Use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+  method:
+<pre>
 public void onCreate(Bundle savedInstanceState) {
     super.onCreate(savedInstanceState);
     setContentView(R.layout.main);
 
-    mTabHost = getTabHost();
-    
-    mTabHost.addTab(mTabHost.newTabSpec("tab_test1").setIndicator("TAB 1").setContent(R.id.textview1));
-    mTabHost.addTab(mTabHost.newTabSpec("tab_test2").setIndicator("TAB 2").setContent(R.id.textview2));
-    mTabHost.addTab(mTabHost.newTabSpec("tab_test3").setIndicator("TAB 3").setContent(R.id.textview3));
-    
-    mTabHost.setCurrentTab(0);
+    Resources res = getResources(); // Resource object to get Drawables
+    TabHost tabHost = getTabHost();  // The activity TabHost
+    TabHost.TabSpec spec;  // Resusable TabSpec for each tab
+    Intent intent;  // Reusable Intent for each tab
+
+    // Create an Intent to launch an Activity for the tab (to be reused)
+    intent = new Intent().setClass(this, ArtistsActivity.class);
+
+    // Initialize a TabSpec for each tab and add it to the TabHost
+    spec = tabHost.newTabSpec("artists").setIndicator("Artists",
+                      res.getDrawable(R.drawable.ic_tab_artists))
+                  .setContent(intent);
+    tabHost.addTab(spec);
+
+    // Do the same for the other tabs
+    intent = new Intent().setClass(this, AlbumsActivity.class);
+    spec = tabHost.newTabSpec("albums").setIndicator("Albums",
+                      res.getDrawable(R.drawable.ic_tab_albums))
+                  .setContent(intent);
+    mTabHost.addTab(spec);
+
+    intent = new Intent().setClass(this, SongsActivity.class);
+    spec = tabHost.newTabSpec("songs").setIndicator("Songs",
+                      res.getDrawable(R.drawable.ic_tab_songs))
+                  .setContent(intent);
+    tabHost.addTab(spec);
+
+    tabHost.setCurrentTab(getIntent());
 }
 </pre>
-    <p>As usual, we start by setting our layout.</p>
-    <p>We then call the TabActivity method <code>getTabHost()</code>,
-    which returns us a reference to the TabHost we created in our layout. Upon our TabHost, we call <code>addTab()</code>
-    for each of the tabs that we want to add to the TabWidget. Each time we call this, we pass a 
-    {@link android.widget.TabHost.TabSpec} that we build on the fly, and with it, chain together two necessary methods:
-    <code>setIndicator()</code> to set the text for the tab button, and <code>setContent()</code> to define
-    which View we want to associate with the tab and reveal when pressed. Our indicator is just a text string and 
-    our content is an ID reference to the TextView elements we inserted in the FrameLayout.</p>
-    <p>At the end, we call <code>setCurrentTab()</code> to define which tab should be opened by default. The tabs
-    are saved like a zero-based array, so to open the first tab, we pass zero (<var>0</var>).</p>
+    <p>This sets up each tab with their text and icon, and assigns each one an {@link
+android.app.Activity}.</p>
+    <p>A reference to the {@link android.widget.TabHost} is first captured with {@link
+android.app.TabActivity#getTabHost()}. Then, for
+each tab, a {@link android.widget.TabHost.TabSpec} is created to define the tab properties. The
+{@link android.widget.TabHost#newTabSpec(String)} method creates a new {@link
+android.widget.TabHost.TabSpec} identified by the given string tag. For each
+{@link android.widget.TabHost.TabSpec}, {@link
+android.widget.TabHost.TabSpec#setIndicator(CharSequence,Drawable)} is called to set the text and
+icon for the tab, and {@link android.widget.TabHost.TabSpec#setContent(Intent)} is called to specify
+the {@link android.content.Intent} to opens the appropriate {@link android.app.Activity}. Each
+{@link android.widget.TabHost.TabSpec} is then added to the {@link android.widget.TabHost} by
+calling {@link android.widget.TabHost#addTab(TabHost.TabSpec)}.</p>
+
+    <p>At the very end, {@link
+    android.widget.TabHost#setCurrentTab(int)} opens the tab to be displayed by default, specified
+    by the index position of the tab.</p>
+
+    <p>Notice that not once was the {@link android.widget.TabWidget} object referenced. This is
+    because a {@link android.widget.TabWidget} must always be a child of a {@link
+    android.widget.TabHost}, which is what you use for almost all interaction with the tabs. So when
+    a tab is added to the {@link android.widget.TabHost}, it's automatically added to the child
+    {@link android.widget.TabWidget}.</p>
   </li>
-  <li>To clean-up the presentation a bit more, let's remove the window title that appears at the top of the layout.
-  Android includes a theme that removes that title for us. To add it, open the Android Manifest file and add
-  the <var>NoTitleBar</var> theme to the <code>&lt;application></code> tag. It should end up like this:
-    <pre>
-&lt;application android:icon="&#64;drawable/icon" android:theme="&#64;android:style/Theme.NoTitleBar">
+
+  <li>Now open the Android Manifest file and add the <code>NoTitleBar</code> theme to the
+<em>HelloTabWidget</em>'s
+  <code>&lt;activity></code> tag. This will remove the default application title from the top
+  of the layout, leaving more space for the tabs, which effectively operate as their own titles.
+  The <code>&lt;activity></code> tag should look like this:
+<pre>
+&lt;activity android:name=".HelloTabWidget" android:label="@string/app_name"
+          android:theme="&#64;android:style/Theme.NoTitleBar">
 </pre>
   </li>
-  <li>That's it. Run your application.</li>
 
+  <li>Run the application.</li>
 </ol>
 
 
 <p>Your application should look like this:</p>
 <img src="images/hello-tabwidget.png" width="150px" />
 
-<div class="special"><p>You can include icons in your tabs by passing a 
-{@link android.graphics.drawable.Drawable} when you call <code>setIndicator()</code>. Here's an example
-that uses a Drawable created from an image in the project resources:</p>
-<pre>setIndicator("TAB 1", getResources().getDrawable(R.drawable.tab_icon))</pre>
-</div>
-
 <h3>References</h3>
 <ul>
 <li>{@link android.widget.TabWidget}</li>
diff --git a/docs/html/resources/tutorials/views/hello-timepicker.jd b/docs/html/resources/tutorials/views/hello-timepicker.jd
index 1a6c8f9..cf16c8e 100644
--- a/docs/html/resources/tutorials/views/hello-timepicker.jd
+++ b/docs/html/resources/tutorials/views/hello-timepicker.jd
@@ -1,106 +1,97 @@
-page.title=Hello, TimePicker
+page.title=Time Picker
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.widget.TimePicker} is a widget that allows the 
-user to select the time by hour, minute and AM or PM.</p>
+<p>To provide a widget for selecting a time, use the {@link android.widget.TimePicker}
+widget, which allows the user to select the hour and minute in a familiar interface.</p>
 
+<p>In this tutorial, you'll create a {@link android.app.TimePickerDialog}, which presents the
+time picker in a floating dialog box at the press of a button. When the time is set by
+the user, a {@link android.widget.TextView} will update with the new date.</p>
 
 <ol>
-  <li>Start a new project/Activity called HelloTimePicker.</li>
-  <li>Open the layout file and make it like so:
+  <li>Start a new project named <em>HelloTimePicker</em>.</li>
+  <li>Open the <code>res/layout/main.xml</code> file and insert the following:
     <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
 &lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="wrap_content"
     android:layout_height="wrap_content"
     android:orientation="vertical">
-
     &lt;TextView android:id="@+id/timeDisplay"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text=""/>
-
     &lt;Button android:id="@+id/pickTime"
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
         android:text="Change the time"/>
-
 &lt;/LinearLayout>
 </pre>
-	<p>For the layout, we're using a vertical LinearLayout, with a {@link android.widget.TextView} that
-	will display the time and a {@link android.widget.Button} that will initiate the 
-        {@link android.widget.TimePicker} dialog.
-	With this layout, the TextView will sit above the Button.
-	The text value in the TextView is set empty, as it will be filled by our Activity
-	with the current time.</p>
-    </li> 
+<p>This is a basic {@link android.widget.LinearLayout} with a {@link android.widget.TextView}
+that will display the time and a {@link android.widget.Button} that will open the {@link
+android.app.TimePickerDialog}.</p>
+    </li>
 
-  <li>Open HelloTimePicker.java. Insert the following to the HelloTimePicker class:
+  <li>Open <code>HelloTimePicker.java</code> and insert the following class members:
 <pre>
-private TextView mTimeDisplay;
-private Button mPickTime;
+    private TextView mTimeDisplay;
+    private Button mPickTime;
 
-private int mHour;
-private int mMinute;
+    private int mHour;
+    private int mMinute;
 
-static final int TIME_DIALOG_ID = 0;
-
-&#64;Override
-protected void onCreate(Bundle savedInstanceState) {
-    super.onCreate(savedInstanceState);
-    setContentView(R.layout.main);
-    
-    // capture our View elements
-    mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
-    mPickTime = (Button) findViewById(R.id.pickTime);
-
-    // add a click listener to the button
-    mPickTime.setOnClickListener(new View.OnClickListener() {
-        public void onClick(View v) {
-            showDialog(TIME_DIALOG_ID);
-        }
-    });
-
-    // get the current time
-    final Calendar c = Calendar.getInstance();
-    mHour = c.get(Calendar.HOUR_OF_DAY);
-    mMinute = c.get(Calendar.MINUTE);
-
-    // display the current date
-    updateDisplay();
-}
+    static final int TIME_DIALOG_ID = 0;
 </pre>
-<p class="note"><strong>Tip:</strong> Press Ctrl(or Cmd) + Shift + O to import all needed packages.</p>
-        <p>We start by instantiating variables for our View elements and time fields.
-	The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog. In the
-	<code>onCreate()</code> method, we get prepared by setting the layout and capturing the View elements. 
-	We then set an on-click listener for the Button, so that when it is clicked, it will
-	show our TimePicker dialog. The <code>showDialog()</code> method will perform a callback
-	to our Activity. (We'll define this callback in the next section.) We then create an
-	instance of {@link java.util.Calendar} and get the current hour and minute. Finally, we call 
-	<code>updateDisplay()</code>&mdash;our own method that will fill the TextView with the time.</p>
-</li>
-
-<li>After the <code>onCreate()</code> method, add the <code>onCreateDialog()</code> callback method:
+<p>This declares variables for the layout elements and time fields.
+The <code>TIME_DIALOG_ID</code> is a static integer that uniquely identifies the dialog.</p>
+  </li>
+  <li>Now insert the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+method:
 <pre>
-&#64;Override
-protected Dialog onCreateDialog(int id) {
-    switch (id) {
-    case TIME_DIALOG_ID:
-        return new TimePickerDialog(this,
-                mTimeSetListener, mHour, mMinute, false);
+    &#64;Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+        setContentView(R.layout.main);
+
+        // capture our View elements
+        mTimeDisplay = (TextView) findViewById(R.id.timeDisplay);
+        mPickTime = (Button) findViewById(R.id.pickTime);
+
+        // add a click listener to the button
+        mPickTime.setOnClickListener(new View.OnClickListener() {
+            public void onClick(View v) {
+                showDialog(TIME_DIALOG_ID);
+            }
+        });
+
+        // get the current time
+        final Calendar c = Calendar.getInstance();
+        mHour = c.get(Calendar.HOUR_OF_DAY);
+        mMinute = c.get(Calendar.MINUTE);
+
+        // display the current date
+        updateDisplay();
     }
-    return null;
-}
 </pre>
-	<p>This is passed the identifier we gave <code>showDialog()</code> and initializes
-	the TimePicker to the time we retrieved from our Calendar instance. It will be called by 
-        <code>showDialog()</code>.</p>
+
+<p>First, the content is set to the <code>main.xml</code> layout and then the {@link
+android.widget.TextView} and {@link android.widget.Button} are captured with {@link
+android.app.Activity#findViewById(int)}.
+Then an {@link android.view.View.OnClickListener} is created for the {@link android.widget.Button},
+so that when clicked, it will call {@link
+android.app.Activity#showDialog(int)}, passing the unique integer ID for the time picker
+dialog. Using {@link android.app.Activity#showDialog(int)} allows the {@link
+android.app.Activity} to manage the life-cycle of the dialog and will call the {@link
+android.app.Activity#onCreateDialog(int)} callback method to request the {@link android.app.Dialog}
+that should be displayed (which you'll define later). After the on-click listener is set, a new
+{@link java.util.Calendar} is created to get the current hour and minute. Finally, the
+private <code>updateDisplay()</code> method is called in order to fill the {@link
+android.widget.TextView} with the current time.</p>
 </li>
 
-<li>Now add our <code>updateDisplay()</code> method:
+<li>Add the <code>updateDisplay()</code> and <code>pad()</code> methods:
 <pre>
 // updates the time we display in the TextView
 private void updateDisplay() {
@@ -109,13 +100,22 @@
                 .append(pad(mHour)).append(":")
                 .append(pad(mMinute)));
 }
+
+private static String pad(int c) {
+    if (c >= 10)
+        return String.valueOf(c);
+    else
+        return "0" + String.valueOf(c);
+}
 </pre>
-	<p>This simply takes our member fields for the time and inserts them in 
-	the <code>mTimeDisplay</code> TextView. Note that we call a new method, <code>pad()</code>,
-	on the hour and minute. (We'll create this method in the last step.)</p>
+<p>The <code>updateDisplay()</code> method uses the member fields for the time and inserts them in
+the <code>mTimeDisplay</code> {@link android.widget.TextView}. The <code>pad()</code> method returns
+the appropriate string representation of the hour or minute&mdash;it will prefix a zero to the
+number if it's a single digit.</p>
 </li>
 
-<li>Next, add a listener to be called when the time is reset:
+<li>Add a class member for a {@link android.app.TimePickerDialog.OnTimeSetListener} that will be
+called when the user sets a new time:
 <pre>
 // the callback received when the user "sets" the time in the dialog
 private TimePickerDialog.OnTimeSetListener mTimeSetListener =
@@ -127,24 +127,31 @@
         }
     };
 </pre>
-	<p>Now when the user is done setting the time (clicks the "Set" button), we update our member fields with
-	the new time and update our TextView.</p>
-</li>
-<li>Finally, add the <code>pad()</code> method that we called from the <code>updateDisplay()</code>:
-<pre>
-private static String pad(int c) {
-    if (c >= 10)
-        return String.valueOf(c);
-    else
-        return "0" + String.valueOf(c);
-}
-</pre>
-	<p>This method returns the appropriate String representation of the hour or minute.
-	It will prefix a zero to the number if it's a single digit.
-  	</p>
+<p>When the user is done setting the time (clicks the "Set" button), the
+<code>onTimeSet()</code> method is called and it updates the member fields with
+the new time and updates the layout's {@link android.widget.TextView}.</p>
 </li>
 
-<li>Now run it.</li>
+<li>Add the {@link android.app.Activity#onCreateDialog(int)} callback method:
+<pre>
+&#64;Override
+protected Dialog onCreateDialog(int id) {
+    switch (id) {
+    case TIME_DIALOG_ID:
+        return new TimePickerDialog(this,
+                mTimeSetListener, mHour, mMinute, false);
+    }
+    return null;
+}
+</pre>
+<p>This is an {@link android.app.Activity} callback that is passed the identifier you passed to
+{@link android.app.Activity#showDialog(int)}, in the {@link android.widget.Button}'s on-click
+listener. When the ID matches, this initializes the {@link android.app.TimePickerDialog} with the
+member variables initialized at the end of <code>onCreate()</code> and the {@link
+android.app.TimePickerDialog.OnTimeSetListener} created in the previous step.</p>
+</li>
+
+<li>Run the application.</li>
 </ol>
 <p>When you press the "Change the time" button, you should see the following:</p>
 <img src="images/hello-timepicker.png" width="150px" />
@@ -152,6 +159,7 @@
 <h3>References</h3>
 <ol>
   <li>{@link android.widget.TimePicker}</li>
+  <li>{@link android.app.TimePickerDialog.OnTimeSetListener}</li>
   <li>{@link android.widget.Button}</li>
   <li>{@link android.widget.TextView}</li>
   <li>{@link java.util.Calendar}</li>
diff --git a/docs/html/resources/tutorials/views/hello-webview.jd b/docs/html/resources/tutorials/views/hello-webview.jd
index c4388ea..17b2646 100644
--- a/docs/html/resources/tutorials/views/hello-webview.jd
+++ b/docs/html/resources/tutorials/views/hello-webview.jd
@@ -1,63 +1,72 @@
-page.title=Hello, WebView 
+page.title=Web View
 parent.title=Hello, Views
 parent.link=index.html
 @jd:body
 
-<p>A {@link android.webkit.WebView} allows you to create your own web browser Activity. In this tutorial, 
-we'll create a simple Activity that can view web pages.</p>
+<p>{@link android.webkit.WebView} allows you to create your own window for viewing web pages (or even
+develop a complete browser). In this tutorial, you'll create a simple {@link android.app.Activity}
+that can view and navigate web pages.</p>
 
 <ol>
-   <li>Create a new project/Activity called HelloWebView.</li>
-   <li>Open the layout file. Insert a WebView so it looks like so:
+   <li>Create a new project named <em>HelloWebView</em>.</li>
+   <li>Open the <code>res/layout/main.xml</code> file and insert the following:
 <pre>
 &lt;?xml version="1.0" encoding="utf-8"?>
-&lt;LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:layout_width="wrap_content"
-    android:layout_height="wrap_content"
-    android:orientation="vertical">
-
-    &lt;WebView 
-        android:id="@+id/webview"
-        android:layout_width="fill_parent"
-        android:layout_height="fill_parent"
-    />
-
-&lt;/LinearLayout>
-</pre></li>
-
-   <li>Now open the HelloWebView.java file.
-      At the top of the class, instantiate a WebView object:
-<pre>WebView webview;</pre>
-  <p> Then add the following  at the end of the <code>onCreate()</code> method:</p>
-<pre>
-webview = (WebView) findViewById(R.id.webview);
-webview.getSettings().setJavaScriptEnabled(true);
-webview.loadUrl("http://www.google.com");
+&lt;WebView
+    android:id="@+id/webview"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+/>
 </pre>
+  </li>
 
-      <p>This captures the WebView we created in our layout, then requests a 
-	{@link android.webkit.WebSettings} object and enables JavaScript.
-	Then we load a URL.</p></li>
+   <li>Now open the <code>HelloWebView.java</code> file.
+      At the top of the class, declare a {@link android.webkit.WebView} object:
+<pre>WebView mWebView;</pre>
+  <p>Then use the following code for the {@link android.app.Activity#onCreate(Bundle) onCreate()}
+  method:</p>
+<pre>
+public void onCreate(Bundle savedInstanceState) {
+    super.onCreate(savedInstanceState);
+    setContentView(R.layout.main);
 
-   <li>Because we're accessing the internet, we need to add the appropriate 
-   permissions to the Android manifest file. So open the AndroidManifest.xml file 
-   and, add the following as a child of the <code>&lt;manifest></code> element:
+    mWebView = (WebView) findViewById(R.id.webview);
+    mWebView.getSettings().setJavaScriptEnabled(true);
+    mWebView.loadUrl("http://www.google.com");
+}
+</pre>
+  <p>This initializes the member {@link android.webkit.WebView} with the one from the
+  {@link android.app.Activity} layout; requests a {@link android.webkit.WebSettings} object with
+  {@link android.webkit.WebView#getSettings()}; and enables JavaScript for the {@link
+  android.webkit.WebView} with {@link android.webkit.WebSettings#setJavaScriptEnabled(boolean)}.
+  Finally, an initial web page is loaded with {@link
+  android.webkit.WebView#loadUrl(String)}.</p>
+  </li>
 
-      <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre></li>
+  <li>Because this application needs access to the Internet, you need to add the appropriate
+  permissions to the Android manifest file. Open the <code>AndroidManifest.xml</code> file
+  and add the following as a child of the <code>&lt;manifest></code> element:
 
-   <li>Now run it.</li>
-</ol>
-<p> You now have the world's simplest web page viewer.
-   It's not quite a browser yet. It only loads the page we've requested.</p>
+    <pre>&lt;uses-permission android:name="android.permission.INTERNET" /></pre></li>
 
-<hr/>
+  <li>While you're in the manifest, give some more space for web pages by removing the title
+  bar, with the "NoTitleBar" theme:
+<pre>
+&lt;activity android:name=".HelloGoogleMaps" android:label="@string/app_name"
+     <strong>android:theme="@android:style/Theme.NoTitleBar"</strong>&gt;
+</pre>
+  </li>
 
-<p>We can load a page, but as soon as we click a link, the default Android web browser 
-handles the Intent, instead of our own WebView handling the action. So now we'll 
-override the {@link android.webkit.WebViewClient} to enable us to handle our own URL loading.</p>
+  <li>Now run the application.
+  <p>You now have a simplest web page viewer.
+  It's not quite a browser yet because as soon as you click a link, the default Android Browser
+  handles the Intent to view a web page, because this {@link android.app.Activity} isn't
+  technically enabled to do so. Instead of adding an intent filter to view web pages, you can
+  override the {@link android.webkit.WebViewClient} class and enable this {@link
+  android.app.Activity} to handle its own URL requests.</p>
+  </li>
 
-<ol>
-   <li>In the HelloAndroid Activity, add this nested private class:
+  <li>In the <code>HelloAndroid</code> Activity, add this nested class:
 <pre>
 private class HelloWebViewClient extends WebViewClient {
     &#64;Override
@@ -65,42 +74,51 @@
         view.loadUrl(url);
         return true;
     }
-}</pre></li>
+}
+</pre>
+  </li>
+  <li>Then towards the end of the {@link android.app.Activity#onCreate(Bundle)} method, set an
+  instance of the <code>HelloWebViewClient</code> as the {@link android.webkit.WebViewClient}:
+   <pre>mWebView.setWebViewClient(new WebViewClientDemo());</pre>
 
-   <li>Now, in the <code>onCreate()</code> method, set an instance of the <code>HelloWebViewClient</code>
-   as our WebViewClient:
-      <pre>webview.setWebViewClient(new WebViewClientDemo());</pre>
-
-      <p>This line should immediately follow the initialization of our WebView object.</p>
-      <p>What we've done is create a WebViewClient that will load any URL selected in our
-WebView in the same WebView. You can see this in the <code>shouldOverrideUrlLoading()</code>
-method, above&mdash;it is passed the current WebView and the URL, so all we do
-is load the URL in the given view. Returning <var>true</var> says that we've handled the URL
-ourselves and the event should not bubble-up.</p>
-      <p>If you try it again, new pages will now load in the HelloWebView Activity. However, you'll notice that 
-we can't navigate back. We need to handle the back button 
-on the device, so that it will return to the previous page, rather than exit the application.</p>
+    <p>This line can go anywhere following the initialization of the {@link
+    android.webkit.WebView} object.</p>
+    <p>This creates a {@link android.webkit.WebViewClient} that will load any URL selected from this
+    {@link android.webkit.WebView} into the same {@link android.webkit.WebView}. The 
+    {@link android.webkit.WebViewClient#shouldOverrideUrlLoading(WebView,String)} method is passed
+the current    {@link android.webkit.WebView} and the URL requested, so all it needs to do is load
+the URL in    the given view. Returning <code>true</code> says that the method has handled the URL
+and the    event should not propagate (in which case, an Intent would be created that's handled by
+the    Browser application).</p>
+    <p>If you run the application again, new pages will now load in this Activity.
+    However, you can't navigate back to previous pages. To do this, you need to handle the BACK
+    button on the device, so that it will return to the previous page, rather than exit the
+    application.</p>
     </li>
 
-   <li>To handle the back button key press, add the following method inside the HelloWebView 
-Activity:
+  <li>To handle the BACK button key press, add the following method inside the
+  <code>HelloWebView</code> Activity:
 <pre> 
 &#64;Override
 public boolean onKeyDown(int keyCode, KeyEvent event) {
-    if ((keyCode == KeyEvent.KEYCODE_BACK) && webview.canGoBack()) {
-        webview.goBack();
+    if ((keyCode == KeyEvent.KEYCODE_BACK) &amp;&amp; mWebView.canGoBack()) {
+        mWebView.goBack();
         return true;
     }
     return super.onKeyDown(keyCode, event);
-}</pre>
-      <p>The condition uses a {@link android.view.KeyEvent} to check
-      whether the key pressed is the BACK button and whether the 
-      WebView is actually capable of navigating back (if it has a history). If both are 
-      <em>not</em> true, then we send the event up the chain (and the Activity will close). 
-      But if both <em>are</em> true, then we call <code>goBack()</code>, 
-      which will navigate back one step in the history. We then return true to indicate 
-      that we've handled the event.</p>
+}
+</pre>
+    <p>This {@link android.app.Activity#onKeyDown(int,KeyEvent)} callback method will be called
+    anytime a button is pressed while in the Activity. The condition inside uses the {@link
+    android.view.KeyEvent} to check whether the key pressed is the BACK button and whether the
+    {@link android.webkit.WebView} is actually capable of navigating back (if it has a history). If
+    both are true, then the {@link android.webkit.WebView#goBack()} method is called,
+    which will navigate back one step in the {@link android.webkit.WebView}
+    history.Returning <code>true</code> indicates that the event has been handled. If this condition
+    is not met, then the event is sent back to the system.</p>
 </li>
+<li>Run the application again. You'll now be able to follow links and navigate back through the
+page history.</li>
 </ol>
 <p>When you open the application, it should look like this:</p>
 <img src="images/hello-webview.png" width="150px" />
@@ -111,8 +129,3 @@
 <li>{@link android.webkit.WebViewClient}</li>
 <li>{@link android.view.KeyEvent}</li>
 </ul>
-
-      
-
-
-
diff --git a/docs/html/resources/tutorials/views/images/android_focused.png b/docs/html/resources/tutorials/views/images/android_focused.png
new file mode 100644
index 0000000..f84d0fe
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_focused.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/android_normal.png b/docs/html/resources/tutorials/views/images/android_normal.png
new file mode 100644
index 0000000..94a7084
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_normal.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/android_pressed.png b/docs/html/resources/tutorials/views/images/android_pressed.png
new file mode 100644
index 0000000..fe81ff9
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/android_pressed.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-autocomplete.png b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
old mode 100755
new mode 100644
index e1fd80d..0b3e680
--- a/docs/html/resources/tutorials/views/images/hello-autocomplete.png
+++ b/docs/html/resources/tutorials/views/images/hello-autocomplete.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-formstuff.png b/docs/html/resources/tutorials/views/images/hello-formstuff.png
old mode 100755
new mode 100644
index 3b4bf54..949319f
--- a/docs/html/resources/tutorials/views/images/hello-formstuff.png
+++ b/docs/html/resources/tutorials/views/images/hello-formstuff.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-listview.png b/docs/html/resources/tutorials/views/images/hello-listview.png
old mode 100755
new mode 100644
index a1cf7aa..165b1ac
--- a/docs/html/resources/tutorials/views/images/hello-listview.png
+++ b/docs/html/resources/tutorials/views/images/hello-listview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-mapview.png b/docs/html/resources/tutorials/views/images/hello-mapview.png
old mode 100755
new mode 100644
index 0956760..6bd9740
--- a/docs/html/resources/tutorials/views/images/hello-mapview.png
+++ b/docs/html/resources/tutorials/views/images/hello-mapview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-tabwidget.png b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
index 6a52356..6580c5b 100644
--- a/docs/html/resources/tutorials/views/images/hello-tabwidget.png
+++ b/docs/html/resources/tutorials/views/images/hello-tabwidget.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/hello-webview.png b/docs/html/resources/tutorials/views/images/hello-webview.png
old mode 100755
new mode 100644
index 283ce7d..248c6d4
--- a/docs/html/resources/tutorials/views/images/hello-webview.png
+++ b/docs/html/resources/tutorials/views/images/hello-webview.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png b/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png
new file mode 100644
index 0000000..9baa30e
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/ic_tab_artists_grey.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png b/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png
new file mode 100644
index 0000000..3b010d5
--- /dev/null
+++ b/docs/html/resources/tutorials/views/images/ic_tab_artists_white.png
Binary files differ
diff --git a/docs/html/resources/tutorials/views/index.jd b/docs/html/resources/tutorials/views/index.jd
index 2cb5d3a..6ea7683 100644
--- a/docs/html/resources/tutorials/views/index.jd
+++ b/docs/html/resources/tutorials/views/index.jd
@@ -3,74 +3,95 @@
 
 <style>
 .view {float:left; margin:10px; font-size:120%; font-weight:bold;}
-.view img {border:1px solid black; margin:5px 0 0; padding:5px;}
+#jd-content .view img {border:1px solid black; margin:8px 0 0 0; padding:5px;}
 </style>
 
-<p>This collection of "Hello World"-style tutorials is designed
-to get you quickly started with common Android Views and widgets. The aim is to let you copy and paste
-these kinds of boring bits so you can focus on developing the code that makes your Android application rock.
-Of course, we'll discuss some of the given code so that it all makes sense.</p>
+<p>This is a collection of "Hello World"-style tutorials designed
+to get you started quickly with common Android layouts and widgets.</p>
 
-<p>Note that a certain amount of knowledge is assumed for these tutorials. If you haven't
-completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello, World</a> tutorial, 
-please do so&mdash;it will teach you many things you should know about basic 
-Android development and Eclipse features. More specifically, you should know:</p>
+<p>A certain amount of knowledge is assumed for these tutorials. Before you start,
+you should have completed the <a href="{@docRoot}resources/tutorials/hello-world.html">Hello,
+World</a> tutorial&mdash;it will teach you several things about basic
+Android development. More specifically, you should know:</p>
 <ul>
-  <li>How to create a new Android project.</li>
-  <li>The basic structure of an Android project (resource files, layout files, etc.).</li>
-  <li>The essential components of an {@link android.app.Activity}.</li>
-  <li>How to build and run a project.</li>
+  <li>How to create an Android project and run it</li>
+  <li>The basic structure of an Android project (resource files, layout files, etc.)</li>
+  <li>The basic components of an {@link android.app.Activity}</li>
 </ul>
-<p>Please, also notice that, in order to make these tutorials simple, some may
-not convey the better Android coding practices. In particular, many of them
-use hard-coded strings in the layout files&mdash;the better practice is to reference strings from
-your strings.xml file.</p>
-<p>With this knowledge, you're ready to begin, so take your pick.</p>
 
-<div>
+<p class="note"><strong>Note:</strong> In order to make these tutorials as simple as possible,
+some code may not conform to best practices for coding Android applications. In particular,
+hard-coded strings are used in places, when the better practice is to reference strings from a
+<code>res/values/strings.xml</code> resource file.</p>
+
+<p class="note"><strong>Tip:</strong> After you have pasted sample code into an Eclipse project,
+press <strong>Ctrl (or Cmd) + Shift + O</strong> to import the required packages.</p>
+
+<h2>Layouts</h2>
 
 <div class="view">
-<a href="hello-linearlayout.html">LinearLayout</a><br/>
-<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-relativelayout.html">RelativeLayout</a><br/>
-<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-tablelayout.html">TableLayout</a><br/>
-<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200" /></a>
+<a href="hello-linearlayout.html">Linear Layout</a><br/>
+<a href="hello-linearlayout.html"><img src="images/hello-linearlayout.png" height="285" width="200"
+/></a>
 </div>
 
 <div class="view">
-<a href="hello-datepicker.html">DatePicker</a><br/>
-<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200" /></a>
+<a href="hello-relativelayout.html">Relative Layout</a><br/>
+<a href="hello-relativelayout.html"><img src="images/hello-relativelayout.png" height="285"
+width="200" /></a>
 </div>
 
 <div class="view">
-<a href="hello-timepicker.html">TimePicker</a><br/>
-<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200" /></a>
+<a href="hello-tablelayout.html">Table Layout</a><br/>
+<a href="hello-tablelayout.html"><img src="images/hello-tablelayout.png" height="285" width="200"
+/></a>
 </div>
+
+<div class="view">
+<a href="hello-gridview.html">Grid View</a><br/>
+<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-tabwidget.html">Tab Layout</a><br/>
+<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
+</div>
+
+<div class="view">
+<a href="hello-listview.html">List View</a><br/>
+<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
+</div>
+
+<p style="clear:left">&nbsp;</p>
+
+<h2>Widgets &amp; Other Views</h2>
+
+<div class="view">
+<a href="hello-datepicker.html">Date Picker</a><br/>
+<a href="hello-datepicker.html"><img src="images/hello-datepicker.png" height="285" width="200"
+/></a>
+</div>
+
+<div class="view">
+<a href="hello-timepicker.html">Time Picker</a><br/>
+<a href="hello-timepicker.html"><img src="images/hello-timepicker.png" height="285" width="200"
+/></a>
+</div>
+
 <div class="view">
 <a href="hello-formstuff.html">Form Stuff</a><br/>
 <a href="hello-formstuff.html"><img src="images/hello-formstuff.png" height="285" width="200" /></a>
 </div>
+
 <div class="view">
 <a href="hello-spinner.html">Spinner</a><br/>
 <a href="hello-spinner.html"><img src="images/hello-spinner.png" height="285" width="200" /></a>
 </div>
 
 <div class="view">
-<a href="hello-autocomplete.html">AutoComplete</a><br/>
-<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-listview.html">ListView</a><br/>
-<a href="hello-listview.html"><img src="images/hello-listview.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-gridview.html">GridView</a><br/>
-<a href="hello-gridview.html"><img src="images/hello-gridview.png" height="285" width="200" /></a>
+<a href="hello-autocomplete.html">Auto Complete</a><br/>
+<a href="hello-autocomplete.html"><img src="images/hello-autocomplete.png" height="285" width="200"
+/></a>
 </div>
 
 <div class="view">
@@ -79,40 +100,20 @@
 </div>
 
 <div class="view">
-<a href="hello-tabwidget.html">TabWidget</a><br/>
-<a href="hello-tabwidget.html"><img src="images/hello-tabwidget.png" height="285" width="200" /></a>
-</div>
-
-<div class="view">
-<a href="hello-mapview.html">MapView</a><br/>
+<a href="hello-mapview.html">Google Map View</a><br/>
 <a href="hello-mapview.html"><img src="images/hello-mapview.png" height="285" width="200" /></a>
 </div>
 
 <div class="view">
-<a href="hello-webview.html">WebView</a><br/>
+<a href="hello-webview.html">Web View</a><br/>
 <a href="hello-webview.html"><img src="images/hello-webview.png" height="285" width="200" /></a>
 </div>
 
-<!--
-TODO
-
-<div class="view">
-<a href="hello-popupwindow.html">PopupWindow<br/>
-<img src="images/hello-popupwindow.png" height="285" width="200" /></a>
-</div>
-<div class="view">
-<a href="hello-tabhost.html">TabHost / TabWidget<br/>
-<img src="images/hello-tabhost.png" height="285" width="200" /></a>
-</div>
-ProgressBar; RatingBar; FrameLayout
-
--->
 
 <p class="note" style="clear:left">
-There are plenty more Views and widgets available. See the {@link android.view.View} class
+There are plenty more layouts and widgets available. See the {@link android.view.View} class
 for more on View layouts, and the {@link android.widget widget package}
 for more useful widgets. And for more raw code samples, visit the 
-<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api Demos</a>.
-These can also be found offline, in <code>/&lt;sdk&gt;/samples/ApiDemos</code>.</p>
-</div>
+<a href="{@docRoot}resources/samples/ApiDemos/src/com/example/android/apis/view/index.html">Api
+Demos</a>.</p>
 
diff --git a/docs/html/sdk/download.jd b/docs/html/sdk/download.jd
index 47505e6..029de21 100644
--- a/docs/html/sdk/download.jd
+++ b/docs/html/sdk/download.jd
@@ -58,10 +58,8 @@
   <h2>Thank you for downloading the Android SDK!</h2>
   <p>Your download should be underway. If not, <a id="click-download">click here to start the download</a>.</p>
   <p>To set up your Android development environment, please read the guide to
-    <a href="installing.html">Installing the Android SDK</a>.
-    Once you have completed the installation, see the
-    <a href="/guide/index.html">Dev Guide</a> for documentation about
-    developing Android applications.</p>
+    <a href="installing.html">Installing the Android SDK</a> and ensure that your development
+    machine meets the system requirements linked on that page.</p>
 </div>
 
 <script type="text/javascript">
diff --git a/docs/html/sdk/eclipse-adt.jd b/docs/html/sdk/eclipse-adt.jd
index f861afd..1d99c91 100644
--- a/docs/html/sdk/eclipse-adt.jd
+++ b/docs/html/sdk/eclipse-adt.jd
@@ -25,7 +25,7 @@
 Development Tools (ADT), that is designed to give you a powerful,
 integrated environment in which to build Android applications. </p>
 
-<p>ADT extends the capabilites of Eclipse to let you quickly set up new Android
+<p>ADT extends the capabilities of Eclipse to let you quickly set up new Android
 projects, create an application UI, add components based on the Android
 Framework API, debug your applications using the Android SDK tools, and even
 export signed (or unsigned) APKs in order to distribute your application.</p>
@@ -77,7 +77,7 @@
     <li>Click <strong>Add Site...</strong> </li>
     <li>In the Add Site dialog that appears, enter this URL in the "Location" field:
       <pre style="margin-left:0">https://dl-ssl.google.com/android/eclipse/</pre>
-        <p>Note: If you have trouble aqcuiring the plugin, try using "http" in the Location URL,
+        <p>Note: If you have trouble acquiring the plugin, try using "http" in the Location URL,
         instead of "https" (https is preferred for security reasons).</p>   
       <p>Click <strong>OK</strong>.</p></li>
     <li>Back in the Available Software view, you should see the plugin listed by the URL,
@@ -94,13 +94,13 @@
 <!-- 3.5 steps -->
 <ol>
     <li>Start Eclipse, then select <strong>Help</strong> &gt; <strong>Install
-        New Softare</strong>. </li>
+        New Software</strong>. </li>
     <li>In the Available Software dialog, click <strong>Add...</strong>.</li>
     <li>In the Add Site dialog that appears, enter a name for the remote site
         (for example, "Android Plugin") in the "Name" field. 
         <p>In the "Location" field, enter this URL:</p>
         <pre>https://dl-ssl.google.com/android/eclipse/</pre>
-        <p>Note: If you have trouble aqcuiring the plugin, you can try
+        <p>Note: If you have trouble acquiring the plugin, you can try
            using "http" in the URL, instead of "https" (https is preferred for 
            security reasons).</p>
         <p>Click <strong>OK</strong>.</p>
diff --git a/docs/html/shareables/sample_images.zip b/docs/html/shareables/sample_images.zip
new file mode 100644
index 0000000..007a68a
--- /dev/null
+++ b/docs/html/shareables/sample_images.zip
Binary files differ
diff --git a/libs/rs/Android.mk b/libs/rs/Android.mk
index 262ac8d..d364277 100644
--- a/libs/rs/Android.mk
+++ b/libs/rs/Android.mk
@@ -107,9 +107,5 @@
 
 include $(BUILD_SHARED_LIBRARY)
 
-# Include the subdirectories ====================
-include $(addprefix $(LOCAL_PATH)/,$(addsuffix /Android.mk,\
-            java \
-    	))
 
 endif #simulator
diff --git a/libs/rs/rsScriptC_Lib.cpp b/libs/rs/rsScriptC_Lib.cpp
index 23888ff..de8df39 100644
--- a/libs/rs/rsScriptC_Lib.cpp
+++ b/libs/rs/rsScriptC_Lib.cpp
@@ -776,6 +776,17 @@
     glDrawTexfOES(x, y, z, w, h);
 }
 
+static void SC_drawSpriteScreenspaceCropped(float x, float y, float z, float w, float h,
+        float cx0, float cy0, float cx1, float cy1)
+{
+    GET_TLS();
+    rsc->setupCheck();
+
+    GLint crop[4] = {cx0, cy0, cx1, cy1};
+    glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop);
+    glDrawTexfOES(x, y, z, w, h);
+}
+
 static void SC_drawSprite(float x, float y, float z, float w, float h)
 {
     GET_TLS();
@@ -1271,6 +1282,8 @@
         "void", "(float x, float y, float z, float w, float h)" },
     { "drawSpriteScreenspace", (void *)&SC_drawSpriteScreenspace,
         "void", "(float x, float y, float z, float w, float h)" },
+    { "drawSpriteScreenspaceCropped", (void *)&SC_drawSpriteScreenspaceCropped,
+        "void", "(float x, float y, float z, float w, float h, float cx0, float cy0, float cx1, float cy1)" },
     { "drawLine", (void *)&SC_drawLine,
         "void", "(float x1, float y1, float z1, float x2, float y2, float z2)" },
     { "drawPoint", (void *)&SC_drawPoint,
diff --git a/location/java/com/android/internal/location/GpsLocationProvider.java b/location/java/com/android/internal/location/GpsLocationProvider.java
index 7763549..134756e 100755
--- a/location/java/com/android/internal/location/GpsLocationProvider.java
+++ b/location/java/com/android/internal/location/GpsLocationProvider.java
@@ -182,6 +182,9 @@
 
     // true if GPS is navigating
     private boolean mNavigating;
+
+    // true if GPS engine is on
+    private boolean mEngineOn;
     
     // requested frequency of fixes, in seconds
     private int mFixInterval = 1;
@@ -556,13 +559,17 @@
             mNetworkThread = null;
         }
 
+        // do this before releasing wakelock
+        native_cleanup();
+
         // The GpsEventThread does not wait for the GPS to shutdown
         // so we need to report the GPS_STATUS_ENGINE_OFF event here
         if (mNavigating) {
+            reportStatus(GPS_STATUS_SESSION_END);
+        }
+        if (mEngineOn) {
             reportStatus(GPS_STATUS_ENGINE_OFF);
         }
-
-        native_cleanup();
     }
 
     public boolean isEnabled() {
@@ -874,9 +881,24 @@
 
         synchronized(mListeners) {
             boolean wasNavigating = mNavigating;
-            mNavigating = (status == GPS_STATUS_SESSION_BEGIN);
 
-            if (mNavigating && !mWakeLock.isHeld()) {
+            switch (status) {
+                case GPS_STATUS_SESSION_BEGIN:
+                    mNavigating = true;
+                    break;
+                case GPS_STATUS_SESSION_END:
+                    mNavigating = false;
+                    break;
+                case GPS_STATUS_ENGINE_ON:
+                    mEngineOn = true;
+                    break;
+                case GPS_STATUS_ENGINE_OFF:
+                    mEngineOn = false;
+                    break;
+            }
+
+            // beware, the events can come out of order
+            if ((mNavigating || mEngineOn) && !mWakeLock.isHeld()) {
                 if (DEBUG) Log.d(TAG, "Acquiring wakelock");
                  mWakeLock.acquire();
             }
@@ -919,7 +941,8 @@
                 mContext.sendBroadcast(intent);
             }
 
-            if (status == GPS_STATUS_ENGINE_OFF && mWakeLock.isHeld()) {
+            // beware, the events can come out of order
+            if (!mNavigating && !mEngineOn && mWakeLock.isHeld()) {
                 if (DEBUG) Log.d(TAG, "Releasing wakelock");
                 mWakeLock.release();
             }
diff --git a/services/java/com/android/server/ConnectivityService.java b/services/java/com/android/server/ConnectivityService.java
index 5c0f352..08eceac 100644
--- a/services/java/com/android/server/ConnectivityService.java
+++ b/services/java/com/android/server/ConnectivityService.java
@@ -454,6 +454,7 @@
         IBinder mBinder;
         int mPid;
         int mUid;
+        long mCreateTime;
 
         FeatureUser(int type, String feature, IBinder binder) {
             super();
@@ -462,6 +463,7 @@
             mBinder = binder;
             mPid = getCallingPid();
             mUid = getCallingUid();
+            mCreateTime = System.currentTimeMillis();
 
             try {
                 mBinder.linkToDeath(this, 0);
@@ -476,15 +478,22 @@
 
         public void binderDied() {
             Log.d(TAG, "ConnectivityService FeatureUser binderDied(" +
-                    mNetworkType + ", " + mFeature + ", " + mBinder);
+                    mNetworkType + ", " + mFeature + ", " + mBinder + "), created " +
+                    (System.currentTimeMillis() - mCreateTime) + " mSec ago");
             stopUsingNetworkFeature(this, false);
         }
 
         public void expire() {
             Log.d(TAG, "ConnectivityService FeatureUser expire(" +
-                    mNetworkType + ", " + mFeature + ", " + mBinder);
+                    mNetworkType + ", " + mFeature + ", " + mBinder +"), created " +
+                    (System.currentTimeMillis() - mCreateTime) + " mSec ago");
             stopUsingNetworkFeature(this, false);
         }
+
+        public String toString() {
+            return "FeatureUser("+mNetworkType+","+mFeature+","+mPid+","+mUid+"), created " +
+                    (System.currentTimeMillis() - mCreateTime) + " mSec ago";
+        }
     }
 
     // javadoc from interface
@@ -596,6 +605,7 @@
             return stopUsingNetworkFeature(u, true);
         } else {
             // none found!
+            if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature - not a live request");
             return 1;
         }
     }
@@ -640,6 +650,7 @@
                     if (x.mUid == u.mUid && x.mPid == u.mPid &&
                             x.mNetworkType == u.mNetworkType &&
                             TextUtils.equals(x.mFeature, u.mFeature)) {
+                        if (DBG) Log.d(TAG, "ignoring stopUsingNetworkFeature as dup is found");
                         return 1;
                     }
                 }
@@ -661,8 +672,8 @@
             tracker =  mNetTrackers[usedNetworkType];
             if(usedNetworkType != networkType) {
                 Integer currentPid = new Integer(pid);
-                reassessPidDns(pid, true);
                 mNetRequestersPids[usedNetworkType].remove(currentPid);
+                reassessPidDns(pid, true);
                 if (mNetRequestersPids[usedNetworkType].size() != 0) {
                     if (DBG) Log.d(TAG, "not tearing down special network - " +
                            "others still using it");
@@ -1198,14 +1209,32 @@
         }
         pw.println();
         for (NetworkStateTracker nst : mNetTrackers) {
-            if (nst.getNetworkInfo().isConnected()) {
-                pw.println("Active network: " + nst.getNetworkInfo().
-                        getTypeName());
+            if (nst != null) {
+                if (nst.getNetworkInfo().isConnected()) {
+                    pw.println("Active network: " + nst.getNetworkInfo().
+                            getTypeName());
+                }
+                pw.println(nst.getNetworkInfo());
+                pw.println(nst);
+                pw.println();
             }
-            pw.println(nst.getNetworkInfo());
-            pw.println(nst);
-            pw.println();
         }
+
+        pw.println("Network Requester Pids:");
+        for (int net : mPriorityList) {
+            String pidString = net + ": ";
+            for (Object pid : mNetRequestersPids[net]) {
+                pidString = pidString + pid.toString() + ", ";
+            }
+            pw.println(pidString);
+        }
+        pw.println();
+
+        pw.println("FeatureUsers:");
+        for (Object requester : mFeatureUsers) {
+            pw.println(requester.toString());
+        }
+        pw.println();
     }
 
     // must be stateless - things change under us.
diff --git a/services/java/com/android/server/WifiService.java b/services/java/com/android/server/WifiService.java
index b29e769..3a08e4d 100644
--- a/services/java/com/android/server/WifiService.java
+++ b/services/java/com/android/server/WifiService.java
@@ -329,6 +329,17 @@
             return false;
         }
 
+        /**
+         * Multiple calls to unregisterReceiver() cause exception and a system crash.
+         * This can happen if a supplicant is lost (or firmware crash occurs) and user indicates
+         * disable wifi at the same time.
+         * Avoid doing a disable when the current Wifi state is UNKNOWN
+         * TODO: Handle driver load fail and supplicant lost as seperate states
+         */
+        if (mWifiState == WIFI_STATE_UNKNOWN && !enable) {
+            return false;
+        }
+
         setWifiEnabledState(enable ? WIFI_STATE_ENABLING : WIFI_STATE_DISABLING, uid);
 
         if (enable) {
diff --git a/services/java/com/android/server/am/ActivityManagerService.java b/services/java/com/android/server/am/ActivityManagerService.java
index f3cba69..20ccfdc 100644
--- a/services/java/com/android/server/am/ActivityManagerService.java
+++ b/services/java/com/android/server/am/ActivityManagerService.java
@@ -10276,7 +10276,7 @@
                                 sInfo.applicationInfo.uid, sInfo.packageName,
                                 sInfo.name);
                     }
-                    r = new ServiceRecord(ss, name, filter, sInfo, res);
+                    r = new ServiceRecord(this, ss, name, filter, sInfo, res);
                     res.setService(r);
                     mServices.put(name, r);
                     mServicesByIntent.put(filter, r);
diff --git a/services/java/com/android/server/am/ServiceRecord.java b/services/java/com/android/server/am/ServiceRecord.java
index 2534410..89761a8 100644
--- a/services/java/com/android/server/am/ServiceRecord.java
+++ b/services/java/com/android/server/am/ServiceRecord.java
@@ -40,6 +40,7 @@
  * A running application service.
  */
 class ServiceRecord extends Binder {
+    final ActivityManagerService ams;
     final BatteryStatsImpl.Uid.Pkg.Serv stats;
     final ComponentName name; // service component.
     final String shortName; // name.flattenToShortString().
@@ -192,8 +193,10 @@
         }
     }
 
-    ServiceRecord(BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
+    ServiceRecord(ActivityManagerService ams,
+            BatteryStatsImpl.Uid.Pkg.Serv servStats, ComponentName name,
             Intent.FilterComparison intent, ServiceInfo sInfo, Runnable restarter) {
+        this.ams = ams;
         this.stats = servStats;
         this.name = name;
         shortName = name.flattenToShortString();
@@ -249,27 +252,46 @@
     
     public void postNotification() {
         if (foregroundId != 0 && foregroundNoti != null) {
-            INotificationManager inm = NotificationManager.getService();
-            if (inm != null) {
-                try {
-                    int[] outId = new int[1];
-                    inm.enqueueNotification(packageName, foregroundId,
-                            foregroundNoti, outId);
-                } catch (RemoteException e) {
+            // Do asynchronous communication with notification manager to
+            // avoid deadlocks.
+            final String localPackageName = packageName;
+            final int localForegroundId = foregroundId;
+            final Notification localForegroundNoti = foregroundNoti;
+            ams.mHandler.post(new Runnable() {
+                public void run() {
+                    INotificationManager inm = NotificationManager.getService();
+                    if (inm == null) {
+                        return;
+                    }
+                    try {
+                        int[] outId = new int[1];
+                        inm.enqueueNotification(localPackageName, localForegroundId,
+                                localForegroundNoti, outId);
+                    } catch (RemoteException e) {
+                    }
                 }
-            }
+            });
         }
     }
     
     public void cancelNotification() {
         if (foregroundId != 0) {
-            INotificationManager inm = NotificationManager.getService();
-            if (inm != null) {
-                try {
-                    inm.cancelNotification(packageName, foregroundId);
-                } catch (RemoteException e) {
+            // Do asynchronous communication with notification manager to
+            // avoid deadlocks.
+            final String localPackageName = packageName;
+            final int localForegroundId = foregroundId;
+            ams.mHandler.post(new Runnable() {
+                public void run() {
+                    INotificationManager inm = NotificationManager.getService();
+                    if (inm == null) {
+                        return;
+                    }
+                    try {
+                        inm.cancelNotification(localPackageName, localForegroundId);
+                    } catch (RemoteException e) {
+                    }
                 }
-            }
+            });
         }
     }
     
diff --git a/telephony/java/com/android/internal/telephony/TelephonyEventLog.java b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java
index cdce488..0f15cb6 100644
--- a/telephony/java/com/android/internal/telephony/TelephonyEventLog.java
+++ b/telephony/java/com/android/internal/telephony/TelephonyEventLog.java
@@ -32,4 +32,5 @@
     public static final int EVENT_LOG_PDP_NETWORK_DROP = 50109;
     public static final int EVENT_LOG_CDMA_DATA_SETUP_FAILED = 50110;
     public static final int EVENT_LOG_CDMA_DATA_DROP = 50111;
+    public static final int EVENT_LOG_GSM_RAT_SWITCHED = 50112;
 }
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
index d014a7e..7b60474 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmDataConnectionTracker.java
@@ -748,7 +748,6 @@
     }
 
     private void notifyDefaultData(String reason) {
-        setupDnsProperties();
         setState(State.CONNECTED);
         phone.notifyDataConnection(reason);
         startNetStatPoll();
@@ -757,42 +756,6 @@
         mReregisterOnReconnectFailure = false;
     }
 
-    private void setupDnsProperties() {
-        int mypid = android.os.Process.myPid();
-        String[] servers = getDnsServers(null);
-        String propName;
-        String propVal;
-        int count;
-
-        count = 0;
-        for (int i = 0; i < servers.length; i++) {
-            String serverAddr = servers[i];
-            if (!TextUtils.equals(serverAddr, "0.0.0.0")) {
-                SystemProperties.set("net.dns" + (i+1) + "." + mypid, serverAddr);
-                count++;
-            }
-        }
-        for (int i = count+1; i <= 4; i++) {
-            propName = "net.dns" + i + "." + mypid;
-            propVal = SystemProperties.get(propName);
-            if (propVal.length() != 0) {
-                SystemProperties.set(propName, "");
-            }
-        }
-        /*
-         * Bump the property that tells the name resolver library
-         * to reread the DNS server list from the properties.
-         */
-        propVal = SystemProperties.get("net.dnschange");
-        if (propVal.length() != 0) {
-            try {
-                int n = Integer.parseInt(propVal);
-                SystemProperties.set("net.dnschange", "" + (n+1));
-            } catch (NumberFormatException e) {
-            }
-        }
-    }
-
     /**
      * This is a kludge to deal with the fact that
      * the PDP state change notification doesn't always work
diff --git a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
index 8140654..3bf76d4 100644
--- a/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
+++ b/telephony/java/com/android/internal/telephony/gsm/GsmServiceStateTracker.java
@@ -38,6 +38,7 @@
 import android.provider.Telephony.Intents;
 import android.telephony.ServiceState;
 import android.telephony.SignalStrength;
+import android.telephony.TelephonyManager;
 import android.telephony.gsm.GsmCellLocation;
 import android.text.TextUtils;
 import android.util.Config;
@@ -848,6 +849,21 @@
         cellLoc = newCellLoc;
         newCellLoc = tcl;
 
+
+        // Add an event log when network type switched
+        // TODO: we may add filtering to reduce the event logged,
+        // i.e. check preferred network setting, only switch to 2G, etc
+        if (hasNetworkTypeChanged) {
+            int cid = -1;
+            GsmCellLocation loc = ((GsmCellLocation)phone.getCellLocation());
+            if (loc != null) cid = loc.getCid();
+            EventLog.List val = new EventLog.List(cid, networkType, newNetworkType);
+            EventLog.writeEvent(TelephonyEventLog.EVENT_LOG_GSM_RAT_SWITCHED, val);
+            Log.d(LOG_TAG,
+                    "RAT switched " + networkTypeToString(networkType) + " -> "
+                    + networkTypeToString(newNetworkType) + " at cell " + cid);
+        }
+
         gprsState = newGPRSState;
         networkType = newNetworkType;
 
diff --git a/tests/DumpRenderTree/AndroidManifest.xml b/tests/DumpRenderTree/AndroidManifest.xml
index efa41131..03b7e26 100644
--- a/tests/DumpRenderTree/AndroidManifest.xml
+++ b/tests/DumpRenderTree/AndroidManifest.xml
@@ -17,14 +17,16 @@
 <manifest xmlns:android="http://schemas.android.com/apk/res/android" package="com.android.dumprendertree">
     <application android:name="HTMLHostApp">
         <uses-library android:name="android.test.runner" />
-        <activity android:name="Menu" android:label="1 Dump Render Tree">
+        <activity android:name="Menu" android:label="Dump Render Tree"
+          android:screenOrientation="portrait">
             <intent-filter>
                 <action android:name="android.intent.action.MAIN" />
                 <category android:name="android.intent.category.TEST" />
             </intent-filter>
         </activity>
-        <activity android:name="TestShellActivity" android:launchMode="singleTop" />
-        <activity android:name="ReliabilityTestActivity" />
+        <activity android:name="TestShellActivity" android:launchMode="singleTop"
+          android:screenOrientation="portrait"/>
+        <activity android:name="ReliabilityTestActivity" android:screenOrientation="portrait"/>
     </application>
 
     <instrumentation android:name=".LayoutTestsAutoRunner"
diff --git a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
index 52286d1..31ee120 100644
--- a/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
+++ b/tests/TransformTest/src/com/google/android/test/transform/TransformTestActivity.java
@@ -24,9 +24,8 @@
 import android.os.Bundle;
 import android.util.DisplayMetrics;
 import android.util.Log;
-import android.view.LayoutInflater;
 import android.view.MotionEvent;
-import android.view.TransformGestureDetector;
+import android.view.ScaleGestureDetector;
 import android.view.View;
 import android.widget.LinearLayout;
 
@@ -48,9 +47,6 @@
     @Override
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
-
-        final LayoutInflater li = (LayoutInflater)getSystemService(
-                LAYOUT_INFLATER_SERVICE);
         
         this.setTitle(R.string.act_title);
         LinearLayout root = new LinearLayout(this);
@@ -71,15 +67,19 @@
         private float mPosY;
         private float mScale = 1.f;
         private Matrix mMatrix;
-        private TransformGestureDetector mDetector;
+        private ScaleGestureDetector mDetector;
         
-        private class Listener implements TransformGestureDetector.OnTransformGestureListener {
+        private float mLastX;
+        private float mLastY;
+        
+        private class Listener implements ScaleGestureDetector.OnScaleGestureListener {
 
-            public boolean onTransform(TransformGestureDetector detector) {
-                Log.d("ttest", "Translation: (" + detector.getTranslateX() +
-                        ", " + detector.getTranslateY() + ")");
+            public boolean onScale(ScaleGestureDetector detector) {
                 float scale = detector.getScaleFactor();
+                
                 Log.d("ttest", "Scale: " + scale);
+                
+                // Limit the scale so our object doesn't get too big or disappear
                 if (mScale * scale > 0.1f) {
                     if (mScale * scale < 10.f) {
                         mScale *= scale;
@@ -89,16 +89,13 @@
                 } else {
                     mScale = 0.1f;
                 }
-
-                mPosX += detector.getTranslateX();
-                mPosY += detector.getTranslateY();
                 
                 Log.d("ttest", "mScale: " + mScale + " mPos: (" + mPosX + ", " + mPosY + ")");
                 
                 float sizeX = mDrawable.getIntrinsicWidth()/2;
                 float sizeY = mDrawable.getIntrinsicHeight()/2;
-                float centerX = detector.getCenterX();
-                float centerY = detector.getCenterY();
+                float centerX = detector.getFocusX();
+                float centerY = detector.getFocusY();
                 float diffX = centerX - mPosX;
                 float diffY = centerY - mPosY;
                 diffX = diffX*scale - diffX;
@@ -115,24 +112,20 @@
                 return true;
             }
 
-            public boolean onTransformBegin(TransformGestureDetector detector) {
+            public boolean onScaleBegin(ScaleGestureDetector detector) {
                 return true;
             }
 
-            public boolean onTransformEnd(TransformGestureDetector detector) {
-                return true;
-            }
-
-            public boolean onTransformFling(TransformGestureDetector detector) {
-                return false;
-            }
-            
+            public void onScaleEnd(ScaleGestureDetector detector) {
+                mLastX = detector.getFocusX();
+                mLastY = detector.getFocusY();
+            }            
         }
         
         public TransformView(Context context) {
             super(context);
             mMatrix = new Matrix();
-            mDetector = new TransformGestureDetector(context, new Listener());
+            mDetector = new ScaleGestureDetector(context, new Listener());
             DisplayMetrics metrics = context.getResources().getDisplayMetrics();
             mPosX = metrics.widthPixels/2;
             mPosY = metrics.heightPixels/2;
@@ -151,12 +144,37 @@
         
         @Override
         public boolean onTouchEvent(MotionEvent event) {
-            boolean handled = mDetector.onTouchEvent(event);
+            mDetector.onTouchEvent(event);
             
-            int pointerCount = event.getPointerCount();
-            Log.d("ttest", "pointerCount: " + pointerCount);
+            // Handling single finger pan
+            if (!mDetector.isInProgress()) {
+                switch (event.getAction()) {
+                    case MotionEvent.ACTION_DOWN:
+                        mLastX = event.getX();
+                        mLastY = event.getY();
+                        break;
+                        
+                    case MotionEvent.ACTION_MOVE:
+                        final float x = event.getX();
+                        final float y = event.getY();
+                        mPosX += x - mLastX;
+                        mPosY += y - mLastY;
+                        mLastX = x;
+                        mLastY = y;
+                        
+                        float sizeX = mDrawable.getIntrinsicWidth()/2;
+                        float sizeY = mDrawable.getIntrinsicHeight()/2;
+                        
+                        mMatrix.reset();
+                        mMatrix.postTranslate(-sizeX, -sizeY);
+                        mMatrix.postScale(mScale, mScale);
+                        mMatrix.postTranslate(mPosX, mPosY);
+                        invalidate();
+                        break;
+                }
+            }
 
-            return handled;
+            return true;
         }
         
         @Override
diff --git a/tools/aapt/Resource.cpp b/tools/aapt/Resource.cpp
index 02b46aa..e2aeddf 100644
--- a/tools/aapt/Resource.cpp
+++ b/tools/aapt/Resource.cpp
@@ -1727,51 +1727,59 @@
         depth++;
         String8 tag(tree.getElementName(&len));
         // printf("Depth %d tag %s\n", depth, tag.string());
+        bool keepTag = false;
         if (depth == 1) {
             if (tag != "manifest") {
                 fprintf(stderr, "ERROR: manifest does not start with <manifest> tag\n");
                 return -1;
             }
             pkg = getAttribute(tree, NULL, "package", NULL);
-        } else if (depth == 2 && tag == "application") {
-            inApplication = true;
+        } else if (depth == 2) {
+            if (tag == "application") {
+                inApplication = true;
+                keepTag = true;
+            } else if (tag == "instrumentation") {
+                keepTag = true;
+            }
         }
-        if (inApplication) {
-            if (tag == "application" || tag == "activity" || tag == "service" || tag == "receiver"
-                    || tag == "provider") {
-                String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
-                        "name", &error);
-                if (error != "") {
-                    fprintf(stderr, "ERROR: %s\n", error.string());
-                    return -1;
+        if (!keepTag && inApplication && depth == 3) {
+            if (tag == "activity" || tag == "service" || tag == "receiver" || tag == "provider") {
+                keepTag = true;
+            }
+        }
+        if (keepTag) {
+            String8 name = getAttribute(tree, "http://schemas.android.com/apk/res/android",
+                    "name", &error);
+            if (error != "") {
+                fprintf(stderr, "ERROR: %s\n", error.string());
+                return -1;
+            }
+            if (name.length() > 0) {
+                // asdf     --> package.asdf
+                // .asdf  .a.b  --> package.asdf package.a.b
+                // asdf.adsf --> asdf.asdf
+                String8 rule("-keep class ");
+                const char* p = name.string();
+                const char* q = strchr(p, '.');
+                if (p == q) {
+                    rule += pkg;
+                    rule += name;
+                } else if (q == NULL) {
+                    rule += pkg;
+                    rule += ".";
+                    rule += name;
+                } else {
+                    rule += name;
                 }
-                if (name.length() > 0) {
-                    // asdf     --> package.asdf
-                    // .asdf  .a.b  --> package.asdf package.a.b
-                    // asdf.adsf --> asdf.asdf
-                    String8 rule("-keep class ");
-                    const char* p = name.string();
-                    const char* q = strchr(p, '.');
-                    if (p == q) {
-                        rule += pkg;
-                        rule += name;
-                    } else if (q == NULL) {
-                        rule += pkg;
-                        rule += ".";
-                        rule += name;
-                    } else {
-                        rule += name;
-                    }
 
-                    String8 location = tag;
-                    location += " ";
-                    location += assFile->getSourceFile();
-                    char lineno[20];
-                    sprintf(lineno, ":%d", tree.getLineNumber());
-                    location += lineno;
+                String8 location = tag;
+                location += " ";
+                location += assFile->getSourceFile();
+                char lineno[20];
+                sprintf(lineno, ":%d", tree.getLineNumber());
+                location += lineno;
 
-                    keep->add(rule, location);
-                }
+                keep->add(rule, location);
             }
         }
     }
diff --git a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
index ff1b295..35f022e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Bitmap.java
@@ -20,6 +20,7 @@
 import java.awt.image.BufferedImage;
 import java.io.File;
 import java.io.IOException;
+import java.io.InputStream;
 
 import javax.imageio.ImageIO;
 
@@ -33,6 +34,12 @@
         mImage = ImageIO.read(input);
     }
 
+    public Bitmap(InputStream is) throws IOException {
+        super(1, true, null, -1);
+
+        mImage = ImageIO.read(is);
+    }
+
     Bitmap(BufferedImage image) {
         super(1, true, null, -1);
         mImage = image;
@@ -237,4 +244,35 @@
         return createBitmap(colors, 0, width, width, height, config);
     }
 
+    public static Bitmap createScaledBitmap(Bitmap src, int dstWidth,
+            int dstHeight, boolean filter) {
+        Matrix m;
+        synchronized (Bitmap.class) {
+            // small pool of just 1 matrix
+            m = sScaleMatrix;
+            sScaleMatrix = null;
+        }
+
+        if (m == null) {
+            m = new Matrix();
+        }
+
+        final int width = src.getWidth();
+        final int height = src.getHeight();
+        final float sx = dstWidth  / (float)width;
+        final float sy = dstHeight / (float)height;
+        m.setScale(sx, sy);
+        Bitmap b = Bitmap.createBitmap(src, 0, 0, width, height, m, filter);
+
+        synchronized (Bitmap.class) {
+            // do we need to check for null? why not just assign everytime?
+            if (sScaleMatrix == null) {
+                sScaleMatrix = m;
+            }
+        }
+
+        return b;
+    }
+
+
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
new file mode 100644
index 0000000..e978fe8
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapFactory.java
@@ -0,0 +1,566 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+import android.content.res.AssetManager;
+import android.content.res.Resources;
+import android.util.DisplayMetrics;
+import android.util.TypedValue;
+
+import java.io.BufferedInputStream;
+import java.io.FileDescriptor;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Creates Bitmap objects from various sources, including files, streams,
+ * and byte-arrays.
+ */
+public class BitmapFactory {
+    public static class Options {
+        /**
+         * Create a default Options object, which if left unchanged will give
+         * the same result from the decoder as if null were passed.
+         */
+        public Options() {
+            inDither = true;
+            inScaled = true;
+        }
+
+        /**
+         * If set to true, the decoder will return null (no bitmap), but
+         * the out... fields will still be set, allowing the caller to query
+         * the bitmap without having to allocate the memory for its pixels.
+         */
+        public boolean inJustDecodeBounds;
+
+        /**
+         * If set to a value > 1, requests the decoder to subsample the original
+         * image, returning a smaller image to save memory. The sample size is
+         * the number of pixels in either dimension that correspond to a single
+         * pixel in the decoded bitmap. For example, inSampleSize == 4 returns
+         * an image that is 1/4 the width/height of the original, and 1/16 the
+         * number of pixels. Any value <= 1 is treated the same as 1. Note: the
+         * decoder will try to fulfill this request, but the resulting bitmap
+         * may have different dimensions that precisely what has been requested.
+         * Also, powers of 2 are often faster/easier for the decoder to honor.
+         */
+        public int inSampleSize;
+
+        /**
+         * If this is non-null, the decoder will try to decode into this
+         * internal configuration. If it is null, or the request cannot be met,
+         * the decoder will try to pick the best matching config based on the
+         * system's screen depth, and characteristics of the original image such
+         * as if it has per-pixel alpha (requiring a config that also does).
+         */
+        public Bitmap.Config inPreferredConfig;
+
+        /**
+         * If dither is true, the decoder will attempt to dither the decoded
+         * image.
+         */
+        public boolean inDither;
+
+        /**
+         * The pixel density to use for the bitmap.  This will always result
+         * in the returned bitmap having a density set for it (see
+         * {@link Bitmap#setDensity(int) Bitmap.setDensity(int)).  In addition,
+         * if {@link #inScaled} is set (which it is by default} and this
+         * density does not match {@link #inTargetDensity}, then the bitmap
+         * will be scaled to the target density before being returned.
+         *
+         * <p>If this is 0,
+         * {@link BitmapFactory#decodeResource(Resources, int)},
+         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+         * and {@link BitmapFactory#decodeResourceStream}
+         * will fill in the density associated with the resource.  The other
+         * functions will leave it as-is and no density will be applied.
+         *
+         * @see #inTargetDensity
+         * @see #inScreenDensity
+         * @see #inScaled
+         * @see Bitmap#setDensity(int)
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inDensity;
+
+        /**
+         * The pixel density of the destination this bitmap will be drawn to.
+         * This is used in conjunction with {@link #inDensity} and
+         * {@link #inScaled} to determine if and how to scale the bitmap before
+         * returning it.
+         *
+         * <p>If this is 0,
+         * {@link BitmapFactory#decodeResource(Resources, int)},
+         * {@link BitmapFactory#decodeResource(Resources, int, android.graphics.BitmapFactory.Options)},
+         * and {@link BitmapFactory#decodeResourceStream}
+         * will fill in the density associated the Resources object's
+         * DisplayMetrics.  The other
+         * functions will leave it as-is and no scaling for density will be
+         * performed.
+         *
+         * @see #inDensity
+         * @see #inScreenDensity
+         * @see #inScaled
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inTargetDensity;
+
+        /**
+         * The pixel density of the actual screen that is being used.  This is
+         * purely for applications running in density compatibility code, where
+         * {@link #inTargetDensity} is actually the density the application
+         * sees rather than the real screen density.
+         *
+         * <p>By setting this, you
+         * allow the loading code to avoid scaling a bitmap that is currently
+         * in the screen density up/down to the compatibility density.  Instead,
+         * if {@link #inDensity} is the same as {@link #inScreenDensity}, the
+         * bitmap will be left as-is.  Anything using the resulting bitmap
+         * must also used {@link Bitmap#getScaledWidth(int)
+         * Bitmap.getScaledWidth} and {@link Bitmap#getScaledHeight
+         * Bitmap.getScaledHeight} to account for any different between the
+         * bitmap's density and the target's density.
+         *
+         * <p>This is never set automatically for the caller by
+         * {@link BitmapFactory} itself.  It must be explicitly set, since the
+         * caller must deal with the resulting bitmap in a density-aware way.
+         *
+         * @see #inDensity
+         * @see #inTargetDensity
+         * @see #inScaled
+         * @see android.util.DisplayMetrics#densityDpi
+         */
+        public int inScreenDensity;
+
+        /**
+         * When this flag is set, if {@link #inDensity} and
+         * {@link #inTargetDensity} are not 0, the
+         * bitmap will be scaled to match {@link #inTargetDensity} when loaded,
+         * rather than relying on the graphics system scaling it each time it
+         * is drawn to a Canvas.
+         *
+         * <p>This flag is turned on by default and should be turned off if you need
+         * a non-scaled version of the bitmap.  Nine-patch bitmaps ignore this
+         * flag and are always scaled.
+         */
+        public boolean inScaled;
+
+        /**
+         * If this is set to true, then the resulting bitmap will allocate its
+         * pixels such that they can be purged if the system needs to reclaim
+         * memory. In that instance, when the pixels need to be accessed again
+         * (e.g. the bitmap is drawn, getPixels() is called), they will be
+         * automatically re-decoded.
+         *
+         * For the re-decode to happen, the bitmap must have access to the
+         * encoded data, either by sharing a reference to the input
+         * or by making a copy of it. This distinction is controlled by
+         * inInputShareable. If this is true, then the bitmap may keep a shallow
+         * reference to the input. If this is false, then the bitmap will
+         * explicitly make a copy of the input data, and keep that. Even if
+         * sharing is allowed, the implementation may still decide to make a
+         * deep copy of the input data.
+         */
+        public boolean inPurgeable;
+
+        /**
+         * This field works in conjuction with inPurgeable. If inPurgeable is
+         * false, then this field is ignored. If inPurgeable is true, then this
+         * field determines whether the bitmap can share a reference to the
+         * input data (inputstream, array, etc.) or if it must make a deep copy.
+         */
+        public boolean inInputShareable;
+
+        /**
+         * Normally bitmap allocations count against the dalvik heap, which
+         * means they help trigger GCs when a lot have been allocated. However,
+         * in rare cases, the caller may want to allocate the bitmap outside of
+         * that heap. To request that, set inNativeAlloc to true. In these
+         * rare instances, it is solely up to the caller to ensure that OOM is
+         * managed explicitly by calling bitmap.recycle() as soon as such a
+         * bitmap is no longer needed.
+         *
+         * @hide pending API council approval
+         */
+        public boolean inNativeAlloc;
+
+        /**
+         * The resulting width of the bitmap, set independent of the state of
+         * inJustDecodeBounds. However, if there is an error trying to decode,
+         * outWidth will be set to -1.
+         */
+        public int outWidth;
+
+        /**
+         * The resulting height of the bitmap, set independent of the state of
+         * inJustDecodeBounds. However, if there is an error trying to decode,
+         * outHeight will be set to -1.
+         */
+        public int outHeight;
+
+        /**
+         * If known, this string is set to the mimetype of the decoded image.
+         * If not know, or there is an error, it is set to null.
+         */
+        public String outMimeType;
+
+        /**
+         * Temp storage to use for decoding.  Suggest 16K or so.
+         */
+        public byte[] inTempStorage;
+
+        private native void requestCancel();
+
+        /**
+         * Flag to indicate that cancel has been called on this object.  This
+         * is useful if there's an intermediary that wants to first decode the
+         * bounds and then decode the image.  In that case the intermediary
+         * can check, inbetween the bounds decode and the image decode, to see
+         * if the operation is canceled.
+         */
+        public boolean mCancel;
+
+        /**
+         *  This can be called from another thread while this options object is
+         *  inside a decode... call. Calling this will notify the decoder that
+         *  it should cancel its operation. This is not guaranteed to cancel
+         *  the decode, but if it does, the decoder... operation will return
+         *  null, or if inJustDecodeBounds is true, will set outWidth/outHeight
+         *  to -1
+         */
+        public void requestCancelDecode() {
+            mCancel = true;
+            requestCancel();
+        }
+    }
+
+    /**
+     * Decode a file path into a bitmap. If the specified file name is null,
+     * or cannot be decoded into a bitmap, the function returns null.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeFile(String pathName, Options opts) {
+        Bitmap bm = null;
+        InputStream stream = null;
+        try {
+            stream = new FileInputStream(pathName);
+            bm = decodeStream(stream, null, opts);
+        } catch (Exception e) {
+            /*  do nothing.
+                If the exception happened on open, bm will be null.
+            */
+        } finally {
+            if (stream != null) {
+                try {
+                    stream.close();
+                } catch (IOException e) {
+                    // do nothing here
+                }
+            }
+        }
+        return bm;
+    }
+
+    /**
+     * Decode a file path into a bitmap. If the specified file name is null,
+     * or cannot be decoded into a bitmap, the function returns null.
+     *
+     * @param pathName complete path name for the file to be decoded.
+     * @return the resulting decoded bitmap, or null if it could not be decoded.
+     */
+    public static Bitmap decodeFile(String pathName) {
+        return decodeFile(pathName, null);
+    }
+
+    /**
+     * Decode a new Bitmap from an InputStream. This InputStream was obtained from
+     * resources, which we pass to be able to scale the bitmap accordingly.
+     */
+    public static Bitmap decodeResourceStream(Resources res, TypedValue value,
+            InputStream is, Rect pad, Options opts) {
+
+        if (opts == null) {
+            opts = new Options();
+        }
+
+        if (opts.inDensity == 0 && value != null) {
+            final int density = value.density;
+            if (density == TypedValue.DENSITY_DEFAULT) {
+                opts.inDensity = DisplayMetrics.DENSITY_DEFAULT;
+            } else if (density != TypedValue.DENSITY_NONE) {
+                opts.inDensity = density;
+            }
+        }
+
+        if (opts.inTargetDensity == 0 && res != null) {
+            opts.inTargetDensity = res.getDisplayMetrics().densityDpi;
+        }
+
+        return decodeStream(is, pad, opts);
+    }
+
+    /**
+     * Synonym for opening the given resource and calling
+     * {@link #decodeResourceStream}.
+     *
+     * @param res   The resources object containing the image data
+     * @param id The resource id of the image data
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeResource(Resources res, int id, Options opts) {
+        Bitmap bm = null;
+        InputStream is = null;
+
+        try {
+            final TypedValue value = new TypedValue();
+            is = res.openRawResource(id, value);
+
+            bm = decodeResourceStream(res, value, is, null, opts);
+        } catch (Exception e) {
+            /*  do nothing.
+                If the exception happened on open, bm will be null.
+                If it happened on close, bm is still valid.
+            */
+        } finally {
+            try {
+                if (is != null) is.close();
+            } catch (IOException e) {
+                // Ignore
+            }
+        }
+
+        return bm;
+    }
+
+    /**
+     * Synonym for {@link #decodeResource(Resources, int, android.graphics.BitmapFactory.Options)}
+     * will null Options.
+     *
+     * @param res The resources object containing the image data
+     * @param id The resource id of the image data
+     * @return The decoded bitmap, or null if the image could not be decode.
+     */
+    public static Bitmap decodeResource(Resources res, int id) {
+        return decodeResource(res, id, null);
+    }
+
+    /**
+     * Decode an immutable bitmap from the specified byte array.
+     *
+     * @param data byte array of compressed image data
+     * @param offset offset into imageData for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeByteArray(byte[] data, int offset, int length, Options opts) {
+        if ((offset | length) < 0 || data.length < offset + length) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        // FIXME: implement as needed, but it's unlikely that this is needed in the context of the bridge.
+        return null;
+        //return nativeDecodeByteArray(data, offset, length, opts);
+    }
+
+    /**
+     * Decode an immutable bitmap from the specified byte array.
+     *
+     * @param data byte array of compressed image data
+     * @param offset offset into imageData for where the decoder should begin
+     *               parsing.
+     * @param length the number of bytes, beginning at offset, to parse
+     * @return The decoded bitmap, or null if the image could not be decode.
+     */
+    public static Bitmap decodeByteArray(byte[] data, int offset, int length) {
+        return decodeByteArray(data, offset, length, null);
+    }
+
+    /**
+     * Decode an input stream into a bitmap. If the input stream is null, or
+     * cannot be used to decode a bitmap, the function returns null.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           bitmap.
+     * @param outPadding If not null, return the padding rect for the bitmap if
+     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
+     *                   no bitmap is returned (null) then padding is
+     *                   unchanged.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeStream(InputStream is, Rect outPadding, Options opts) {
+        // we don't throw in this case, thus allowing the caller to only check
+        // the cache, and not force the image to be decoded.
+        if (is == null) {
+            return null;
+        }
+
+        // we need mark/reset to work properly
+
+        if (!is.markSupported()) {
+            is = new BufferedInputStream(is, 16 * 1024);
+        }
+
+        // so we can call reset() if a given codec gives up after reading up to
+        // this many bytes. FIXME: need to find out from the codecs what this
+        // value should be.
+        is.mark(1024);
+
+        Bitmap  bm;
+
+        if (is instanceof AssetManager.AssetInputStream) {
+            // FIXME: log this.
+            return null;
+        } else {
+            // pass some temp storage down to the native code. 1024 is made up,
+            // but should be large enough to avoid too many small calls back
+            // into is.read(...) This number is not related to the value passed
+            // to mark(...) above.
+            try {
+                bm = new Bitmap(is);
+            } catch (IOException e) {
+                return null;
+            }
+        }
+
+        return finishDecode(bm, outPadding, opts);
+    }
+
+    private static Bitmap finishDecode(Bitmap bm, Rect outPadding, Options opts) {
+        if (bm == null || opts == null) {
+            return bm;
+        }
+
+        final int density = opts.inDensity;
+        if (density == 0) {
+            return bm;
+        }
+
+        bm.setDensity(density);
+        final int targetDensity = opts.inTargetDensity;
+        if (targetDensity == 0 || density == targetDensity
+                || density == opts.inScreenDensity) {
+            return bm;
+        }
+
+        byte[] np = bm.getNinePatchChunk();
+        final boolean isNinePatch = false; //np != null && NinePatch.isNinePatchChunk(np);
+        if (opts.inScaled || isNinePatch) {
+            float scale = targetDensity / (float)density;
+            // TODO: This is very inefficient and should be done in native by Skia
+            final Bitmap oldBitmap = bm;
+            bm = Bitmap.createScaledBitmap(oldBitmap, (int) (bm.getWidth() * scale + 0.5f),
+                    (int) (bm.getHeight() * scale + 0.5f), true);
+            oldBitmap.recycle();
+
+            if (isNinePatch) {
+                //np = nativeScaleNinePatch(np, scale, outPadding);
+                bm.setNinePatchChunk(np);
+            }
+            bm.setDensity(targetDensity);
+        }
+
+        return bm;
+    }
+
+    /**
+     * Decode an input stream into a bitmap. If the input stream is null, or
+     * cannot be used to decode a bitmap, the function returns null.
+     * The stream's position will be where ever it was after the encoded data
+     * was read.
+     *
+     * @param is The input stream that holds the raw data to be decoded into a
+     *           bitmap.
+     * @return The decoded bitmap, or null if the image data could not be
+     *         decoded, or, if opts is non-null, if opts requested only the
+     *         size be returned (in opts.outWidth and opts.outHeight)
+     */
+    public static Bitmap decodeStream(InputStream is) {
+        return decodeStream(is, null, null);
+    }
+
+    /**
+     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+     * return null. The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as-is.
+     *
+     * @param fd The file descriptor containing the bitmap data to decode
+     * @param outPadding If not null, return the padding rect for the bitmap if
+     *                   it exists, otherwise set padding to [-1,-1,-1,-1]. If
+     *                   no bitmap is returned (null) then padding is
+     *                   unchanged.
+     * @param opts null-ok; Options that control downsampling and whether the
+     *             image should be completely decoded, or just is size returned.
+     * @return the decoded bitmap, or null
+     */
+    public static Bitmap decodeFileDescriptor(FileDescriptor fd, Rect outPadding, Options opts) {
+        return null;
+
+        /* FIXME: implement as needed
+        try {
+            if (MemoryFile.isMemoryFile(fd)) {
+                int mappedlength = MemoryFile.getMappedSize(fd);
+                MemoryFile file = new MemoryFile(fd, mappedlength, "r");
+                InputStream is = file.getInputStream();
+                Bitmap bm = decodeStream(is, outPadding, opts);
+                return finishDecode(bm, outPadding, opts);
+            }
+        } catch (IOException ex) {
+            // invalid filedescriptor, no need to call nativeDecodeFileDescriptor()
+            return null;
+        }
+        //Bitmap bm = nativeDecodeFileDescriptor(fd, outPadding, opts);
+        //return finishDecode(bm, outPadding, opts);
+        */
+    }
+
+    /**
+     * Decode a bitmap from the file descriptor. If the bitmap cannot be decoded
+     * return null. The position within the descriptor will not be changed when
+     * this returns, so the descriptor can be used again as is.
+     *
+     * @param fd The file descriptor containing the bitmap data to decode
+     * @return the decoded bitmap, or null
+     */
+    public static Bitmap decodeFileDescriptor(FileDescriptor fd) {
+        return decodeFileDescriptor(fd, null, null);
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
index 8bf7fcc..ad3974c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/BitmapShader.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 public class BitmapShader extends Shader {
 
     // we hold on just for the GC, since our native counterpart is using it
@@ -31,11 +33,16 @@
     public BitmapShader(Bitmap bitmap, TileMode tileX, TileMode tileY) {
         mBitmap = bitmap;
     }
-    
+
     //---------- Custom methods
-    
+
     public Bitmap getBitmap() {
         return mBitmap;
     }
+
+    @Override
+    Paint getJavaPaint() {
+        return null;
+    }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/Canvas.java b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
index 4986c77..8bf5e85 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Canvas.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Canvas.java
@@ -31,6 +31,7 @@
 import android.graphics.Region.Op;
 
 import java.awt.AlphaComposite;
+import java.awt.BasicStroke;
 import java.awt.Color;
 import java.awt.Composite;
 import java.awt.Graphics2D;
@@ -103,13 +104,37 @@
      * Creates a new {@link Graphics2D} based on the {@link Paint} parameters.
      * <p/>The object must be disposed ({@link Graphics2D#dispose()}) after being used.
      */
-    private Graphics2D getNewGraphics(Paint paint, Graphics2D g) {
+    private Graphics2D getCustomGraphics(Paint paint) {
         // make new one
+        Graphics2D g = getGraphics2d();
         g = (Graphics2D)g.create();
+
+        // configure it
         g.setColor(new Color(paint.getColor()));
         int alpha = paint.getAlpha();
         float falpha = alpha / 255.f;
 
+        Style style = paint.getStyle();
+        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+            PathEffect e = paint.getPathEffect();
+            if (e instanceof DashPathEffect) {
+                DashPathEffect dpe = (DashPathEffect)e;
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getStrokeCap().getJavaCap(),
+                        paint.getStrokeJoin().getJavaJoin(),
+                        paint.getStrokeMiter(),
+                        dpe.getIntervals(),
+                        dpe.getPhase()));
+            } else {
+                g.setStroke(new BasicStroke(
+                        paint.getStrokeWidth(),
+                        paint.getStrokeCap().getJavaCap(),
+                        paint.getStrokeJoin().getJavaJoin(),
+                        paint.getStrokeMiter()));
+            }
+        }
+
         Xfermode xfermode = paint.getXfermode();
         if (xfermode instanceof PorterDuffXfermode) {
             PorterDuff.Mode mode = ((PorterDuffXfermode)xfermode).getMode();
@@ -125,13 +150,16 @@
         }
 
         Shader shader = paint.getShader();
-        if (shader instanceof LinearGradient) {
-            g.setPaint(((LinearGradient)shader).getPaint());
-        } else {
-            if (mLogger != null && shader != null) {
-                mLogger.warning(String.format(
-                        "Shader '%1$s' is not supported in the Layout Editor.",
-                        shader.getClass().getCanonicalName()));
+        if (shader != null) {
+            java.awt.Paint shaderPaint = shader.getJavaPaint();
+            if (shaderPaint != null) {
+                g.setPaint(shaderPaint);
+            } else {
+                if (mLogger != null) {
+                    mLogger.warning(String.format(
+                            "Shader '%1$s' is not supported in the Layout Editor.",
+                            shader.getClass().getCanonicalName()));
+                }
             }
         }
 
@@ -236,10 +264,15 @@
      */
     @Override
     public int save() {
+        // get the current save count
+        int count = mGraphicsStack.size();
+
+        // create a new graphics and add it to the stack
         Graphics2D g = (Graphics2D)getGraphics2d().create();
         mGraphicsStack.push(g);
 
-        return mGraphicsStack.size() - 1;
+        // return the old save count
+        return count;
     }
 
     /* (non-Javadoc)
@@ -274,10 +307,9 @@
      */
     @Override
     public int getSaveCount() {
-        return mGraphicsStack.size() - 1;
+        return mGraphicsStack.size();
     }
 
-
     /* (non-Javadoc)
      * @see android.graphics.Canvas#clipRect(float, float, float, float, android.graphics.Region.Op)
      */
@@ -405,7 +437,7 @@
 
         g.setColor(new Color(color));
 
-        getGraphics2d().fillRect(0, 0, getWidth(), getHeight());
+        g.fillRect(0, 0, getWidth(), getHeight());
 
         g.setComposite(composite);
 
@@ -776,24 +808,24 @@
     }
 
     private final void doDrawRect(int left, int top, int width, int height, Paint paint) {
-        // get current graphisc
-        Graphics2D g = getGraphics2d();
+        if (width > 0 && height > 0) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = getCustomGraphics(paint);
 
-        g = getNewGraphics(paint, g);
+            Style style = paint.getStyle();
 
-        Style style = paint.getStyle();
+            // draw
+            if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
+                g.fillRect(left, top, width, height);
+            }
 
-        // draw
-        if (style == Style.FILL || style == Style.FILL_AND_STROKE) {
-            g.fillRect(left, top, width, height);
+            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+                g.drawRect(left, top, width, height);
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
         }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawRect(left, top, width, height);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
     }
 
     /* (non-Javadoc)
@@ -801,30 +833,30 @@
      */
     @Override
     public void drawRoundRect(RectF rect, float rx, float ry, Paint paint) {
-        // get current graphisc
-        Graphics2D g = getGraphics2d();
+        if (rect.width() > 0 && rect.height() > 0) {
+            // get a Graphics2D object configured with the drawing parameters.
+            Graphics2D g = getCustomGraphics(paint);
 
-        g = getNewGraphics(paint, g);
+            Style style = paint.getStyle();
 
-        Style style = paint.getStyle();
+            // draw
 
-        // draw
+            int arcWidth = (int)(rx * 2);
+            int arcHeight = (int)(ry * 2);
 
-        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);
+            }
 
-        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);
+            if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
+                g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
+                        arcWidth, arcHeight);
+            }
+
+            // dispose Graphics2D object
+            g.dispose();
         }
-
-        if (style == Style.STROKE || style == Style.FILL_AND_STROKE) {
-            g.drawRoundRect((int)rect.left, (int)rect.top, (int)rect.width(), (int)rect.height(),
-                    arcWidth, arcHeight);
-        }
-
-        // dispose Graphics2D object
-        g.dispose();
     }
 
 
@@ -833,10 +865,8 @@
      */
     @Override
     public void drawLine(float startX, float startY, float stopX, float stopY, Paint paint) {
-        // get current graphisc
-        Graphics2D g = getGraphics2d();
-
-        g = getNewGraphics(paint, g);
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = getCustomGraphics(paint);
 
         g.drawLine((int)startX, (int)startY, (int)stopX, (int)stopY);
 
@@ -849,10 +879,8 @@
      */
     @Override
     public void drawLines(float[] pts, int offset, int count, Paint paint) {
-        // get current graphisc
-        Graphics2D g = getGraphics2d();
-
-        g = getNewGraphics(paint, g);
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = getCustomGraphics(paint);
 
         for (int i = 0 ; i < count ; i += 4) {
             g.drawLine((int)pts[i + offset], (int)pts[i + offset + 1],
@@ -876,10 +904,8 @@
      */
     @Override
     public void drawCircle(float cx, float cy, float radius, Paint paint) {
-        // get current graphisc
-        Graphics2D g = getGraphics2d();
-
-        g = getNewGraphics(paint, g);
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = getCustomGraphics(paint);
 
         Style style = paint.getStyle();
 
@@ -903,10 +929,8 @@
      */
     @Override
     public void drawOval(RectF oval, Paint paint) {
-        // get current graphics
-        Graphics2D g = getGraphics2d();
-
-        g = getNewGraphics(paint, g);
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = getCustomGraphics(paint);
 
         Style style = paint.getStyle();
 
@@ -928,10 +952,8 @@
      */
     @Override
     public void drawPath(Path path, Paint paint) {
-        // get current graphics
-        Graphics2D g = getGraphics2d();
-
-        g = getNewGraphics(paint, g);
+        // get a Graphics2D object configured with the drawing parameters.
+        Graphics2D g = getCustomGraphics(paint);
 
         Style style = paint.getStyle();
 
@@ -953,10 +975,6 @@
      */
     @Override
     public void setMatrix(Matrix matrix) {
-        // since SetMatrix *replaces* all the other transformation, we have to restore/save
-        restore();
-        save();
-
         // get the new current graphics
         Graphics2D g = getGraphics2d();
 
@@ -968,6 +986,27 @@
         }
     }
 
+    /* (non-Javadoc)
+     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
+     */
+    @Override
+    public void concat(Matrix matrix) {
+        // get the current top graphics2D object.
+        Graphics2D g = getGraphics2d();
+
+        // get its current matrix
+        AffineTransform currentTx = g.getTransform();
+        // get the AffineTransform of the given matrix
+        AffineTransform matrixTx = matrix.getTransform();
+
+        // combine them so that the given matrix is applied after.
+        currentTx.preConcatenate(matrixTx);
+
+        // give it to the graphics2D as a new matrix replacing all previous transform
+        g.setTransform(currentTx);
+    }
+
+
     // --------------------
 
     /* (non-Javadoc)
@@ -1008,15 +1047,6 @@
     }
 
     /* (non-Javadoc)
-     * @see android.graphics.Canvas#concat(android.graphics.Matrix)
-     */
-    @Override
-    public void concat(Matrix matrix) {
-        // TODO Auto-generated method stub
-        super.concat(matrix);
-    }
-
-    /* (non-Javadoc)
      * @see android.graphics.Canvas#drawArc(android.graphics.RectF, float, float, boolean, android.graphics.Paint)
      */
     @Override
diff --git a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
index 968a597..863d64a 100644
--- a/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/ComposeShader.java
@@ -16,6 +16,8 @@
 
 package android.graphics;
 
+import java.awt.Paint;
+
 /** A subclass of shader that returns the composition of two other shaders, combined by
     an {@link android.graphics.Xfermode} subclass.
 */
@@ -42,5 +44,10 @@
     public ComposeShader(Shader shaderA, Shader shaderB, PorterDuff.Mode mode) {
         // FIXME Implement shader
     }
+
+    @Override
+    Paint getJavaPaint() {
+        return null;
+    }
 }
 
diff --git a/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
new file mode 100644
index 0000000..46d4c70
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/DashPathEffect.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+public class DashPathEffect extends PathEffect {
+
+    private final float[] mIntervals;
+    private final float mPhase;
+
+    /**
+     * The intervals array must contain an even number of entries (>=2), with
+     * the even indices specifying the "on" intervals, and the odd indices
+     * specifying the "off" intervals. phase is an offset into the intervals
+     * array (mod the sum of all of the intervals). The intervals array
+     * controls the length of the dashes. The paint's strokeWidth controls the
+     * thickness of the dashes.
+     * Note: this patheffect only affects drawing with the paint's style is set
+     * to STROKE or STROKE_AND_FILL. It is ignored if the drawing is done with
+     * style == FILL.
+     * @param intervals array of ON and OFF distances
+     * @param phase offset into the intervals array
+     */
+    public DashPathEffect(float intervals[], float phase) {
+        if (intervals.length < 2) {
+            throw new ArrayIndexOutOfBoundsException();
+        }
+
+        mIntervals = intervals;
+        mPhase = phase;
+    }
+
+    public float[] getIntervals() {
+        return mIntervals;
+    }
+
+    public float getPhase() {
+        return mPhase;
+    }
+}
+
diff --git a/tools/layoutlib/bridge/src/android/graphics/GradientShader.java b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
new file mode 100644
index 0000000..8c5a2a4
--- /dev/null
+++ b/tools/layoutlib/bridge/src/android/graphics/GradientShader.java
@@ -0,0 +1,205 @@
+/*
+ * Copyright (C) 2010 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.graphics;
+
+
+/**
+ * Base class for Gradient shader. This is not a standard android class and is just used
+ * as a base class for the re-implemented gradient classes.
+ *
+ * It also provides a base class to handle common code between the different shaders'
+ * implementations of {@link java.awt.Paint}.
+ *
+ * @see LinearGradient
+ * @see RadialGradient
+ * @see SweepGradient
+ */
+public abstract class GradientShader extends Shader {
+
+    protected final int[] mColors;
+    protected final float[] mPositions;
+
+    /**
+     * Creates the base shader and do some basic test on the parameters.
+     *
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     */
+    protected GradientShader(int colors[], float positions[]) {
+        if (colors.length < 2) {
+            throw new IllegalArgumentException("needs >= 2 number of colors");
+        }
+        if (positions != null && colors.length != positions.length) {
+            throw new IllegalArgumentException("color and position arrays must be of equal length");
+        }
+
+        if (positions == null) {
+            float spacing = 1.f / (colors.length - 1);
+            positions = new float[colors.length];
+            positions[0] = 0.f;
+            positions[colors.length-1] = 1.f;
+            for (int i = 1; i < colors.length - 1 ; i++) {
+                positions[i] = spacing * i;
+            }
+        }
+
+        mColors = colors;
+        mPositions = positions;
+    }
+
+    /**
+     * Base class for (Java) Gradient Paints. This handles computing the gradient colors based
+     * on the color and position lists, as well as the {@link TileMode}
+     *
+     */
+    protected abstract static class GradientPaint implements java.awt.Paint {
+        private final static int GRADIENT_SIZE = 100;
+
+        private final int[] mColors;
+        private final float[] mPositions;
+        private final TileMode mTileMode;
+        private int[] mGradient;
+
+        protected GradientPaint(int[] colors, float[] positions, TileMode tileMode) {
+            mColors = colors;
+            mPositions = positions;
+            mTileMode = tileMode;
+        }
+
+        public int getTransparency() {
+            return java.awt.Paint.TRANSLUCENT;
+        }
+
+        /**
+         * Pre-computes the colors for the gradient. This must be called once before any call
+         * to {@link #getGradientColor(float)}
+         */
+        protected synchronized void precomputeGradientColors() {
+            if (mGradient == null) {
+                // actually create an array with an extra size, so that we can really go
+                // from 0 to SIZE (100%), or currentPos in the loop below will never equal 1.0
+                mGradient = new int[GRADIENT_SIZE+1];
+
+                int prevPos = 0;
+                int nextPos = 1;
+                for (int i  = 0 ; i <= GRADIENT_SIZE ; i++) {
+                    // compute current position
+                    float currentPos = (float)i/GRADIENT_SIZE;
+                    while (currentPos > mPositions[nextPos]) {
+                        prevPos = nextPos++;
+                    }
+
+                    float percent = (currentPos - mPositions[prevPos]) /
+                            (mPositions[nextPos] - mPositions[prevPos]);
+
+                    mGradient[i] = computeColor(mColors[prevPos], mColors[nextPos], percent);
+                }
+            }
+        }
+
+        /**
+         * Returns the color based on the position in the gradient.
+         * <var>pos</var> can be anything, even &lt; 0 or &gt; > 1, as the gradient
+         * will use {@link TileMode} value to convert it into a [0,1] value.
+         */
+        protected int getGradientColor(float pos) {
+            if (pos < 0.f) {
+                if (mTileMode != null) {
+                    switch (mTileMode) {
+                        case CLAMP:
+                            pos = 0.f;
+                            break;
+                        case REPEAT:
+                            // remove the integer part to stay in the [0,1] range
+                            // careful: this is a negative value, so use ceil instead of floor
+                            pos = pos - (float)Math.ceil(pos);
+                            break;
+                        case MIRROR:
+                            // get the integer and the decimal part
+                            // careful: this is a negative value, so use ceil instead of floor
+                            int intPart = (int)Math.ceil(pos);
+                            pos = pos - intPart;
+                            // 0  -> -1 : mirrored order
+                            // -1 -> -2: normal order
+                            // etc..
+                            // this means if the intpart is even we invert
+                            if ((intPart % 2) == 0) {
+                                pos = 1.f - pos;
+                            }
+                            break;
+                    }
+                } else {
+                    pos = 0.0f;
+                }
+            } else if (pos > 1f) {
+                if (mTileMode != null) {
+                    switch (mTileMode) {
+                        case CLAMP:
+                            pos = 1.f;
+                            break;
+                        case REPEAT:
+                            // remove the integer part to stay in the [0,1] range
+                            pos = pos - (float)Math.floor(pos);
+                            break;
+                        case MIRROR:
+                            // get the integer and the decimal part
+                            int intPart = (int)Math.floor(pos);
+                            pos = pos - intPart;
+                            // 0 -> 1 : normal order
+                            // 1 -> 2: mirrored
+                            // etc..
+                            // this means if the intpart is odd we invert
+                            if ((intPart % 2) == 1) {
+                                pos = 1.f - pos;
+                            }
+                            break;
+                    }
+                } else {
+                    pos = 1.0f;
+                }
+            }
+
+            int index = (int)((pos * GRADIENT_SIZE) + .5);
+
+            return mGradient[index];
+        }
+
+        /**
+         * Returns the color between c1, and c2, based on the percent of the distance
+         * between c1 and c2.
+         */
+        private int computeColor(int c1, int c2, float percent) {
+            int a = computeChannel((c1 >> 24) & 0xFF, (c2 >> 24) & 0xFF, percent);
+            int r = computeChannel((c1 >> 16) & 0xFF, (c2 >> 16) & 0xFF, percent);
+            int g = computeChannel((c1 >>  8) & 0xFF, (c2 >>  8) & 0xFF, percent);
+            int b = computeChannel((c1      ) & 0xFF, (c2      ) & 0xFF, percent);
+            return a << 24 | r << 16 | g << 8 | b;
+        }
+
+        /**
+         * Returns the channel value between 2 values based on the percent of the distance between
+         * the 2 values..
+         */
+        private int computeChannel(int c1, int c2, float percent) {
+            return c1 + (int)((percent * (c2-c1)) + .5);
+        }
+
+
+    }
+}
diff --git a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
index 1a0dc05..10c4a5e 100644
--- a/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/LinearGradient.java
@@ -16,56 +16,158 @@
 
 package android.graphics;
 
-import java.awt.GradientPaint;
-import java.awt.Color;
-import java.awt.Paint;
+public class LinearGradient extends GradientShader {
 
-public class LinearGradient extends Shader {
-    
-    private GradientPaint mGradientPaint;
+    private java.awt.Paint mJavaPaint;
 
-    /** Create a shader that draws a linear gradient along a line.
-        @param x0           The x-coordinate for the start of the gradient line
-        @param y0           The y-coordinate for the start of the gradient line
-        @param x1           The x-coordinate for the end of the gradient line
-        @param y1           The y-coordinate for the end of the gradient line
-        @param  colors      The colors to be distributed along the gradient line
-        @param  positions   May be null. The relative positions [0..1] of
-                            each corresponding color in the colors array. If this is null,
-                            the the colors are distributed evenly along the gradient line.
-        @param  tile        The Shader tiling mode
-    */
-    public LinearGradient(float x0, float y0, float x1, float y1,
-                          int colors[], float positions[], TileMode tile) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (positions != null && colors.length != positions.length) {
-            throw new IllegalArgumentException("color and position arrays must be of equal length");
-        }
-        
-        // FIXME implement multi color linear gradient
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param colors The colors to be distributed along the gradient line
+     * @param positions May be null. The relative positions [0..1] of each
+     *            corresponding color in the colors array. If this is null, the
+     *            the colors are distributed evenly along the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, int colors[], float positions[],
+            TileMode tile) {
+        super(colors, positions);
+        mJavaPaint = new LinearGradientPaint(x0, y0, x1, y1, mColors, mPositions, tile);
     }
 
-    /** Create a shader that draws a linear gradient along a line.
-        @param x0       The x-coordinate for the start of the gradient line
-        @param y0       The y-coordinate for the start of the gradient line
-        @param x1       The x-coordinate for the end of the gradient line
-        @param y1       The y-coordinate for the end of the gradient line
-        @param  color0  The color at the start of the gradient line.
-        @param  color1  The color at the end of the gradient line.
-        @param  tile    The Shader tiling mode
-    */
-    public LinearGradient(float x0, float y0, float x1, float y1,
-                          int color0, int color1, TileMode tile) {
-        mGradientPaint = new GradientPaint(x0, y0, new Color(color0, true /* hasalpha */),
-                x1,y1, new Color(color1, true /* hasalpha */), tile != TileMode.CLAMP);
+    /**
+     * Create a shader that draws a linear gradient along a line.
+     *
+     * @param x0 The x-coordinate for the start of the gradient line
+     * @param y0 The y-coordinate for the start of the gradient line
+     * @param x1 The x-coordinate for the end of the gradient line
+     * @param y1 The y-coordinate for the end of the gradient line
+     * @param color0 The color at the start of the gradient line.
+     * @param color1 The color at the end of the gradient line.
+     * @param tile The Shader tiling mode
+     */
+    public LinearGradient(float x0, float y0, float x1, float y1, int color0, int color1,
+            TileMode tile) {
+        this(x0, y0, x1, y1, new int[] { color0, color1}, null /*positions*/, tile);
     }
-    
-    //---------- Custom Methods
-    
-    public Paint getPaint() {
-        return mGradientPaint;
+
+    // ---------- Custom Methods
+
+    @Override
+    java.awt.Paint getJavaPaint() {
+        return mJavaPaint;
+    }
+
+    /**
+     * Linear Gradient (Java) Paint able to handle more than 2 points, as
+     * {@link java.awt.GradientPaint} only supports 2 points and does not support Android's tile
+     * modes.
+     */
+    private static class LinearGradientPaint extends GradientPaint {
+
+        private final float mX0;
+        private final float mY0;
+        private final float mDx;
+        private final float mDy;
+        private final float mDSize2;
+
+        public LinearGradientPaint(float x0, float y0, float x1, float y1, int colors[],
+                float positions[], TileMode tile) {
+            super(colors, positions, tile);
+                mX0 = x0;
+                mY0 = y0;
+                mDx = x1 - x0;
+                mDy = y1 - y0;
+                mDSize2 = mDx * mDx + mDy * mDy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel      colorModel,
+                java.awt.Rectangle             deviceBounds,
+                java.awt.geom.Rectangle2D      userBounds,
+                java.awt.geom.AffineTransform  xform,
+                java.awt.RenderingHints        hints) {
+            precomputeGradientColors();
+            return new LinearGradientPaintContext(colorModel);
+        }
+
+        private class LinearGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public LinearGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+                // FIXME: so far all this is always the same rect gotten in getRaster with an indentity matrix?
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                if (mDx == 0) { // vertical gradient
+                    // compute first column and copy to all other columns
+                    int index = 0;
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        int color = getColor(iy + y, mY0, mDy);
+                        for (int ix = 0 ; ix < w ; ix++) {
+                            data[index++] = color;
+                        }
+                    }
+                } else if (mDy == 0) { // horizontal
+                    // compute first line in a tmp array and copy to all lines
+                    int[] line = new int[w];
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        line[ix] = getColor(ix + x, mX0, mDx);
+                    }
+
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        System.arraycopy(line, 0, data, iy*w, line.length);
+                    }
+                } else {
+                    int index = 0;
+                    for (int iy = 0 ; iy < h ; iy++) {
+                        for (int ix = 0 ; ix < w ; ix++) {
+                            data[index++] = getColor(ix + x, iy + y);
+                        }
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+        }
+
+        /** Returns a color for the easy vertical/horizontal mode */
+        private int getColor(float absPos, float refPos, float refSize) {
+            float pos = (absPos - refPos) / refSize;
+
+            return getGradientColor(pos);
+        }
+
+        /**
+         * Returns a color for an arbitrary point.
+         */
+        private int getColor(float x, float y) {
+            // find the x position on the gradient vector.
+            float _x = (mDx*mDy*(y-mY0) + mDy*mDy*mX0 + mDx*mDx*x) / mDSize2;
+            // from it get the position relative to the vector
+            float pos = (float) ((_x - mX0) / mDx);
+
+            return getGradientColor(pos);
+        }
     }
 }
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/Matrix.java b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
index 3974e08..522415c 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Matrix.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Matrix.java
@@ -87,8 +87,12 @@
     }
 
     public AffineTransform getTransform() {
-        return new AffineTransform(mValues[0], mValues[1], mValues[2],
-                mValues[3], mValues[4], mValues[5]);
+        // the AffineTransform constructor takes the value in a different order
+        // for a matrix [ 0 1 2 ]
+        //              [ 3 4 5 ]
+        // the order is 0, 3, 1, 4, 2, 5...
+        return new AffineTransform(mValues[0], mValues[3], mValues[1],
+                mValues[4], mValues[2], mValues[5]);
     }
 
     public boolean hasPerspective() {
diff --git a/tools/layoutlib/bridge/src/android/graphics/Paint.java b/tools/layoutlib/bridge/src/android/graphics/Paint.java
index f3af133..2d03618 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Paint.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Paint.java
@@ -21,6 +21,7 @@
 import android.text.SpannedString;
 import android.text.TextUtils;
 
+import java.awt.BasicStroke;
 import java.awt.Font;
 import java.awt.Toolkit;
 import java.awt.font.FontRenderContext;
@@ -59,23 +60,14 @@
     private final FontRenderContext mFontContext = new FontRenderContext(
             new AffineTransform(), true, true);
 
-    @SuppressWarnings("hiding")
     public static final int ANTI_ALIAS_FLAG       = _Original_Paint.ANTI_ALIAS_FLAG;
-    @SuppressWarnings("hiding")
     public static final int FILTER_BITMAP_FLAG    = _Original_Paint.FILTER_BITMAP_FLAG;
-    @SuppressWarnings("hiding")
     public static final int DITHER_FLAG           = _Original_Paint.DITHER_FLAG;
-    @SuppressWarnings("hiding")
     public static final int UNDERLINE_TEXT_FLAG   = _Original_Paint.UNDERLINE_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int STRIKE_THRU_TEXT_FLAG = _Original_Paint.STRIKE_THRU_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int FAKE_BOLD_TEXT_FLAG   = _Original_Paint.FAKE_BOLD_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int LINEAR_TEXT_FLAG      = _Original_Paint.LINEAR_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int SUBPIXEL_TEXT_FLAG    = _Original_Paint.SUBPIXEL_TEXT_FLAG;
-    @SuppressWarnings("hiding")
     public static final int DEV_KERN_TEXT_FLAG    = _Original_Paint.DEV_KERN_TEXT_FLAG;
 
     public static class FontMetrics extends _Original_Paint.FontMetrics {
@@ -136,6 +128,19 @@
             this.nativeInt = nativeInt;
         }
         final int nativeInt;
+
+        /** custom for layoutlib */
+        public int getJavaCap() {
+            switch (this) {
+                case BUTT:
+                    return BasicStroke.CAP_BUTT;
+                case ROUND:
+                    return BasicStroke.CAP_ROUND;
+                default:
+                case SQUARE:
+                    return BasicStroke.CAP_SQUARE;
+            }
+        }
     }
 
     /**
@@ -160,6 +165,19 @@
             this.nativeInt = nativeInt;
         }
         final int nativeInt;
+
+        /** custom for layoutlib */
+        public int getJavaJoin() {
+            switch (this) {
+                default:
+                case MITER:
+                    return BasicStroke.JOIN_MITER;
+                case ROUND:
+                    return BasicStroke.JOIN_ROUND;
+                case BEVEL:
+                    return BasicStroke.JOIN_BEVEL;
+            }
+        }
     }
 
     /**
diff --git a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
index 61b693a..4409a80 100644
--- a/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/RadialGradient.java
@@ -16,47 +16,117 @@
 
 package android.graphics;
 
-public class RadialGradient extends Shader {
+public class RadialGradient extends GradientShader {
 
-   /** Create a shader that draws a radial gradient given the center and radius.
-       @param x        The x-coordinate of the center of the radius
-       @param y        The y-coordinate of the center of the radius
-       @param radius   Must be positive. The radius of the circle for this gradient
-       @param colors   The colors to be distributed between the center and edge of the circle
-       @param positions May be NULL. The relative position of
-                       each corresponding color in the colors array. If this is NULL,
-                       the the colors are distributed evenly between the center and edge of the circle.
-       @param  tile    The Shader tiling mode
-   */
-   public RadialGradient(float x, float y, float radius,
-                         int colors[], float positions[], TileMode tile) {
-       if (radius <= 0) {
-           throw new IllegalArgumentException("radius must be > 0");
-       }
-       if (colors.length < 2) {
-           throw new IllegalArgumentException("needs >= 2 number of colors");
-       }
-       if (positions != null && colors.length != positions.length) {
-           throw new IllegalArgumentException("color and position arrays must be of equal length");
-       }
+    private RadialGradientPaint mPaint;
 
-       // FIXME Implement shader
-   }
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param x The x-coordinate of the center of the radius
+     * @param y The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the circle for this
+     *            gradient
+     * @param colors The colors to be distributed between the center and edge of
+     *            the circle
+     * @param positions May be NULL. The relative position of each corresponding
+     *            color in the colors array. If this is NULL, the the colors are
+     *            distributed evenly between the center and edge of the circle.
+     * @param tile The Shader tiling mode
+     */
+    public RadialGradient(float x, float y, float radius, int colors[], float positions[],
+            TileMode tile) {
+        super(colors, positions);
+        if (radius <= 0) {
+            throw new IllegalArgumentException("radius must be > 0");
+        }
 
-   /** Create a shader that draws a radial gradient given the center and radius.
-       @param x        The x-coordinate of the center of the radius
-       @param y        The y-coordinate of the center of the radius
-       @param radius   Must be positive. The radius of the circle for this gradient
-       @param color0   The color at the center of the circle.
-       @param color1   The color at the edge of the circle.
-       @param tile     The Shader tiling mode
-   */
-   public RadialGradient(float x, float y, float radius,
-                         int color0, int color1, TileMode tile) {
-       if (radius <= 0) {
-           throw new IllegalArgumentException("radius must be > 0");
-       }
-       // FIXME Implement shader
-   }
+        mPaint = new RadialGradientPaint(x, y, radius, mColors, mPositions, tile);
+    }
+
+    /**
+     * Create a shader that draws a radial gradient given the center and radius.
+     *
+     * @param x The x-coordinate of the center of the radius
+     * @param y The y-coordinate of the center of the radius
+     * @param radius Must be positive. The radius of the circle for this
+     *            gradient
+     * @param color0 The color at the center of the circle.
+     * @param color1 The color at the edge of the circle.
+     * @param tile The Shader tiling mode
+     */
+    public RadialGradient(float x, float y, float radius, int color0, int color1, TileMode tile) {
+        this(x, y, radius, new int[] { color0, color1 }, null /* positions */, tile);
+    }
+
+    @Override
+    java.awt.Paint getJavaPaint() {
+        return mPaint;
+    }
+
+    private static class RadialGradientPaint extends GradientPaint {
+
+        private final float mX;
+        private final float mY;
+        private final float mRadius;
+
+        public RadialGradientPaint(float x, float y, float radius, int[] colors, float[] positions, TileMode mode) {
+            super(colors, positions, mode);
+            mX = x;
+            mY = y;
+            mRadius = radius;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+            return new RadialGradientPaintContext(colorModel);
+        }
+
+        private class RadialGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public RadialGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute distance from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        float _x = x + ix - mX;
+                        float _y = y + iy - mY;
+                        float distance = (float) Math.sqrt(_x * _x + _y * _y);
+
+                        data[index++] = getGradientColor(distance / mRadius);
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+
 }
-
diff --git a/tools/layoutlib/bridge/src/android/graphics/Shader.java b/tools/layoutlib/bridge/src/android/graphics/Shader.java
index 3a9fda5..0cc5940 100644
--- a/tools/layoutlib/bridge/src/android/graphics/Shader.java
+++ b/tools/layoutlib/bridge/src/android/graphics/Shader.java
@@ -16,14 +16,16 @@
 
 package android.graphics;
 
+
+
 /**
  * Shader is the based class for objects that return horizontal spans of colors
  * during drawing. A subclass of Shader is installed in a Paint calling
  * paint.setShader(shader). After that any object (other than a bitmap) that is
  * drawn with that paint will get its color(s) from the shader.
  */
-public class Shader {
-    
+public abstract class Shader {
+
     private final Matrix mMatrix = new Matrix();
 
     public enum TileMode {
@@ -41,7 +43,7 @@
          * mirror images so that adjacent images always seam
          */
         MIRROR  (2);
-    
+
         TileMode(int nativeInt) {
             this.nativeInt = nativeInt;
         }
@@ -57,7 +59,7 @@
         if (localM != null) {
             localM.set(mMatrix);
         }
-        
+
         return !mMatrix.isIdentity();
     }
 
@@ -73,4 +75,9 @@
             mMatrix.reset();
         }
     }
+
+    /**
+     * Returns a java.awt.Paint object matching this shader.
+     */
+    abstract java.awt.Paint getJavaPaint();
 }
diff --git a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
index e79e970..87036ed 100644
--- a/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
+++ b/tools/layoutlib/bridge/src/android/graphics/SweepGradient.java
@@ -16,7 +16,9 @@
 
 package android.graphics;
 
-public class SweepGradient extends Shader {
+public class SweepGradient extends GradientShader {
+
+    private SweepGradientPaint mPaint;
 
     /**
      * A subclass of Shader that draws a sweep gradient around a center point.
@@ -34,15 +36,9 @@
      */
     public SweepGradient(float cx, float cy,
                          int colors[], float positions[]) {
-        if (colors.length < 2) {
-            throw new IllegalArgumentException("needs >= 2 number of colors");
-        }
-        if (positions != null && colors.length != positions.length) {
-            throw new IllegalArgumentException(
-                        "color and position arrays must be of equal length");
-        }
-        
-        // FIXME Implement shader
+        super(colors, positions);
+
+        mPaint = new SweepGradientPaint(cx, cy, mColors, mPositions);
     }
 
     /**
@@ -54,7 +50,91 @@
      * @param color1   The color to use at the end of the sweep
      */
     public SweepGradient(float cx, float cy, int color0, int color1) {
-        // FIXME Implement shader
+        this(cx, cy, new int[] { color0, color1}, null /*positions*/);
     }
+
+    @Override
+    java.awt.Paint getJavaPaint() {
+        return mPaint;
+    }
+
+    private static class SweepGradientPaint extends GradientPaint {
+
+        private final float mCx;
+        private final float mCy;
+
+        public SweepGradientPaint(float cx, float cy, int[] colors, float[] positions) {
+            super(colors, positions, null /*tileMode*/);
+            mCx = cx;
+            mCy = cy;
+        }
+
+        public java.awt.PaintContext createContext(
+                java.awt.image.ColorModel     colorModel,
+                java.awt.Rectangle            deviceBounds,
+                java.awt.geom.Rectangle2D     userBounds,
+                java.awt.geom.AffineTransform xform,
+                java.awt.RenderingHints       hints) {
+            precomputeGradientColors();
+            return new SweepGradientPaintContext(colorModel);
+        }
+
+        private class SweepGradientPaintContext implements java.awt.PaintContext {
+
+            private final java.awt.image.ColorModel mColorModel;
+
+            public SweepGradientPaintContext(java.awt.image.ColorModel colorModel) {
+                mColorModel = colorModel;
+            }
+
+            public void dispose() {
+            }
+
+            public java.awt.image.ColorModel getColorModel() {
+                return mColorModel;
+            }
+
+            public java.awt.image.Raster getRaster(int x, int y, int w, int h) {
+                java.awt.image.BufferedImage image = new java.awt.image.BufferedImage(w, h,
+                        java.awt.image.BufferedImage.TYPE_INT_ARGB);
+
+                int[] data = new int[w*h];
+
+                // compute angle from each point to the center, and figure out the distance from
+                // it.
+                int index = 0;
+                for (int iy = 0 ; iy < h ; iy++) {
+                    for (int ix = 0 ; ix < w ; ix++) {
+                        float dx = x + ix - mCx;
+                        float dy = y + iy - mCy;
+                        float angle;
+                        if (dx == 0) {
+                            angle = (float) (dy < 0 ? 3 * Math.PI / 2 : Math.PI / 2);
+                        } else if (dy == 0) {
+                            angle = (float) (dx < 0 ? Math.PI : 0);
+                        } else {
+                            angle = (float) Math.atan(dy / dx);
+                            if (dx > 0) {
+                                if (dy < 0) {
+                                    angle += Math.PI * 2;
+                                }
+                            } else {
+                                angle += Math.PI;
+                            }
+                        }
+
+                        // convert to 0-1. value and get color
+                        data[index++] = getGradientColor((float) (angle / (2 * Math.PI)));
+                    }
+                }
+
+                image.setRGB(0 /*startX*/, 0 /*startY*/, w, h, data, 0 /*offset*/, w /*scansize*/);
+
+                return image.getRaster();
+            }
+
+        }
+    }
+
 }
 
diff --git a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
index 5a13b0b..2ed8641 100644
--- a/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
+++ b/tools/layoutlib/create/src/com/android/tools/layoutlib/create/CreateInfo.java
@@ -43,9 +43,11 @@
     public final static String[] RENAMED_CLASSES =
         new String[] {
             "android.graphics.Bitmap",              "android.graphics._Original_Bitmap",
+            "android.graphics.BitmapFactory",       "android.graphics._Original_BitmapFactory",
             "android.graphics.BitmapShader",        "android.graphics._Original_BitmapShader",
             "android.graphics.Canvas",              "android.graphics._Original_Canvas",
             "android.graphics.ComposeShader",       "android.graphics._Original_ComposeShader",
+            "android.graphics.DashPathEffect",       "android.graphics._Original_DashPathEffect",
             "android.graphics.LinearGradient",      "android.graphics._Original_LinearGradient",
             "android.graphics.Matrix",              "android.graphics._Original_Matrix",
             "android.graphics.Paint",               "android.graphics._Original_Paint",