auto import from //depot/cupcake/@135843
diff --git a/core/java/android/view/AbsSavedState.java b/core/java/android/view/AbsSavedState.java
new file mode 100644
index 0000000..840d7c1
--- /dev/null
+++ b/core/java/android/view/AbsSavedState.java
@@ -0,0 +1,89 @@
+/*
+ * Copyright (C) 2006 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.os.Parcel;
+import android.os.Parcelable;
+
+/**
+ * A {@link Parcelable} implementation that should be used by inheritance
+ * hierarchies to ensure the state of all classes along the chain is saved.
+ */
+public abstract class AbsSavedState implements Parcelable {
+    public static final AbsSavedState EMPTY_STATE = new AbsSavedState() {};
+
+    private final Parcelable mSuperState;
+
+    /**
+     * Constructor used to make the EMPTY_STATE singleton
+     */
+    private AbsSavedState() {
+        mSuperState = null;
+    }
+
+    /**
+     * Constructor called by derived classes when creating their SavedState objects
+     * 
+     * @param superState The state of the superclass of this view
+     */
+    protected AbsSavedState(Parcelable superState) {
+        if (superState == null) {
+            throw new IllegalArgumentException("superState must not be null");
+        }
+        mSuperState = superState != EMPTY_STATE ? superState : null;
+    }
+
+    /**
+     * Constructor used when reading from a parcel. Reads the state of the superclass.
+     * 
+     * @param source
+     */
+    protected AbsSavedState(Parcel source) {
+        // FIXME need class loader
+        Parcelable superState = (Parcelable) source.readParcelable(null);
+         
+        mSuperState = superState != null ? superState : EMPTY_STATE;
+    }
+
+    final public Parcelable getSuperState() {
+        return mSuperState;
+    }
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel dest, int flags) {
+         dest.writeParcelable(mSuperState, flags);
+    }
+
+    public static final Parcelable.Creator<AbsSavedState> CREATOR 
+        = new Parcelable.Creator<AbsSavedState>() {
+        
+        public AbsSavedState createFromParcel(Parcel in) {
+            Parcelable superState = (Parcelable) in.readParcelable(null);
+            if (superState != null) {
+                throw new IllegalStateException("superState must be null");
+            }
+            return EMPTY_STATE;
+        }
+
+        public AbsSavedState[] newArray(int size) {
+            return new AbsSavedState[size];
+        }
+    };
+}
diff --git a/core/java/android/view/ContextMenu.java b/core/java/android/view/ContextMenu.java
new file mode 100644
index 0000000..dd1d7db
--- /dev/null
+++ b/core/java/android/view/ContextMenu.java
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2007 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.app.Activity;
+import android.graphics.drawable.Drawable;
+import android.widget.AdapterView;
+
+/**
+ * Extension of {@link Menu} for context menus providing functionality to modify
+ * the header of the context menu.
+ * <p>
+ * Context menus do not support item shortcuts and item icons.
+ * <p>
+ * To show a context menu on long click, most clients will want to call
+ * {@link Activity#registerForContextMenu} and override
+ * {@link Activity#onCreateContextMenu}.
+ */
+public interface ContextMenu extends Menu {
+    /**
+     * Sets the context menu header's title to the title given in <var>titleRes</var>
+     * resource identifier.
+     * 
+     * @param titleRes The string resource identifier used for the title.
+     * @return This ContextMenu so additional setters can be called.
+     */
+    public ContextMenu setHeaderTitle(int titleRes);
+
+    /**
+     * Sets the context menu header's title to the title given in <var>title</var>.
+     * 
+     * @param title The character sequence used for the title.
+     * @return This ContextMenu so additional setters can be called.
+     */
+    public ContextMenu setHeaderTitle(CharSequence title);
+    
+    /**
+     * Sets the context menu header's icon to the icon given in <var>iconRes</var>
+     * resource id.
+     * 
+     * @param iconRes The resource identifier used for the icon.
+     * @return This ContextMenu so additional setters can be called.
+     */
+    public ContextMenu setHeaderIcon(int iconRes);
+
+    /**
+     * Sets the context menu header's icon to the icon given in <var>icon</var>
+     * {@link Drawable}.
+     * 
+     * @param icon The {@link Drawable} used for the icon.
+     * @return This ContextMenu so additional setters can be called.
+     */
+    public ContextMenu setHeaderIcon(Drawable icon);
+    
+    /**
+     * Sets the header of the context menu to the {@link View} given in
+     * <var>view</var>. This replaces the header title and icon (and those
+     * replace this).
+     * 
+     * @param view The {@link View} used for the header.
+     * @return This ContextMenu so additional setters can be called.
+     */
+    public ContextMenu setHeaderView(View view);
+    
+    /**
+     * Clears the header of the context menu.
+     */
+    public void clearHeader();
+    
+    /**
+     * Additional information regarding the creation of the context menu.  For example,
+     * {@link AdapterView}s use this to pass the exact item position within the adapter
+     * that initiated the context menu.
+     */
+    public interface ContextMenuInfo {
+    }
+}
diff --git a/core/java/android/view/ContextThemeWrapper.java b/core/java/android/view/ContextThemeWrapper.java
new file mode 100644
index 0000000..2045a98
--- /dev/null
+++ b/core/java/android/view/ContextThemeWrapper.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2006 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.content.ContextWrapper;
+import android.content.res.Resources;
+
+/**
+ * A ContextWrapper that allows you to modify the theme from what is in the 
+ * wrapped context. 
+ */
+public class ContextThemeWrapper extends ContextWrapper {
+    private Context mBase;
+    private int mThemeResource;
+    private Resources.Theme mTheme;
+    private LayoutInflater mInflater;
+
+    public ContextThemeWrapper() {
+        super(null);
+    }
+    
+    public ContextThemeWrapper(Context base, int themeres) {
+        super(base);
+        mBase = base;
+        mThemeResource = themeres;
+    }
+
+    @Override protected void attachBaseContext(Context newBase) {
+        super.attachBaseContext(newBase);
+        mBase = newBase;
+    }
+    
+    @Override public void setTheme(int resid) {
+        mThemeResource = resid;
+        initializeTheme();
+    }
+    
+    @Override public Resources.Theme getTheme() {
+        if (mTheme != null) {
+            return mTheme;
+        }
+
+        if (mThemeResource == 0) {
+            mThemeResource = com.android.internal.R.style.Theme;
+        }
+        initializeTheme();
+
+        return mTheme;
+    }
+
+    @Override public Object getSystemService(String name) {
+        if (LAYOUT_INFLATER_SERVICE.equals(name)) {
+            if (mInflater == null) {
+                mInflater = LayoutInflater.from(mBase).cloneInContext(this);
+            }
+            return mInflater;
+        }
+        return mBase.getSystemService(name);
+    }
+    
+    /**
+     * Called by {@link #setTheme} and {@link #getTheme} to apply a theme
+     * resource to the current Theme object.  Can override to change the
+     * default (simple) behavior.  This method will not be called in multiple
+     * threads simultaneously.
+     *
+     * @param theme The Theme object being modified.
+     * @param resid The theme style resource being applied to <var>theme</var>.
+     * @param first Set to true if this is the first time a style is being
+     *              applied to <var>theme</var>.
+     */
+    protected void onApplyThemeResource(Resources.Theme theme, int resid, boolean first) {
+        theme.applyStyle(resid, true);
+    }
+
+    private void initializeTheme() {
+        final boolean first = mTheme == null;
+        if (first) {
+            mTheme = getResources().newTheme();
+            Resources.Theme theme = mBase.getTheme();
+            if (theme != null) {
+                mTheme.setTo(theme);
+            }
+        }
+        onApplyThemeResource(mTheme, mThemeResource, first);
+    }
+}
+
diff --git a/core/java/android/view/Display.java b/core/java/android/view/Display.java
new file mode 100644
index 0000000..09ebeed5
--- /dev/null
+++ b/core/java/android/view/Display.java
@@ -0,0 +1,121 @@
+/*
+ * Copyright (C) 2006 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.util.DisplayMetrics;
+
+public class Display
+{
+    /**
+     * Specify the default Display
+     */
+    public static final int DEFAULT_DISPLAY = 0;
+
+    
+    /**
+     * Use the WindowManager interface to create a Display object.
+     * Display gives you access to some information about a particular display
+     * connected to the device.
+     */
+    Display(int display) {
+        // initalize the statics when this class is first instansiated. This is
+        // done here instead of in the static block because Zygote
+        synchronized (mStaticInit) {
+            if (!mInitialized) {
+                nativeClassInit();
+                mInitialized = true;
+            }
+        }
+        mDisplay = display;
+        init(display);
+    }
+    
+    /**
+     * @return index of this display.
+     */
+    public int getDisplayId() {
+        return mDisplay;
+    }
+
+    /**
+     * @return the number of displays connected to the device.
+     */
+    native static int getDisplayCount();
+    
+    /**
+     * @return width of this display in pixels.
+     */
+    native public int getWidth();
+    
+    /**
+     * @return height of this display in pixels.
+     */
+    native public int getHeight();
+
+    /**
+     * @return orientation of this display.
+     */
+    native public int getOrientation();
+
+    /**
+     * @return pixel format of this display.
+     */
+    public int getPixelFormat() {
+        return mPixelFormat;
+    }
+    
+    /**
+     * @return refresh rate of this display in frames per second.
+     */
+    public float getRefreshRate() {
+        return mRefreshRate;
+    }
+    
+    /**
+     * Initialize a DisplayMetrics object from this display's data.
+     * 
+     * @param outMetrics
+     */
+    public void getMetrics(DisplayMetrics outMetrics) {
+        outMetrics.widthPixels  = getWidth();
+        outMetrics.heightPixels = getHeight();
+        outMetrics.density      = mDensity;
+        outMetrics.scaledDensity= outMetrics.density;
+        outMetrics.xdpi         = mDpiX;
+        outMetrics.ydpi         = mDpiY;
+    }
+
+    /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+    native private static void nativeClassInit();
+    
+    private native void init(int display);
+
+    private int         mDisplay;
+    // Following fields are initialized from native code
+    private int         mPixelFormat;
+    private float       mRefreshRate;
+    private float       mDensity;
+    private float       mDpiX;
+    private float       mDpiY;
+    
+    private static final Object mStaticInit = new Object();
+    private static boolean mInitialized = false;
+}
+
diff --git a/core/java/android/view/FocusFinder.java b/core/java/android/view/FocusFinder.java
new file mode 100644
index 0000000..15fb839
--- /dev/null
+++ b/core/java/android/view/FocusFinder.java
@@ -0,0 +1,480 @@
+/*
+ * Copyright (C) 2007 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.graphics.Rect;
+
+import java.util.ArrayList;
+
+/**
+ * The algorithm used for finding the next focusable view in a given direction
+ * from a view that currently has focus.
+ */
+public class FocusFinder {
+
+    private static ThreadLocal<FocusFinder> tlFocusFinder =
+            new ThreadLocal<FocusFinder>() {
+
+                protected FocusFinder initialValue() {
+                    return new FocusFinder();
+                }
+            };
+
+    /**
+     * Get the focus finder for this thread.
+     */
+    public static FocusFinder getInstance() {
+        return tlFocusFinder.get();
+    }
+
+    Rect mFocusedRect = new Rect();
+    Rect mOtherRect = new Rect();
+    Rect mBestCandidateRect = new Rect();
+
+    // enforce thread local access
+    private FocusFinder() {}
+
+    /**
+     * Find the next view to take focus in root's descendants, starting from the view
+     * that currently is focused.
+     * @param root Contains focused
+     * @param focused Has focus now.
+     * @param direction Direction to look.
+     * @return The next focusable view, or null if none exists.
+     */
+    public final View findNextFocus(ViewGroup root, View focused, int direction) {
+
+        if (focused != null) {
+            // check for user specified next focus
+            View userSetNextFocus = focused.findUserSetNextFocus(root, direction);
+            if (userSetNextFocus != null &&
+                userSetNextFocus.isFocusable() &&
+                (!userSetNextFocus.isInTouchMode() ||
+                 userSetNextFocus.isFocusableInTouchMode())) {
+                return userSetNextFocus;
+            }
+
+            // fill in interesting rect from focused
+            focused.getFocusedRect(mFocusedRect);
+            root.offsetDescendantRectToMyCoords(focused, mFocusedRect);
+        } else {
+            // make up a rect at top left or bottom right of root
+            switch (direction) {
+                case View.FOCUS_RIGHT:
+                case View.FOCUS_DOWN:
+                    final int rootTop = root.getScrollY();
+                    final int rootLeft = root.getScrollX();
+                    mFocusedRect.set(rootLeft, rootTop, rootLeft, rootTop);
+                    break;
+
+                case View.FOCUS_LEFT:
+                case View.FOCUS_UP:
+                    final int rootBottom = root.getScrollY() + root.getHeight();
+                    final int rootRight = root.getScrollX() + root.getWidth();
+                    mFocusedRect.set(rootRight, rootBottom,
+                            rootRight, rootBottom);
+                    break;
+            }
+        }
+        return findNextFocus(root, focused, mFocusedRect, direction);
+    }
+
+    /**
+     * Find the next view to take focus in root's descendants, searching from
+     * a particular rectangle in root's coordinates.
+     * @param root Contains focusedRect.
+     * @param focusedRect The starting point of the search.
+     * @param direction Direction to look.
+     * @return The next focusable view, or null if none exists.
+     */
+    public View findNextFocusFromRect(ViewGroup root, Rect focusedRect, int direction) {
+        return findNextFocus(root, null, focusedRect, direction);
+    }
+
+    private View findNextFocus(ViewGroup root, View focused, Rect focusedRect, int direction) {
+        ArrayList<View> focusables = root.getFocusables(direction);
+
+        // initialize the best candidate to something impossible
+        // (so the first plausible view will become the best choice)
+        mBestCandidateRect.set(focusedRect);
+        switch(direction) {
+            case View.FOCUS_LEFT:
+                mBestCandidateRect.offset(focusedRect.width() + 1, 0);
+                break;
+            case View.FOCUS_RIGHT:
+                mBestCandidateRect.offset(-(focusedRect.width() + 1), 0);
+                break;
+            case View.FOCUS_UP:
+                mBestCandidateRect.offset(0, focusedRect.height() + 1);
+                break;
+            case View.FOCUS_DOWN:
+                mBestCandidateRect.offset(0, -(focusedRect.height() + 1));
+        }
+
+        View closest = null;
+
+        int numFocusables = focusables.size();
+        for (int i = 0; i < numFocusables; i++) {
+            View focusable = focusables.get(i);
+
+            // only interested in other non-root views
+            if (focusable == focused || focusable == root) continue;
+
+            // get visible bounds of other view in same coordinate system
+            focusable.getDrawingRect(mOtherRect);
+            root.offsetDescendantRectToMyCoords(focusable, mOtherRect);
+
+            if (isBetterCandidate(direction, focusedRect, mOtherRect, mBestCandidateRect)) {
+                mBestCandidateRect.set(mOtherRect);
+                closest = focusable;
+            }
+        }
+        return closest;
+    }
+
+    /**
+     * Is rect1 a better candidate than rect2 for a focus search in a particular
+     * direction from a source rect?  This is the core routine that determines
+     * the order of focus searching.
+     * @param direction the direction (up, down, left, right)
+     * @param source The source we are searching from
+     * @param rect1 The candidate rectangle
+     * @param rect2 The current best candidate.
+     * @return Whether the candidate is the new best.
+     */
+    boolean isBetterCandidate(int direction, Rect source, Rect rect1, Rect rect2) {
+
+        // to be a better candidate, need to at least be a candidate in the first
+        // place :)
+        if (!isCandidate(source, rect1, direction)) {
+            return false;
+        }
+
+        // we know that rect1 is a candidate.. if rect2 is not a candidate,
+        // rect1 is better
+        if (!isCandidate(source, rect2, direction)) {
+            return true;
+        }
+
+        // if rect1 is better by beam, it wins
+        if (beamBeats(direction, source, rect1, rect2)) {
+            return true;
+        }
+
+        // if rect2 is better, then rect1 cant' be :)
+        if (beamBeats(direction, source, rect2, rect1)) {
+            return false;
+        }
+
+        // otherwise, do fudge-tastic comparison of the major and minor axis
+        return (getWeightedDistanceFor(
+                        majorAxisDistance(direction, source, rect1),
+                        minorAxisDistance(direction, source, rect1))
+                < getWeightedDistanceFor(
+                        majorAxisDistance(direction, source, rect2),
+                        minorAxisDistance(direction, source, rect2)));
+    }
+
+    /**
+     * One rectangle may be another candidate than another by virtue of being
+     * exclusively in the beam of the source rect.
+     * @return Whether rect1 is a better candidate than rect2 by virtue of it being in src's
+     *      beam
+     */
+    boolean beamBeats(int direction, Rect source, Rect rect1, Rect rect2) {
+        final boolean rect1InSrcBeam = beamsOverlap(direction, source, rect1);
+        final boolean rect2InSrcBeam = beamsOverlap(direction, source, rect2);
+
+        // if rect1 isn't exclusively in the src beam, it doesn't win
+        if (rect2InSrcBeam || !rect1InSrcBeam) {
+            return false;
+        }
+
+        // we know rect1 is in the beam, and rect2 is not
+
+        // if rect1 is to the direction of, and rect2 is not, rect1 wins.
+        // for example, for direction left, if rect1 is to the left of the source
+        // and rect2 is below, then we always prefer the in beam rect1, since rect2
+        // could be reached by going down.
+        if (!isToDirectionOf(direction, source, rect2)) {
+            return true;
+        }
+
+        // for horizontal directions, being exclusively in beam always wins
+        if ((direction == View.FOCUS_LEFT || direction == View.FOCUS_RIGHT)) {
+            return true;
+        }        
+
+        // for vertical directions, beams only beat up to a point:
+        // now, as long as rect2 isn't completely closer, rect1 wins
+        // e.g for direction down, completely closer means for rect2's top
+        // edge to be closer to the source's top edge than rect1's bottom edge.
+        return (majorAxisDistance(direction, source, rect1)
+                < majorAxisDistanceToFarEdge(direction, source, rect2));
+    }
+
+    /**
+     * Fudge-factor opportunity: how to calculate distance given major and minor
+     * axis distances.  Warning: this fudge factor is finely tuned, be sure to
+     * run all focus tests if you dare tweak it.
+     */
+    int getWeightedDistanceFor(int majorAxisDistance, int minorAxisDistance) {
+        return 13 * majorAxisDistance * majorAxisDistance
+                + minorAxisDistance * minorAxisDistance;
+    }
+
+    /**
+     * Is destRect a candidate for the next focus given the direction?  This
+     * checks whether the dest is at least partially to the direction of (e.g left of)
+     * from source.
+     *
+     * Includes an edge case for an empty rect (which is used in some cases when
+     * searching from a point on the screen).
+     */
+    boolean isCandidate(Rect srcRect, Rect destRect, int direction) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return (srcRect.right > destRect.right || srcRect.left >= destRect.right) 
+                        && srcRect.left > destRect.left;
+            case View.FOCUS_RIGHT:
+                return (srcRect.left < destRect.left || srcRect.right <= destRect.left)
+                        && srcRect.right < destRect.right;
+            case View.FOCUS_UP:
+                return (srcRect.bottom > destRect.bottom || srcRect.top >= destRect.bottom)
+                        && srcRect.top > destRect.top;
+            case View.FOCUS_DOWN:
+                return (srcRect.top < destRect.top || srcRect.bottom <= destRect.top)
+                        && srcRect.bottom < destRect.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+
+    /**
+     * Do the "beams" w.r.t the given direcition's axos of rect1 and rect2 overlap?
+     * @param direction the direction (up, down, left, right)
+     * @param rect1 The first rectangle
+     * @param rect2 The second rectangle
+     * @return whether the beams overlap
+     */
+    boolean beamsOverlap(int direction, Rect rect1, Rect rect2) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+            case View.FOCUS_RIGHT:
+                return (rect2.bottom >= rect1.top) && (rect2.top <= rect1.bottom);
+            case View.FOCUS_UP:
+            case View.FOCUS_DOWN:
+                return (rect2.right >= rect1.left) && (rect2.left <= rect1.right);
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * e.g for left, is 'to left of'
+     */
+    boolean isToDirectionOf(int direction, Rect src, Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return src.left >= dest.right;
+            case View.FOCUS_RIGHT:
+                return src.right <= dest.left;
+            case View.FOCUS_UP:
+                return src.top >= dest.bottom;
+            case View.FOCUS_DOWN:
+                return src.bottom <= dest.top;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * @return The distance from the edge furthest in the given direction
+     *   of source to the edge nearest in the given direction of dest.  If the
+     *   dest is not in the direction from source, return 0.
+     */
+    static int majorAxisDistance(int direction, Rect source, Rect dest) {
+        return Math.max(0, majorAxisDistanceRaw(direction, source, dest));
+    }
+
+    static int majorAxisDistanceRaw(int direction, Rect source, Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return source.left - dest.right;
+            case View.FOCUS_RIGHT:
+                return dest.left - source.right;
+            case View.FOCUS_UP:
+                return source.top - dest.bottom;
+            case View.FOCUS_DOWN:
+                return dest.top - source.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * @return The distance along the major axis w.r.t the direction from the
+     *   edge of source to the far edge of dest. If the
+     *   dest is not in the direction from source, return 1 (to break ties with
+     *   {@link #majorAxisDistance}).
+     */
+    static int majorAxisDistanceToFarEdge(int direction, Rect source, Rect dest) {
+        return Math.max(1, majorAxisDistanceToFarEdgeRaw(direction, source, dest));
+    }
+
+    static int majorAxisDistanceToFarEdgeRaw(int direction, Rect source, Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return source.left - dest.left;
+            case View.FOCUS_RIGHT:
+                return dest.right - source.right;
+            case View.FOCUS_UP:
+                return source.top - dest.top;
+            case View.FOCUS_DOWN:
+                return dest.bottom - source.bottom;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * Find the distance on the minor axis w.r.t the direction to the nearest
+     * edge of the destination rectange.
+     * @param direction the direction (up, down, left, right)
+     * @param source The source rect.
+     * @param dest The destination rect.
+     * @return The distance.
+     */
+    static int minorAxisDistance(int direction, Rect source, Rect dest) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+            case View.FOCUS_RIGHT:
+                // the distance between the center verticals
+                return Math.abs(
+                        ((source.top + source.height() / 2) -
+                        ((dest.top + dest.height() / 2))));
+            case View.FOCUS_UP:
+            case View.FOCUS_DOWN:
+                // the distance between the center horizontals
+                return Math.abs(
+                        ((source.left + source.width() / 2) -
+                        ((dest.left + dest.width() / 2))));
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+
+    /**
+     * Find the nearest touchable view to the specified view.
+     * 
+     * @param root The root of the tree in which to search
+     * @param x X coordinate from which to start the search
+     * @param y Y coordinate from which to start the search
+     * @param direction Direction to look
+     * @param deltas Offset from the <x, y> to the edge of the nearest view. Note that this array
+     *        may already be populated with values.
+     * @return The nearest touchable view, or null if none exists.
+     */
+    public View findNearestTouchable(ViewGroup root, int x, int y, int direction, int[] deltas) {
+        ArrayList<View> touchables = root.getTouchables();
+        int minDistance = Integer.MAX_VALUE;
+        View closest = null;
+
+        int numTouchables = touchables.size();
+        
+        int edgeSlop = ViewConfiguration.get(root.mContext).getScaledEdgeSlop();
+        
+        Rect closestBounds = new Rect();
+        Rect touchableBounds = mOtherRect;
+        
+        for (int i = 0; i < numTouchables; i++) {
+            View touchable = touchables.get(i);
+
+            // get visible bounds of other view in same coordinate system
+            touchable.getDrawingRect(touchableBounds);
+            
+            root.offsetRectBetweenParentAndChild(touchable, touchableBounds, true, true);
+
+            if (!isTouchCandidate(x, y, touchableBounds, direction)) {
+                continue;
+            }
+
+            int distance = Integer.MAX_VALUE;
+
+            switch (direction) {
+            case View.FOCUS_LEFT:
+                distance = x - touchableBounds.right + 1;
+                break;
+            case View.FOCUS_RIGHT:
+                distance = touchableBounds.left;
+                break;
+            case View.FOCUS_UP:
+                distance = y - touchableBounds.bottom + 1;
+                break;
+            case View.FOCUS_DOWN:
+                distance = touchableBounds.top;
+                break;
+            }
+
+            if (distance < edgeSlop) {
+                // Give preference to innermost views
+                if (closest == null ||
+                        closestBounds.contains(touchableBounds) ||
+                        (!touchableBounds.contains(closestBounds) && distance < minDistance)) {
+                    minDistance = distance;
+                    closest = touchable;
+                    closestBounds.set(touchableBounds);
+                    switch (direction) {
+                    case View.FOCUS_LEFT:
+                        deltas[0] = -distance;
+                        break;
+                    case View.FOCUS_RIGHT:
+                        deltas[0] = distance;
+                        break;
+                    case View.FOCUS_UP:
+                        deltas[1] = -distance;
+                        break;
+                    case View.FOCUS_DOWN:
+                        deltas[1] = distance;
+                        break;
+                    }
+                }
+            }
+        }
+        return closest;
+    }
+
+
+    /**
+     * Is destRect a candidate for the next touch given the direction?
+     */
+    private boolean isTouchCandidate(int x, int y, Rect destRect, int direction) {
+        switch (direction) {
+            case View.FOCUS_LEFT:
+                return destRect.left <= x && destRect.top <= y && y <= destRect.bottom;
+            case View.FOCUS_RIGHT:
+                return destRect.left >= x && destRect.top <= y && y <= destRect.bottom;
+            case View.FOCUS_UP:
+                return destRect.top <= y && destRect.left <= x && x <= destRect.right;
+            case View.FOCUS_DOWN:
+                return destRect.top >= y && destRect.left <= x && x <= destRect.right;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT}.");
+    }
+}
diff --git a/core/java/android/view/FocusFinderHelper.java b/core/java/android/view/FocusFinderHelper.java
new file mode 100644
index 0000000..69dc056
--- /dev/null
+++ b/core/java/android/view/FocusFinderHelper.java
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+
+/**
+ * A helper class that allows unit tests to access FocusFinder methods.
+ * @hide
+ */
+public class FocusFinderHelper {
+    
+    private FocusFinder mFocusFinder;
+
+    /**
+     * Wrap the FocusFinder object
+     */
+    public FocusFinderHelper(FocusFinder focusFinder) {
+        mFocusFinder = focusFinder;
+    }
+    
+    public boolean isBetterCandidate(int direction, Rect source, Rect rect1, Rect rect2) {
+        return mFocusFinder.isBetterCandidate(direction, source, rect1, rect2);
+    }
+    
+    public boolean beamBeats(int direction, Rect source, Rect rect1, Rect rect2) {
+        return mFocusFinder.beamBeats(direction, source, rect1, rect2);
+    }
+
+    public boolean isCandidate(Rect srcRect, Rect destRect, int direction) {
+        return mFocusFinder.isCandidate(srcRect, destRect, direction);
+    }
+    
+    public boolean beamsOverlap(int direction, Rect rect1, Rect rect2) {
+        return mFocusFinder.beamsOverlap(direction, rect1, rect2);
+    }
+    
+    public static int majorAxisDistance(int direction, Rect source, Rect dest) {
+        return FocusFinder.majorAxisDistance(direction, source, dest);
+    }
+    
+    public static int majorAxisDistanceToFarEdge(int direction, Rect source, Rect dest) {
+        return FocusFinder.majorAxisDistanceToFarEdge(direction, source, dest);
+    }
+}
diff --git a/core/java/android/view/GestureDetector.java b/core/java/android/view/GestureDetector.java
new file mode 100644
index 0000000..e0231a7b
--- /dev/null
+++ b/core/java/android/view/GestureDetector.java
@@ -0,0 +1,564 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.os.Handler;
+import android.os.Message;
+import android.content.Context;
+
+/**
+ * Detects various gestures and events using the supplied {@link MotionEvent}s.
+ * The {@link OnGestureListener} callback will notify users when a particular
+ * motion event has occurred. This class should only be used with {@link MotionEvent}s
+ * reported via touch (don't use for trackball events).
+ *
+ * To use this class:
+ * <ul>
+ *  <li>Create an instance of the {@code GestureDetector} 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>
+ */
+public class GestureDetector {
+    /**
+     * The listener that is used to notify 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}.
+     */
+    public interface OnGestureListener {
+
+        /**
+         * Notified when a tap occurs with the down {@link MotionEvent}
+         * that triggered it. This will be triggered immediately for
+         * every down event. All other events should be preceded by this.
+         *
+         * @param e The down motion event.
+         */
+        boolean onDown(MotionEvent e);
+
+        /**
+         * The user has performed a down {@link MotionEvent} and not performed
+         * a move or up yet. This event is commonly used to provide visual
+         * feedback to the user to let them know that their action has been
+         * recognized i.e. highlight an element.
+         *
+         * @param e The down motion event
+         */
+        void onShowPress(MotionEvent e);
+
+        /**
+         * Notified when a tap occurs with the up {@link MotionEvent}
+         * that triggered it.
+         *
+         * @param e The up motion event that completed the first tap
+         * @return true if the event is consumed, else false
+         */
+        boolean onSingleTapUp(MotionEvent e);
+
+        /**
+         * Notified when a scroll occurs with the initial on down {@link MotionEvent} and the
+         * current move {@link MotionEvent}. The distance in x and y is also supplied for
+         * convenience.
+         *
+         * @param e1 The first down motion event that started the scrolling.
+         * @param e2 The move motion event that triggered the current onScroll.
+         * @param distanceX The distance along the X axis that has been scrolled since the last
+         *              call to onScroll. This is NOT the distance between {@code e1}
+         *              and {@code e2}.
+         * @param distanceY The distance along the Y axis that has been scrolled since the last
+         *              call to onScroll. This is NOT the distance between {@code e1}
+         *              and {@code e2}.
+         * @return true if the event is consumed, else false
+         */
+        boolean onScroll(MotionEvent e1, MotionEvent e2, float distanceX, float distanceY);
+
+        /**
+         * Notified when a long press occurs with the initial on down {@link MotionEvent}
+         * that trigged it.
+         *
+         * @param e The initial on down motion event that started the longpress.
+         */
+        void onLongPress(MotionEvent e);
+
+        /**
+         * Notified of a fling event when it occurs with the initial on down {@link MotionEvent}
+         * and the matching up {@link MotionEvent}. The calculated velocity is supplied along
+         * the x and y axis in pixels per second.
+         *
+         * @param e1 The first down motion event that started the fling.
+         * @param e2 The move motion event that triggered the current onFling.
+         * @param velocityX The velocity of this fling measured in pixels per second
+         *              along the x axis.
+         * @param velocityY The velocity of this fling measured in pixels per second
+         *              along the y axis.
+         * @return true if the event is consumed, else false
+         */
+        boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX, float velocityY);
+    }
+
+    /**
+     * The listener that is used to notify when a double-tap or a confirmed
+     * single-tap occur.
+     */
+    public interface OnDoubleTapListener {
+        /**
+         * Notified when a single-tap occurs.
+         * <p>
+         * Unlike {@link OnGestureListener#onSingleTapUp(MotionEvent)}, this
+         * will only be called after the detector is confident that the user's
+         * first tap is not followed by a second tap leading to a double-tap
+         * gesture.
+         *
+         * @param e The down motion event of the single-tap.
+         * @return true if the event is consumed, else false
+         */
+        boolean onSingleTapConfirmed(MotionEvent e);
+ 
+        /**
+         * Notified when a double-tap occurs.
+         *
+         * @param e The down motion event of the first tap of the double-tap.
+         * @return true if the event is consumed, else false
+         */
+        boolean onDoubleTap(MotionEvent e);
+
+        /**
+         * Notified when an event within a double-tap gesture occurs, including
+         * the down, move, and up events.
+         *
+         * @param e The motion event that occurred during the double-tap gesture.
+         * @return true if the event is consumed, else false
+         */
+        boolean onDoubleTapEvent(MotionEvent e);
+    }
+
+    /**
+     * A convenience class to extend when you only want to listen for a subset
+     * of all the gestures. This implements all methods in the
+     * {@link OnGestureListener} and {@link OnDoubleTapListener} but does
+     * nothing and return {@code false} for all applicable methods.
+     */
+    public static class SimpleOnGestureListener implements OnGestureListener, OnDoubleTapListener {
+        public boolean onSingleTapUp(MotionEvent e) {
+            return false;
+        }
+
+        public void onLongPress(MotionEvent e) {
+        }
+
+        public boolean onScroll(MotionEvent e1, MotionEvent e2,
+                float distanceX, float distanceY) {
+            return false;
+        }
+
+        public boolean onFling(MotionEvent e1, MotionEvent e2, float velocityX,
+                float velocityY) {
+            return false;
+        }
+
+        public void onShowPress(MotionEvent e) {
+        }
+
+        public boolean onDown(MotionEvent e) {
+            return false;
+        }
+
+        public boolean onDoubleTap(MotionEvent e) {
+            return false;
+        }
+
+        public boolean onDoubleTapEvent(MotionEvent e) {
+            return false;
+        }
+
+        public boolean onSingleTapConfirmed(MotionEvent e) {
+            return false;
+        }
+    }
+
+    // TODO: ViewConfiguration
+    private int mBiggerTouchSlopSquare = 20 * 20;
+
+    private int mTouchSlopSquare;
+    private int mDoubleTapSlopSquare;
+    private int mMinimumFlingVelocity;
+
+    private static final int LONGPRESS_TIMEOUT = ViewConfiguration.getLongPressTimeout();
+    private static final int TAP_TIMEOUT = ViewConfiguration.getTapTimeout();
+    // TODO make new double-tap timeout, and define its events (i.e. either time
+    // between down-down or time between up-down)
+    private static final int DOUBLE_TAP_TIMEOUT = ViewConfiguration.getDoubleTapTimeout();
+
+    // constants for Message.what used by GestureHandler below
+    private static final int SHOW_PRESS = 1;
+    private static final int LONG_PRESS = 2;
+    private static final int TAP = 3;
+
+    private final Handler mHandler;
+    private final OnGestureListener mListener;
+    private OnDoubleTapListener mDoubleTapListener;
+
+    private boolean mStillDown;
+    private boolean mInLongPress;
+    private boolean mAlwaysInTapRegion;
+    private boolean mAlwaysInBiggerTapRegion;
+
+    private MotionEvent mCurrentDownEvent;
+    private MotionEvent mPreviousUpEvent;
+
+    /**
+     * True when the user is still touching for the second tap (down, move, and
+     * up events). Can only be true if there is a double tap listener attached.
+     */
+    private boolean mIsDoubleTapping;
+
+    private float mLastMotionY;
+    private float mLastMotionX;
+
+    private boolean mIsLongpressEnabled;
+
+    /**
+     * Determines speed during touch scrolling
+     */
+    private VelocityTracker mVelocityTracker;
+
+    private class GestureHandler extends Handler {
+        GestureHandler() {
+            super();
+        }
+
+        GestureHandler(Handler handler) {
+            super(handler.getLooper());
+        }
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+            case SHOW_PRESS:
+                mListener.onShowPress(mCurrentDownEvent);
+                break;
+                
+            case LONG_PRESS:
+                dispatchLongPress();
+                break;
+                
+            case TAP:
+                // If the user's finger is still down, do not count it as a tap
+                if (mDoubleTapListener != null && !mStillDown) {
+                    mDoubleTapListener.onSingleTapConfirmed(mCurrentDownEvent);
+                }
+                break;
+
+            default:
+                throw new RuntimeException("Unknown message " + msg); //never
+            }
+        }
+    }
+
+    /**
+     * Creates a GestureDetector with the supplied listener.
+     * This variant of the constructor should be used from a non-UI thread 
+     * (as it allows specifying the Handler).
+     * 
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null.
+     * @param handler the handler to use
+     *
+     * @throws NullPointerException if either {@code listener} or
+     * {@code handler} is null.
+     *
+     * @deprecated Use {@link #GestureDetector(android.content.Context,
+     *      android.view.GestureDetector.OnGestureListener, android.os.Handler)} instead.
+     */
+    @Deprecated
+    public GestureDetector(OnGestureListener listener, Handler handler) {
+        this(null, listener, handler);
+    }
+
+    /**
+     * Creates a GestureDetector with the supplied listener.
+     * You may only use this constructor from a UI thread (this is the usual situation).
+     * @see android.os.Handler#Handler()
+     * 
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null.
+     * 
+     * @throws NullPointerException if {@code listener} is null.
+     *
+     * @deprecated Use {@link #GestureDetector(android.content.Context,
+     *      android.view.GestureDetector.OnGestureListener)} instead.
+     */
+    @Deprecated
+    public GestureDetector(OnGestureListener listener) {
+        this(null, listener, null);
+    }
+
+    /**
+     * Creates a GestureDetector with the supplied listener.
+     * You may only use this constructor from a UI thread (this is the usual situation).
+     * @see android.os.Handler#Handler()
+     *
+     * @param context the application's context
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null.
+     *
+     * @throws NullPointerException if {@code listener} is null.
+     */
+    public GestureDetector(Context context, OnGestureListener listener) {
+        this(context, listener, null);
+    }
+
+    /**
+     * Creates a GestureDetector with the supplied listener.
+     * You may only use this constructor from a UI thread (this is the usual situation).
+     * @see android.os.Handler#Handler()
+     *
+     * @param context the application's context
+     * @param listener the listener invoked for all the callbacks, this must
+     * not be null.
+     * @param handler the handler to use     
+     *
+     * @throws NullPointerException if {@code listener} is null.
+     */
+    public GestureDetector(Context context, OnGestureListener listener, Handler handler) {
+        if (handler != null) {
+            mHandler = new GestureHandler(handler);
+        } else {
+            mHandler = new GestureHandler();
+        }
+        mListener = listener;
+        if (listener instanceof OnDoubleTapListener) {
+            setOnDoubleTapListener((OnDoubleTapListener) listener);
+        }
+        init(context);
+    }
+
+    private void init(Context context) {
+        if (mListener == null) {
+            throw new NullPointerException("OnGestureListener must not be null");
+        }
+        mIsLongpressEnabled = true;
+
+        // Fallback to support pre-donuts releases
+        int touchSlop, doubleTapSlop;
+        if (context == null) {
+            //noinspection deprecation
+            touchSlop = ViewConfiguration.getTouchSlop();
+            doubleTapSlop = ViewConfiguration.getDoubleTapSlop();
+            //noinspection deprecation
+            mMinimumFlingVelocity = ViewConfiguration.getMinimumFlingVelocity();
+        } else {
+            final ViewConfiguration configuration = ViewConfiguration.get(context);
+            touchSlop = configuration.getScaledTouchSlop();
+            doubleTapSlop = configuration.getScaledDoubleTapSlop();
+            mMinimumFlingVelocity = configuration.getScaledMinimumFlingVelocity();
+        }
+        mTouchSlopSquare = touchSlop * touchSlop;
+        mDoubleTapSlopSquare = doubleTapSlop * doubleTapSlop;
+    }
+
+    /**
+     * Sets the listener which will be called for double-tap and related
+     * gestures.
+     * 
+     * @param onDoubleTapListener the listener invoked for all the callbacks, or
+     *        null to stop listening for double-tap gestures.
+     */
+    public void setOnDoubleTapListener(OnDoubleTapListener onDoubleTapListener) {
+        mDoubleTapListener = onDoubleTapListener;
+    }
+
+    /**
+     * Set whether longpress is enabled, if this is enabled when a user
+     * presses and holds down you get a longpress event and nothing further.
+     * If it's disabled the user can press and hold down and then later
+     * moved their finger and you will get scroll events. By default
+     * longpress is enabled.
+     *
+     * @param isLongpressEnabled whether longpress should be enabled.
+     */
+    public void setIsLongpressEnabled(boolean isLongpressEnabled) {
+        mIsLongpressEnabled = isLongpressEnabled;
+    }
+
+    /**
+     * @return true if longpress is enabled, else false.
+     */
+    public boolean isLongpressEnabled() {
+        return mIsLongpressEnabled;
+    }
+
+    /**
+     * Analyzes the given motion event and if applicable triggers the
+     * appropriate callbacks on the {@link OnGestureListener} supplied.
+     *
+     * @param ev The current motion event.
+     * @return true if the {@link OnGestureListener} consumed the event,
+     *              else false.
+     */
+    public boolean onTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+        final float y = ev.getY();
+        final float x = ev.getX();
+
+        if (mVelocityTracker == null) {
+            mVelocityTracker = VelocityTracker.obtain();
+        }
+        mVelocityTracker.addMovement(ev);
+
+        boolean handled = false;
+
+        switch (action) {
+        case MotionEvent.ACTION_DOWN:
+            if (mDoubleTapListener != null) {
+                boolean hadTapMessage = mHandler.hasMessages(TAP);
+                if (hadTapMessage) mHandler.removeMessages(TAP);
+                if ((mCurrentDownEvent != null) && (mPreviousUpEvent != null) && hadTapMessage &&
+                        isConsideredDoubleTap(mCurrentDownEvent, mPreviousUpEvent, ev)) {
+                    // This is a second tap
+                    mIsDoubleTapping = true;
+                    // Give a callback with the first tap of the double-tap
+                    handled |= mDoubleTapListener.onDoubleTap(mCurrentDownEvent);
+                    // Give a callback with down event of the double-tap
+                    handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+                } else {
+                    // This is a first tap
+                    mHandler.sendEmptyMessageDelayed(TAP, DOUBLE_TAP_TIMEOUT);
+                }
+            }
+
+            mLastMotionX = x;
+            mLastMotionY = y;
+            mCurrentDownEvent = MotionEvent.obtain(ev);
+            mAlwaysInTapRegion = true;
+            mAlwaysInBiggerTapRegion = true;
+            mStillDown = true;
+            mInLongPress = false;
+            
+            if (mIsLongpressEnabled) {
+                mHandler.removeMessages(LONG_PRESS);
+                mHandler.sendEmptyMessageAtTime(LONG_PRESS, mCurrentDownEvent.getDownTime()
+                        + TAP_TIMEOUT + LONGPRESS_TIMEOUT);
+            }
+            mHandler.sendEmptyMessageAtTime(SHOW_PRESS, mCurrentDownEvent.getDownTime() + TAP_TIMEOUT);
+            handled |= mListener.onDown(ev);
+            break;
+
+        case MotionEvent.ACTION_MOVE:
+            if (mInLongPress) {
+                break;
+            }
+            final float scrollX = mLastMotionX - x;
+            final float scrollY = mLastMotionY - y;
+            if (mIsDoubleTapping) {
+                // Give the move events of the double-tap
+                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+            } else if (mAlwaysInTapRegion) {
+                final int deltaX = (int) (x - mCurrentDownEvent.getX());
+                final int deltaY = (int) (y - mCurrentDownEvent.getY());
+                int distance = (deltaX * deltaX) + (deltaY * deltaY);
+                if (distance > mTouchSlopSquare) {
+                    handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
+                    mLastMotionX = x;
+                    mLastMotionY = y;
+                    mAlwaysInTapRegion = false;
+                    mHandler.removeMessages(TAP);
+                    mHandler.removeMessages(SHOW_PRESS);
+                    mHandler.removeMessages(LONG_PRESS);
+                }
+                if (distance > mBiggerTouchSlopSquare) {
+                    mAlwaysInBiggerTapRegion = false;
+                }
+            } else if ((Math.abs(scrollX) >= 1) || (Math.abs(scrollY) >= 1)) {
+                handled = mListener.onScroll(mCurrentDownEvent, ev, scrollX, scrollY);
+                mLastMotionX = x;
+                mLastMotionY = y;
+            }
+            break;
+
+        case MotionEvent.ACTION_UP:
+            mStillDown = false;
+            MotionEvent currentUpEvent = MotionEvent.obtain(ev);
+            if (mIsDoubleTapping) {
+                // Finally, give the up event of the double-tap
+                handled |= mDoubleTapListener.onDoubleTapEvent(ev);
+                mIsDoubleTapping = false;
+                break;
+            } else if (mInLongPress) {
+                mHandler.removeMessages(TAP);
+                mInLongPress = false;
+                break;
+            }
+            if (mAlwaysInTapRegion) {
+                handled = mListener.onSingleTapUp(ev);
+            } else {
+
+                // A fling must travel the minimum tap distance
+                final VelocityTracker velocityTracker = mVelocityTracker;
+                velocityTracker.computeCurrentVelocity(1000);
+                final float velocityY = velocityTracker.getYVelocity();
+                final float velocityX = velocityTracker.getXVelocity();
+
+                if ((Math.abs(velocityY) > mMinimumFlingVelocity)
+                        || (Math.abs(velocityX) > mMinimumFlingVelocity)){
+                    handled = mListener.onFling(mCurrentDownEvent, currentUpEvent, velocityX, velocityY);
+                }
+            }
+            mPreviousUpEvent = MotionEvent.obtain(ev);
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+            mHandler.removeMessages(SHOW_PRESS);
+            mHandler.removeMessages(LONG_PRESS);
+            break;
+        case MotionEvent.ACTION_CANCEL:
+            mHandler.removeMessages(SHOW_PRESS);
+            mHandler.removeMessages(LONG_PRESS);
+            mHandler.removeMessages(TAP);
+            mVelocityTracker.recycle();
+            mVelocityTracker = null;
+            mStillDown = false;
+            if (mInLongPress) {
+                mInLongPress = false;
+                break;
+            }
+        }
+        return handled;
+    }
+
+    private boolean isConsideredDoubleTap(MotionEvent firstDown, MotionEvent firstUp,
+            MotionEvent secondDown) {
+        if (!mAlwaysInBiggerTapRegion) {
+            return false;
+        }
+
+        if (secondDown.getEventTime() - firstUp.getEventTime() > DOUBLE_TAP_TIMEOUT) {
+            return false;
+        }
+
+        int deltaX = (int) firstDown.getX() - (int) secondDown.getX();
+        int deltaY = (int) firstDown.getY() - (int) secondDown.getY();
+        return (deltaX * deltaX + deltaY * deltaY < mDoubleTapSlopSquare);
+    }
+
+    private void dispatchLongPress() {
+        mHandler.removeMessages(TAP);
+        mInLongPress = true;
+        mListener.onLongPress(mCurrentDownEvent);
+    }
+}
diff --git a/core/java/android/view/Gravity.java b/core/java/android/view/Gravity.java
new file mode 100644
index 0000000..36d8ce6
--- /dev/null
+++ b/core/java/android/view/Gravity.java
@@ -0,0 +1,306 @@
+/*
+ * Copyright (C) 2006 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.graphics.Rect;
+
+/**
+ * Standard constants and tools for placing an object within a potentially
+ * larger container.
+ */
+public class Gravity
+{
+    /** Constant indicating that no gravity has been set **/
+    public static final int NO_GRAVITY = 0x0000;
+    
+    /** Raw bit indicating the gravity for an axis has been specified. */
+    public static final int AXIS_SPECIFIED = 0x0001;
+
+    /** Raw bit controlling how the left/top edge is placed. */
+    public static final int AXIS_PULL_BEFORE = 0x0002;
+    /** Raw bit controlling how the right/bottom edge is placed. */
+    public static final int AXIS_PULL_AFTER = 0x0004;
+    /** Raw bit controlling whether the right/bottom edge is clipped to its
+     * container, based on the gravity direction being applied. */
+    public static final int AXIS_CLIP = 0x0008;
+
+    /** Bits defining the horizontal axis. */
+    public static final int AXIS_X_SHIFT = 0;
+    /** Bits defining the vertical axis. */
+    public static final int AXIS_Y_SHIFT = 4;
+
+    /** Push object to the top of its container, not changing its size. */
+    public static final int TOP = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
+    /** Push object to the bottom of its container, not changing its size. */
+    public static final int BOTTOM = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_Y_SHIFT;
+    /** Push object to the left of its container, not changing its size. */
+    public static final int LEFT = (AXIS_PULL_BEFORE|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
+    /** Push object to the right of its container, not changing its size. */
+    public static final int RIGHT = (AXIS_PULL_AFTER|AXIS_SPECIFIED)<<AXIS_X_SHIFT;
+
+    /** Place object in the vertical center of its container, not changing its
+     *  size. */
+    public static final int CENTER_VERTICAL = AXIS_SPECIFIED<<AXIS_Y_SHIFT;
+    /** Grow the vertical size of the object if needed so it completely fills
+     *  its container. */
+    public static final int FILL_VERTICAL = TOP|BOTTOM;
+
+    /** Place object in the horizontal center of its container, not changing its
+     *  size. */
+    public static final int CENTER_HORIZONTAL = AXIS_SPECIFIED<<AXIS_X_SHIFT;
+    /** Grow the horizontal size of the object if needed so it completely fills
+     *  its container. */
+    public static final int FILL_HORIZONTAL = LEFT|RIGHT;
+
+    /** Place the object in the center of its container in both the vertical
+     *  and horizontal axis, not changing its size. */
+    public static final int CENTER = CENTER_VERTICAL|CENTER_HORIZONTAL;
+
+    /** Grow the horizontal and vertical size of the object if needed so it
+     *  completely fills its container. */
+    public static final int FILL = FILL_VERTICAL|FILL_HORIZONTAL;
+
+    /** Flag to clip the edges of the object to its container along the
+     *  vertical axis. */
+    public static final int CLIP_VERTICAL = AXIS_CLIP<<AXIS_Y_SHIFT;
+    
+    /** Flag to clip the edges of the object to its container along the
+     *  horizontal axis. */
+    public static final int CLIP_HORIZONTAL = AXIS_CLIP<<AXIS_X_SHIFT;
+    
+    /**
+     * Binary mask to get the horizontal gravity of a gravity.
+     */
+    public static final int HORIZONTAL_GRAVITY_MASK = (AXIS_SPECIFIED |
+            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_X_SHIFT;
+    /**
+     * Binary mask to get the vertical gravity of a gravity.
+     */
+    public static final int VERTICAL_GRAVITY_MASK = (AXIS_SPECIFIED |
+            AXIS_PULL_BEFORE | AXIS_PULL_AFTER) << AXIS_Y_SHIFT;
+
+    /** Special constant to enable clipping to an overall display along the
+     *  vertical dimension.  This is not applied by default by
+     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
+     *  yourself by calling {@link #applyDisplay}.
+     */
+    public static final int DISPLAY_CLIP_VERTICAL = 0x10000000;
+    
+    /** Special constant to enable clipping to an overall display along the
+     *  horizontal dimension.  This is not applied by default by
+     *  {@link #apply(int, int, int, Rect, int, int, Rect)}; you must do so
+     *  yourself by calling {@link #applyDisplay}.
+     */
+    public static final int DISPLAY_CLIP_HORIZONTAL = 0x01000000;
+    
+    /**
+     * Apply a gravity constant to an object.
+     * 
+     * @param gravity The desired placement of the object, as defined by the
+     *                constants in this class.
+     * @param w The horizontal size of the object.
+     * @param h The vertical size of the object.
+     * @param container The frame of the containing space, in which the object
+     *                  will be placed.  Should be large enough to contain the
+     *                  width and height of the object.
+     * @param outRect Receives the computed frame of the object in its
+     *                container.
+     */
+    public static void apply(int gravity, int w, int h, Rect container,
+                             Rect outRect) {
+        apply(gravity, w, h, container, 0, 0, outRect);
+    }
+
+    /**
+     * Apply a gravity constant to an object.
+     * 
+     * @param gravity The desired placement of the object, as defined by the
+     *                constants in this class.
+     * @param w The horizontal size of the object.
+     * @param h The vertical size of the object.
+     * @param container The frame of the containing space, in which the object
+     *                  will be placed.  Should be large enough to contain the
+     *                  width and height of the object.
+     * @param xAdj Offset to apply to the X axis.  If gravity is LEFT this
+     *             pushes it to the right; if gravity is RIGHT it pushes it to
+     *             the left; if gravity is CENTER_HORIZONTAL it pushes it to the
+     *             right or left; otherwise it is ignored.
+     * @param yAdj Offset to apply to the Y axis.  If gravity is TOP this pushes
+     *             it down; if gravity is BOTTOM it pushes it up; if gravity is
+     *             CENTER_VERTICAL it pushes it down or up; otherwise it is
+     *             ignored.
+     * @param outRect Receives the computed frame of the object in its
+     *                container.
+     */
+    public static void apply(int gravity, int w, int h, Rect container,
+                             int xAdj, int yAdj, Rect outRect) {
+        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_X_SHIFT)) {
+            case 0:
+                outRect.left = container.left
+                        + ((container.right - container.left - w)/2) + xAdj;
+                outRect.right = outRect.left + w;
+                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
+                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
+                    if (outRect.left < container.left) {
+                        outRect.left = container.left;
+                    }
+                    if (outRect.right > container.right) {
+                        outRect.right = container.right;
+                    }
+                }
+                break;
+            case AXIS_PULL_BEFORE<<AXIS_X_SHIFT:
+                outRect.left = container.left + xAdj;
+                outRect.right = outRect.left + w;
+                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
+                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
+                    if (outRect.right > container.right) {
+                        outRect.right = container.right;
+                    }
+                }
+                break;
+            case AXIS_PULL_AFTER<<AXIS_X_SHIFT:
+                outRect.right = container.right - xAdj;
+                outRect.left = outRect.right - w;
+                if ((gravity&(AXIS_CLIP<<AXIS_X_SHIFT))
+                        == (AXIS_CLIP<<AXIS_X_SHIFT)) {
+                    if (outRect.left < container.left) {
+                        outRect.left = container.left;
+                    }
+                }
+                break;
+            default:
+                outRect.left = container.left + xAdj;
+                outRect.right = container.right + xAdj;
+                break;
+        }
+        
+        switch (gravity&((AXIS_PULL_BEFORE|AXIS_PULL_AFTER)<<AXIS_Y_SHIFT)) {
+            case 0:
+                outRect.top = container.top
+                        + ((container.bottom - container.top - h)/2) + yAdj;
+                outRect.bottom = outRect.top + h;
+                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
+                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
+                    if (outRect.top < container.top) {
+                        outRect.top = container.top;
+                    }
+                    if (outRect.bottom > container.bottom) {
+                        outRect.bottom = container.bottom;
+                    }
+                }
+                break;
+            case AXIS_PULL_BEFORE<<AXIS_Y_SHIFT:
+                outRect.top = container.top + yAdj;
+                outRect.bottom = outRect.top + h;
+                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
+                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
+                    if (outRect.bottom > container.bottom) {
+                        outRect.bottom = container.bottom;
+                    }
+                }
+                break;
+            case AXIS_PULL_AFTER<<AXIS_Y_SHIFT:
+                outRect.bottom = container.bottom - yAdj;
+                outRect.top = outRect.bottom - h;
+                if ((gravity&(AXIS_CLIP<<AXIS_Y_SHIFT))
+                        == (AXIS_CLIP<<AXIS_Y_SHIFT)) {
+                    if (outRect.top < container.top) {
+                        outRect.top = container.top;
+                    }
+                }
+                break;
+            default:
+                outRect.top = container.top + yAdj;
+                outRect.bottom = container.bottom + yAdj;
+                break;
+        }
+    }
+
+    /**
+     * Apply addition gravity behavior based on the overall "display" that an
+     * object exists in.  This can be used after
+     * {@link #apply(int, int, int, Rect, int, int, Rect)} to place the object
+     * within a visible display.  By default this moves or clips the object
+     * to be visible in the display; the gravity flags
+     * {@link #DISPLAY_CLIP_HORIZONTAL} and {@link #DISPLAY_CLIP_VERTICAL}
+     * can be used to change this behavior.
+     * 
+     * @param gravity Gravity constants to modify the placement within the
+     * display.
+     * @param display The rectangle of the display in which the object is
+     * being placed.
+     * @param inoutObj Supplies the current object position; returns with it
+     * modified if needed to fit in the display.
+     */
+    public static void applyDisplay(int gravity, Rect display, Rect inoutObj) {
+        if ((gravity&DISPLAY_CLIP_VERTICAL) != 0) {
+            if (inoutObj.top < display.top) inoutObj.top = display.top;
+            if (inoutObj.bottom > display.bottom) inoutObj.bottom = display.bottom;
+        } else {
+            int off = 0;
+            if (inoutObj.top < display.top) off = display.top-inoutObj.top;
+            else if (inoutObj.bottom > display.bottom) off = display.bottom-inoutObj.bottom;
+            if (off != 0) {
+                if (inoutObj.height() > (display.bottom-display.top)) {
+                    inoutObj.top = display.top;
+                    inoutObj.bottom = display.bottom;
+                } else {
+                    inoutObj.top += off;
+                    inoutObj.bottom += off;
+                }
+            }
+        }
+        
+        if ((gravity&DISPLAY_CLIP_HORIZONTAL) != 0) {
+            if (inoutObj.left < display.left) inoutObj.left = display.left;
+            if (inoutObj.right > display.right) inoutObj.right = display.right;
+        } else {
+            int off = 0;
+            if (inoutObj.left < display.left) off = display.left-inoutObj.left;
+            else if (inoutObj.right > display.right) off = display.right-inoutObj.right;
+            if (off != 0) {
+                if (inoutObj.width() > (display.right-display.left)) {
+                    inoutObj.left = display.left;
+                    inoutObj.right = display.right;
+                } else {
+                    inoutObj.left += off;
+                    inoutObj.right += off;
+                }
+            }
+        }
+    }
+    
+    /**
+     * <p>Indicate whether the supplied gravity has a vertical pull.</p>
+     *
+     * @param gravity the gravity to check for vertical pull
+     * @return true if the supplied gravity has a vertical pull
+     */
+    public static boolean isVertical(int gravity) {
+        return gravity > 0 && (gravity & VERTICAL_GRAVITY_MASK) != 0;
+    }
+
+    /**
+     * <p>Indicate whether the supplied gravity has an horizontal pull.</p>
+     *
+     * @param gravity the gravity to check for horizontal pull
+     * @return true if the supplied gravity has an horizontal pull
+     */
+    public static boolean isHorizontal(int gravity) {
+        return gravity > 0 && (gravity & HORIZONTAL_GRAVITY_MASK) != 0;
+    }
+}
diff --git a/core/java/android/view/HapticFeedbackConstants.java b/core/java/android/view/HapticFeedbackConstants.java
new file mode 100644
index 0000000..cc3563c
--- /dev/null
+++ b/core/java/android/view/HapticFeedbackConstants.java
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * Constants to be used to perform haptic feedback effects via
+ * {@link View#performHapticFeedback(int)} 
+ */
+public class HapticFeedbackConstants {
+
+    private HapticFeedbackConstants() {}
+
+    public static final int LONG_PRESS = 0;
+    
+    /** @hide pending API council */
+    public static final int ZOOM_RING_TICK = 1;
+    
+    /**
+     * Flag for {@link View#performHapticFeedback(int, int)
+     * View.performHapticFeedback(int, int)}: Ignore the setting in the
+     * view for whether to perform haptic feedback, do it always.
+     */
+    public static final int FLAG_IGNORE_VIEW_SETTING = 0x0001;
+    
+    /**
+     * Flag for {@link View#performHapticFeedback(int, int)
+     * View.performHapticFeedback(int, int)}: Ignore the global setting
+     * for whether to perform haptic feedback, do it always.
+     */
+    public static final int FLAG_IGNORE_GLOBAL_SETTING = 0x0002;
+}
diff --git a/core/java/android/view/IApplicationToken.aidl b/core/java/android/view/IApplicationToken.aidl
new file mode 100644
index 0000000..6bff5b3
--- /dev/null
+++ b/core/java/android/view/IApplicationToken.aidl
@@ -0,0 +1,28 @@
+/* //device/java/android/android/view/IApplicationToken.aidl
+**
+** Copyright 2007, 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;
+
+/** {@hide} */
+interface IApplicationToken
+{
+    void windowsVisible();
+    void windowsGone();
+    boolean keyDispatchingTimedOut();
+    long getKeyDispatchingTimeout();
+}
+
diff --git a/core/java/android/view/IOnKeyguardExitResult.aidl b/core/java/android/view/IOnKeyguardExitResult.aidl
new file mode 100644
index 0000000..47d5220
--- /dev/null
+++ b/core/java/android/view/IOnKeyguardExitResult.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/hardware/ISensorListener.aidl
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+/** @hide */
+oneway interface IOnKeyguardExitResult {
+
+    void onKeyguardExitResult(boolean success);
+
+}
diff --git a/core/java/android/view/IRotationWatcher.aidl b/core/java/android/view/IRotationWatcher.aidl
new file mode 100644
index 0000000..2c83642
--- /dev/null
+++ b/core/java/android/view/IRotationWatcher.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/hardware/ISensorListener.aidl
+**
+** Copyright 2008, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License");
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+
+package android.view;
+
+/**
+ * {@hide}
+ */
+oneway interface IRotationWatcher {
+    void onRotationChanged(int rotation);
+}
diff --git a/core/java/android/view/IWindow.aidl b/core/java/android/view/IWindow.aidl
new file mode 100644
index 0000000..99d5c0c
--- /dev/null
+++ b/core/java/android/view/IWindow.aidl
@@ -0,0 +1,59 @@
+/* //device/java/android/android/view/IWindow.aidl
+**
+** Copyright 2007, 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.graphics.Rect;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+import android.os.ParcelFileDescriptor;
+
+/**
+ * API back to a client window that the Window Manager uses to inform it of
+ * interesting things happening.
+ *
+ * {@hide}
+ */
+oneway interface IWindow {
+    /**
+     * ===== NOTICE =====
+     * The first method must remain the first method. Scripts
+     * and tools rely on their transaction number to work properly.
+     */
+
+    /**
+     * Invoked by the view server to tell a window to execute the specified
+     * command. Any response from the receiver must be sent through the
+     * specified file descriptor.
+     */
+    void executeCommand(String command, String parameters, in ParcelFileDescriptor descriptor);
+
+    void resized(int w, int h, in Rect coveredInsets, in Rect visibleInsets,
+            boolean reportDraw);
+    void dispatchKey(in KeyEvent event);
+    void dispatchPointer(in MotionEvent event, long eventTime);
+    void dispatchTrackball(in MotionEvent event, long eventTime);
+    void dispatchAppVisibility(boolean visible);
+    void dispatchGetNewSurface();
+
+    /**
+     * Tell the window that it is either gaining or losing focus.  Keep it up
+     * to date on the current state showing navigational focus (touch mode) too.
+     */
+    void windowFocusChanged(boolean hasFocus, boolean inTouchMode);
+}
diff --git a/core/java/android/view/IWindowManager.aidl b/core/java/android/view/IWindowManager.aidl
new file mode 100644
index 0000000..a856b24
--- /dev/null
+++ b/core/java/android/view/IWindowManager.aidl
@@ -0,0 +1,136 @@
+/* //device/java/android/android/view/IWindowManager.aidl
+**
+** Copyright 2006, 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 com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodClient;
+
+import android.content.res.Configuration;
+import android.view.IApplicationToken;
+import android.view.IOnKeyguardExitResult;
+import android.view.IRotationWatcher;
+import android.view.IWindowSession;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * System private interface to the window manager.
+ *
+ * {@hide}
+ */
+interface IWindowManager
+{
+    /**
+     * ===== NOTICE =====
+     * The first three methods must remain the first three methods. Scripts
+     * and tools rely on their transaction number to work properly.
+     */
+    // This is used for debugging
+    boolean startViewServer(int port);   // Transaction #1
+    boolean stopViewServer();            // Transaction #2
+    boolean isViewServerRunning();       // Transaction #3
+
+    IWindowSession openSession(in IInputMethodClient client,
+            in IInputContext inputContext);
+    boolean inputMethodClientHasFocus(IInputMethodClient client);
+    
+    // These can only be called when injecting events to your own window,
+    // or by holding the INJECT_EVENTS permission.
+    boolean injectKeyEvent(in KeyEvent ev, boolean sync);
+    boolean injectPointerEvent(in MotionEvent ev, boolean sync);
+    boolean injectTrackballEvent(in MotionEvent ev, boolean sync);
+    
+    // These can only be called when holding the MANAGE_APP_TOKENS permission.
+    void pauseKeyDispatching(IBinder token);
+    void resumeKeyDispatching(IBinder token);
+    void setEventDispatching(boolean enabled);
+    void addAppToken(int addPos, IApplicationToken token,
+            int groupId, int requestedOrientation, boolean fullscreen);
+    void setAppGroupId(IBinder token, int groupId);
+    Configuration updateOrientationFromAppTokens(IBinder freezeThisOneIfNeeded);
+    void setAppOrientation(IApplicationToken token, int requestedOrientation);
+    int getAppOrientation(IApplicationToken token);
+    void setFocusedApp(IBinder token, boolean moveFocusNow);
+    void prepareAppTransition(int transit);
+    int getPendingAppTransition();
+    void executeAppTransition();
+    void setAppStartingWindow(IBinder token, String pkg, int theme,
+            CharSequence nonLocalizedLabel, int labelRes,
+            int icon, IBinder transferFrom, boolean createIfNeeded);
+    void setAppWillBeHidden(IBinder token);
+    void setAppVisibility(IBinder token, boolean visible);
+    void startAppFreezingScreen(IBinder token, int configChanges);
+    void stopAppFreezingScreen(IBinder token, boolean force);
+    void removeAppToken(IBinder token);
+    void moveAppToken(int index, IBinder token);
+    void moveAppTokensToTop(in List<IBinder> tokens);
+    void moveAppTokensToBottom(in List<IBinder> tokens);
+    void addWindowToken(IBinder token, int type);
+    void removeWindowToken(IBinder token);
+
+    // these require DISABLE_KEYGUARD permission
+    void disableKeyguard(IBinder token, String tag);
+    void reenableKeyguard(IBinder token);
+    void exitKeyguardSecurely(IOnKeyguardExitResult callback);
+    boolean inKeyguardRestrictedInputMode();
+
+    
+    // These can only be called with the SET_ANIMATON_SCALE permission.
+    float getAnimationScale(int which);
+    float[] getAnimationScales();
+    void setAnimationScale(int which, float scale);
+    void setAnimationScales(in float[] scales);
+    
+    // These require the READ_INPUT_STATE permission.
+    int getSwitchState(int sw);
+    int getSwitchStateForDevice(int devid, int sw);
+    int getScancodeState(int sw);
+    int getScancodeStateForDevice(int devid, int sw);
+    int getKeycodeState(int sw);
+    int getKeycodeStateForDevice(int devid, int sw);
+    
+    // Report whether the hardware supports the given keys; returns true if successful
+    boolean hasKeys(in int[] keycodes, inout boolean[] keyExists);
+    
+    // For testing
+    void setInTouchMode(boolean showFocus);
+    
+    // These can only be called with the SET_ORIENTATION permission.
+    /**
+     * Change the current screen rotation, constants as per
+     * {@link android.view.Surface}.
+     * @param rotation the intended rotation.
+     * @param alwaysSendConfiguration Flag to force a new configuration to
+     * be evaluated.  This can be used when there are other parameters in
+     * configuration that are changing.
+     * {@link android.view.Surface}.
+     */
+    void setRotation(int rotation, boolean alwaysSendConfiguration);
+
+    /**
+     * Retrieve the current screen orientation, constants as per
+     * {@link android.view.Surface}.
+     */
+    int getRotation();
+    
+    /**
+     * Watch the rotation of the screen.  Returns the current rotation,
+     * calls back when it changes.
+     */
+    int watchRotation(IRotationWatcher watcher);
+}
diff --git a/core/java/android/view/IWindowSession.aidl b/core/java/android/view/IWindowSession.aidl
new file mode 100644
index 0000000..1156856
--- /dev/null
+++ b/core/java/android/view/IWindowSession.aidl
@@ -0,0 +1,111 @@
+/* //device/java/android/android/view/IWindowSession.aidl
+**
+** Copyright 2006, 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.graphics.Rect;
+import android.graphics.Region;
+import android.view.IWindow;
+import android.view.MotionEvent;
+import android.view.WindowManager;
+import android.view.Surface;
+
+/**
+ * System private per-application interface to the window manager.
+ *
+ * {@hide}
+ */
+interface IWindowSession {
+    int add(IWindow window, in WindowManager.LayoutParams attrs,
+            in int viewVisibility, out Rect outContentInsets);
+    void remove(IWindow window);
+    
+    /**
+     * Change the parameters of a window.  You supply the
+     * new parameters, it returns the new frame of the window on screen (the
+     * position should be ignored) and surface of the window.  The surface
+     * will be invalid if the window is currently hidden, else you can use it
+     * to draw the window's contents.
+     * 
+     * @param window The window being modified.
+     * @param attrs If non-null, new attributes to apply to the window.
+     * @param requestedWidth The width the window wants to be.
+     * @param requestedHeight The height the window wants to be.
+     * @param viewVisibility Window root view's visibility.
+     * @param insetsPending Set to true if the client will be later giving
+     * internal insets; as a result, the window will not impact other window
+     * layouts until the insets are given.
+     * @param outFrame Rect in which is placed the new position/size on
+     * screen.
+     * @param outContentInsets Rect in which is placed the offsets from
+     * <var>outFrame</var> in which the content of the window should be
+     * placed.  This can be used to modify the window layout to ensure its
+     * contents are visible to the user, taking into account system windows
+     * like the status bar or a soft keyboard.
+     * @param outVisibleInsets Rect in which is placed the offsets from
+     * <var>outFrame</var> in which the window is actually completely visible
+     * to the user.  This can be used to temporarily scroll the window's
+     * contents to make sure the user can see it.  This is different than
+     * <var>outContentInsets</var> in that these insets change transiently,
+     * so complex relayout of the window should not happen based on them.
+     * @param outSurface Object in which is placed the new display surface.
+     * 
+     * @return int Result flags: {@link WindowManagerImpl#RELAYOUT_SHOW_FOCUS},
+     * {@link WindowManagerImpl#RELAYOUT_FIRST_TIME}.
+     */
+    int relayout(IWindow window, in WindowManager.LayoutParams attrs,
+            int requestedWidth, int requestedHeight, int viewVisibility,
+            boolean insetsPending, out Rect outFrame, out Rect outContentInsets,
+            out Rect outVisibleInsets, out Surface outSurface);
+
+    /**
+     * Give the window manager a hint of the part of the window that is
+     * completely transparent, allowing it to work with the surface flinger
+     * to optimize compositing of this part of the window.
+     */
+    void setTransparentRegion(IWindow window, in Region region);
+    
+    /**
+     * Tell the window manager about the content and visible insets of the
+     * given window, which can be used to adjust the <var>outContentInsets</var>
+     * and <var>outVisibleInsets</var> values returned by
+     * {@link #relayout relayout()} for windows behind this one.
+     *
+     * @param touchableInsets Controls which part of the window inside of its
+     * frame can receive pointer events, as defined by
+     * {@link android.view.ViewTreeObserver.InternalInsetsInfo}.
+     */
+    void setInsets(IWindow window, int touchableInsets, in Rect contentInsets,
+            in Rect visibleInsets);
+    
+    /**
+     * Return the current display size in which the window is being laid out,
+     * accounting for screen decorations around it.
+     */
+    void getDisplayFrame(IWindow window, out Rect outDisplayFrame);
+    
+    void finishDrawing(IWindow window);
+
+    void finishKey(IWindow window);
+    MotionEvent getPendingPointerMove(IWindow window);
+    MotionEvent getPendingTrackballMove(IWindow window);
+    
+    void setInTouchMode(boolean showFocus);
+    boolean getInTouchMode();
+    
+    boolean performHapticFeedback(IWindow window, int effectId, boolean always);
+}
diff --git a/core/java/android/view/InflateException.java b/core/java/android/view/InflateException.java
new file mode 100644
index 0000000..7b39d33
--- /dev/null
+++ b/core/java/android/view/InflateException.java
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2007 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;
+
+/**
+ * This exception is thrown by an inflater on error conditions.
+ */
+public class InflateException extends RuntimeException {
+
+    public InflateException() {
+        super();
+    }
+
+    public InflateException(String detailMessage, Throwable throwable) {
+        super(detailMessage, throwable);
+    }
+
+    public InflateException(String detailMessage) {
+        super(detailMessage);
+    }
+
+    public InflateException(Throwable throwable) {
+        super(throwable);
+    }
+
+}
diff --git a/core/java/android/view/KeyCharacterMap.java b/core/java/android/view/KeyCharacterMap.java
new file mode 100644
index 0000000..25958aa
--- /dev/null
+++ b/core/java/android/view/KeyCharacterMap.java
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2007 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.text.method.MetaKeyKeyListener;
+import android.util.SparseIntArray;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.os.SystemClock;
+import android.util.SparseArray;
+
+import java.lang.Character;
+import java.lang.ref.WeakReference;
+
+public class KeyCharacterMap
+{
+    /**
+     * The id of the device's primary built in keyboard is always 0.
+     */
+    public static final int BUILT_IN_KEYBOARD = 0;
+
+    /** A numeric (12-key) keyboard. */
+    public static final int NUMERIC = 1;
+
+    /** A keyboard with all the letters, but with more than one letter
+     *  per key. */
+    public static final int PREDICTIVE = 2;
+
+    /** A keyboard with all the letters, and maybe some numbers. */
+    public static final int ALPHA = 3;
+
+    /**
+     * This private-use character is used to trigger Unicode character
+     * input by hex digits.
+     */
+    public static final char HEX_INPUT = '\uEF00';
+
+    /**
+     * This private-use character is used to bring up a character picker for
+     * miscellaneous symbols.
+     */
+    public static final char PICKER_DIALOG_INPUT = '\uEF01';
+
+    private static Object sLock = new Object();
+    private static SparseArray<WeakReference<KeyCharacterMap>> sInstances 
+        = new SparseArray<WeakReference<KeyCharacterMap>>();
+
+    public static KeyCharacterMap load(int keyboard)
+    {
+        synchronized (sLock) {
+            KeyCharacterMap result;
+            WeakReference<KeyCharacterMap> ref = sInstances.get(keyboard);
+            if (ref != null) {
+                result = ref.get();
+                if (result != null) {
+                    return result;
+                }
+            }
+            result = new KeyCharacterMap(keyboard);
+            sInstances.put(keyboard, new WeakReference<KeyCharacterMap>(result));
+            return result;
+        }
+    }
+
+    private KeyCharacterMap(int keyboardDevice)
+    {
+        mKeyboardDevice = keyboardDevice;
+        mPointer = ctor_native(keyboardDevice);
+    }
+
+    /**
+     * <p>
+     * Returns the Unicode character that the specified key would produce
+     * when the specified meta bits (see {@link MetaKeyKeyListener})
+     * were active.
+     * </p><p>
+     * Returns 0 if the key is not one that is used to type Unicode
+     * characters.
+     * </p><p>
+     * If the return value has bit {@link #COMBINING_ACCENT} set, the
+     * key is a "dead key" that should be combined with another to
+     * actually produce a character -- see {@link #getDeadChar} --
+     * after masking with {@link #COMBINING_ACCENT_MASK}.
+     * </p>
+     */
+    public int get(int keyCode, int meta)
+    {
+        if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
+            meta |= KeyEvent.META_SHIFT_ON;
+        }
+        if ((meta & MetaKeyKeyListener.META_ALT_LOCKED) != 0) {
+            meta |= KeyEvent.META_ALT_ON;
+        }
+
+        // Ignore caps lock on keys where alt and shift have the same effect.
+        if ((meta & MetaKeyKeyListener.META_CAP_LOCKED) != 0) {
+            if (get_native(mPointer, keyCode, KeyEvent.META_SHIFT_ON) ==
+                get_native(mPointer, keyCode, KeyEvent.META_ALT_ON)) {
+                meta &= ~KeyEvent.META_SHIFT_ON;
+            }
+        }
+
+        int ret = get_native(mPointer, keyCode, meta);
+        int map = COMBINING.get(ret);
+
+        if (map != 0) {
+            return map;
+        } else {
+            return ret;
+        }
+    }
+
+    /**
+     * Gets the number or symbol associated with the key.  The character value
+     * is returned, not the numeric value.  If the key is not a number, but is
+     * a symbol, the symbol is retuned.
+     */
+    public char getNumber(int keyCode)
+    {
+        return getNumber_native(mPointer, keyCode);
+    }
+
+    /**
+     * The same as {@link #getMatch(int,char[],int) getMatch(keyCode, chars, 0)}.
+     */
+    public char getMatch(int keyCode, char[] chars)
+    {
+        return getMatch(keyCode, chars, 0);
+    }
+
+    /**
+     * If one of the chars in the array can be generated by keyCode,
+     * return the char; otherwise return '\0'.
+     * @param keyCode the key code to look at
+     * @param chars the characters to try to find
+     * @param modifiers the modifier bits to prefer.  If any of these bits
+     *                  are set, if there are multiple choices, that could
+     *                  work, the one for this modifier will be set.
+     */
+    public char getMatch(int keyCode, char[] chars, int modifiers)
+    {
+        if (chars == null) {
+            // catch it here instead of in native
+            throw new NullPointerException();
+        }
+        return getMatch_native(mPointer, keyCode, chars, modifiers);
+    }
+
+    /**
+     * Get the primary character for this key.  In other words, the label
+     * that is physically printed on it.
+     */
+    public char getDisplayLabel(int keyCode)
+    {
+        return getDisplayLabel_native(mPointer, keyCode);
+    }
+
+    /**
+     * Get the character that is produced by putting accent on the character
+     * c.
+     * For example, getDeadChar('`', 'e') returns &egrave;.
+     */
+    public static int getDeadChar(int accent, int c)
+    {
+        return DEAD.get((accent << 16) | c);
+    }
+
+    public static class KeyData {
+        public static final int META_LENGTH = 4;
+
+        /**
+         * The display label (see {@link #getDisplayLabel}).
+         */
+        public char displayLabel;
+        /**
+         * The "number" value (see {@link #getNumber}).
+         */
+        public char number;
+        /**
+         * The character that will be generated in various meta states
+         * (the same ones used for {@link #get} and defined as
+         * {@link KeyEvent#META_SHIFT_ON} and {@link KeyEvent#META_ALT_ON}).
+         *      <table>
+         *          <tr><th>Index</th><th align="left">Value</th></tr>
+         *          <tr><td>0</td><td>no modifiers</td></tr>
+         *          <tr><td>1</td><td>caps</td></tr>
+         *          <tr><td>2</td><td>alt</td></tr>
+         *          <tr><td>3</td><td>caps + alt</td></tr>
+         *      </table>
+         */
+        public char[] meta = new char[META_LENGTH];
+    }
+    
+    /**
+     * Get the characters conversion data for a given keyCode.
+     *
+     * @param keyCode the keyCode to look for
+     * @param results a {@link KeyData} that will be filled with the results.
+     *
+     * @return whether the key was mapped or not.  If the key was not mapped,
+     *         results is not modified.
+     */
+    public boolean getKeyData(int keyCode, KeyData results)
+    {
+        if (results.meta.length >= KeyData.META_LENGTH) {
+            return getKeyData_native(mPointer, keyCode, results);
+        } else {
+            throw new IndexOutOfBoundsException("results.meta.length must be >= " +
+                                                KeyData.META_LENGTH);
+        }
+    }
+
+    /**
+     * Get an array of KeyEvent objects that if put into the input stream
+     * could plausibly generate the provided sequence of characters.  It is
+     * not guaranteed that the sequence is the only way to generate these
+     * events or that it is optimal.
+     * 
+     * @return an array of KeyEvent objects, or null if the given char array
+     *         can not be generated using the current key character map.
+     */
+    public KeyEvent[] getEvents(char[] chars)
+    {
+        if (chars == null) {
+            throw new NullPointerException();
+        }
+
+        long[] keys = getEvents_native(mPointer, chars);
+        if (keys == null) {
+            return null;
+        }
+
+        // how big should the array be
+        int len = keys.length*2;
+        int N = keys.length;
+        for (int i=0; i<N; i++) {
+            int mods = (int)(keys[i] >> 32);
+            if ((mods & KeyEvent.META_ALT_ON) != 0) {
+                len += 2;
+            }
+            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
+                len += 2;
+            }
+            if ((mods & KeyEvent.META_SYM_ON) != 0) {
+                len += 2;
+            }
+        }
+
+        // create the events
+        KeyEvent[] rv = new KeyEvent[len];
+        int index = 0;
+        long now = SystemClock.uptimeMillis();
+        int device = mKeyboardDevice;
+        for (int i=0; i<N; i++) {
+            int mods = (int)(keys[i] >> 32);
+            int meta = 0;
+
+            if ((mods & KeyEvent.META_ALT_ON) != 0) {
+                meta |= KeyEvent.META_ALT_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                        KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
+                index++;
+            }
+            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
+                meta |= KeyEvent.META_SHIFT_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                        KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
+                index++;
+            }
+            if ((mods & KeyEvent.META_SYM_ON) != 0) {
+                meta |= KeyEvent.META_SYM_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                        KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
+                index++;
+            }
+
+            int key = (int)(keys[i]);
+            rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_DOWN,
+                    key, 0, meta, device, 0);
+            index++;
+            rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
+                    key, 0, meta, device, 0);
+            index++;
+
+            if ((mods & KeyEvent.META_ALT_ON) != 0) {
+                meta &= ~KeyEvent.META_ALT_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
+                        KeyEvent.KEYCODE_ALT_LEFT, 0, meta, device, 0);
+                index++;
+            }
+            if ((mods & KeyEvent.META_SHIFT_ON) != 0) {
+                meta &= ~KeyEvent.META_SHIFT_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
+                        KeyEvent.KEYCODE_SHIFT_LEFT, 0, meta, device, 0);
+                index++;
+            }
+            if ((mods & KeyEvent.META_SYM_ON) != 0) {
+                meta &= ~KeyEvent.META_SYM_ON;
+                rv[index] = new KeyEvent(now, now, KeyEvent.ACTION_UP,
+                        KeyEvent.KEYCODE_SYM, 0, meta, device, 0);
+                index++;
+            }
+        }
+
+        return rv;
+    }
+
+    /**
+     * Does this character key produce a glyph?
+     */
+    public boolean isPrintingKey(int keyCode)
+    {
+        int type = Character.getType(get(keyCode, 0));
+
+        switch (type)
+        {
+            case Character.SPACE_SEPARATOR:
+            case Character.LINE_SEPARATOR:
+            case Character.PARAGRAPH_SEPARATOR:
+            case Character.CONTROL:
+            case Character.FORMAT:
+                return false;
+            default:
+                return true;
+        }
+    }
+
+    protected void finalize() throws Throwable
+    {
+        dtor_native(mPointer);
+    }
+
+    /**
+     * Returns {@link #NUMERIC}, {@link #PREDICTIVE} or {@link #ALPHA}.
+     */
+    public int getKeyboardType()
+    {
+        return getKeyboardType_native(mPointer);
+    }
+
+    /**
+     * Queries the framework about whether any physical keys exist on the
+     * device that are capable of producing the given key codes.
+     */
+    public static boolean deviceHasKey(int keyCode) {
+        int[] codeArray = new int[1];
+        codeArray[0] = keyCode;
+        boolean[] ret = deviceHasKeys(codeArray);
+        return ret[0];
+    }
+    
+    public static boolean[] deviceHasKeys(int[] keyCodes) {
+        boolean[] ret = new boolean[keyCodes.length];
+        IWindowManager wm = IWindowManager.Stub.asInterface(ServiceManager.getService("window"));
+        try {
+            wm.hasKeys(keyCodes, ret);
+        } catch (RemoteException e) {
+            // no fallback; just return the empty array
+        }
+        return ret;
+    }
+
+    private int mPointer;
+    private int mKeyboardDevice;
+
+    private static native int ctor_native(int id);
+    private static native void dtor_native(int ptr);
+    private static native char get_native(int ptr, int keycode,
+                                    int meta);
+    private static native char getNumber_native(int ptr, int keycode);
+    private static native char getMatch_native(int ptr, int keycode,
+                                    char[] chars, int modifiers);
+    private static native char getDisplayLabel_native(int ptr, int keycode);
+    private static native boolean getKeyData_native(int ptr, int keycode,
+                                    KeyData results);
+    private static native int getKeyboardType_native(int ptr);
+    private static native long[] getEvents_native(int ptr, char[] str);
+
+    /**
+     * Maps Unicode combining diacritical to display-form dead key
+     * (display character shifted left 16 bits).
+     */
+    private static SparseIntArray COMBINING = new SparseIntArray();
+
+    /**
+     * Maps combinations of (display-form) dead key and second character
+     * to combined output character.
+     */
+    private static SparseIntArray DEAD = new SparseIntArray();
+
+    /*
+     * TODO: Change the table format to support full 21-bit-wide
+     * accent characters and combined characters if ever necessary.    
+     */
+    private static final int ACUTE = '\u00B4' << 16;
+    private static final int GRAVE = '`' << 16;
+    private static final int CIRCUMFLEX = '^' << 16;
+    private static final int TILDE = '~' << 16;
+    private static final int UMLAUT = '\u00A8' << 16;
+
+    /*
+     * This bit will be set in the return value of {@link #get(int, int)} if the
+     * key is a "dead key."
+     */
+    public static final int COMBINING_ACCENT = 0x80000000;
+    /**
+     * Mask the return value from {@link #get(int, int)} with this value to get
+     * a printable representation of the accent character of a "dead key."
+     */
+    public static final int COMBINING_ACCENT_MASK = 0x7FFFFFFF;
+
+    static {
+        COMBINING.put('\u0300', (GRAVE >> 16) | COMBINING_ACCENT);
+        COMBINING.put('\u0301', (ACUTE >> 16) | COMBINING_ACCENT);
+        COMBINING.put('\u0302', (CIRCUMFLEX >> 16) | COMBINING_ACCENT);
+        COMBINING.put('\u0303', (TILDE >> 16) | COMBINING_ACCENT);
+        COMBINING.put('\u0308', (UMLAUT >> 16) | COMBINING_ACCENT);
+
+        DEAD.put(ACUTE | 'A', '\u00C1');
+        DEAD.put(ACUTE | 'C', '\u0106');
+        DEAD.put(ACUTE | 'E', '\u00C9');
+        DEAD.put(ACUTE | 'G', '\u01F4');
+        DEAD.put(ACUTE | 'I', '\u00CD');
+        DEAD.put(ACUTE | 'K', '\u1E30');
+        DEAD.put(ACUTE | 'L', '\u0139');
+        DEAD.put(ACUTE | 'M', '\u1E3E');
+        DEAD.put(ACUTE | 'N', '\u0143');
+        DEAD.put(ACUTE | 'O', '\u00D3');
+        DEAD.put(ACUTE | 'P', '\u1E54');
+        DEAD.put(ACUTE | 'R', '\u0154');
+        DEAD.put(ACUTE | 'S', '\u015A');
+        DEAD.put(ACUTE | 'U', '\u00DA');
+        DEAD.put(ACUTE | 'W', '\u1E82');
+        DEAD.put(ACUTE | 'Y', '\u00DD');
+        DEAD.put(ACUTE | 'Z', '\u0179');
+        DEAD.put(ACUTE | 'a', '\u00E1');
+        DEAD.put(ACUTE | 'c', '\u0107');
+        DEAD.put(ACUTE | 'e', '\u00E9');
+        DEAD.put(ACUTE | 'g', '\u01F5');
+        DEAD.put(ACUTE | 'i', '\u00ED');
+        DEAD.put(ACUTE | 'k', '\u1E31');
+        DEAD.put(ACUTE | 'l', '\u013A');
+        DEAD.put(ACUTE | 'm', '\u1E3F');
+        DEAD.put(ACUTE | 'n', '\u0144');
+        DEAD.put(ACUTE | 'o', '\u00F3');
+        DEAD.put(ACUTE | 'p', '\u1E55');
+        DEAD.put(ACUTE | 'r', '\u0155');
+        DEAD.put(ACUTE | 's', '\u015B');
+        DEAD.put(ACUTE | 'u', '\u00FA');
+        DEAD.put(ACUTE | 'w', '\u1E83');
+        DEAD.put(ACUTE | 'y', '\u00FD');
+        DEAD.put(ACUTE | 'z', '\u017A');
+        DEAD.put(CIRCUMFLEX | 'A', '\u00C2');
+        DEAD.put(CIRCUMFLEX | 'C', '\u0108');
+        DEAD.put(CIRCUMFLEX | 'E', '\u00CA');
+        DEAD.put(CIRCUMFLEX | 'G', '\u011C');
+        DEAD.put(CIRCUMFLEX | 'H', '\u0124');
+        DEAD.put(CIRCUMFLEX | 'I', '\u00CE');
+        DEAD.put(CIRCUMFLEX | 'J', '\u0134');
+        DEAD.put(CIRCUMFLEX | 'O', '\u00D4');
+        DEAD.put(CIRCUMFLEX | 'S', '\u015C');
+        DEAD.put(CIRCUMFLEX | 'U', '\u00DB');
+        DEAD.put(CIRCUMFLEX | 'W', '\u0174');
+        DEAD.put(CIRCUMFLEX | 'Y', '\u0176');
+        DEAD.put(CIRCUMFLEX | 'Z', '\u1E90');
+        DEAD.put(CIRCUMFLEX | 'a', '\u00E2');
+        DEAD.put(CIRCUMFLEX | 'c', '\u0109');
+        DEAD.put(CIRCUMFLEX | 'e', '\u00EA');
+        DEAD.put(CIRCUMFLEX | 'g', '\u011D');
+        DEAD.put(CIRCUMFLEX | 'h', '\u0125');
+        DEAD.put(CIRCUMFLEX | 'i', '\u00EE');
+        DEAD.put(CIRCUMFLEX | 'j', '\u0135');
+        DEAD.put(CIRCUMFLEX | 'o', '\u00F4');
+        DEAD.put(CIRCUMFLEX | 's', '\u015D');
+        DEAD.put(CIRCUMFLEX | 'u', '\u00FB');
+        DEAD.put(CIRCUMFLEX | 'w', '\u0175');
+        DEAD.put(CIRCUMFLEX | 'y', '\u0177');
+        DEAD.put(CIRCUMFLEX | 'z', '\u1E91');
+        DEAD.put(GRAVE | 'A', '\u00C0');
+        DEAD.put(GRAVE | 'E', '\u00C8');
+        DEAD.put(GRAVE | 'I', '\u00CC');
+        DEAD.put(GRAVE | 'N', '\u01F8');
+        DEAD.put(GRAVE | 'O', '\u00D2');
+        DEAD.put(GRAVE | 'U', '\u00D9');
+        DEAD.put(GRAVE | 'W', '\u1E80');
+        DEAD.put(GRAVE | 'Y', '\u1EF2');
+        DEAD.put(GRAVE | 'a', '\u00E0');
+        DEAD.put(GRAVE | 'e', '\u00E8');
+        DEAD.put(GRAVE | 'i', '\u00EC');
+        DEAD.put(GRAVE | 'n', '\u01F9');
+        DEAD.put(GRAVE | 'o', '\u00F2');
+        DEAD.put(GRAVE | 'u', '\u00F9');
+        DEAD.put(GRAVE | 'w', '\u1E81');
+        DEAD.put(GRAVE | 'y', '\u1EF3');
+        DEAD.put(TILDE | 'A', '\u00C3');
+        DEAD.put(TILDE | 'E', '\u1EBC');
+        DEAD.put(TILDE | 'I', '\u0128');
+        DEAD.put(TILDE | 'N', '\u00D1');
+        DEAD.put(TILDE | 'O', '\u00D5');
+        DEAD.put(TILDE | 'U', '\u0168');
+        DEAD.put(TILDE | 'V', '\u1E7C');
+        DEAD.put(TILDE | 'Y', '\u1EF8');
+        DEAD.put(TILDE | 'a', '\u00E3');
+        DEAD.put(TILDE | 'e', '\u1EBD');
+        DEAD.put(TILDE | 'i', '\u0129');
+        DEAD.put(TILDE | 'n', '\u00F1');
+        DEAD.put(TILDE | 'o', '\u00F5');
+        DEAD.put(TILDE | 'u', '\u0169');
+        DEAD.put(TILDE | 'v', '\u1E7D');
+        DEAD.put(TILDE | 'y', '\u1EF9');
+        DEAD.put(UMLAUT | 'A', '\u00C4');
+        DEAD.put(UMLAUT | 'E', '\u00CB');
+        DEAD.put(UMLAUT | 'H', '\u1E26');
+        DEAD.put(UMLAUT | 'I', '\u00CF');
+        DEAD.put(UMLAUT | 'O', '\u00D6');
+        DEAD.put(UMLAUT | 'U', '\u00DC');
+        DEAD.put(UMLAUT | 'W', '\u1E84');
+        DEAD.put(UMLAUT | 'X', '\u1E8C');
+        DEAD.put(UMLAUT | 'Y', '\u0178');
+        DEAD.put(UMLAUT | 'a', '\u00E4');
+        DEAD.put(UMLAUT | 'e', '\u00EB');
+        DEAD.put(UMLAUT | 'h', '\u1E27');
+        DEAD.put(UMLAUT | 'i', '\u00EF');
+        DEAD.put(UMLAUT | 'o', '\u00F6');
+        DEAD.put(UMLAUT | 't', '\u1E97');
+        DEAD.put(UMLAUT | 'u', '\u00FC');
+        DEAD.put(UMLAUT | 'w', '\u1E85');
+        DEAD.put(UMLAUT | 'x', '\u1E8D');
+        DEAD.put(UMLAUT | 'y', '\u00FF');
+    }
+}
diff --git a/core/java/android/view/KeyEvent.aidl b/core/java/android/view/KeyEvent.aidl
new file mode 100644
index 0000000..dc15ecf
--- /dev/null
+++ b/core/java/android/view/KeyEvent.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android.view.KeyEvent.aidl
+**
+** Copyright 2007, 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;
+
+parcelable KeyEvent;
diff --git a/core/java/android/view/KeyEvent.java b/core/java/android/view/KeyEvent.java
new file mode 100644
index 0000000..430cc71
--- /dev/null
+++ b/core/java/android/view/KeyEvent.java
@@ -0,0 +1,886 @@
+/*
+ * Copyright (C) 2006 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.os.Parcel;
+import android.os.Parcelable;
+import android.view.KeyCharacterMap;
+import android.view.KeyCharacterMap.KeyData;
+
+/**
+ * Contains constants for key events.
+ */
+public class KeyEvent implements Parcelable {
+    // key codes
+    public static final int KEYCODE_UNKNOWN         = 0;
+    public static final int KEYCODE_SOFT_LEFT       = 1;
+    public static final int KEYCODE_SOFT_RIGHT      = 2;
+    public static final int KEYCODE_HOME            = 3;
+    public static final int KEYCODE_BACK            = 4;
+    public static final int KEYCODE_CALL            = 5;
+    public static final int KEYCODE_ENDCALL         = 6;
+    public static final int KEYCODE_0               = 7;
+    public static final int KEYCODE_1               = 8;
+    public static final int KEYCODE_2               = 9;
+    public static final int KEYCODE_3               = 10;
+    public static final int KEYCODE_4               = 11;
+    public static final int KEYCODE_5               = 12;
+    public static final int KEYCODE_6               = 13;
+    public static final int KEYCODE_7               = 14;
+    public static final int KEYCODE_8               = 15;
+    public static final int KEYCODE_9               = 16;
+    public static final int KEYCODE_STAR            = 17;
+    public static final int KEYCODE_POUND           = 18;
+    public static final int KEYCODE_DPAD_UP         = 19;
+    public static final int KEYCODE_DPAD_DOWN       = 20;
+    public static final int KEYCODE_DPAD_LEFT       = 21;
+    public static final int KEYCODE_DPAD_RIGHT      = 22;
+    public static final int KEYCODE_DPAD_CENTER     = 23;
+    public static final int KEYCODE_VOLUME_UP       = 24;
+    public static final int KEYCODE_VOLUME_DOWN     = 25;
+    public static final int KEYCODE_POWER           = 26;
+    public static final int KEYCODE_CAMERA          = 27;
+    public static final int KEYCODE_CLEAR           = 28;
+    public static final int KEYCODE_A               = 29;
+    public static final int KEYCODE_B               = 30;
+    public static final int KEYCODE_C               = 31;
+    public static final int KEYCODE_D               = 32;
+    public static final int KEYCODE_E               = 33;
+    public static final int KEYCODE_F               = 34;
+    public static final int KEYCODE_G               = 35;
+    public static final int KEYCODE_H               = 36;
+    public static final int KEYCODE_I               = 37;
+    public static final int KEYCODE_J               = 38;
+    public static final int KEYCODE_K               = 39;
+    public static final int KEYCODE_L               = 40;
+    public static final int KEYCODE_M               = 41;
+    public static final int KEYCODE_N               = 42;
+    public static final int KEYCODE_O               = 43;
+    public static final int KEYCODE_P               = 44;
+    public static final int KEYCODE_Q               = 45;
+    public static final int KEYCODE_R               = 46;
+    public static final int KEYCODE_S               = 47;
+    public static final int KEYCODE_T               = 48;
+    public static final int KEYCODE_U               = 49;
+    public static final int KEYCODE_V               = 50;
+    public static final int KEYCODE_W               = 51;
+    public static final int KEYCODE_X               = 52;
+    public static final int KEYCODE_Y               = 53;
+    public static final int KEYCODE_Z               = 54;
+    public static final int KEYCODE_COMMA           = 55;
+    public static final int KEYCODE_PERIOD          = 56;
+    public static final int KEYCODE_ALT_LEFT        = 57;
+    public static final int KEYCODE_ALT_RIGHT       = 58;
+    public static final int KEYCODE_SHIFT_LEFT      = 59;
+    public static final int KEYCODE_SHIFT_RIGHT     = 60;
+    public static final int KEYCODE_TAB             = 61;
+    public static final int KEYCODE_SPACE           = 62;
+    public static final int KEYCODE_SYM             = 63;
+    public static final int KEYCODE_EXPLORER        = 64;
+    public static final int KEYCODE_ENVELOPE        = 65;
+    public static final int KEYCODE_ENTER           = 66;
+    public static final int KEYCODE_DEL             = 67;
+    public static final int KEYCODE_GRAVE           = 68;
+    public static final int KEYCODE_MINUS           = 69;
+    public static final int KEYCODE_EQUALS          = 70;
+    public static final int KEYCODE_LEFT_BRACKET    = 71;
+    public static final int KEYCODE_RIGHT_BRACKET   = 72;
+    public static final int KEYCODE_BACKSLASH       = 73;
+    public static final int KEYCODE_SEMICOLON       = 74;
+    public static final int KEYCODE_APOSTROPHE      = 75;
+    public static final int KEYCODE_SLASH           = 76;
+    public static final int KEYCODE_AT              = 77;
+    public static final int KEYCODE_NUM             = 78;
+    public static final int KEYCODE_HEADSETHOOK     = 79;
+    public static final int KEYCODE_FOCUS           = 80;   // *Camera* focus
+    public static final int KEYCODE_PLUS            = 81;
+    public static final int KEYCODE_MENU            = 82;
+    public static final int KEYCODE_NOTIFICATION    = 83;
+    public static final int KEYCODE_SEARCH          = 84;
+    public static final int KEYCODE_PLAYPAUSE       = 85;
+    public static final int KEYCODE_STOP            = 86;
+    public static final int KEYCODE_NEXTSONG        = 87;
+    public static final int KEYCODE_PREVIOUSSONG    = 88;
+    public static final int KEYCODE_REWIND          = 89;
+    public static final int KEYCODE_FORWARD         = 90;
+    public static final int KEYCODE_MUTE            = 91;
+    private static final int LAST_KEYCODE           = KEYCODE_MUTE;
+
+    // NOTE: If you add a new keycode here you must also add it to:
+    //  isSystem()
+    //  frameworks/base/include/ui/KeycodeLabels.h
+    //  tools/puppet_master/PuppetMaster/nav_keys.py
+    //  frameworks/base/core/res/res/values/attrs.xml
+    //  commands/monkey/Monkey.java
+    //  emulator?
+   
+    /**
+     * @deprecated There are now more than MAX_KEYCODE keycodes.
+     * Use {@link #getMaxKeyCode()} instead.
+     */
+    @Deprecated
+    public static final int MAX_KEYCODE             = 84;
+
+    /**
+     * {@link #getAction} value: the key has been pressed down.
+     */
+    public static final int ACTION_DOWN             = 0;
+    /**
+     * {@link #getAction} value: the key has been released.
+     */
+    public static final int ACTION_UP               = 1;
+    /**
+     * {@link #getAction} value: multiple duplicate key events have
+     * occurred in a row, or a complex string is being delivered.  If the
+     * key code is not {#link {@link #KEYCODE_UNKNOWN} then the
+     * {#link {@link #getRepeatCount()} method returns the number of times
+     * the given key code should be executed.
+     * Otherwise, if the key code {@link #KEYCODE_UNKNOWN}, then
+     * this is a sequence of characters as returned by {@link #getCharacters}.
+     */
+    public static final int ACTION_MULTIPLE         = 2;
+
+    /**
+     * <p>This mask is used to check whether one of the ALT meta keys is pressed.</p>
+     *
+     * @see #isAltPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_ALT_LEFT
+     * @see #KEYCODE_ALT_RIGHT
+     */
+    public static final int META_ALT_ON = 0x02;
+
+    /**
+     * <p>This mask is used to check whether the left ALT meta key is pressed.</p>
+     *
+     * @see #isAltPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_ALT_LEFT
+     */
+    public static final int META_ALT_LEFT_ON = 0x10;
+
+    /**
+     * <p>This mask is used to check whether the right the ALT meta key is pressed.</p>
+     *
+     * @see #isAltPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_ALT_RIGHT
+     */
+    public static final int META_ALT_RIGHT_ON = 0x20;
+
+    /**
+     * <p>This mask is used to check whether one of the SHIFT meta keys is pressed.</p>
+     *
+     * @see #isShiftPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_SHIFT_LEFT
+     * @see #KEYCODE_SHIFT_RIGHT
+     */
+    public static final int META_SHIFT_ON = 0x1;
+
+    /**
+     * <p>This mask is used to check whether the left SHIFT meta key is pressed.</p>
+     *
+     * @see #isShiftPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_SHIFT_LEFT
+     */
+    public static final int META_SHIFT_LEFT_ON = 0x40;
+
+    /**
+     * <p>This mask is used to check whether the right SHIFT meta key is pressed.</p>
+     *
+     * @see #isShiftPressed()
+     * @see #getMetaState()
+     * @see #KEYCODE_SHIFT_RIGHT
+     */
+    public static final int META_SHIFT_RIGHT_ON = 0x80;
+
+    /**
+     * <p>This mask is used to check whether the SYM meta key is pressed.</p>
+     *
+     * @see #isSymPressed()
+     * @see #getMetaState()
+     */
+    public static final int META_SYM_ON = 0x4;
+
+    /**
+     * This mask is set if the device woke because of this key event.
+     */
+    public static final int FLAG_WOKE_HERE = 0x1;
+    
+    /**
+     * This mask is set if the key event was generated by a software keyboard.
+     */
+    public static final int FLAG_SOFT_KEYBOARD = 0x2;
+    
+    /**
+     * This mask is set if we don't want the key event to cause us to leave
+     * touch mode.
+     */
+    public static final int FLAG_KEEP_TOUCH_MODE = 0x4;
+    
+    /**
+     * Returns the maximum keycode.
+     */
+    public static int getMaxKeyCode() {
+        return LAST_KEYCODE;
+    }
+
+    /**
+     * Get the character that is produced by putting accent on the character
+     * c.
+     * For example, getDeadChar('`', 'e') returns &egrave;.
+     */
+    public static int getDeadChar(int accent, int c) {
+        return KeyCharacterMap.getDeadChar(accent, c);
+    }
+    
+    private int mMetaState;
+    private int mAction;
+    private int mKeyCode;
+    private int mScancode;
+    private int mRepeatCount;
+    private int mDeviceId;
+    private int mFlags;
+    private long mDownTime;
+    private long mEventTime;
+    private String mCharacters;
+
+    public interface Callback {
+        /**
+         * Called when a key down event has occurred.
+         * 
+         * @param keyCode The value in event.getKeyCode().
+         * @param event Description of the key event.
+         * 
+         * @return If you handled the event, return true.  If you want to allow
+         *         the event to be handled by the next receiver, return false.
+         */
+        boolean onKeyDown(int keyCode, KeyEvent event);
+
+        /**
+         * Called when a key up event has occurred.
+         * 
+         * @param keyCode The value in event.getKeyCode().
+         * @param event Description of the key event.
+         * 
+         * @return If you handled the event, return true.  If you want to allow
+         *         the event to be handled by the next receiver, return false.
+         */
+        boolean onKeyUp(int keyCode, KeyEvent event);
+
+        /**
+         * Called when multiple down/up pairs of the same key have occurred
+         * in a row.
+         * 
+         * @param keyCode The value in event.getKeyCode().
+         * @param count Number of pairs as returned by event.getRepeatCount().
+         * @param event Description of the key event.
+         * 
+         * @return If you handled the event, return true.  If you want to allow
+         *         the event to be handled by the next receiver, return false.
+         */
+        boolean onKeyMultiple(int keyCode, int count, KeyEvent event);
+    }
+
+    /**
+     * Create a new key event.
+     * 
+     * @param action Action code: either {@link #ACTION_DOWN},
+     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * @param code The key code.
+     */
+    public KeyEvent(int action, int code) {
+        mAction = action;
+        mKeyCode = code;
+        mRepeatCount = 0;
+    }
+
+    /**
+     * Create a new key event.
+     * 
+     * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this key code originally went down.
+     * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this event happened.
+     * @param action Action code: either {@link #ACTION_DOWN},
+     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * @param code The key code.
+     * @param repeat A repeat count for down events (> 0 if this is after the
+     * initial down) or event count for multiple events.
+     */
+    public KeyEvent(long downTime, long eventTime, int action,
+                    int code, int repeat) {
+        mDownTime = downTime;
+        mEventTime = eventTime;
+        mAction = action;
+        mKeyCode = code;
+        mRepeatCount = repeat;
+    }
+
+    /**
+     * Create a new key event.
+     * 
+     * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this key code originally went down.
+     * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this event happened.
+     * @param action Action code: either {@link #ACTION_DOWN},
+     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * @param code The key code.
+     * @param repeat A repeat count for down events (> 0 if this is after the
+     * initial down) or event count for multiple events.
+     * @param metaState Flags indicating which meta keys are currently pressed.
+     */
+    public KeyEvent(long downTime, long eventTime, int action,
+                    int code, int repeat, int metaState) {
+        mDownTime = downTime;
+        mEventTime = eventTime;
+        mAction = action;
+        mKeyCode = code;
+        mRepeatCount = repeat;
+        mMetaState = metaState;
+    }
+
+    /**
+     * Create a new key event.
+     * 
+     * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this key code originally went down.
+     * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this event happened.
+     * @param action Action code: either {@link #ACTION_DOWN},
+     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * @param code The key code.
+     * @param repeat A repeat count for down events (> 0 if this is after the
+     * initial down) or event count for multiple events.
+     * @param metaState Flags indicating which meta keys are currently pressed.
+     * @param device The device ID that generated the key event.
+     * @param scancode Raw device scan code of the event.
+     */
+    public KeyEvent(long downTime, long eventTime, int action,
+                    int code, int repeat, int metaState,
+                    int device, int scancode) {
+        mDownTime = downTime;
+        mEventTime = eventTime;
+        mAction = action;
+        mKeyCode = code;
+        mRepeatCount = repeat;
+        mMetaState = metaState;
+        mDeviceId = device;
+        mScancode = scancode;
+    }
+
+    /**
+     * Create a new key event.
+     * 
+     * @param downTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this key code originally went down.
+     * @param eventTime The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this event happened.
+     * @param action Action code: either {@link #ACTION_DOWN},
+     * {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * @param code The key code.
+     * @param repeat A repeat count for down events (> 0 if this is after the
+     * initial down) or event count for multiple events.
+     * @param metaState Flags indicating which meta keys are currently pressed.
+     * @param device The device ID that generated the key event.
+     * @param scancode Raw device scan code of the event.
+     * @param flags The flags for this key event
+     */
+    public KeyEvent(long downTime, long eventTime, int action,
+                    int code, int repeat, int metaState,
+                    int device, int scancode, int flags) {
+        mDownTime = downTime;
+        mEventTime = eventTime;
+        mAction = action;
+        mKeyCode = code;
+        mRepeatCount = repeat;
+        mMetaState = metaState;
+        mDeviceId = device;
+        mScancode = scancode;
+        mFlags = flags;
+    }
+
+    /**
+     * Create a new key event for a string of characters.  The key code,
+     * action, and repeat could will automatically be set to
+     * {@link #KEYCODE_UNKNOWN}, {@link #ACTION_MULTIPLE}, and 0 for you.
+     * 
+     * @param time The time (in {@link android.os.SystemClock#uptimeMillis})
+     * at which this event occured.
+     * @param characters The string of characters.
+     * @param device The device ID that generated the key event.
+     * @param flags The flags for this key event
+     */
+    public KeyEvent(long time, String characters, int device, int flags) {
+        mDownTime = time;
+        mEventTime = time;
+        mCharacters = characters;
+        mAction = ACTION_MULTIPLE;
+        mKeyCode = KEYCODE_UNKNOWN;
+        mRepeatCount = 0;
+        mDeviceId = device;
+        mFlags = flags;
+    }
+
+    /**
+     * Copy an existing key event, modifying its time and repeat count.
+     * 
+     * @param origEvent The existing event to be copied.
+     * @param eventTime The new event time
+     * (in {@link android.os.SystemClock#uptimeMillis}) of the event.
+     * @param newRepeat The new repeat count of the event.
+     */
+    public KeyEvent(KeyEvent origEvent, long eventTime, int newRepeat) {
+        mDownTime = origEvent.mDownTime;
+        mEventTime = eventTime;
+        mAction = origEvent.mAction;
+        mKeyCode = origEvent.mKeyCode;
+        mRepeatCount = newRepeat;
+        mMetaState = origEvent.mMetaState;
+        mDeviceId = origEvent.mDeviceId;
+        mScancode = origEvent.mScancode;
+        mFlags = origEvent.mFlags;
+        mCharacters = origEvent.mCharacters;
+    }
+
+    /**
+     * Copy an existing key event, modifying its action.
+     * 
+     * @param origEvent The existing event to be copied.
+     * @param action The new action code of the event.
+     */
+    public KeyEvent(KeyEvent origEvent, int action) {
+        mDownTime = origEvent.mDownTime;
+        mEventTime = origEvent.mEventTime;
+        mAction = action;
+        mKeyCode = origEvent.mKeyCode;
+        mRepeatCount = origEvent.mRepeatCount;
+        mMetaState = origEvent.mMetaState;
+        mDeviceId = origEvent.mDeviceId;
+        mScancode = origEvent.mScancode;
+        mFlags = origEvent.mFlags;
+        // Don't copy mCharacters, since one way or the other we'll lose it
+        // when changing the action.
+    }
+
+    /**
+     * Don't use in new code, instead explicitly check
+     * {@link #getAction()}.
+     * 
+     * @return If the action is ACTION_DOWN, returns true; else false.
+     *
+     * @deprecated
+     * @hide
+     */
+    @Deprecated public final boolean isDown() {
+        return mAction == ACTION_DOWN;
+    }
+
+    /**
+     * Is this a system key?  System keys can not be used for menu shortcuts.
+     * 
+     * TODO: this information should come from a table somewhere.
+     * TODO: should the dpad keys be here?  arguably, because they also shouldn't be menu shortcuts
+     */
+    public final boolean isSystem() {
+        switch (mKeyCode) {
+        case KEYCODE_MENU:
+        case KEYCODE_SOFT_RIGHT:
+        case KEYCODE_HOME:
+        case KEYCODE_BACK:
+        case KEYCODE_CALL:
+        case KEYCODE_ENDCALL:
+        case KEYCODE_VOLUME_UP:
+        case KEYCODE_VOLUME_DOWN:
+        case KEYCODE_MUTE:
+        case KEYCODE_POWER:
+        case KEYCODE_HEADSETHOOK:
+        case KEYCODE_PLAYPAUSE:
+        case KEYCODE_STOP:
+        case KEYCODE_NEXTSONG:
+        case KEYCODE_PREVIOUSSONG:
+        case KEYCODE_REWIND:
+        case KEYCODE_FORWARD:
+        case KEYCODE_CAMERA:
+        case KEYCODE_FOCUS:
+        case KEYCODE_SEARCH:
+            return true;
+        default:
+            return false;
+        }
+    }
+
+
+    /**
+     * <p>Returns the state of the meta keys.</p>
+     *
+     * @return an integer in which each bit set to 1 represents a pressed
+     *         meta key
+     *
+     * @see #isAltPressed()
+     * @see #isShiftPressed()
+     * @see #isSymPressed()
+     * @see #META_ALT_ON
+     * @see #META_SHIFT_ON
+     * @see #META_SYM_ON
+     */
+    public final int getMetaState() {
+        return mMetaState;
+    }
+
+    /**
+     * Returns the flags for this key event.
+     *
+     * @see #FLAG_WOKE_HERE
+     */
+    public final int getFlags() {
+        return mFlags;
+    }
+
+    /**
+     * Returns true if this key code is a modifier key.
+     *
+     * @return whether the provided keyCode is one of
+     * {@link #KEYCODE_SHIFT_LEFT} {@link #KEYCODE_SHIFT_RIGHT},
+     * {@link #KEYCODE_ALT_LEFT}, {@link #KEYCODE_ALT_RIGHT}
+     * or {@link #KEYCODE_SYM}.
+     */
+    public static boolean isModifierKey(int keyCode) {
+        return keyCode == KEYCODE_SHIFT_LEFT || keyCode == KEYCODE_SHIFT_RIGHT
+                || keyCode == KEYCODE_ALT_LEFT || keyCode == KEYCODE_ALT_RIGHT
+                || keyCode == KEYCODE_SYM;
+    }
+
+    /**
+     * <p>Returns the pressed state of the ALT meta key.</p>
+     *
+     * @return true if the ALT key is pressed, false otherwise
+     *
+     * @see #KEYCODE_ALT_LEFT
+     * @see #KEYCODE_ALT_RIGHT
+     * @see #META_ALT_ON
+     */
+    public final boolean isAltPressed() {
+        return (mMetaState & META_ALT_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the SHIFT meta key.</p>
+     *
+     * @return true if the SHIFT key is pressed, false otherwise
+     *
+     * @see #KEYCODE_SHIFT_LEFT
+     * @see #KEYCODE_SHIFT_RIGHT
+     * @see #META_SHIFT_ON
+     */
+    public final boolean isShiftPressed() {
+        return (mMetaState & META_SHIFT_ON) != 0;
+    }
+
+    /**
+     * <p>Returns the pressed state of the SYM meta key.</p>
+     *
+     * @return true if the SYM key is pressed, false otherwise
+     *
+     * @see #KEYCODE_SYM
+     * @see #META_SYM_ON
+     */
+    public final boolean isSymPressed() {
+        return (mMetaState & META_SYM_ON) != 0;
+    }
+
+    /**
+     * Retrieve the action of this key event.  May be either
+     * {@link #ACTION_DOWN}, {@link #ACTION_UP}, or {@link #ACTION_MULTIPLE}.
+     * 
+     * @return The event action: ACTION_DOWN, ACTION_UP, or ACTION_MULTIPLE.
+     */
+    public final int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Retrieve the key code of the key event.  This is the physical key that
+     * was pressed, <em>not</em> the Unicode character.
+     * 
+     * @return The key code of the event.
+     */
+    public final int getKeyCode() {
+        return mKeyCode;
+    }
+
+    /**
+     * For the special case of a {@link #ACTION_MULTIPLE} event with key
+     * code of {@link #KEYCODE_UNKNOWN}, this is a raw string of characters
+     * associated with the event.  In all other cases it is null.
+     * 
+     * @return Returns a String of 1 or more characters associated with
+     * the event.
+     */
+    public final String getCharacters() {
+        return mCharacters;
+    }
+    
+    /**
+     * Retrieve the hardware key id of this key event.  These values are not
+     * reliable and vary from device to device.
+     *
+     * {@more}
+     * Mostly this is here for debugging purposes.
+     */
+    public final int getScanCode() {
+        return mScancode;
+    }
+
+    /**
+     * Retrieve the repeat count of the event.  For both key up and key down
+     * events, this is the number of times the key has repeated with the first
+     * down starting at 0 and counting up from there.  For multiple key
+     * events, this is the number of down/up pairs that have occurred.
+     * 
+     * @return The number of times the key has repeated.
+     */
+    public final int getRepeatCount() {
+        return mRepeatCount;
+    }
+
+    /**
+     * Retrieve the time of the most recent key down event,
+     * in the {@link android.os.SystemClock#uptimeMillis} time base.  If this
+     * is a down event, this will be the same as {@link #getEventTime()}.
+     * Note that when chording keys, this value is the down time of the
+     * most recently pressed key, which may <em>not</em> be the same physical
+     * key of this event.
+     * 
+     * @return Returns the most recent key down time, in the
+     * {@link android.os.SystemClock#uptimeMillis} time base
+     */
+    public final long getDownTime() {
+        return mDownTime;
+    }
+
+    /**
+     * Retrieve the time this event occurred, 
+     * in the {@link android.os.SystemClock#uptimeMillis} time base.
+     * 
+     * @return Returns the time this event occurred, 
+     * in the {@link android.os.SystemClock#uptimeMillis} time base.
+     */
+    public final long getEventTime() {
+        return mEventTime;
+    }
+
+    /**
+     * Return the id for the keyboard that this event came from.  A device
+     * id of 0 indicates the event didn't come from a physical device and
+     * maps to the default keymap.  The other numbers are arbitrary and
+     * you shouldn't depend on the values.
+     * 
+     * @see KeyCharacterMap#load
+     */
+    public final int getDeviceId() {
+        return mDeviceId;
+    }
+
+    /**
+     * Renamed to {@link #getDeviceId}.
+     * 
+     * @hide
+     * @deprecated
+     */
+    public final int getKeyboardDevice() {
+        return mDeviceId;
+    }
+
+    /**
+     * Get the primary character for this key.  In other words, the label
+     * that is physically printed on it.
+     */
+    public char getDisplayLabel() {
+        return KeyCharacterMap.load(mDeviceId).getDisplayLabel(mKeyCode);
+    }
+    
+    /**
+     * <p>
+     * Returns the Unicode character that the key would produce.
+     * </p><p>
+     * Returns 0 if the key is not one that is used to type Unicode
+     * characters.
+     * </p><p>
+     * If the return value has bit 
+     * {@link KeyCharacterMap#COMBINING_ACCENT} 
+     * set, the key is a "dead key" that should be combined with another to
+     * actually produce a character -- see {@link #getDeadChar} --
+     * after masking with 
+     * {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
+     * </p>
+     */
+    public int getUnicodeChar() {
+        return getUnicodeChar(mMetaState);
+    }
+    
+    /**
+     * <p>
+     * Returns the Unicode character that the key would produce.
+     * </p><p>
+     * Returns 0 if the key is not one that is used to type Unicode
+     * characters.
+     * </p><p>
+     * If the return value has bit 
+     * {@link KeyCharacterMap#COMBINING_ACCENT} 
+     * set, the key is a "dead key" that should be combined with another to
+     * actually produce a character -- see {@link #getDeadChar} -- after masking
+     * with {@link KeyCharacterMap#COMBINING_ACCENT_MASK}.
+     * </p>
+     */
+    public int getUnicodeChar(int meta) {
+        return KeyCharacterMap.load(mDeviceId).get(mKeyCode, meta);
+    }
+    
+    /**
+     * Get the characters conversion data for the key event..
+     *
+     * @param results a {@link KeyData} that will be filled with the results.
+     *
+     * @return whether the key was mapped or not.  If the key was not mapped,
+     *         results is not modified.
+     */
+    public boolean getKeyData(KeyData results) {
+        return KeyCharacterMap.load(mDeviceId).getKeyData(mKeyCode, results);
+    }
+    
+    /**
+     * The same as {@link #getMatch(char[],int) getMatch(chars, 0)}.
+     */
+    public char getMatch(char[] chars) {
+        return getMatch(chars, 0);
+    }
+    
+    /**
+     * If one of the chars in the array can be generated by the keyCode of this
+     * key event, return the char; otherwise return '\0'.
+     * @param chars the characters to try to find
+     * @param modifiers the modifier bits to prefer.  If any of these bits
+     *                  are set, if there are multiple choices, that could
+     *                  work, the one for this modifier will be set.
+     */
+    public char getMatch(char[] chars, int modifiers) {
+        return KeyCharacterMap.load(mDeviceId).getMatch(mKeyCode, chars, modifiers);
+    }
+    
+    /**
+     * Gets the number or symbol associated with the key.  The character value
+     * is returned, not the numeric value.  If the key is not a number, but is
+     * a symbol, the symbol is retuned.
+     */
+    public char getNumber() {
+        return KeyCharacterMap.load(mDeviceId).getNumber(mKeyCode);
+    }
+    
+    /**
+     * Does the key code of this key produce a glyph?
+     */
+    public boolean isPrintingKey() {
+        return KeyCharacterMap.load(mDeviceId).isPrintingKey(mKeyCode);
+    }
+    
+    /**
+     * Deliver this key event to a {@link Callback} interface.  If this is
+     * an ACTION_MULTIPLE event and it is not handled, then an attempt will
+     * be made to deliver a single normal event.
+     * 
+     * @param receiver The Callback that will be given the event.
+     * 
+     * @return The return value from the Callback method that was called.
+     */
+    public final boolean dispatch(Callback receiver) {
+        switch (mAction) {
+            case ACTION_DOWN:
+                return receiver.onKeyDown(mKeyCode, this);
+            case ACTION_UP:
+                return receiver.onKeyUp(mKeyCode, this);
+            case ACTION_MULTIPLE:
+                final int count = mRepeatCount;
+                final int code = mKeyCode;
+                if (receiver.onKeyMultiple(code, count, this)) {
+                    return true;
+                }
+                if (code != KeyEvent.KEYCODE_UNKNOWN) {
+                    mAction = ACTION_DOWN;
+                    mRepeatCount = 0;
+                    boolean handled = receiver.onKeyDown(code, this);
+                    if (handled) {
+                        mAction = ACTION_UP;
+                        receiver.onKeyUp(code, this);
+                    }
+                    mAction = ACTION_MULTIPLE;
+                    mRepeatCount = count;
+                    return handled;
+                }
+        }
+        return false;
+    }
+
+    public String toString() {
+        return "KeyEvent{action=" + mAction + " code=" + mKeyCode
+            + " repeat=" + mRepeatCount
+            + " meta=" + mMetaState + " scancode=" + mScancode
+            + " mFlags=" + mFlags + "}";
+    }
+
+    public static final Parcelable.Creator<KeyEvent> CREATOR
+            = new Parcelable.Creator<KeyEvent>() {
+        public KeyEvent createFromParcel(Parcel in) {
+            return new KeyEvent(in);
+        }
+
+        public KeyEvent[] newArray(int size) {
+            return new KeyEvent[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeInt(mAction);
+        out.writeInt(mKeyCode);
+        out.writeInt(mRepeatCount);
+        out.writeInt(mMetaState);
+        out.writeInt(mDeviceId);
+        out.writeInt(mScancode);
+        out.writeInt(mFlags);
+        out.writeLong(mDownTime);
+        out.writeLong(mEventTime);
+    }
+
+    private KeyEvent(Parcel in) {
+        mAction = in.readInt();
+        mKeyCode = in.readInt();
+        mRepeatCount = in.readInt();
+        mMetaState = in.readInt();
+        mDeviceId = in.readInt();
+        mScancode = in.readInt();
+        mFlags = in.readInt();
+        mDownTime = in.readLong();
+        mEventTime = in.readLong();
+    }
+}
diff --git a/core/java/android/view/LayoutInflater.java b/core/java/android/view/LayoutInflater.java
new file mode 100644
index 0000000..94acd3f
--- /dev/null
+++ b/core/java/android/view/LayoutInflater.java
@@ -0,0 +1,744 @@
+/*
+ * Copyright (C) 2007 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.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import java.io.IOException;
+import java.lang.reflect.Constructor;
+import java.util.HashMap;
+
+/**
+ * This class is used to instantiate layout XML file into its corresponding View
+ * objects. It is never be used directly -- use
+ * {@link android.app.Activity#getLayoutInflater()} or
+ * {@link Context#getSystemService} to retrieve a standard LayoutInflater instance
+ * that is already hooked up to the current context and correctly configured
+ * for the device you are running on.  For example:
+ *
+ * <pre>LayoutInflater inflater = (LayoutInflater)context.getSystemService
+ *      Context.LAYOUT_INFLATER_SERVICE);</pre>
+ * 
+ * <p>
+ * To create a new LayoutInflater with an additional {@link Factory} for your
+ * own views, you can use {@link #cloneInContext} to clone an existing
+ * ViewFactory, and then call {@link #setFactory} on it to include your
+ * Factory.
+ * 
+ * <p>
+ * For performance reasons, view inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use LayoutInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource
+ * (R.<em>something</em> file.)
+ * 
+ * @see Context#getSystemService
+ */
+public abstract class LayoutInflater {
+    private final boolean DEBUG = false;
+
+    /**
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected final Context mContext;
+
+    // these are optional, set by the caller
+    private boolean mFactorySet;
+    private Factory mFactory;
+    private Filter mFilter;
+
+    private final Object[] mConstructorArgs = new Object[2];
+
+    private static final Class[] mConstructorSignature = new Class[] {
+            Context.class, AttributeSet.class};
+
+    private static final HashMap<String, Constructor> sConstructorMap =
+            new HashMap<String, Constructor>();
+    
+    private HashMap<String, Boolean> mFilterMap;
+
+    private static final String TAG_MERGE = "merge";
+    private static final String TAG_INCLUDE = "include";
+    private static final String TAG_REQUEST_FOCUS = "requestFocus";
+
+    /**
+     * Hook to allow clients of the LayoutInflater to restrict the set of Views that are allowed
+     * to be inflated.
+     * 
+     */
+    public interface Filter {
+        /**
+         * Hook to allow clients of the LayoutInflater to restrict the set of Views 
+         * that are allowed to be inflated.
+         * 
+         * @param clazz The class object for the View that is about to be inflated
+         * 
+         * @return True if this class is allowed to be inflated, or false otherwise
+         */
+        boolean onLoadClass(Class clazz);
+    }
+    
+    public interface Factory {
+        /**
+         * Hook you can supply that is called when inflating from a LayoutInflater.
+         * You can use this to customize the tag names available in your XML
+         * layout files.
+         * 
+         * <p>
+         * Note that it is good practice to prefix these custom names with your
+         * package (i.e., com.coolcompany.apps) to avoid conflicts with system
+         * names.
+         * 
+         * @param name Tag name to be inflated.
+         * @param context The context the view is being created in.
+         * @param attrs Inflation attributes as specified in XML file.
+         * 
+         * @return View Newly created view. Return null for the default
+         *         behavior.
+         */
+        public View onCreateView(String name, Context context, AttributeSet attrs);
+    }
+
+    private static class FactoryMerger implements Factory {
+        private final Factory mF1, mF2;
+        
+        FactoryMerger(Factory f1, Factory f2) {
+            mF1 = f1;
+            mF2 = f2;
+        }
+        
+        public View onCreateView(String name, Context context, AttributeSet attrs) {
+            View v = mF1.onCreateView(name, context, attrs);
+            if (v != null) return v;
+            return mF2.onCreateView(name, context, attrs);
+        }
+    }
+    
+    /**
+     * Create a new LayoutInflater instance associated with a particular Context.
+     * Applications will almost always want to use
+     * {@link Context#getSystemService Context.getSystemService()} to retrieve
+     * the standard {@link Context#LAYOUT_INFLATER_SERVICE Context.INFLATER_SERVICE}.
+     * 
+     * @param context The Context in which this LayoutInflater will create its
+     * Views; most importantly, this supplies the theme from which the default
+     * values for their attributes are retrieved.
+     */
+    protected LayoutInflater(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Create a new LayoutInflater instance that is a copy of an existing
+     * LayoutInflater, optionally with its Context changed.  For use in
+     * implementing {@link #cloneInContext}.
+     * 
+     * @param original The original LayoutInflater to copy.
+     * @param newContext The new Context to use.
+     */
+    protected LayoutInflater(LayoutInflater original, Context newContext) {
+        mContext = newContext;
+        mFactory = original.mFactory;
+        mFilter = original.mFilter;
+    }
+    
+    /**
+     * Obtains the LayoutInflater from the given context.
+     */
+    public static LayoutInflater from(Context context) {
+        LayoutInflater LayoutInflater =
+                (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        if (LayoutInflater == null) {
+            throw new AssertionError("LayoutInflater not found.");
+        }
+        return LayoutInflater;
+    }
+
+    /**
+     * Create a copy of the existing LayoutInflater object, with the copy
+     * pointing to a different Context than the original.  This is used by
+     * {@link ContextThemeWrapper} to create a new LayoutInflater to go along
+     * with the new Context theme.
+     * 
+     * @param newContext The new Context to associate with the new LayoutInflater.
+     * May be the same as the original Context if desired.
+     * 
+     * @return Returns a brand spanking new LayoutInflater object associated with
+     * the given Context.
+     */
+    public abstract LayoutInflater cloneInContext(Context newContext);
+    
+    /**
+     * Return the context we are running in, for access to resources, class
+     * loader, etc.
+     */
+    public Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Return the current factory (or null). This is called on each element
+     * name. If the factory returns a View, add that to the hierarchy. If it
+     * returns null, proceed to call onCreateView(name).
+     */
+    public final Factory getFactory() {
+        return mFactory;
+    }
+
+    /**
+     * Attach a custom Factory interface for creating views while using
+     * this LayoutInflater.  This must not be null, and can only be set once;
+     * after setting, you can not change the factory.  This is
+     * called on each element name as the xml is parsed. If the factory returns
+     * a View, that is added to the hierarchy. If it returns null, the next
+     * factory default {@link #onCreateView} method is called.
+     * 
+     * <p>If you have an existing
+     * LayoutInflater and want to add your own factory to it, use
+     * {@link #cloneInContext} to clone the existing instance and then you
+     * can use this function (once) on the returned new instance.  This will
+     * merge your own factory with whatever factory the original instance is
+     * using.
+     */
+    public void setFactory(Factory factory) {
+        if (mFactorySet) {
+            throw new IllegalStateException("A factory has already been set on this LayoutInflater");
+        }
+        if (factory == null) {
+            throw new NullPointerException("Given factory can not be null");
+        }
+        mFactorySet = true;
+        if (mFactory == null) {
+            mFactory = factory;
+        } else {
+            mFactory = new FactoryMerger(factory, mFactory);
+        }
+    }
+
+    /**
+     * @return The {@link Filter} currently used by this LayoutInflater to restrict the set of Views
+     * that are allowed to be inflated.
+     */
+    public Filter getFilter() {
+        return mFilter;
+    }
+    
+    /**
+     * Sets the {@link Filter} to by this LayoutInflater. If a view is attempted to be inflated
+     * which is not allowed by the {@link Filter}, the {@link #inflate(int, ViewGroup)} call will
+     * throw an {@link InflateException}. This filter will replace any previous filter set on this
+     * LayoutInflater.
+     * 
+     * @param filter The Filter which restricts the set of Views that are allowed to be inflated.
+     *        This filter will replace any previous filter set on this LayoutInflater.
+     */
+    public void setFilter(Filter filter) {
+        mFilter = filter;
+        if (filter != null) {
+            mFilterMap = new HashMap<String, Boolean>();
+        }
+    }
+
+    /**
+     * Inflate a new view hierarchy from the specified xml resource. Throws
+     * {@link InflateException} if there is an error.
+     * 
+     * @param resource ID for an XML layout resource to load (e.g.,
+     *        <code>R.layout.main_page</code>)
+     * @param root Optional view to be the parent of the generated hierarchy.
+     * @return The root View of the inflated hierarchy. If root was supplied,
+     *         this is the root View; otherwise it is the root of the inflated
+     *         XML file.
+     */
+    public View inflate(int resource, ViewGroup root) {
+        return inflate(resource, root, root != null);
+    }
+
+    /**
+     * Inflate a new view hierarchy from the specified xml node. Throws
+     * {@link InflateException} if there is an error. *
+     * <p>
+     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
+     * reasons, view inflation relies heavily on pre-processing of XML files
+     * that is done at build time. Therefore, it is not currently possible to
+     * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
+     * 
+     * @param parser XML dom node containing the description of the view
+     *        hierarchy.
+     * @param root Optional view to be the parent of the generated hierarchy.
+     * @return The root View of the inflated hierarchy. If root was supplied,
+     *         this is the root View; otherwise it is the root of the inflated
+     *         XML file.
+     */
+    public View inflate(XmlPullParser parser, ViewGroup root) {
+        return inflate(parser, root, root != null);
+    }
+
+    /**
+     * Inflate a new view hierarchy from the specified xml resource. Throws
+     * {@link InflateException} if there is an error.
+     * 
+     * @param resource ID for an XML layout resource to load (e.g.,
+     *        <code>R.layout.main_page</code>)
+     * @param root Optional view to be the parent of the generated hierarchy (if
+     *        <em>attachToRoot</em> is true), or else simply an object that
+     *        provides a set of LayoutParams values for root of the returned
+     *        hierarchy (if <em>attachToRoot</em> is false.)
+     * @param attachToRoot Whether the inflated hierarchy should be attached to
+     *        the root parameter? If false, root is only used to create the
+     *        correct subclass of LayoutParams for the root view in the XML.
+     * @return The root View of the inflated hierarchy. If root was supplied and
+     *         attachToRoot is true, this is root; otherwise it is the root of
+     *         the inflated XML file.
+     */
+    public View inflate(int resource, ViewGroup root, boolean attachToRoot) {
+        if (DEBUG) System.out.println("INFLATING from resource: " + resource);
+        XmlResourceParser parser = getContext().getResources().getLayout(resource);
+        try {
+            return inflate(parser, root, attachToRoot);
+        } finally {
+            parser.close();
+        }
+    }
+
+    /**
+     * Inflate a new view hierarchy from the specified XML node. Throws
+     * {@link InflateException} if there is an error.
+     * <p>
+     * <em><strong>Important</strong></em>&nbsp;&nbsp;&nbsp;For performance
+     * reasons, view inflation relies heavily on pre-processing of XML files
+     * that is done at build time. Therefore, it is not currently possible to
+     * use LayoutInflater with an XmlPullParser over a plain XML file at runtime.
+     * 
+     * @param parser XML dom node containing the description of the view
+     *        hierarchy.
+     * @param root Optional view to be the parent of the generated hierarchy (if
+     *        <em>attachToRoot</em> is true), or else simply an object that
+     *        provides a set of LayoutParams values for root of the returned
+     *        hierarchy (if <em>attachToRoot</em> is false.)
+     * @param attachToRoot Whether the inflated hierarchy should be attached to
+     *        the root parameter? If false, root is only used to create the
+     *        correct subclass of LayoutParams for the root view in the XML.
+     * @return The root View of the inflated hierarchy. If root was supplied and
+     *         attachToRoot is true, this is root; otherwise it is the root of
+     *         the inflated XML file.
+     */
+    public View inflate(XmlPullParser parser, ViewGroup root, boolean attachToRoot) {
+        synchronized (mConstructorArgs) {
+            final AttributeSet attrs = Xml.asAttributeSet(parser);
+            mConstructorArgs[0] = mContext;
+            View result = root;
+
+            try {
+                // Look for the root node.
+                int type;
+                while ((type = parser.next()) != XmlPullParser.START_TAG &&
+                        type != XmlPullParser.END_DOCUMENT) {
+                    // Empty
+                }
+
+                if (type != XmlPullParser.START_TAG) {
+                    throw new InflateException(parser.getPositionDescription()
+                            + ": No start tag found!");
+                }
+
+                final String name = parser.getName();
+                
+                if (DEBUG) {
+                    System.out.println("**************************");
+                    System.out.println("Creating root view: "
+                            + name);
+                    System.out.println("**************************");
+                }
+
+                if (TAG_MERGE.equals(name)) {
+                    if (root == null || !attachToRoot) {
+                        throw new InflateException("<merge /> can be used only with a valid "
+                                + "ViewGroup root and attachToRoot=true");
+                    }
+
+                    rInflate(parser, root, attrs);
+                } else {
+                    // Temp is the root view that was found in the xml
+                    View temp = createViewFromTag(name, attrs);
+
+                    ViewGroup.LayoutParams params = null;
+
+                    if (root != null) {
+                        if (DEBUG) {
+                            System.out.println("Creating params from root: " +
+                                    root);
+                        }
+                        // Create layout params that match root, if supplied
+                        params = root.generateLayoutParams(attrs);
+                        if (!attachToRoot) {
+                            // Set the layout params for temp if we are not
+                            // attaching. (If we are, we use addView, below)
+                            temp.setLayoutParams(params);
+                        }
+                    }
+
+                    if (DEBUG) {
+                        System.out.println("-----> start inflating children");
+                    }
+                    // Inflate all children under temp
+                    rInflate(parser, temp, attrs);
+                    if (DEBUG) {
+                        System.out.println("-----> done inflating children");
+                    }
+
+                    // We are supposed to attach all the views we found (int temp)
+                    // to root. Do that now.
+                    if (root != null && attachToRoot) {
+                        root.addView(temp, params);
+                    }
+
+                    // Decide whether to return the root that was passed in or the
+                    // top view found in xml.
+                    if (root == null || !attachToRoot) {
+                        result = temp;
+                    }
+                }
+
+            } catch (XmlPullParserException e) {
+                InflateException ex = new InflateException(e.getMessage());
+                ex.initCause(e);
+                throw ex;
+            } catch (IOException e) {
+                InflateException ex = new InflateException(
+                        parser.getPositionDescription()
+                        + ": " + e.getMessage());
+                ex.initCause(e);
+                throw ex;
+            }
+
+            return result;
+        }
+    }
+
+    /**
+     * Low-level function for instantiating a view by name. This attempts to
+     * instantiate a view class of the given <var>name</var> found in this
+     * LayoutInflater's ClassLoader.
+     * 
+     * <p>
+     * There are two things that can happen in an error case: either the
+     * exception describing the error will be thrown, or a null will be
+     * returned. You must deal with both possibilities -- the former will happen
+     * the first time createView() is called for a class of a particular name,
+     * the latter every time there-after for that class name.
+     * 
+     * @param name The full name of the class to be instantiated.
+     * @param attrs The XML attributes supplied for this instance.
+     * 
+     * @return View The newly instantied view, or null.
+     */
+    public final View createView(String name, String prefix, AttributeSet attrs)
+            throws ClassNotFoundException, InflateException {
+        Constructor constructor = sConstructorMap.get(name);
+
+        try {
+            if (constructor == null) {
+                // Class not found in the cache, see if it's real, and try to add it
+                Class clazz = mContext.getClassLoader().loadClass(
+                        prefix != null ? (prefix + name) : name);
+                
+                if (mFilter != null && clazz != null) {
+                    boolean allowed = mFilter.onLoadClass(clazz);
+                    if (!allowed) {
+                        failNotAllowed(name, prefix, attrs);
+                    }
+                }
+                constructor = clazz.getConstructor(mConstructorSignature);
+                sConstructorMap.put(name, constructor);
+            } else {
+                // If we have a filter, apply it to cached constructor
+                if (mFilter != null) {
+                    // Have we seen this name before?
+                    Boolean allowedState = mFilterMap.get(name);
+                    if (allowedState == null) {
+                        // New class -- remember whether it is allowed
+                        Class clazz = mContext.getClassLoader().loadClass(
+                                prefix != null ? (prefix + name) : name);
+                        
+                        boolean allowed = clazz != null && mFilter.onLoadClass(clazz);
+                        mFilterMap.put(name, allowed);
+                        if (!allowed) {
+                            failNotAllowed(name, prefix, attrs);
+                        }
+                    } else if (allowedState.equals(Boolean.FALSE)) {
+                        failNotAllowed(name, prefix, attrs);
+                    }
+                }
+            }
+
+            Object[] args = mConstructorArgs;
+            args[1] = attrs;
+            return (View) constructor.newInstance(args);
+
+        } catch (NoSuchMethodException e) {
+            InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Error inflating class "
+                    + (prefix != null ? (prefix + name) : name));
+            ie.initCause(e);
+            throw ie;
+
+        } catch (ClassNotFoundException e) {
+            // If loadClass fails, we should propagate the exception.
+            throw e;
+        } catch (Exception e) {
+            InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Error inflating class "
+                    + (constructor == null ? "<unknown>" : constructor.getClass().getName()));
+            ie.initCause(e);
+            throw ie;
+        }
+    }
+
+    /**
+     * Throw an excpetion because the specified class is not allowed to be inflated.
+     */
+    private void failNotAllowed(String name, String prefix, AttributeSet attrs) {
+        InflateException ie = new InflateException(attrs.getPositionDescription()
+                + ": Class not allowed to be inflated "
+                + (prefix != null ? (prefix + name) : name));
+        throw ie;
+    }
+
+    /**
+     * This routine is responsible for creating the correct subclass of View
+     * given the xml element name. Override it to handle custom view objects. If
+     * you override this in your subclass be sure to call through to
+     * super.onCreateView(name) for names you do not recognize.
+     * 
+     * @param name The fully qualified class name of the View to be create.
+     * @param attrs An AttributeSet of attributes to apply to the View.
+     * 
+     * @return View The View created.
+     */
+    protected View onCreateView(String name, AttributeSet attrs)
+            throws ClassNotFoundException {
+        return createView(name, "android.view.", attrs);
+    }
+
+    /*
+     * default visibility so the BridgeInflater can override it.
+     */
+    View createViewFromTag(String name, AttributeSet attrs) {
+        if (name.equals("view")) {
+            name = attrs.getAttributeValue(null, "class");
+        }
+
+        if (DEBUG) System.out.println("******** Creating view: " + name);
+
+        try {
+            View view = (mFactory == null) ? null : mFactory.onCreateView(name,
+                    mContext, attrs);
+
+            if (view == null) {
+                if (-1 == name.indexOf('.')) {
+                    view = onCreateView(name, attrs);
+                } else {
+                    view = createView(name, null, attrs);
+                }
+            }
+
+            if (DEBUG) System.out.println("Created view is: " + view);
+            return view;
+
+        } catch (InflateException e) {
+            throw e;
+
+        } catch (ClassNotFoundException e) {
+            InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Error inflating class " + name);
+            ie.initCause(e);
+            throw ie;
+
+        } catch (Exception e) {
+            InflateException ie = new InflateException(attrs.getPositionDescription()
+                    + ": Error inflating class " + name);
+            ie.initCause(e);
+            throw ie;
+        }
+    }
+
+    /**
+     * Recursive method used to descend down the xml hierarchy and instantiate
+     * views, instantiate their children, and then call onFinishInflate().
+     */
+    private void rInflate(XmlPullParser parser, View parent, final AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        final int depth = parser.getDepth();
+        int type;
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > depth) && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            final String name = parser.getName();
+            
+            if (TAG_REQUEST_FOCUS.equals(name)) {
+                parseRequestFocus(parser, parent);
+            } else if (TAG_INCLUDE.equals(name)) {
+                if (parser.getDepth() == 0) {
+                    throw new InflateException("<include /> cannot be the root element");
+                }
+                parseInclude(parser, parent, attrs);
+            } else if (TAG_MERGE.equals(name)) {
+                throw new InflateException("<merge /> must be the root element");
+            } else {
+                final View view = createViewFromTag(name, attrs);
+                final ViewGroup viewGroup = (ViewGroup) parent;
+                final ViewGroup.LayoutParams params = viewGroup.generateLayoutParams(attrs);
+                rInflate(parser, view, attrs);
+                viewGroup.addView(view, params);
+            }
+        }
+
+        parent.onFinishInflate();
+    }
+
+    private void parseRequestFocus(XmlPullParser parser, View parent)
+            throws XmlPullParserException, IOException {
+        int type;
+        parent.requestFocus();
+        final int currentDepth = parser.getDepth();
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+    }
+
+    private void parseInclude(XmlPullParser parser, View parent, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        int type;
+
+        if (parent instanceof ViewGroup) {
+            final int layout = attrs.getAttributeResourceValue(null, "layout", 0);
+            if (layout == 0) {
+                final String value = attrs.getAttributeValue(null, "layout");
+                if (value == null) {
+                    throw new InflateException("You must specifiy a layout in the"
+                            + " include tag: <include layout=\"@layout/layoutID\" />");
+                } else {
+                    throw new InflateException("You must specifiy a valid layout "
+                            + "reference. The layout ID " + value + " is not valid.");
+                }
+            } else {
+                final XmlResourceParser childParser =
+                        getContext().getResources().getLayout(layout);
+
+                try {
+                    final AttributeSet childAttrs = Xml.asAttributeSet(childParser);
+
+                    while ((type = childParser.next()) != XmlPullParser.START_TAG &&
+                            type != XmlPullParser.END_DOCUMENT) {
+                        // Empty.
+                    }
+
+                    if (type != XmlPullParser.START_TAG) {
+                        throw new InflateException(childParser.getPositionDescription() +
+                                ": No start tag found!");
+                    }
+
+                    final String childName = childParser.getName();
+
+                    if (TAG_MERGE.equals(childName)) {
+                        // Inflate all children.
+                        rInflate(childParser, parent, childAttrs);
+                    } else {
+                        final View view = createViewFromTag(childName, childAttrs);
+                        final ViewGroup group = (ViewGroup) parent;
+
+                        // We try to load the layout params set in the <include /> tag. If
+                        // they don't exist, we will rely on the layout params set in the
+                        // included XML file.
+                        // During a layoutparams generation, a runtime exception is thrown
+                        // if either layout_width or layout_height is missing. We catch
+                        // this exception and set localParams accordingly: true means we
+                        // successfully loaded layout params from the <include /> tag,
+                        // false means we need to rely on the included layout params.
+                        ViewGroup.LayoutParams params = null;
+                        try {
+                            params = group.generateLayoutParams(attrs);
+                        } catch (RuntimeException e) {
+                            params = group.generateLayoutParams(childAttrs);
+                        } finally {
+                            if (params != null) {
+                                view.setLayoutParams(params);
+                            }
+                        }
+
+                        // Inflate all children.
+                        rInflate(childParser, view, childAttrs);
+
+                        // Attempt to override the included layout's android:id with the
+                        // one set on the <include /> tag itself.
+                        TypedArray a = mContext.obtainStyledAttributes(attrs,
+                            com.android.internal.R.styleable.View, 0, 0);
+                        int id = a.getResourceId(com.android.internal.R.styleable.View_id, View.NO_ID);
+                        // While we're at it, let's try to override android:visibility.
+                        int visibility = a.getInt(com.android.internal.R.styleable.View_visibility, -1);
+                        a.recycle();
+
+                        if (id != View.NO_ID) {
+                            view.setId(id);
+                        }
+
+                        switch (visibility) {
+                            case 0:
+                                view.setVisibility(View.VISIBLE);
+                                break;
+                            case 1:
+                                view.setVisibility(View.INVISIBLE);
+                                break;
+                            case 2:
+                                view.setVisibility(View.GONE);
+                                break;
+                        }
+
+                        group.addView(view);
+                    }
+                } finally {
+                    childParser.close();
+                }
+            }
+        } else {
+            throw new InflateException("<include /> can only be used inside of a ViewGroup");
+        }
+
+        final int currentDepth = parser.getDepth();
+        while (((type = parser.next()) != XmlPullParser.END_TAG ||
+                parser.getDepth() > currentDepth) && type != XmlPullParser.END_DOCUMENT) {
+            // Empty
+        }
+    }    
+}
diff --git a/core/java/android/view/Menu.java b/core/java/android/view/Menu.java
new file mode 100644
index 0000000..97825e6
--- /dev/null
+++ b/core/java/android/view/Menu.java
@@ -0,0 +1,441 @@
+/*
+ * Copyright (C) 2006 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.app.Activity;
+import android.content.ComponentName;
+import android.content.Intent;
+
+/**
+ * Interface for managing the items in a menu.
+ * <p>
+ * By default, every Activity supports an options menu of actions or options.
+ * You can add items to this menu and handle clicks on your additions. The
+ * easiest way of adding menu items is inflating an XML file into the
+ * {@link Menu} via {@link MenuInflater}. The easiest way of attaching code to
+ * clicks is via {@link Activity#onOptionsItemSelected(MenuItem)} and
+ * {@link Activity#onContextItemSelected(MenuItem)}.
+ * <p>
+ * Different menu types support different features:
+ * <ol>
+ * <li><b>Context menus</b>: Do not support item shortcuts and item icons.
+ * <li><b>Options menus</b>: The <b>icon menus</b> do not support item check
+ * marks and only show the item's
+ * {@link MenuItem#setTitleCondensed(CharSequence) condensed title}. The
+ * <b>expanded menus</b> (only available if six or more menu items are visible,
+ * reached via the 'More' item in the icon menu) do not show item icons, and
+ * item check marks are discouraged.
+ * <li><b>Sub menus</b>: Do not support item icons, or nested sub menus.
+ * </ol>
+ */
+public interface Menu {
+
+    /**
+     * This is the part of an order integer that the user can provide.
+     * @hide
+     */
+    static final int USER_MASK = 0x0000ffff;
+    /**
+     * Bit shift of the user portion of the order integer.
+     * @hide
+     */
+    static final int USER_SHIFT = 0;
+
+    /**
+     * This is the part of an order integer that supplies the category of the
+     * item.
+     * @hide
+     */
+    static final int CATEGORY_MASK = 0xffff0000;
+    /**
+     * Bit shift of the category portion of the order integer.
+     * @hide
+     */
+    static final int CATEGORY_SHIFT = 16;
+
+    /**
+     * Value to use for group and item identifier integers when you don't care
+     * about them.
+     */
+    static final int NONE = 0;
+
+    /**
+     * First value for group and item identifier integers.
+     */
+    static final int FIRST = 1;
+
+    // Implementation note: Keep these CATEGORY_* in sync with the category enum
+    // in attrs.xml
+
+    /**
+     * Category code for the order integer for items/groups that are part of a
+     * container -- or/add this with your base value.
+     */
+    static final int CATEGORY_CONTAINER = 0x00010000;
+
+    /**
+     * Category code for the order integer for items/groups that are provided by
+     * the system -- or/add this with your base value.
+     */
+    static final int CATEGORY_SYSTEM = 0x00020000;
+
+    /**
+     * Category code for the order integer for items/groups that are
+     * user-supplied secondary (infrequently used) options -- or/add this with
+     * your base value.
+     */
+    static final int CATEGORY_SECONDARY = 0x00030000;
+
+    /**
+     * Category code for the order integer for items/groups that are 
+     * alternative actions on the data that is currently displayed -- or/add
+     * this with your base value.
+     */
+    static final int CATEGORY_ALTERNATIVE = 0x00040000;
+
+    /**
+     * Flag for {@link #addIntentOptions}: if set, do not automatically remove
+     * any existing menu items in the same group.
+     */
+    static final int FLAG_APPEND_TO_GROUP = 0x0001;
+
+    /**
+     * Flag for {@link #performShortcut}: if set, do not close the menu after
+     * executing the shortcut.
+     */
+    static final int FLAG_PERFORM_NO_CLOSE = 0x0001;
+
+    /**
+     * Flag for {@link #performShortcut(int, KeyEvent, int)}: if set, always
+     * close the menu after executing the shortcut. Closing the menu also resets
+     * the prepared state.
+     */
+    static final int FLAG_ALWAYS_PERFORM_CLOSE = 0x0002;
+    
+    /**
+     * Add a new item to the menu. This item displays the given title for its
+     * label.
+     * 
+     * @param title The text to display for the item.
+     * @return The newly added menu item.
+     */
+    public MenuItem add(CharSequence title);
+    
+    /**
+     * Add a new item to the menu. This item displays the given title for its
+     * label.
+     * 
+     * @param titleRes Resource identifier of title string.
+     * @return The newly added menu item.
+     */
+    public MenuItem add(int titleRes);
+
+    /**
+     * Add a new item to the menu. This item displays the given title for its
+     * label.
+     * 
+     * @param groupId The group identifier that this item should be part of.
+     *        This can be used to define groups of items for batch state
+     *        changes. Normally use {@link #NONE} if an item should not be in a
+     *        group.
+     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+     *        unique ID.
+     * @param order The order for the item. Use {@link #NONE} if you do not care
+     *        about the order. See {@link MenuItem#getOrder()}.
+     * @param title The text to display for the item.
+     * @return The newly added menu item.
+     */
+    public MenuItem add(int groupId, int itemId, int order, CharSequence title);
+
+    /**
+     * Variation on {@link #add(int, int, int, CharSequence)} that takes a
+     * string resource identifier instead of the string itself.
+     * 
+     * @param groupId The group identifier that this item should be part of.
+     *        This can also be used to define groups of items for batch state
+     *        changes. Normally use {@link #NONE} if an item should not be in a
+     *        group.
+     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+     *        unique ID.
+     * @param order The order for the item. Use {@link #NONE} if you do not care
+     *        about the order. See {@link MenuItem#getOrder()}.
+     * @param titleRes Resource identifier of title string.
+     * @return The newly added menu item.
+     */
+    public MenuItem add(int groupId, int itemId, int order, int titleRes);
+
+    /**
+     * Add a new sub-menu to the menu. This item displays the given title for
+     * its label. To modify other attributes on the submenu's menu item, use
+     * {@link SubMenu#getItem()}.
+     * 
+     * @param title The text to display for the item.
+     * @return The newly added sub-menu
+     */
+    SubMenu addSubMenu(final CharSequence title);
+
+    /**
+     * Add a new sub-menu to the menu. This item displays the given title for
+     * its label. To modify other attributes on the submenu's menu item, use
+     * {@link SubMenu#getItem()}.
+     * 
+     * @param titleRes Resource identifier of title string.
+     * @return The newly added sub-menu
+     */
+    SubMenu addSubMenu(final int titleRes);
+
+    /**
+     * Add a new sub-menu to the menu. This item displays the given
+     * <var>title</var> for its label. To modify other attributes on the
+     * submenu's menu item, use {@link SubMenu#getItem()}.
+     *<p>
+     * Note that you can only have one level of sub-menus, i.e. you cannnot add
+     * a subMenu to a subMenu: An {@link UnsupportedOperationException} will be
+     * thrown if you try.
+     * 
+     * @param groupId The group identifier that this item should be part of.
+     *        This can also be used to define groups of items for batch state
+     *        changes. Normally use {@link #NONE} if an item should not be in a
+     *        group.
+     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+     *        unique ID.
+     * @param order The order for the item. Use {@link #NONE} if you do not care
+     *        about the order. See {@link MenuItem#getOrder()}.
+     * @param title The text to display for the item.
+     * @return The newly added sub-menu
+     */
+    SubMenu addSubMenu(final int groupId, final int itemId, int order, final CharSequence title);
+
+    /**
+     * Variation on {@link #addSubMenu(int, int, int, CharSequence)} that takes
+     * a string resource identifier for the title instead of the string itself.
+     * 
+     * @param groupId The group identifier that this item should be part of.
+     *        This can also be used to define groups of items for batch state
+     *        changes. Normally use {@link #NONE} if an item should not be in a group.
+     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a unique ID.
+     * @param order The order for the item. Use {@link #NONE} if you do not care about the
+     *        order. See {@link MenuItem#getOrder()}.
+     * @param titleRes Resource identifier of title string.
+     * @return The newly added sub-menu
+     */
+    SubMenu addSubMenu(int groupId, int itemId, int order, int titleRes);
+
+    /**
+     * Add a group of menu items corresponding to actions that can be performed
+     * for a particular Intent. The Intent is most often configured with a null
+     * action, the data that the current activity is working with, and includes
+     * either the {@link Intent#CATEGORY_ALTERNATIVE} or
+     * {@link Intent#CATEGORY_SELECTED_ALTERNATIVE} to find activities that have
+     * said they would like to be included as optional action. You can, however,
+     * use any Intent you want.
+     * 
+     * <p>
+     * See {@link android.content.pm.PackageManager#queryIntentActivityOptions}
+     * for more * details on the <var>caller</var>, <var>specifics</var>, and
+     * <var>intent</var> arguments. The list returned by that function is used
+     * to populate the resulting menu items.
+     * 
+     * <p>
+     * All of the menu items of possible options for the intent will be added
+     * with the given group and id. You can use the group to control ordering of
+     * the items in relation to other items in the menu. Normally this function
+     * will automatically remove any existing items in the menu in the same
+     * group and place a divider above and below the added items; this behavior
+     * can be modified with the <var>flags</var> parameter. For each of the
+     * generated items {@link MenuItem#setIntent} is called to associate the
+     * appropriate Intent with the item; this means the activity will
+     * automatically be started for you without having to do anything else.
+     * 
+     * @param groupId The group identifier that the items should be part of.
+     *        This can also be used to define groups of items for batch state
+     *        changes. Normally use {@link #NONE} if the items should not be in
+     *        a group.
+     * @param itemId Unique item ID. Use {@link #NONE} if you do not need a
+     *        unique ID.
+     * @param order The order for the items. Use {@link #NONE} if you do not
+     *        care about the order. See {@link MenuItem#getOrder()}.
+     * @param caller The current activity component name as defined by
+     *        queryIntentActivityOptions().
+     * @param specifics Specific items to place first as defined by
+     *        queryIntentActivityOptions().
+     * @param intent Intent describing the kinds of items to populate in the
+     *        list as defined by queryIntentActivityOptions().
+     * @param flags Additional options controlling how the items are added.
+     * @param outSpecificItems Optional array in which to place the menu items
+     *        that were generated for each of the <var>specifics</var> that were
+     *        requested. Entries may be null if no activity was found for that
+     *        specific action.
+     * @return The number of menu items that were added.
+     * 
+     * @see #FLAG_APPEND_TO_GROUP
+     * @see MenuItem#setIntent
+     * @see android.content.pm.PackageManager#queryIntentActivityOptions
+     */
+    public int addIntentOptions(int groupId, int itemId, int order,
+                                ComponentName caller, Intent[] specifics,
+                                Intent intent, int flags, MenuItem[] outSpecificItems);
+
+    /**
+     * Remove the item with the given identifier.
+     *
+     * @param id The item to be removed.  If there is no item with this
+     *           identifier, nothing happens.
+     */
+    public void removeItem(int id);
+
+    /**
+     * Remove all items in the given group.
+     *
+     * @param groupId The group to be removed.  If there are no items in this
+     *           group, nothing happens.
+     */
+    public void removeGroup(int groupId);
+
+    /**
+     * Remove all existing items from the menu, leaving it empty as if it had
+     * just been created.
+     */
+    public void clear();
+
+    /**
+     * Control whether a particular group of items can show a check mark.  This
+     * is similar to calling {@link MenuItem#setCheckable} on all of the menu items
+     * with the given group identifier, but in addition you can control whether
+     * this group contains a mutually-exclusive set items.  This should be called
+     * after the items of the group have been added to the menu.
+     *
+     * @param group The group of items to operate on.
+     * @param checkable Set to true to allow a check mark, false to
+     *                  disallow.  The default is false.
+     * @param exclusive If set to true, only one item in this group can be
+     *                  checked at a time; checking an item will automatically
+     *                  uncheck all others in the group.  If set to false, each
+     *                  item can be checked independently of the others.
+     *
+     * @see MenuItem#setCheckable
+     * @see MenuItem#setChecked
+     */
+    public void setGroupCheckable(int group, boolean checkable, boolean exclusive);
+
+    /**
+     * Show or hide all menu items that are in the given group.
+     *
+     * @param group The group of items to operate on.
+     * @param visible If true the items are visible, else they are hidden.
+     *
+     * @see MenuItem#setVisible
+     */
+    public void setGroupVisible(int group, boolean visible);
+    
+    /**
+     * Enable or disable all menu items that are in the given group.
+     *
+     * @param group The group of items to operate on.
+     * @param enabled If true the items will be enabled, else they will be disabled.
+     *
+     * @see MenuItem#setEnabled
+     */
+    public void setGroupEnabled(int group, boolean enabled);
+    
+    /**
+     * Return whether the menu currently has item items that are visible.
+     *
+     * @return True if there is one or more item visible,
+     *         else false.
+     */
+    public boolean hasVisibleItems();
+
+    /**
+     * Return the menu item with a particular identifier.
+     *
+     * @param id The identifier to find.
+     *
+     * @return The menu item object, or null if there is no item with
+     *         this identifier.
+     */
+    public MenuItem findItem(int id);
+
+    /**
+     * Get the number of items in the menu.  Note that this will change any
+     * times items are added or removed from the menu.
+     *
+     * @return The item count.
+     */
+    public int size();
+
+    /**
+     * Gets the menu item at the given index.
+     * 
+     * @param index The index of the menu item to return.
+     * @return The menu item.
+     * @exception IndexOutOfBoundsException
+     *                when {@code index < 0 || >= size()}
+     */
+    public MenuItem getItem(int index);
+    
+    /**
+     * Closes the menu, if open.
+     */
+    public void close();
+    
+    /**
+     * Execute the menu item action associated with the given shortcut
+     * character.
+     *
+     * @param keyCode The keycode of the shortcut key.
+     * @param event Key event message.
+     * @param flags Additional option flags or 0.
+     *
+     * @return If the given shortcut exists and is shown, returns
+     *         true; else returns false.
+     *
+     * @see #FLAG_PERFORM_NO_CLOSE
+     */
+    public boolean performShortcut(int keyCode, KeyEvent event, int flags);
+
+    /**
+     * Is a keypress one of the defined shortcut keys for this window.
+     * @param keyCode the key code from {@link KeyEvent} to check.
+     * @param event the {@link KeyEvent} to use to help check.
+     */
+    boolean isShortcutKey(int keyCode, KeyEvent event);
+    
+    /**
+     * Execute the menu item action associated with the given menu identifier.
+     * 
+     * @param id Identifier associated with the menu item. 
+     * @param flags Additional option flags or 0.
+     * 
+     * @return If the given identifier exists and is shown, returns
+     *         true; else returns false.
+     * 
+     * @see #FLAG_PERFORM_NO_CLOSE
+     */
+    public boolean performIdentifierAction(int id, int flags);
+
+
+    /**
+     * Control whether the menu should be running in qwerty mode (alphabetic
+     * shortcuts) or 12-key mode (numeric shortcuts).
+     * 
+     * @param isQwerty If true the menu will use alphabetic shortcuts; else it
+     *                 will use numeric shortcuts.
+     */
+    public void setQwertyMode(boolean isQwerty);
+}
+
diff --git a/core/java/android/view/MenuInflater.java b/core/java/android/view/MenuInflater.java
new file mode 100644
index 0000000..46c805c
--- /dev/null
+++ b/core/java/android/view/MenuInflater.java
@@ -0,0 +1,325 @@
+/*
+ * Copyright (C) 2006 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 com.android.internal.view.menu.MenuItemImpl;
+
+import java.io.IOException;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.app.Activity;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.util.AttributeSet;
+import android.util.Xml;
+
+/**
+ * This class is used to instantiate menu XML files into Menu objects.
+ * <p>
+ * For performance reasons, menu inflation relies heavily on pre-processing of
+ * XML files that is done at build time. Therefore, it is not currently possible
+ * to use MenuInflater with an XmlPullParser over a plain XML file at runtime;
+ * it only works with an XmlPullParser returned from a compiled resource (R.
+ * <em>something</em> file.)
+ */
+public class MenuInflater {
+    /** Menu tag name in XML. */
+    private static final String XML_MENU = "menu";
+    
+    /** Group tag name in XML. */
+    private static final String XML_GROUP = "group";
+    
+    /** Item tag name in XML. */
+    private static final String XML_ITEM = "item";
+
+    private static final int NO_ID = 0;
+    
+    private Context mContext;
+    
+    /**
+     * Constructs a menu inflater.
+     * 
+     * @see Activity#getMenuInflater()
+     */
+    public MenuInflater(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Inflate a menu hierarchy from the specified XML resource. Throws
+     * {@link InflateException} if there is an error.
+     * 
+     * @param menuRes Resource ID for an XML layout resource to load (e.g.,
+     *            <code>R.menu.main_activity</code>)
+     * @param menu The Menu to inflate into. The items and submenus will be
+     *            added to this Menu.
+     */
+    public void inflate(int menuRes, Menu menu) {
+        XmlResourceParser parser = null;
+        try {
+            parser = mContext.getResources().getLayout(menuRes);
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            
+            parseMenu(parser, attrs, menu);
+        } catch (XmlPullParserException e) {
+            throw new InflateException("Error inflating menu XML", e);
+        } catch (IOException e) {
+            throw new InflateException("Error inflating menu XML", e);
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    /**
+     * Called internally to fill the given menu. If a sub menu is seen, it will
+     * call this recursively.
+     */
+    private void parseMenu(XmlPullParser parser, AttributeSet attrs, Menu menu)
+            throws XmlPullParserException, IOException {
+        MenuState menuState = new MenuState(menu);
+
+        int eventType = parser.getEventType();
+        String tagName;
+        boolean lookingForEndOfUnknownTag = false;
+        String unknownTagName = null;
+
+        // This loop will skip to the menu start tag
+        do {
+            if (eventType == XmlPullParser.START_TAG) {
+                tagName = parser.getName();
+                if (tagName.equals(XML_MENU)) {
+                    // Go to next tag
+                    eventType = parser.next();
+                    break;
+                }
+                
+                throw new RuntimeException("Expecting menu, got " + tagName);
+            }
+            eventType = parser.next();
+        } while (eventType != XmlPullParser.END_DOCUMENT);
+        
+        boolean reachedEndOfMenu = false;
+        while (!reachedEndOfMenu) {
+            switch (eventType) {
+                case XmlPullParser.START_TAG:
+                    if (lookingForEndOfUnknownTag) {
+                        break;
+                    }
+                    
+                    tagName = parser.getName();
+                    if (tagName.equals(XML_GROUP)) {
+                        menuState.readGroup(attrs);
+                    } else if (tagName.equals(XML_ITEM)) {
+                        menuState.readItem(attrs);
+                    } else if (tagName.equals(XML_MENU)) {
+                        // A menu start tag denotes a submenu for an item
+                        SubMenu subMenu = menuState.addSubMenuItem();
+
+                        // Parse the submenu into returned SubMenu
+                        parseMenu(parser, attrs, subMenu);
+                    } else {
+                        lookingForEndOfUnknownTag = true;
+                        unknownTagName = tagName;
+                    }
+                    break;
+                    
+                case XmlPullParser.END_TAG:
+                    tagName = parser.getName();
+                    if (lookingForEndOfUnknownTag && tagName.equals(unknownTagName)) {
+                        lookingForEndOfUnknownTag = false;
+                        unknownTagName = null;
+                    } else if (tagName.equals(XML_GROUP)) {
+                        menuState.resetGroup();
+                    } else if (tagName.equals(XML_ITEM)) {
+                        // Add the item if it hasn't been added (if the item was
+                        // a submenu, it would have been added already)
+                        if (!menuState.hasAddedItem()) {
+                            menuState.addItem();
+                        }
+                    } else if (tagName.equals(XML_MENU)) {
+                        reachedEndOfMenu = true;
+                    }
+                    break;
+                    
+                case XmlPullParser.END_DOCUMENT:
+                    throw new RuntimeException("Unexpected end of document");
+            }
+            
+            eventType = parser.next();
+        }
+    }
+    
+    /**
+     * State for the current menu.
+     * <p>
+     * Groups can not be nested unless there is another menu (which will have
+     * its state class).
+     */
+    private class MenuState {
+        private Menu menu;
+
+        /*
+         * Group state is set on items as they are added, allowing an item to
+         * override its group state. (As opposed to set on items at the group end tag.)
+         */
+        private int groupId;
+        private int groupCategory;
+        private int groupOrder;
+        private int groupCheckable;
+        private boolean groupVisible;
+        private boolean groupEnabled;
+
+        private boolean itemAdded;
+        private int itemId;
+        private int itemCategoryOrder;
+        private String itemTitle;
+        private String itemTitleCondensed;
+        private int itemIconResId;
+        private char itemAlphabeticShortcut;
+        private char itemNumericShortcut;
+        /**
+         * Sync to attrs.xml enum:
+         * - 0: none
+         * - 1: all
+         * - 2: exclusive
+         */
+        private int itemCheckable;
+        private boolean itemChecked;
+        private boolean itemVisible;
+        private boolean itemEnabled;
+        
+        private static final int defaultGroupId = NO_ID;
+        private static final int defaultItemId = NO_ID;
+        private static final int defaultItemCategory = 0;
+        private static final int defaultItemOrder = 0;
+        private static final int defaultItemCheckable = 0;
+        private static final boolean defaultItemChecked = false;
+        private static final boolean defaultItemVisible = true;
+        private static final boolean defaultItemEnabled = true;
+        
+        public MenuState(final Menu menu) {
+            this.menu = menu;
+            
+            resetGroup();
+        }
+        
+        public void resetGroup() {
+            groupId = defaultGroupId;
+            groupCategory = defaultItemCategory;
+            groupOrder = defaultItemOrder;
+            groupCheckable = defaultItemCheckable;
+            groupVisible = defaultItemVisible;
+            groupEnabled = defaultItemEnabled;
+        }
+
+        /**
+         * Called when the parser is pointing to a group tag.
+         */
+        public void readGroup(AttributeSet attrs) {
+            TypedArray a = mContext.obtainStyledAttributes(attrs,
+                    com.android.internal.R.styleable.MenuGroup);
+            
+            groupId = a.getResourceId(com.android.internal.R.styleable.MenuGroup_id, defaultGroupId);
+            groupCategory = a.getInt(com.android.internal.R.styleable.MenuGroup_menuCategory, defaultItemCategory);
+            groupOrder = a.getInt(com.android.internal.R.styleable.MenuGroup_orderInCategory, defaultItemOrder);
+            groupCheckable = a.getInt(com.android.internal.R.styleable.MenuGroup_checkableBehavior, defaultItemCheckable);
+            groupVisible = a.getBoolean(com.android.internal.R.styleable.MenuGroup_visible, defaultItemVisible);
+            groupEnabled = a.getBoolean(com.android.internal.R.styleable.MenuGroup_enabled, defaultItemEnabled);
+
+            a.recycle();
+        }
+        
+        /**
+         * Called when the parser is pointing to an item tag.
+         */
+        public void readItem(AttributeSet attrs) {
+            TypedArray a = mContext.obtainStyledAttributes(attrs,
+                    com.android.internal.R.styleable.MenuItem);
+
+            // Inherit attributes from the group as default value
+            itemId = a.getResourceId(com.android.internal.R.styleable.MenuItem_id, defaultItemId);
+            final int category = a.getInt(com.android.internal.R.styleable.MenuItem_menuCategory, groupCategory);
+            final int order = a.getInt(com.android.internal.R.styleable.MenuItem_orderInCategory, groupOrder);
+            itemCategoryOrder = (category & Menu.CATEGORY_MASK) | (order & Menu.USER_MASK);
+            itemTitle = a.getString(com.android.internal.R.styleable.MenuItem_title);
+            itemTitleCondensed = a.getString(com.android.internal.R.styleable.MenuItem_titleCondensed);
+            itemIconResId = a.getResourceId(com.android.internal.R.styleable.MenuItem_icon, 0);
+            itemAlphabeticShortcut =
+                    getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_alphabeticShortcut));
+            itemNumericShortcut =
+                    getShortcut(a.getString(com.android.internal.R.styleable.MenuItem_numericShortcut));
+            if (a.hasValue(com.android.internal.R.styleable.MenuItem_checkable)) {
+                // Item has attribute checkable, use it
+                itemCheckable = a.getBoolean(com.android.internal.R.styleable.MenuItem_checkable, false) ? 1 : 0;
+            } else {
+                // Item does not have attribute, use the group's (group can have one more state
+                // for checkable that represents the exclusive checkable)
+                itemCheckable = groupCheckable;
+            }
+            itemChecked = a.getBoolean(com.android.internal.R.styleable.MenuItem_checked, defaultItemChecked);
+            itemVisible = a.getBoolean(com.android.internal.R.styleable.MenuItem_visible, groupVisible);
+            itemEnabled = a.getBoolean(com.android.internal.R.styleable.MenuItem_enabled, groupEnabled);
+            
+            a.recycle();
+            
+            itemAdded = false;
+        }
+
+        private char getShortcut(String shortcutString) {
+            if (shortcutString == null) {
+                return 0;
+            } else {
+                return shortcutString.charAt(0);
+            }
+        }
+        
+        private void setItem(MenuItem item) {
+            item.setChecked(itemChecked)
+                .setVisible(itemVisible)
+                .setEnabled(itemEnabled)
+                .setCheckable(itemCheckable >= 1)
+                .setTitleCondensed(itemTitleCondensed)
+                .setIcon(itemIconResId)
+                .setAlphabeticShortcut(itemAlphabeticShortcut)
+                .setNumericShortcut(itemNumericShortcut);
+
+            if (itemCheckable >= 2) {
+                ((MenuItemImpl) item).setExclusiveCheckable(true);
+            }
+        }
+        
+        public void addItem() {
+            itemAdded = true;
+            setItem(menu.add(groupId, itemId, itemCategoryOrder, itemTitle));
+        }
+        
+        public SubMenu addSubMenuItem() {
+            itemAdded = true;
+            SubMenu subMenu = menu.addSubMenu(groupId, itemId, itemCategoryOrder, itemTitle);
+            setItem(subMenu.getItem());
+            return subMenu;
+        }
+        
+        public boolean hasAddedItem() {
+            return itemAdded;
+        }
+    }
+    
+}
diff --git a/core/java/android/view/MenuItem.java b/core/java/android/view/MenuItem.java
new file mode 100644
index 0000000..fcebec5
--- /dev/null
+++ b/core/java/android/view/MenuItem.java
@@ -0,0 +1,384 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.app.Activity;
+import android.content.Intent;
+import android.graphics.drawable.Drawable;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.View.OnCreateContextMenuListener;
+
+/**
+ * Interface for direct access to a previously created menu item.
+ * <p>
+ * An Item is returned by calling one of the {@link android.view.Menu#add}
+ * methods.
+ * <p>
+ * For a feature set of specific menu types, see {@link Menu}.
+ */
+public interface MenuItem {
+    /**
+     * Interface definition for a callback to be invoked when a menu item is
+     * clicked.
+     *
+     * @see Activity#onContextItemSelected(MenuItem)
+     * @see Activity#onOptionsItemSelected(MenuItem)
+     */
+    public interface OnMenuItemClickListener {
+        /**
+         * Called when a menu item has been invoked.  This is the first code
+         * that is executed; if it returns true, no other callbacks will be
+         * executed.
+         *
+         * @param item The menu item that was invoked.
+         *
+         * @return Return true to consume this click and prevent others from
+         *         executing.
+         */
+        public boolean onMenuItemClick(MenuItem item);
+    }
+
+    /**
+     * Return the identifier for this menu item.  The identifier can not
+     * be changed after the menu is created.
+     *
+     * @return The menu item's identifier.
+     */
+    public int getItemId();
+
+    /**
+     * Return the group identifier that this menu item is part of. The group
+     * identifier can not be changed after the menu is created.
+     * 
+     * @return The menu item's group identifier.
+     */
+    public int getGroupId();
+
+    /**
+     * Return the category and order within the category of this item. This
+     * item will be shown before all items (within its category) that have
+     * order greater than this value.
+     * <p>
+     * An order integer contains the item's category (the upper bits of the
+     * integer; set by or/add the category with the order within the
+     * category) and the ordering of the item within that category (the
+     * lower bits). Example categories are {@link Menu#CATEGORY_SYSTEM},
+     * {@link Menu#CATEGORY_SECONDARY}, {@link Menu#CATEGORY_ALTERNATIVE},
+     * {@link Menu#CATEGORY_CONTAINER}. See {@link Menu} for a full list.
+     * 
+     * @return The order of this item.
+     */
+    public int getOrder();
+    
+    /**
+     * Change the title associated with this item.
+     *
+     * @param title The new text to be displayed.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setTitle(CharSequence title);
+
+    /**
+     * Change the title associated with this item.
+     * <p>
+     * Some menu types do not sufficient space to show the full title, and
+     * instead a condensed title is preferred. See {@link Menu} for more
+     * information.
+     * 
+     * @param title The resource id of the new text to be displayed.
+     * @return This Item so additional setters can be called.
+     * @see #setTitleCondensed(CharSequence)
+     */
+    
+    public MenuItem setTitle(int title);
+
+    /**
+     * Retrieve the current title of the item.
+     *
+     * @return The title.
+     */
+    public CharSequence getTitle();
+
+    /**
+     * Change the condensed title associated with this item. The condensed
+     * title is used in situations where the normal title may be too long to
+     * be displayed.
+     * 
+     * @param title The new text to be displayed as the condensed title.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setTitleCondensed(CharSequence title);
+
+    /**
+     * Retrieve the current condensed title of the item. If a condensed
+     * title was never set, it will return the normal title.
+     * 
+     * @return The condensed title, if it exists.
+     *         Otherwise the normal title.
+     */
+    public CharSequence getTitleCondensed();
+
+    /**
+     * Change the icon associated with this item. This icon will not always be
+     * shown, so the title should be sufficient in describing this item. See
+     * {@link Menu} for the menu types that support icons.
+     * 
+     * @param icon The new icon (as a Drawable) to be displayed.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setIcon(Drawable icon);
+
+    /**
+     * Change the icon associated with this item. This icon will not always be
+     * shown, so the title should be sufficient in describing this item. See
+     * {@link Menu} for the menu types that support icons.
+     * <p>
+     * This method will set the resource ID of the icon which will be used to
+     * lazily get the Drawable when this item is being shown.
+     * 
+     * @param iconRes The new icon (as a resource ID) to be displayed.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setIcon(int iconRes);
+    
+    /**
+     * Returns the icon for this item as a Drawable (getting it from resources if it hasn't been
+     * loaded before).
+     * 
+     * @return The icon as a Drawable.
+     */
+    public Drawable getIcon();
+    
+    /**
+     * Change the Intent associated with this item.  By default there is no
+     * Intent associated with a menu item.  If you set one, and nothing
+     * else handles the item, then the default behavior will be to call
+     * {@link android.content.Context#startActivity} with the given Intent.
+     *
+     * <p>Note that setIntent() can not be used with the versions of
+     * {@link Menu#add} that take a Runnable, because {@link Runnable#run}
+     * does not return a value so there is no way to tell if it handled the
+     * item.  In this case it is assumed that the Runnable always handles
+     * the item, and the intent will never be started.
+     *
+     * @see #getIntent
+     * @param intent The Intent to associated with the item.  This Intent
+     *               object is <em>not</em> copied, so be careful not to
+     *               modify it later.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setIntent(Intent intent);
+
+    /**
+     * Return the Intent associated with this item.  This returns a
+     * reference to the Intent which you can change as desired to modify
+     * what the Item is holding.
+     *
+     * @see #setIntent
+     * @return Returns the last value supplied to {@link #setIntent}, or
+     *         null.
+     */
+    public Intent getIntent();
+
+    /**
+     * Change both the numeric and alphabetic shortcut associated with this
+     * item. Note that the shortcut will be triggered when the key that
+     * generates the given character is pressed alone or along with with the alt
+     * key. Also note that case is not significant and that alphabetic shortcut
+     * characters will be displayed in lower case.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     * 
+     * @param numericChar The numeric shortcut key. This is the shortcut when
+     *        using a numeric (e.g., 12-key) keyboard.
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setShortcut(char numericChar, char alphaChar);
+
+    /**
+     * Change the numeric shortcut associated with this item.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     *
+     * @param numericChar The numeric shortcut key.  This is the shortcut when
+     *                 using a 12-key (numeric) keyboard.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setNumericShortcut(char numericChar);
+
+    /**
+     * Return the char for this menu item's numeric (12-key) shortcut.
+     *
+     * @return Numeric character to use as a shortcut.
+     */
+    public char getNumericShortcut();
+
+    /**
+     * Change the alphabetic shortcut associated with this item. The shortcut
+     * will be triggered when the key that generates the given character is
+     * pressed alone or along with with the alt key. Case is not significant and
+     * shortcut characters will be displayed in lower case. Note that menu items
+     * with the characters '\b' or '\n' as shortcuts will get triggered by the
+     * Delete key or Carriage Return key, respectively.
+     * <p>
+     * See {@link Menu} for the menu types that support shortcuts.
+     * 
+     * @param alphaChar The alphabetic shortcut key. This is the shortcut when
+     *        using a keyboard with alphabetic keys.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setAlphabeticShortcut(char alphaChar);
+
+    /**
+     * Return the char for this menu item's alphabetic shortcut.
+     *
+     * @return Alphabetic character to use as a shortcut.
+     */
+    public char getAlphabeticShortcut();
+
+    /**
+     * Control whether this item can display a check mark. Setting this does
+     * not actually display a check mark (see {@link #setChecked} for that);
+     * rather, it ensures there is room in the item in which to display a
+     * check mark.
+     * <p>
+     * See {@link Menu} for the menu types that support check marks.
+     * 
+     * @param checkable Set to true to allow a check mark, false to
+     *            disallow. The default is false.
+     * @see #setChecked
+     * @see #isCheckable
+     * @see Menu#setGroupCheckable
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setCheckable(boolean checkable);
+
+    /**
+     * Return whether the item can currently display a check mark.
+     *
+     * @return If a check mark can be displayed, returns true.
+     *
+     * @see #setCheckable
+     */
+    public boolean isCheckable();
+
+    /**
+     * Control whether this item is shown with a check mark.  Note that you
+     * must first have enabled checking with {@link #setCheckable} or else
+     * the check mark will not appear.  If this item is a member of a group that contains
+     * mutually-exclusive items (set via {@link Menu#setGroupCheckable(int, boolean, boolean)},
+     * the other items in the group will be unchecked.
+     * <p>
+     * See {@link Menu} for the menu types that support check marks.
+     *
+     * @see #setCheckable
+     * @see #isChecked
+     * @see Menu#setGroupCheckable
+     * @param checked Set to true to display a check mark, false to hide
+     *                it.  The default value is false.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setChecked(boolean checked);
+
+    /**
+     * Return whether the item is currently displaying a check mark.
+     *
+     * @return If a check mark is displayed, returns true.
+     *
+     * @see #setChecked
+     */
+    public boolean isChecked();
+
+    /**
+     * Sets the visibility of the menu item. Even if a menu item is not visible,
+     * it may still be invoked via its shortcut (to completely disable an item,
+     * set it to invisible and {@link #setEnabled(boolean) disabled}).
+     * 
+     * @param visible If true then the item will be visible; if false it is
+     *        hidden.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setVisible(boolean visible);
+
+    /**
+     * Return the visibility of the menu item.
+     *
+     * @return If true the item is visible; else it is hidden.
+     */
+    public boolean isVisible();
+
+    /**
+     * Sets whether the menu item is enabled. Disabling a menu item will not
+     * allow it to be invoked via its shortcut. The menu item will still be
+     * visible.
+     * 
+     * @param enabled If true then the item will be invokable; if false it is
+     *        won't be invokable.
+     * @return This Item so additional setters can be called.
+     */
+    public MenuItem setEnabled(boolean enabled);
+
+    /**
+     * Return the enabled state of the menu item.
+     *
+     * @return If true the item is enabled and hence invokable; else it is not.
+     */
+    public boolean isEnabled();
+
+    /**
+     * Check whether this item has an associated sub-menu.  I.e. it is a
+     * sub-menu of another menu.
+     *
+     * @return If true this item has a menu; else it is a
+     *         normal item.
+     */
+    public boolean hasSubMenu();
+
+    /**
+     * Get the sub-menu to be invoked when this item is selected, if it has
+     * one. See {@link #hasSubMenu()}.
+     *
+     * @return The associated menu if there is one, else null
+     */
+    public SubMenu getSubMenu();
+
+    /**
+     * Set a custom listener for invocation of this menu item. In most
+     * situations, it is more efficient and easier to use
+     * {@link Activity#onOptionsItemSelected(MenuItem)} or
+     * {@link Activity#onContextItemSelected(MenuItem)}.
+     * 
+     * @param menuItemClickListener The object to receive invokations.
+     * @return This Item so additional setters can be called.
+     * @see Activity#onOptionsItemSelected(MenuItem)
+     * @see Activity#onContextItemSelected(MenuItem)
+     */
+    public MenuItem setOnMenuItemClickListener(MenuItem.OnMenuItemClickListener menuItemClickListener);
+
+    /**
+     * Gets the extra information linked to this menu item.  This extra
+     * information is set by the View that added this menu item to the
+     * menu.
+     * 
+     * @see OnCreateContextMenuListener
+     * @return The extra information linked to the View that added this
+     *         menu item to the menu. This can be null.
+     */
+    public ContextMenuInfo getMenuInfo();
+}
\ No newline at end of file
diff --git a/core/java/android/view/MotionEvent.aidl b/core/java/android/view/MotionEvent.aidl
new file mode 100644
index 0000000..3c89988c
--- /dev/null
+++ b/core/java/android/view/MotionEvent.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android.view.KeyEvent.aidl
+**
+** Copyright 2007, 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;
+
+parcelable MotionEvent;
diff --git a/core/java/android/view/MotionEvent.java b/core/java/android/view/MotionEvent.java
new file mode 100644
index 0000000..882a079
--- /dev/null
+++ b/core/java/android/view/MotionEvent.java
@@ -0,0 +1,685 @@
+/*
+ * Copyright (C) 2007 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.os.Parcel;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.Config;
+
+/**
+ * Object used to report movement (mouse, pen, finger, trackball) events.  This
+ * class may hold either absolute or relative movements, depending on what
+ * it is being used for.
+ */
+public final class MotionEvent implements Parcelable {
+    /**
+     * Constant for {@link #getAction}: A pressed gesture has started, the
+     * motion contains the initial starting location.
+     */
+    public static final int ACTION_DOWN             = 0;
+    /**
+     * Constant for {@link #getAction}: A pressed gesture has finished, the
+     * motion contains the final release location as well as any intermediate
+     * points since the last down or move event.
+     */
+    public static final int ACTION_UP               = 1;
+    /**
+     * Constant for {@link #getAction}: A change has happened during a
+     * press gesture (between {@link #ACTION_DOWN} and {@link #ACTION_UP}).
+     * The motion contains the most recent point, as well as any intermediate
+     * points since the last down or move event.
+     */
+    public static final int ACTION_MOVE             = 2;
+    /**
+     * Constant for {@link #getAction}: The current gesture has been aborted.
+     * You will not receive any more points in it.  You should treat this as
+     * an up event, but not perform any action that you normally would.
+     */
+    public static final int ACTION_CANCEL           = 3;
+    /**
+     * Constant for {@link #getAction}: A movement has happened outside of the
+     * normal bounds of the UI element.  This does not provide a full gesture,
+     * but only the initial location of the movement/touch.
+     */
+    public static final int ACTION_OUTSIDE          = 4;
+
+    private static final boolean TRACK_RECYCLED_LOCATION = false;
+    
+    /**
+     * Flag indicating the motion event intersected the top edge of the screen.
+     */
+    public static final int EDGE_TOP = 0x00000001;
+    
+    /**
+     * Flag indicating the motion event intersected the bottom edge of the screen.
+     */
+    public static final int EDGE_BOTTOM = 0x00000002;
+    
+    /**
+     * Flag indicating the motion event intersected the left edge of the screen.
+     */
+    public static final int EDGE_LEFT = 0x00000004;
+    
+    /**
+     * Flag indicating the motion event intersected the right edge of the screen.
+     */
+    public static final int EDGE_RIGHT = 0x00000008;
+    
+    static private final int MAX_RECYCLED = 10;
+    static private Object gRecyclerLock = new Object();
+    static private int gRecyclerUsed = 0;
+    static private MotionEvent gRecyclerTop = null;
+    
+    private long mDownTime;
+    private long mEventTime;
+    private int mAction;
+    private float mX;
+    private float mY;
+    private float mRawX;
+    private float mRawY;
+    private float mPressure;
+    private float mSize;
+    private int mMetaState;
+    private int mNumHistory;
+    private float[] mHistory;
+    private long[] mHistoryTimes;
+    private float mXPrecision;
+    private float mYPrecision;
+    private int mDeviceId;
+    private int mEdgeFlags;
+    
+    private MotionEvent mNext;
+    private RuntimeException mRecycledLocation;
+    private boolean mRecycled;
+
+    private MotionEvent() {
+    }
+    
+    static private MotionEvent obtain() {
+        synchronized (gRecyclerLock) {
+            if (gRecyclerTop == null) {
+                return new MotionEvent();
+            }
+            MotionEvent ev = gRecyclerTop;
+            gRecyclerTop = ev.mNext;
+            gRecyclerUsed--;
+            ev.mRecycledLocation = null;
+            ev.mRecycled = false;
+            return ev;
+        }
+    }
+    
+    /**
+     * Create a new MotionEvent, filling in all of the basic values that
+     * define the motion.
+     * 
+     * @param downTime The time (in ms) when the user originally pressed down to start 
+     * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTime  The the time (in ms) when this specific event was generated.  This 
+     * must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param action The kind of action being performed -- one of either
+     * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+     * {@link #ACTION_CANCEL}.
+     * @param x The X coordinate of this event.
+     * @param y The Y coordinate of this event.
+     * @param pressure The current pressure of this event.  The pressure generally 
+     * ranges from 0 (no pressure at all) to 1 (normal pressure), however 
+     * values higher than 1 may be generated depending on the calibration of 
+     * the input device.
+     * @param size A scaled value of the approximate size of the area being pressed when
+     * touched with the finger. The actual value in pixels corresponding to the finger 
+     * touch is normalized with a device specific range of values
+     * and scaled to a value between 0 and 1.
+     * @param metaState The state of any meta / modifier keys that were in effect when
+     * the event was generated.
+     * @param xPrecision The precision of the X coordinate being reported.
+     * @param yPrecision The precision of the Y coordinate being reported.
+     * @param deviceId The id for the device that this event came from.  An id of
+     * zero indicates that the event didn't come from a physical device; other
+     * numbers are arbitrary and you shouldn't depend on the values.
+     * @param edgeFlags A bitfield indicating which edges, if any, where touched by this
+     * MotionEvent.
+     */
+    static public MotionEvent obtain(long downTime, long eventTime, int action,
+            float x, float y, float pressure, float size, int metaState,
+            float xPrecision, float yPrecision, int deviceId, int edgeFlags) {
+        MotionEvent ev = obtain();
+        ev.mDeviceId = deviceId;
+        ev.mEdgeFlags = edgeFlags;
+        ev.mDownTime = downTime;
+        ev.mEventTime = eventTime;
+        ev.mAction = action;
+        ev.mX = ev.mRawX = x;
+        ev.mY = ev.mRawY = y;
+        ev.mPressure = pressure;
+        ev.mSize = size;
+        ev.mMetaState = metaState;
+        ev.mXPrecision = xPrecision;
+        ev.mYPrecision = yPrecision;
+
+        return ev;
+    }
+    
+    /**
+     * Create a new MotionEvent, filling in a subset of the basic motion
+     * values.  Those not specified here are: device id (always 0), pressure
+     * and size (always 1), x and y precision (always 1), and edgeFlags (always 0).
+     * 
+     * @param downTime The time (in ms) when the user originally pressed down to start 
+     * a stream of position events.  This must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param eventTime  The the time (in ms) when this specific event was generated.  This 
+     * must be obtained from {@link SystemClock#uptimeMillis()}.
+     * @param action The kind of action being performed -- one of either
+     * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+     * {@link #ACTION_CANCEL}.
+     * @param x The X coordinate of this event.
+     * @param y The Y coordinate of this event.
+     * @param metaState The state of any meta / modifier keys that were in effect when
+     * the event was generated.
+     */
+    static public MotionEvent obtain(long downTime, long eventTime, int action,
+            float x, float y, int metaState) {
+        MotionEvent ev = obtain();
+        ev.mDeviceId = 0;
+        ev.mEdgeFlags = 0;
+        ev.mDownTime = downTime;
+        ev.mEventTime = eventTime;
+        ev.mAction = action;
+        ev.mX = ev.mRawX = x;
+        ev.mY = ev.mRawY = y;
+        ev.mPressure = 1.0f;
+        ev.mSize = 1.0f;
+        ev.mMetaState = metaState;
+        ev.mXPrecision = 1.0f;
+        ev.mYPrecision = 1.0f;
+
+        return ev;
+    }
+    
+    /**
+     * Create a new MotionEvent, copying from an existing one.
+     */
+    static public MotionEvent obtain(MotionEvent o) {
+        MotionEvent ev = obtain();
+        ev.mDeviceId = o.mDeviceId;
+        ev.mEdgeFlags = o.mEdgeFlags;
+        ev.mDownTime = o.mDownTime;
+        ev.mEventTime = o.mEventTime;
+        ev.mAction = o.mAction;
+        ev.mX = o.mX;
+        ev.mRawX = o.mRawX;
+        ev.mY = o.mY;
+        ev.mRawY = o.mRawY;
+        ev.mPressure = o.mPressure;
+        ev.mSize = o.mSize;
+        ev.mMetaState = o.mMetaState;
+        ev.mXPrecision = o.mXPrecision;
+        ev.mYPrecision = o.mYPrecision;
+        final int N = o.mNumHistory;
+        ev.mNumHistory = N;
+        if (N > 0) {
+            // could be more efficient about this...
+            ev.mHistory = (float[])o.mHistory.clone();
+            ev.mHistoryTimes = (long[])o.mHistoryTimes.clone();
+        }
+        return ev;
+    }
+    
+    /**
+     * Recycle the MotionEvent, to be re-used by a later caller.  After calling
+     * this function you must not ever touch the event again.
+     */
+    public void recycle() {
+        // Ensure recycle is only called once!
+        if (TRACK_RECYCLED_LOCATION) {
+            if (mRecycledLocation != null) {
+                throw new RuntimeException(toString() + " recycled twice!", mRecycledLocation);
+            }
+            mRecycledLocation = new RuntimeException("Last recycled here");
+        } else if (mRecycled) {
+            throw new RuntimeException(toString() + " recycled twice!");
+        }
+
+        //Log.w("MotionEvent", "Recycling event " + this, mRecycledLocation);
+        synchronized (gRecyclerLock) {
+            if (gRecyclerUsed < MAX_RECYCLED) {
+                gRecyclerUsed++;
+                mNumHistory = 0;
+                mNext = gRecyclerTop;
+                gRecyclerTop = this;
+            }
+        }
+    }
+    
+    /**
+     * Return the kind of action being performed -- one of either
+     * {@link #ACTION_DOWN}, {@link #ACTION_MOVE}, {@link #ACTION_UP}, or
+     * {@link #ACTION_CANCEL}.
+     */
+    public final int getAction() {
+        return mAction;
+    }
+
+    /**
+     * Returns the time (in ms) when the user originally pressed down to start 
+     * a stream of position events. 
+     */
+    public final long getDownTime() {
+        return mDownTime;
+    }
+
+    /**
+     * Returns the time (in ms) when this specific event was generated.
+     */
+    public final long getEventTime() {
+        return mEventTime;
+    }
+
+    /**
+     * Returns the X coordinate of this event.  Whole numbers are pixels; the 
+     * value may have a fraction for input devices that are sub-pixel precise. 
+     */
+    public final float getX() {
+        return mX;
+    }
+
+    /**
+     * Returns the Y coordinate of this event.  Whole numbers are pixels; the 
+     * value may have a fraction for input devices that are sub-pixel precise. 
+     */
+    public final float getY() {
+        return mY;
+    }
+
+    /**
+     * Returns the current pressure of this event.  The pressure generally 
+     * ranges from 0 (no pressure at all) to 1 (normal pressure), however 
+     * values higher than 1 may be generated depending on the calibration of 
+     * the input device.
+     */
+    public final float getPressure() {
+        return mPressure;
+    }
+
+    /**
+     * Returns a scaled value of the approximate size, of the area being pressed when
+     * touched with the finger. The actual value in pixels corresponding to the finger 
+     * touch  is normalized with the device specific range of values
+     * and scaled to a value between 0 and 1. The value of size can be used to 
+     * determine fat touch events.
+     */
+    public final float getSize() {
+        return mSize;
+    }
+
+    /**
+     * Returns the state of any meta / modifier keys that were in effect when
+     * the event was generated.  This is the same values as those
+     * returned by {@link KeyEvent#getMetaState() KeyEvent.getMetaState}.
+     *
+     * @return an integer in which each bit set to 1 represents a pressed
+     *         meta key
+     *
+     * @see KeyEvent#getMetaState()
+     */
+    public final int getMetaState() {
+        return mMetaState;
+    }
+
+    /**
+     * Returns the original raw X coordinate of this event.  For touch
+     * events on the screen, this is the original location of the event
+     * on the screen, before it had been adjusted for the containing window
+     * and views.
+     */
+    public final float getRawX() {
+        return mRawX;
+    }
+
+    /**
+     * Returns the original raw Y coordinate of this event.  For touch
+     * events on the screen, this is the original location of the event
+     * on the screen, before it had been adjusted for the containing window
+     * and views.
+     */
+    public final float getRawY() {
+        return mRawY;
+    }
+
+    /**
+     * Return the precision of the X coordinates being reported.  You can
+     * multiple this number with {@link #getX} to find the actual hardware
+     * value of the X coordinate.
+     * @return Returns the precision of X coordinates being reported.
+     */
+    public final float getXPrecision() {
+        return mXPrecision;
+    }
+    
+    /**
+     * Return the precision of the Y coordinates being reported.  You can
+     * multiple this number with {@link #getY} to find the actual hardware
+     * value of the Y coordinate.
+     * @return Returns the precision of Y coordinates being reported.
+     */
+    public final float getYPrecision() {
+        return mYPrecision;
+    }
+    
+    /**
+     * Returns the number of historical points in this event.  These are
+     * movements that have occurred between this event and the previous event.
+     * This only applies to ACTION_MOVE events -- all other actions will have
+     * a size of 0.
+     * 
+     * @return Returns the number of historical points in the event.
+     */
+    public final int getHistorySize() {
+        return mNumHistory;
+    }
+    
+    /**
+     * Returns the time that a historical movement occurred between this event
+     * and the previous event.  Only applies to ACTION_MOVE events.
+     * 
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * 
+     * @see #getHistorySize
+     * @see #getEventTime
+     */
+    public final long getHistoricalEventTime(int pos) {
+        return mHistoryTimes[pos];
+    }
+    
+    /**
+     * Returns a historical X coordinate that occurred between this event
+     * and the previous event.  Only applies to ACTION_MOVE events.
+     * 
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * 
+     * @see #getHistorySize
+     * @see #getX
+     */
+    public final float getHistoricalX(int pos) {
+        return mHistory[pos*4];
+    }
+    
+    /**
+     * Returns a historical Y coordinate that occurred between this event
+     * and the previous event.  Only applies to ACTION_MOVE events.
+     * 
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * 
+     * @see #getHistorySize
+     * @see #getY
+     */
+    public final float getHistoricalY(int pos) {
+        return mHistory[pos*4 + 1];
+    }
+    
+    /**
+     * Returns a historical pressure coordinate that occurred between this event
+     * and the previous event.  Only applies to ACTION_MOVE events.
+     * 
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * 
+     * @see #getHistorySize
+     * @see #getPressure
+     */
+    public final float getHistoricalPressure(int pos) {
+        return mHistory[pos*4 + 2];
+    }
+    
+    /**
+     * Returns a historical size coordinate that occurred between this event
+     * and the previous event.  Only applies to ACTION_MOVE events.
+     * 
+     * @param pos Which historical value to return; must be less than
+     * {@link #getHistorySize}
+     * 
+     * @see #getHistorySize
+     * @see #getSize
+     */
+    public final float getHistoricalSize(int pos) {
+        return mHistory[pos*4 + 3];
+    }
+    
+    /**
+     * Return the id for the device that this event came from.  An id of
+     * zero indicates that the event didn't come from a physical device; other
+     * numbers are arbitrary and you shouldn't depend on the values.
+     */
+    public final int getDeviceId() {
+        return mDeviceId;
+    }
+    
+    /**
+     * Returns a bitfield indicating which edges, if any, where touched by this
+     * MotionEvent. For touch events, clients can use this to determine if the 
+     * user's finger was touching the edge of the display. 
+     * 
+     * @see #EDGE_LEFT
+     * @see #EDGE_TOP
+     * @see #EDGE_RIGHT
+     * @see #EDGE_BOTTOM
+     */
+    public final int getEdgeFlags() {
+        return mEdgeFlags;
+    }
+    
+
+    /**
+     * Sets the bitfield indicating which edges, if any, where touched by this
+     * MotionEvent. 
+     * 
+     * @see #getEdgeFlags()
+     */
+    public final void setEdgeFlags(int flags) {
+        mEdgeFlags = flags;
+    }
+
+    /**
+     * Sets this event's action.
+     */
+    public final void setAction(int action) {
+        mAction = action;
+    }
+
+    /**
+     * Adjust this event's location.
+     * @param deltaX Amount to add to the current X coordinate of the event.
+     * @param deltaY Amount to add to the current Y coordinate of the event.
+     */
+    public final void offsetLocation(float deltaX, float deltaY) {
+        mX += deltaX;
+        mY += deltaY;
+        final int N = mNumHistory*4;
+        if (N <= 0) {
+            return;
+        }
+        final float[] pos = mHistory;
+        for (int i=0; i<N; i+=4) {
+            pos[i] += deltaX;
+            pos[i+1] += deltaY;
+        }
+    }
+ 
+    /**
+     * Set this event's location.  Applies {@link #offsetLocation} with a
+     * delta from the current location to the given new location.
+     * 
+     * @param x New absolute X location.
+     * @param y New absolute Y location.
+     */
+    public final void setLocation(float x, float y) {
+        float deltaX = x-mX;
+        float deltaY = y-mY;
+        if (deltaX != 0 || deltaY != 0) {
+            offsetLocation(deltaX, deltaY);
+        }
+    }
+    
+    /**
+     * Add a new movement to the batch of movements in this event.  The event's
+     * current location, position and size is updated to the new values.  In
+     * the future, the current values in the event will be added to a list of
+     * historic values.
+     * 
+     * @param x The new X position.
+     * @param y The new Y position.
+     * @param pressure The new pressure.
+     * @param size The new size.
+     */
+    public final void addBatch(long eventTime, float x, float y,
+            float pressure, float size, int metaState) {
+        float[] history = mHistory;
+        long[] historyTimes = mHistoryTimes;
+        int N;
+        int avail;
+        if (history == null) {
+            mHistory = history = new float[8*4];
+            mHistoryTimes = historyTimes = new long[8];
+            mNumHistory = N = 0;
+            avail = 8;
+        } else {
+            N = mNumHistory;
+            avail = history.length/4;
+            if (N == avail) {
+                avail += 8;
+                float[] newHistory = new float[avail*4];
+                System.arraycopy(history, 0, newHistory, 0, N*4);
+                mHistory = history = newHistory;
+                long[] newHistoryTimes = new long[avail];
+                System.arraycopy(historyTimes, 0, newHistoryTimes, 0, N);
+                mHistoryTimes = historyTimes = newHistoryTimes;
+            }
+        }
+        
+        historyTimes[N] = mEventTime;
+        
+        final int pos = N*4;
+        history[pos] = mX;
+        history[pos+1] = mY;
+        history[pos+2] = mPressure;
+        history[pos+3] = mSize;
+        mNumHistory = N+1;
+        
+        mEventTime = eventTime;
+        mX = mRawX = x;
+        mY = mRawY = y;
+        mPressure = pressure;
+        mSize = size;
+        mMetaState |= metaState;
+    }
+    
+    @Override
+    public String toString() {
+        return "MotionEvent{" + Integer.toHexString(System.identityHashCode(this))
+            + " action=" + mAction + " x=" + mX
+            + " y=" + mY + " pressure=" + mPressure + " size=" + mSize + "}";
+    }
+
+    public static final Parcelable.Creator<MotionEvent> CREATOR
+            = new Parcelable.Creator<MotionEvent>() {
+        public MotionEvent createFromParcel(Parcel in) {
+            MotionEvent ev = obtain();
+            ev.readFromParcel(in);
+            return ev;
+        }
+
+        public MotionEvent[] newArray(int size) {
+            return new MotionEvent[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+    public void writeToParcel(Parcel out, int flags) {
+        out.writeLong(mDownTime);
+        out.writeLong(mEventTime);
+        out.writeInt(mAction);
+        out.writeFloat(mX);
+        out.writeFloat(mY);
+        out.writeFloat(mPressure);
+        out.writeFloat(mSize);
+        out.writeInt(mMetaState);
+        out.writeFloat(mRawX);
+        out.writeFloat(mRawY);
+        final int N = mNumHistory;
+        out.writeInt(N);
+        if (N > 0) {
+            final int N4 = N*4;
+            int i;
+            float[] history = mHistory;
+            for (i=0; i<N4; i++) {
+                out.writeFloat(history[i]);
+            }
+            long[] times = mHistoryTimes;
+            for (i=0; i<N; i++) {
+                out.writeLong(times[i]);
+            }
+        }
+        out.writeFloat(mXPrecision);
+        out.writeFloat(mYPrecision);
+        out.writeInt(mDeviceId);
+        out.writeInt(mEdgeFlags);
+    }
+
+    private void readFromParcel(Parcel in) {
+        mDownTime = in.readLong();
+        mEventTime = in.readLong();
+        mAction = in.readInt();
+        mX = in.readFloat();
+        mY = in.readFloat();
+        mPressure = in.readFloat();
+        mSize = in.readFloat();
+        mMetaState = in.readInt();
+        mRawX = in.readFloat();
+        mRawY = in.readFloat();
+        final int N = in.readInt();
+        if ((mNumHistory=N) > 0) {
+            final int N4 = N*4;
+            float[] history = mHistory;
+            if (history == null || history.length < N4) {
+                mHistory = history = new float[N4 + (4*4)];
+            }
+            for (int i=0; i<N4; i++) {
+                history[i] = in.readFloat();
+            }
+            long[] times = mHistoryTimes;
+            if (times == null || times.length < N) {
+                mHistoryTimes = times = new long[N + 4];
+            }
+            for (int i=0; i<N; i++) {
+                times[i] = in.readLong();
+            }
+        }
+        mXPrecision = in.readFloat();
+        mYPrecision = in.readFloat();
+        mDeviceId = in.readInt();
+        mEdgeFlags = in.readInt();
+    }
+
+}
+
diff --git a/core/java/android/view/OrientationEventListener.java b/core/java/android/view/OrientationEventListener.java
new file mode 100755
index 0000000..391ba1e
--- /dev/null
+++ b/core/java/android/view/OrientationEventListener.java
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * Helper class for receiving notifications from the SensorManager when
+ * the orientation of the device has changed.
+ */
+public abstract class OrientationEventListener {
+    private static final String TAG = "OrientationEventListener";
+    private static final boolean DEBUG = false;
+    private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    private int mOrientation = ORIENTATION_UNKNOWN;
+    private SensorManager mSensorManager;
+    private boolean mEnabled = false;
+    private int mRate;
+    private Sensor mSensor;
+    private SensorEventListener mSensorEventListener;
+    private OrientationListener mOldListener;
+    
+    /**
+     * Returned from onOrientationChanged when the device orientation cannot be determined
+     * (typically when the device is in a close to flat position).
+     *
+     *  @see #onOrientationChanged
+     */
+    public static final int ORIENTATION_UNKNOWN = -1;
+
+    /**
+     * Creates a new OrientationEventListener.
+     * 
+     * @param context for the OrientationEventListener.
+     */
+    public OrientationEventListener(Context context) {
+        this(context, SensorManager.SENSOR_DELAY_NORMAL);
+    }
+    
+    /**
+     * Creates a new OrientationEventListener.
+     * 
+     * @param context for the OrientationEventListener.
+     * @param rate at which sensor events are processed (see also
+     * {@link android.hardware.SensorManager SensorManager}). Use the default
+     * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 
+     * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
+     */
+    public OrientationEventListener(Context context, int rate) {
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mRate = rate;
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        if (mSensor != null) {
+            // Create listener only if sensors do exist
+            mSensorEventListener = new SensorEventListenerImpl();
+        }
+    }
+    
+    void registerListener(OrientationListener lis) {
+        mOldListener = lis;
+    }
+
+    /**
+     * Enables the OrientationEventListener so it will monitor the sensor and call
+     * {@link #onOrientationChanged} when the device orientation changes.
+     */
+    public void enable() {
+        if (mSensor == null) {
+            Log.w(TAG, "Cannot detect sensors. Not enabled");
+            return;
+        }
+        if (mEnabled == false) {
+            if (localLOGV) Log.d(TAG, "OrientationEventListener enabled");
+            mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);
+            mEnabled = true;
+        }
+    }
+
+    /**
+     * Disables the OrientationEventListener.
+     */
+    public void disable() {
+        if (mSensor == null) {
+            Log.w(TAG, "Cannot detect sensors. Invalid disable");
+            return;
+        }
+        if (mEnabled == true) {
+            if (localLOGV) Log.d(TAG, "OrientationEventListener disabled");
+            mSensorManager.unregisterListener(mSensorEventListener);
+            mEnabled = false;
+        }
+    }
+
+    class SensorEventListenerImpl implements SensorEventListener {
+        private static final int _DATA_X = 0;
+        private static final int _DATA_Y = 1;
+        private static final int _DATA_Z = 2;
+        
+        public void onSensorChanged(SensorEvent event) {
+            float[] values = event.values;
+            int orientation = ORIENTATION_UNKNOWN;
+            float X = -values[_DATA_X];
+            float Y = -values[_DATA_Y];
+            float Z = -values[_DATA_Z];        
+            float magnitude = X*X + Y*Y;
+            // Don't trust the angle if the magnitude is small compared to the y value
+            if (magnitude * 4 >= Z*Z) {
+                float OneEightyOverPi = 57.29577957855f;
+                float angle = (float)Math.atan2(-Y, X) * OneEightyOverPi;
+                orientation = 90 - (int)Math.round(angle);
+                // normalize to 0 - 359 range
+                while (orientation >= 360) {
+                    orientation -= 360;
+                } 
+                while (orientation < 0) {
+                    orientation += 360;
+                }
+            }
+            if (mOldListener != null) {
+                mOldListener.onSensorChanged(Sensor.TYPE_ACCELEROMETER, event.values);
+            }
+            if (orientation != mOrientation) {
+                mOrientation = orientation;
+                onOrientationChanged(orientation);
+            }
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+        }
+    }
+    
+    /*
+     * Returns true if sensor is enabled and false otherwise
+     */
+    public boolean canDetectOrientation() {
+        return mSensor != null;
+    }
+
+    /**
+     * Called when the orientation of the device has changed.
+     * orientation parameter is in degrees, ranging from 0 to 359.
+     * orientation is 0 degrees when the device is oriented in its natural position,
+     * 90 degrees when its left side is at the top, 180 degrees when it is upside down, 
+     * and 270 degrees when its right side is to the top.
+     * {@link #ORIENTATION_UNKNOWN} is returned when the device is close to flat
+     * and the orientation cannot be determined.
+     *
+     * @param orientation The new orientation of the device.
+     *
+     *  @see #ORIENTATION_UNKNOWN
+     */
+    abstract public void onOrientationChanged(int orientation);
+}
diff --git a/core/java/android/view/OrientationListener.java b/core/java/android/view/OrientationListener.java
new file mode 100644
index 0000000..ce8074e
--- /dev/null
+++ b/core/java/android/view/OrientationListener.java
@@ -0,0 +1,110 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.hardware.SensorListener;
+
+/**
+ * Helper class for receiving notifications from the SensorManager when
+ * the orientation of the device has changed.
+ *  @deprecated use {@link android.view.OrientationEventListener} instead.
+ *  This class internally uses the OrientationEventListener.
+ */
+@Deprecated
+public abstract class OrientationListener implements SensorListener {
+    private OrientationEventListener mOrientationEventLis;
+    
+    /**
+     * Returned from onOrientationChanged when the device orientation cannot be determined
+     * (typically when the device is in a close to flat position).
+     *
+     *  @see #onOrientationChanged
+     */
+    public static final int ORIENTATION_UNKNOWN = OrientationEventListener.ORIENTATION_UNKNOWN;
+
+    /**
+     * Creates a new OrientationListener.
+     * 
+     * @param context for the OrientationListener.
+     */
+    public OrientationListener(Context context) {
+        mOrientationEventLis = new OrientationEventListenerInternal(context);
+    }
+
+    /**
+     * Creates a new OrientationListener.
+     * 
+     * @param context for the OrientationListener.
+     * @param rate at which sensor events are processed (see also
+     * {@link android.hardware.SensorManager SensorManager}). Use the default
+     * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 
+     * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
+     */
+    public OrientationListener(Context context, int rate) {
+        mOrientationEventLis = new OrientationEventListenerInternal(context, rate);
+    }
+    
+    class OrientationEventListenerInternal extends OrientationEventListener {
+        OrientationEventListenerInternal(Context context) {
+            super(context);
+        }
+        
+        OrientationEventListenerInternal(Context context, int rate) {
+            super(context, rate);
+            // register so that onSensorChanged gets invoked
+            registerListener(OrientationListener.this);
+        }
+                
+        public void onOrientationChanged(int orientation) {
+            OrientationListener.this.onOrientationChanged(orientation);
+        }
+    }
+    
+    /**
+     * Enables the OrientationListener so it will monitor the sensor and call
+     * {@link #onOrientationChanged} when the device orientation changes.
+     */
+    public void enable() {
+        mOrientationEventLis.enable();
+    }
+
+    /**
+     * Disables the OrientationListener.
+     */
+    public void disable() {
+        mOrientationEventLis.disable();
+    }
+    
+    public void onAccuracyChanged(int sensor, int accuracy) {
+    }
+    
+    public void onSensorChanged(int sensor, float[] values) {
+        // just ignore the call here onOrientationChanged is invoked anyway
+    }
+
+
+    /**
+     * Look at {@link android.view.OrientationEventListener#onOrientationChanged}
+     * for method description and usage
+     * @param orientation The new orientation of the device.
+     *
+     *  @see #ORIENTATION_UNKNOWN
+     */
+    abstract public void onOrientationChanged(int orientation);
+    
+}
diff --git a/core/java/android/view/RawInputEvent.java b/core/java/android/view/RawInputEvent.java
new file mode 100644
index 0000000..30da83e
--- /dev/null
+++ b/core/java/android/view/RawInputEvent.java
@@ -0,0 +1,170 @@
+/**
+ * 
+ */
+package android.view;
+
+/**
+ * @hide
+ * This really belongs in services.jar; WindowManagerPolicy should go there too.
+ */
+public class RawInputEvent {
+    // Event class as defined by EventHub.
+    public static final int CLASS_KEYBOARD = 0x00000001;
+    public static final int CLASS_ALPHAKEY = 0x00000002;
+    public static final int CLASS_TOUCHSCREEN = 0x00000004;
+    public static final int CLASS_TRACKBALL = 0x00000008;
+    
+    // More special classes for QueuedEvent below.
+    public static final int CLASS_CONFIGURATION_CHANGED = 0x10000000;
+    
+    // Event types.
+
+    public static final int EV_SYN = 0x00;
+    public static final int EV_KEY = 0x01;
+    public static final int EV_REL = 0x02;
+    public static final int EV_ABS = 0x03;
+    public static final int EV_MSC = 0x04;
+    public static final int EV_SW = 0x05;
+    public static final int EV_LED = 0x11;
+    public static final int EV_SND = 0x12;
+    public static final int EV_REP = 0x14;
+    public static final int EV_FF = 0x15;
+    public static final int EV_PWR = 0x16;
+    public static final int EV_FF_STATUS = 0x17;
+
+    // Platform-specific event types.
+    
+    public static final int EV_DEVICE_ADDED = 0x10000000;
+    public static final int EV_DEVICE_REMOVED = 0x20000000;
+    
+    // Special key (EV_KEY) scan codes for pointer buttons.
+
+    public static final int BTN_FIRST = 0x100;
+
+    public static final int BTN_MISC = 0x100;
+    public static final int BTN_0 = 0x100;
+    public static final int BTN_1 = 0x101;
+    public static final int BTN_2 = 0x102;
+    public static final int BTN_3 = 0x103;
+    public static final int BTN_4 = 0x104;
+    public static final int BTN_5 = 0x105;
+    public static final int BTN_6 = 0x106;
+    public static final int BTN_7 = 0x107;
+    public static final int BTN_8 = 0x108;
+    public static final int BTN_9 = 0x109;
+
+    public static final int BTN_MOUSE = 0x110;
+    public static final int BTN_LEFT = 0x110;
+    public static final int BTN_RIGHT = 0x111;
+    public static final int BTN_MIDDLE = 0x112;
+    public static final int BTN_SIDE = 0x113;
+    public static final int BTN_EXTRA = 0x114;
+    public static final int BTN_FORWARD = 0x115;
+    public static final int BTN_BACK = 0x116;
+    public static final int BTN_TASK = 0x117;
+
+    public static final int BTN_JOYSTICK = 0x120;
+    public static final int BTN_TRIGGER = 0x120;
+    public static final int BTN_THUMB = 0x121;
+    public static final int BTN_THUMB2 = 0x122;
+    public static final int BTN_TOP = 0x123;
+    public static final int BTN_TOP2 = 0x124;
+    public static final int BTN_PINKIE = 0x125;
+    public static final int BTN_BASE = 0x126;
+    public static final int BTN_BASE2 = 0x127;
+    public static final int BTN_BASE3 = 0x128;
+    public static final int BTN_BASE4 = 0x129;
+    public static final int BTN_BASE5 = 0x12a;
+    public static final int BTN_BASE6 = 0x12b;
+    public static final int BTN_DEAD = 0x12f;
+
+    public static final int BTN_GAMEPAD = 0x130;
+    public static final int BTN_A = 0x130;
+    public static final int BTN_B = 0x131;
+    public static final int BTN_C = 0x132;
+    public static final int BTN_X = 0x133;
+    public static final int BTN_Y = 0x134;
+    public static final int BTN_Z = 0x135;
+    public static final int BTN_TL = 0x136;
+    public static final int BTN_TR = 0x137;
+    public static final int BTN_TL2 = 0x138;
+    public static final int BTN_TR2 = 0x139;
+    public static final int BTN_SELECT = 0x13a;
+    public static final int BTN_START = 0x13b;
+    public static final int BTN_MODE = 0x13c;
+    public static final int BTN_THUMBL = 0x13d;
+    public static final int BTN_THUMBR = 0x13e;
+
+    public static final int BTN_DIGI = 0x140;
+    public static final int BTN_TOOL_PEN = 0x140;
+    public static final int BTN_TOOL_RUBBER = 0x141;
+    public static final int BTN_TOOL_BRUSH = 0x142;
+    public static final int BTN_TOOL_PENCIL = 0x143;
+    public static final int BTN_TOOL_AIRBRUSH = 0x144;
+    public static final int BTN_TOOL_FINGER = 0x145;
+    public static final int BTN_TOOL_MOUSE = 0x146;
+    public static final int BTN_TOOL_LENS = 0x147;
+    public static final int BTN_TOUCH = 0x14a;
+    public static final int BTN_STYLUS = 0x14b;
+    public static final int BTN_STYLUS2 = 0x14c;
+    public static final int BTN_TOOL_DOUBLETAP = 0x14d;
+    public static final int BTN_TOOL_TRIPLETAP = 0x14e;
+
+    public static final int BTN_WHEEL = 0x150;
+    public static final int BTN_GEAR_DOWN = 0x150;
+    public static final int BTN_GEAR_UP = 0x151;
+
+    public static final int BTN_LAST = 0x15f;
+
+    // Relative axes (EV_REL) scan codes.
+
+    public static final int REL_X = 0x00;
+    public static final int REL_Y = 0x01;
+    public static final int REL_Z = 0x02;
+    public static final int REL_RX = 0x03;
+    public static final int REL_RY = 0x04;
+    public static final int REL_RZ = 0x05;
+    public static final int REL_HWHEEL = 0x06;
+    public static final int REL_DIAL = 0x07;
+    public static final int REL_WHEEL = 0x08;
+    public static final int REL_MISC = 0x09;
+    public static final int REL_MAX = 0x0f;
+
+    // Absolute axes (EV_ABS) scan codes.
+
+    public static final int ABS_X = 0x00;
+    public static final int ABS_Y = 0x01;
+    public static final int ABS_Z = 0x02;
+    public static final int ABS_RX = 0x03;
+    public static final int ABS_RY = 0x04;
+    public static final int ABS_RZ = 0x05;
+    public static final int ABS_THROTTLE = 0x06;
+    public static final int ABS_RUDDER = 0x07;
+    public static final int ABS_WHEEL = 0x08;
+    public static final int ABS_GAS = 0x09;
+    public static final int ABS_BRAKE = 0x0a;
+    public static final int ABS_HAT0X = 0x10;
+    public static final int ABS_HAT0Y = 0x11;
+    public static final int ABS_HAT1X = 0x12;
+    public static final int ABS_HAT1Y = 0x13;
+    public static final int ABS_HAT2X = 0x14;
+    public static final int ABS_HAT2Y = 0x15;
+    public static final int ABS_HAT3X = 0x16;
+    public static final int ABS_HAT3Y = 0x17;
+    public static final int ABS_PRESSURE = 0x18;
+    public static final int ABS_DISTANCE = 0x19;
+    public static final int ABS_TILT_X = 0x1a;
+    public static final int ABS_TILT_Y = 0x1b;
+    public static final int ABS_TOOL_WIDTH = 0x1c;
+    public static final int ABS_VOLUME = 0x20;
+    public static final int ABS_MISC = 0x28;
+    public static final int ABS_MAX = 0x3f;
+
+    public int deviceId;
+    public int type;
+    public int scancode;
+    public int keycode;
+    public int flags;
+    public int value;
+    public long when;
+}
diff --git a/core/java/android/view/RemotableViewMethod.java b/core/java/android/view/RemotableViewMethod.java
new file mode 100644
index 0000000..4318290
--- /dev/null
+++ b/core/java/android/view/RemotableViewMethod.java
@@ -0,0 +1,35 @@
+/*
+ * Copyright (C) 2007 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 java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+/**
+ * @hide
+ * This annotation indicates that a method on a subclass of View
+ * is alllowed to be used with the {@link android.widget.RemoteViews} mechanism.
+ */
+@Target({ ElementType.METHOD })
+@Retention(RetentionPolicy.RUNTIME)
+public @interface RemotableViewMethod {
+}
+
+
+
diff --git a/core/java/android/view/SoundEffectConstants.java b/core/java/android/view/SoundEffectConstants.java
new file mode 100644
index 0000000..4a77af4
--- /dev/null
+++ b/core/java/android/view/SoundEffectConstants.java
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+/**
+ * Constants to be used to play sound effects via {@link View#playSoundEffect(int)} 
+ */
+public class SoundEffectConstants {
+
+    private SoundEffectConstants() {}
+
+    public static final int CLICK = 0;
+
+    public static final int NAVIGATION_LEFT = 1;
+    public static final int NAVIGATION_UP = 2;
+    public static final int NAVIGATION_RIGHT = 3;
+    public static final int NAVIGATION_DOWN = 4;
+
+    /**
+     * Get the sonification constant for the focus directions.
+     * @param direction One of {@link View#FOCUS_UP}, {@link View#FOCUS_DOWN},
+     *     {@link View#FOCUS_LEFT}, {@link View#FOCUS_RIGHT}, {@link View#FOCUS_FORWARD}
+     *     or {@link View#FOCUS_BACKWARD}
+
+     * @return The appropriate sonification constant.
+     */
+    public static int getContantForFocusDirection(int direction) {
+        switch (direction) {
+            case View.FOCUS_RIGHT:
+                return SoundEffectConstants.NAVIGATION_RIGHT;
+            case View.FOCUS_FORWARD:
+            case View.FOCUS_DOWN:
+                return SoundEffectConstants.NAVIGATION_DOWN;
+            case View.FOCUS_LEFT:
+                return SoundEffectConstants.NAVIGATION_LEFT;
+            case View.FOCUS_BACKWARD:
+            case View.FOCUS_UP:
+                return SoundEffectConstants.NAVIGATION_UP;
+        }
+        throw new IllegalArgumentException("direction must be one of "
+                + "{FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, FOCUS_RIGHT, FOCUS_FORWARD, FOCUS_BACKWARD}.");
+    }
+}
diff --git a/core/java/android/view/SubMenu.java b/core/java/android/view/SubMenu.java
new file mode 100644
index 0000000..e981486
--- /dev/null
+++ b/core/java/android/view/SubMenu.java
@@ -0,0 +1,103 @@
+/*
+ * Copyright (C) 2007 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.graphics.drawable.Drawable;
+
+/**
+ * Subclass of {@link Menu} for sub menus.
+ * <p>
+ * Sub menus do not support item icons, or nested sub menus.
+ */
+
+public interface SubMenu extends Menu {
+    /**
+     * Sets the submenu header's title to the title given in <var>titleRes</var>
+     * resource identifier.
+     * 
+     * @param titleRes The string resource identifier used for the title.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setHeaderTitle(int titleRes);
+
+    /**
+     * Sets the submenu header's title to the title given in <var>title</var>.
+     * 
+     * @param title The character sequence used for the title.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setHeaderTitle(CharSequence title);
+    
+    /**
+     * Sets the submenu header's icon to the icon given in <var>iconRes</var>
+     * resource id.
+     * 
+     * @param iconRes The resource identifier used for the icon.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setHeaderIcon(int iconRes);
+
+    /**
+     * Sets the submenu header's icon to the icon given in <var>icon</var>
+     * {@link Drawable}.
+     * 
+     * @param icon The {@link Drawable} used for the icon.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setHeaderIcon(Drawable icon);
+    
+    /**
+     * Sets the header of the submenu to the {@link View} given in
+     * <var>view</var>. This replaces the header title and icon (and those
+     * replace this).
+     * 
+     * @param view The {@link View} used for the header.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setHeaderView(View view);
+    
+    /**
+     * Clears the header of the submenu.
+     */
+    public void clearHeader();
+    
+    /**
+     * Change the icon associated with this submenu's item in its parent menu.
+     * 
+     * @see MenuItem#setIcon(int)
+     * @param iconRes The new icon (as a resource ID) to be displayed.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setIcon(int iconRes);
+    
+    /**
+     * Change the icon associated with this submenu's item in its parent menu.
+     * 
+     * @see MenuItem#setIcon(Drawable)
+     * @param icon The new icon (as a Drawable) to be displayed.
+     * @return This SubMenu so additional setters can be called.
+     */
+    public SubMenu setIcon(Drawable icon);
+    
+    /**
+     * Gets the {@link MenuItem} that represents this submenu in the parent
+     * menu.  Use this for setting additional item attributes.
+     * 
+     * @return The {@link MenuItem} that launches the submenu when invoked.
+     */
+    public MenuItem getItem();
+}
diff --git a/core/java/android/view/Surface.aidl b/core/java/android/view/Surface.aidl
new file mode 100644
index 0000000..90bf37a
--- /dev/null
+++ b/core/java/android/view/Surface.aidl
@@ -0,0 +1,20 @@
+/* //device/java/android/android/view/Surface.aidl
+**
+** Copyright 2007, 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;
+
+parcelable Surface;
diff --git a/core/java/android/view/Surface.java b/core/java/android/view/Surface.java
new file mode 100644
index 0000000..54ccf33
--- /dev/null
+++ b/core/java/android/view/Surface.java
@@ -0,0 +1,298 @@
+/*
+ * Copyright (C) 2007 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.graphics.*;
+import android.os.Parcelable;
+import android.os.Parcel;
+import android.util.Log;
+
+/**
+ * Handle on to a raw buffer that is being managed by the screen compositor.
+ */
+public class Surface implements Parcelable {
+    private static final String LOG_TAG = "Surface";
+    
+    /* flags used in constructor (keep in sync with ISurfaceComposer.h) */
+
+    /** Surface is created hidden */
+    public static final int HIDDEN              = 0x00000004;
+
+    /** The surface is to be used by hardware accelerators or DMA engines */
+    public static final int HARDWARE            = 0x00000010;
+
+    /** Implies "HARDWARE", the surface is to be used by the GPU
+     * additionally the backbuffer is never preserved for these
+     * surfaces. */
+    public static final int GPU                 = 0x00000028;
+
+    /** The surface contains secure content, special measures will
+     * be taken to disallow the surface's content to be copied from
+     * another process. In particular, screenshots and VNC servers will
+     * be disabled, but other measures can take place, for instance the
+     * surface might not be hardware accelerated. */
+    public static final int SECURE              = 0x00000080;
+    
+    /** Creates a surface where color components are interpreted as 
+     *  "non pre-multiplied" by their alpha channel. Of course this flag is
+     *  meaningless for surfaces without an alpha channel. By default
+     *  surfaces are pre-multiplied, which means that each color component is
+     *  already multiplied by its alpha value. In this case the blending
+     *  equation used is:
+     *  
+     *    DEST = SRC + DEST * (1-SRC_ALPHA)
+     *    
+     *  By contrast, non pre-multiplied surfaces use the following equation:
+     *  
+     *    DEST = SRC * SRC_ALPHA * DEST * (1-SRC_ALPHA)
+     *    
+     *  pre-multiplied surfaces must always be used if transparent pixels are
+     *  composited on top of each-other into the surface. A pre-multiplied
+     *  surface can never lower the value of the alpha component of a given
+     *  pixel.
+     *  
+     *  In some rare situations, a non pre-multiplied surface is preferable.
+     *  
+     */
+    public static final int NON_PREMULTIPLIED   = 0x00000100;
+    
+    /**
+     * Creates a surface without a rendering buffer. Instead, the content
+     * of the surface must be pushed by an external entity. This is type
+     * of surface can be used for efficient camera preview or movie
+     * play back.
+     */
+    public static final int PUSH_BUFFERS        = 0x00000200;
+    
+    /** Creates a normal surface. This is the default */
+    public static final int FX_SURFACE_NORMAL   = 0x00000000;
+    
+    /** Creates a Blur surface. Everything behind this surface is blurred
+     * by some amount. The quality and refresh speed of the blur effect
+     * is not settable or guaranteed.
+     * It is an error to lock a Blur surface, since it doesn't have
+     * a backing store.
+     */
+    public static final int FX_SURFACE_BLUR     = 0x00010000;
+    
+    /** Creates a Dim surface. Everything behind this surface is dimmed
+     * by the amount specified in setAlpha(). 
+     * It is an error to lock a Dim surface, since it doesn't have
+     * a backing store.
+     */
+    public static final int FX_SURFACE_DIM     = 0x00020000;
+
+    /** Mask used for FX values above */
+    public static final int FX_SURFACE_MASK     = 0x000F0000;
+
+    /* flags used with setFlags() (keep in sync with ISurfaceComposer.h) */
+    
+    /** Hide the surface. Equivalent to calling hide() */
+    public static final int SURFACE_HIDDEN    = 0x01;
+    
+    /** Freeze the surface. Equivalent to calling freeze() */ 
+    public static final int SURACE_FROZEN     = 0x02;
+    
+    /** Enable dithering when compositing this surface */
+    public static final int SURFACE_DITHER    = 0x04;
+
+    public static final int SURFACE_BLUR_FREEZE= 0x10;
+
+    /* orientations for setOrientation() */
+    public static final int ROTATION_0       = 0;
+    public static final int ROTATION_90      = 1;
+    public static final int ROTATION_180     = 2;
+    public static final int ROTATION_270     = 3;
+    
+    @SuppressWarnings("unused")
+    private int mSurface;
+    @SuppressWarnings("unused")
+    private int mSaveCount;
+    @SuppressWarnings("unused")
+    private Canvas mCanvas;
+
+    /**
+     * Exception thrown when a surface couldn't be created or resized
+     */
+    public static class OutOfResourcesException extends Exception {
+        public OutOfResourcesException() {
+        }
+        public OutOfResourcesException(String name) {
+            super(name);
+        }
+    }
+
+    /*
+     * We use a class initializer to allow the native code to cache some
+     * field offsets.
+     */
+    native private static void nativeClassInit();
+    static { nativeClassInit(); }
+
+    
+    /**
+     * create a surface
+     * {@hide}
+     */
+    public Surface(SurfaceSession s,
+            int pid, int display, int w, int h, int format, int flags)
+        throws OutOfResourcesException {
+        mCanvas = new Canvas();
+        init(s,pid,display,w,h,format,flags);
+    }
+
+    /**
+     * Create an empty surface, which will later be filled in by
+     * readFromParcel().
+     * {@hide}
+     */
+    public Surface() {
+        mCanvas = new Canvas();
+    }
+    
+    /**
+     * Copy another surface to this one.  This surface now holds a reference
+     * to the same data as the original surface, and is -not- the owner.
+     * {@hide}
+     */
+    public native   void copyFrom(Surface o);
+    
+    /**
+     * Does this object hold a valid surface?  Returns true if it holds
+     * a physical surface, so lockCanvas() will succeed.  Otherwise
+     * returns false.
+     */
+    public native   boolean isValid();
+    
+    /** Call this free the surface up. {@hide} */
+    public native   void clear();
+    
+    /** draw into a surface */
+    public Canvas lockCanvas(Rect dirty) throws OutOfResourcesException {
+        /* the dirty rectangle may be expanded to the surface's size, if
+         * for instance it has been resized or if the bits were lost, since
+         * the last call.
+         */
+        return lockCanvasNative(dirty);
+    }
+
+    private native Canvas lockCanvasNative(Rect dirty);
+
+    /** unlock the surface and asks a page flip */
+    public native   void unlockCanvasAndPost(Canvas canvas);
+
+    /** 
+     * unlock the surface. the screen won't be updated until
+     * post() or postAll() is called
+     */
+    public native   void unlockCanvas(Canvas canvas);
+    
+    /** start/end a transaction {@hide} */
+    public static native   void openTransaction();
+    /** {@hide} */
+    public static native   void closeTransaction();
+
+    /**
+     * Freezes the specified display, No updating of the screen will occur
+     * until unfreezeDisplay() is called. Everything else works as usual though,
+     * in particular transactions.
+     * @param display
+     * {@hide}
+     */
+    public static native   void freezeDisplay(int display);
+
+    /**
+     * resume updating the specified display.
+     * @param display
+     * {@hide}
+     */
+    public static native   void unfreezeDisplay(int display);
+
+    /**
+     * set the orientation of the given display.
+     * @param display
+     * @param orientation
+     */
+    public static native   void setOrientation(int display, int orientation);
+
+    /**
+     * set surface parameters.
+     * needs to be inside open/closeTransaction block
+     */
+    public native   void setLayer(int zorder);
+    public native   void setPosition(int x, int y);
+    public native   void setSize(int w, int h);
+
+    public native   void hide();
+    public native   void show();
+    public native   void setTransparentRegionHint(Region region);
+    public native   void setAlpha(float alpha);
+    public native   void setMatrix(float dsdx, float dtdx,
+                                   float dsdy, float dtdy);
+
+    public native   void freeze();
+    public native   void unfreeze();
+
+    public native   void setFreezeTint(int tint);
+
+    public native   void setFlags(int flags, int mask);
+
+    @Override
+    public String toString() {
+        return "Surface(native-token=" + mSurface + ")";
+    }
+
+    private Surface(Parcel source) throws OutOfResourcesException {
+        init(source);
+    }
+    
+    public int describeContents() {
+        return 0;
+    }
+
+    public native   void readFromParcel(Parcel source);
+    public native   void writeToParcel(Parcel dest, int flags);
+
+    public static final Parcelable.Creator<Surface> CREATOR
+            = new Parcelable.Creator<Surface>()
+    {
+        public Surface createFromParcel(Parcel source) {
+            try {
+                return new Surface(source);
+            } catch (Exception e) {
+                Log.e(LOG_TAG, "Exception creating surface from parcel", e);
+            }
+            return null;
+        }
+
+        public Surface[] newArray(int size) {
+            return new Surface[size];
+        }
+    };
+
+    /* no user serviceable parts here ... */
+    @Override
+    protected void finalize() throws Throwable {
+        clear();
+    }
+    
+    private native void init(SurfaceSession s,
+            int pid, int display, int w, int h, int format, int flags)
+            throws OutOfResourcesException;
+
+    private native void init(Parcel source);
+}
diff --git a/core/java/android/view/SurfaceHolder.java b/core/java/android/view/SurfaceHolder.java
new file mode 100644
index 0000000..3d0dda3
--- /dev/null
+++ b/core/java/android/view/SurfaceHolder.java
@@ -0,0 +1,284 @@
+/*
+ * Copyright (C) 2006 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.graphics.Canvas;
+import android.graphics.Rect;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_NORMAL;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_HARDWARE;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_GPU;
+import static android.view.WindowManager.LayoutParams.MEMORY_TYPE_PUSH_BUFFERS;
+
+/**
+ * Abstract interface to someone holding a display surface.  Allows you to
+ * control the surface size and format, edit the pixels in the surface, and
+ * monitor changes to the surface.  This interface is typically available
+ * through the {@link SurfaceView} class.
+ * 
+ * <p>When using this interface from a thread different than the one running
+ * its {@link SurfaceView}, you will want to carefully read the
+ * {@link #lockCanvas} and {@link Callback#surfaceCreated Callback.surfaceCreated}.
+ */
+public interface SurfaceHolder {
+    /**
+     * Surface type.
+     * 
+     * @see #SURFACE_TYPE_NORMAL
+     * @see #SURFACE_TYPE_HARDWARE
+     * @see #SURFACE_TYPE_GPU
+     * @see #SURFACE_TYPE_PUSH_BUFFERS
+     */
+    
+    /** Surface type: creates a regular surface, usually in main, non
+     * contiguous, cached/buffered RAM. */
+    public static final int SURFACE_TYPE_NORMAL = MEMORY_TYPE_NORMAL;
+    /** Surface type: creates a suited to be used with DMA engines and
+     * hardware accelerators. */
+    public static final int SURFACE_TYPE_HARDWARE = MEMORY_TYPE_HARDWARE;
+    /** Surface type: creates a surface suited to be used with the GPU */
+    public static final int SURFACE_TYPE_GPU = MEMORY_TYPE_GPU;
+    /** Surface type: creates a "push" surface, that is a surface that 
+     * doesn't owns its buffers. With such a surface lockCanvas will fail. */
+    public static final int SURFACE_TYPE_PUSH_BUFFERS = MEMORY_TYPE_PUSH_BUFFERS;
+
+    /**
+     * Exception that is thrown from {@link #lockCanvas} when called on a Surface
+     * whose is SURFACE_TYPE_PUSH_BUFFERS.
+     */
+    public static class BadSurfaceTypeException extends RuntimeException {
+        public BadSurfaceTypeException() {
+        }
+
+        public BadSurfaceTypeException(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * A client may implement this interface to receive information about
+     * changes to the surface.  When used with a {@link SurfaceView}, the
+     * Surface being held is only available between calls to
+     * {@link #surfaceCreated(SurfaceHolder)} and
+     * {@link #surfaceDestroyed(SurfaceHolder).  The Callback is set with
+     * {@link SurfaceHolder#addCallback SurfaceHolder.addCallback} method.
+     */
+    public interface Callback {
+        /**
+         * This is called immediately after the surface is first created.
+         * Implementations of this should start up whatever rendering code
+         * they desire.  Note that only one thread can ever draw into
+         * a {@link Surface}, so you should not draw into the Surface here
+         * if your normal rendering will be in another thread.
+         * 
+         * @param holder The SurfaceHolder whose surface is being created.
+         */
+        public void surfaceCreated(SurfaceHolder holder);
+
+        /**
+         * This is called immediately after any structural changes (format or
+         * size) have been made to the surface.  You should at this point update
+         * the imagery in the surface.  This method is always called at least
+         * once, after {@link #surfaceCreated}.
+         * 
+         * @param holder The SurfaceHolder whose surface has changed.
+         * @param format The new PixelFormat of the surface.
+         * @param width The new width of the surface.
+         * @param height The new height of the surface.
+         */
+        public void surfaceChanged(SurfaceHolder holder, int format, int width,
+                int height);
+
+        /**
+         * This is called immediately before a surface is being destroyed. After
+         * returning from this call, you should no longer try to access this
+         * surface.  If you have a rendering thread that directly accesses
+         * the surface, you must ensure that thread is no longer touching the 
+         * Surface before returning from this function.
+         * 
+         * @param holder The SurfaceHolder whose surface is being destroyed.
+         */
+        public void surfaceDestroyed(SurfaceHolder holder);
+    }
+
+    /**
+     * Add a Callback interface for this holder.  There can several Callback
+     * interfaces associated to a holder.
+     * 
+     * @param callback The new Callback interface.
+     */
+    public void addCallback(Callback callback);
+
+    /**
+     * Removes a previously added Callback interface from this holder.
+     * 
+     * @param callback The Callback interface to remove.
+     */
+    public void removeCallback(Callback callback);
+
+    /**
+     * Use this method to find out if the surface is in the process of being
+     * created from Callback methods. This is intended to be used with
+     * {@link Callback#surfaceChanged}.
+     * 
+     * @return true if the surface is in the process of being created.
+     */
+    public boolean isCreating();
+    
+    /**
+     * Sets the surface's type. Surfaces intended to be used with OpenGL ES
+     * should be of SURFACE_TYPE_GPU, surfaces accessed by DMA engines and
+     * hardware accelerators should be of type SURFACE_TYPE_HARDWARE.
+     * Failing to set the surface's type appropriately could result in 
+     * degraded performance or failure. 
+     * 
+     * @param type The surface's memory type.
+     */
+    public void setType(int type);
+
+    /**
+     * Make the surface a fixed size.  It will never change from this size.
+     * When working with a {link SurfaceView}, this must be called from the
+     * same thread running the SurfaceView's window.
+     * 
+     * @param width The surface's width.
+     * @param height The surface's height.
+     */
+    public void setFixedSize(int width, int height);
+
+    /**
+     * Allow the surface to resized based on layout of its container (this is
+     * the default).  When this is enabled, you should monitor
+     * {@link Callback#surfaceChanged} for changes to the size of the surface.
+     * When working with a {link SurfaceView}, this must be called from the
+     * same thread running the SurfaceView's window.
+     */
+    public void setSizeFromLayout();
+
+    /**
+     * Set the desired PixelFormat of the surface.  The default is OPAQUE.
+     * When working with a {link SurfaceView}, this must be called from the
+     * same thread running the SurfaceView's window.
+     * 
+     * @param format A constant from PixelFormat.
+     * 
+     * @see android.graphics.PixelFormat
+     */
+    public void setFormat(int format);
+
+    /**
+     * Enable or disable option to keep the screen turned on while this
+     * surface is displayed.  The default is false, allowing it to turn off.
+     * Enabling the option effectivelty.
+     * This is safe to call from any thread.
+     * 
+     * @param screenOn Supply to true to force the screen to stay on, false
+     * to allow it to turn off.
+     */
+    public void setKeepScreenOn(boolean screenOn);
+    
+    /**
+     * Start editing the pixels in the surface.  The returned Canvas can be used
+     * to draw into the surface's bitmap.  A null is returned if the surface has
+     * not been created or otherwise can not be edited.  You will usually need
+     * to implement {@link Callback#surfaceCreated Callback.surfaceCreated}
+     * to find out when the Surface is available for use.
+     * 
+     * <p>The content of the Surface is never preserved between unlockCanvas() and
+     * lockCanvas(), for this reason, every pixel within the Surface area
+     * must be written. The only exception to this rule is when a dirty
+     * rectangle is specified, in which case, non dirty pixels will be
+     * preserved.
+     * 
+     * <p>If you call this repeatedly when the Surface is not ready (before
+     * {@link Callback#surfaceCreated Callback.surfaceCreated} or after
+     * {@link Callback#surfaceDestroyed Callback.surfaceDestroyed}), your calls
+     * will be throttled to a slow rate in order to avoid consuming CPU.
+     * 
+     * <p>If null is not returned, this function internally holds a lock until
+     * the corresponding {@link #unlockCanvasAndPost} call, preventing
+     * {@link SurfaceView} from creating, destroying, or modifying the surface
+     * while it is being drawn.  This can be more convenience than accessing
+     * the Surface directly, as you do not need to do special synchronization
+     * with a drawing thread in {@link Callback#surfaceDestroyed
+     * Callback.surfaceDestroyed}.
+     * 
+     * @return Canvas Use to draw into the surface.
+     */
+    public Canvas lockCanvas();
+
+    
+    /**
+     * Just like {@link #lockCanvas()} but allows to specify a dirty rectangle.
+     * Every
+     * pixel within that rectangle must be written; however pixels outside
+     * the dirty rectangle will be preserved by the next call to lockCanvas().
+     * 
+     * @see android.view.SurfaceHolder#lockCanvas
+     * 
+     * @param dirty Area of the Surface that will be modified.
+     * @return Canvas Use to draw into the surface.
+     */
+    public Canvas lockCanvas(Rect dirty);
+
+    /**
+     * Finish editing pixels in the surface.  After this call, the surface's
+     * current pixels will be shown on the screen, but its content is lost,
+     * in particular there is no guarantee that the content of the Surface
+     * will remain unchanged when lockCanvas() is called again.
+     * 
+     * @see #lockCanvas()
+     *
+     * @param canvas The Canvas previously returned by lockCanvas().
+     */
+    public void unlockCanvasAndPost(Canvas canvas);
+
+    /**
+     * Retrieve the current size of the surface.  Note: do not modify the
+     * returned Rect.  This is only safe to call from the thread of
+     * {@link SurfaceView}'s window, or while inside of
+     * {@link #lockCanvas()}.
+     * 
+     * @return Rect The surface's dimensions.  The left and top are always 0.
+     */
+    public Rect getSurfaceFrame();
+
+    /**
+     * Direct access to the surface object.  The Surface may not always be
+     * available -- for example when using a {@link SurfaceView} the holder's
+     * Surface is not created until the view has been attached to the window
+     * manager and performed a layout in order to determine the dimensions
+     * and screen position of the Surface.    You will thus usually need
+     * to implement {@link Callback#surfaceCreated Callback.surfaceCreated}
+     * to find out when the Surface is available for use.
+     * 
+     * <p>Note that if you directly access the Surface from another thread,
+     * it is critical that you correctly implement
+     * {@link Callback#surfaceCreated Callback.surfaceCreated} and
+     * {@link Callback#surfaceDestroyed Callback.surfaceDestroyed} to ensure
+     * that thread only accesses the Surface while it is valid, and that the
+     * Surface does not get destroyed while the thread is using it.
+     * 
+     * <p>This method is intended to be used by frameworks which often need
+     * direct access to the Surface object (usually to pass it to native code).
+     * When designing APIs always use SurfaceHolder to pass surfaces around
+     * as opposed to the Surface object itself. A rule of thumb is that
+     * application code should never have to call this method.
+     * 
+     * @return Surface The surface.
+     */
+    public Surface getSurface();
+}
diff --git a/core/java/android/view/SurfaceSession.java b/core/java/android/view/SurfaceSession.java
new file mode 100644
index 0000000..2a04675
--- /dev/null
+++ b/core/java/android/view/SurfaceSession.java
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2006 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;
+
+
+/**
+ * An instance of this class represents a connection to the surface
+ * flinger, in which you can create one or more Surface instances that will
+ * be composited to the screen.
+ * {@hide}
+ */
+public class SurfaceSession {
+    /** Create a new connection with the surface flinger. */
+    public SurfaceSession() {
+        init();
+    }
+
+    /** Forcibly detach native resources associated with this object.
+     *  Unlike destroy(), after this call any surfaces that were created
+     *  from the session will no longer work. The session itself is destroyed.
+     */
+    public native void kill();
+
+    /* no user serviceable parts here ... */
+    @Override
+    protected void finalize() throws Throwable {
+        destroy();
+    }
+    
+    private native void init();
+    private native void destroy();
+    
+    private int mClient;
+}
+
diff --git a/core/java/android/view/SurfaceView.java b/core/java/android/view/SurfaceView.java
new file mode 100644
index 0000000..e928998
--- /dev/null
+++ b/core/java/android/view/SurfaceView.java
@@ -0,0 +1,614 @@
+/*
+ * Copyright (C) 2006 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.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.Handler;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.ParcelFileDescriptor;
+import android.util.AttributeSet;
+import android.util.Config;
+import android.util.Log;
+import java.util.ArrayList;
+
+import java.util.concurrent.locks.ReentrantLock;
+import java.lang.ref.WeakReference;
+
+/**
+ * Provides a dedicated drawing surface embedded inside of a view hierarchy.
+ * You can control the format of this surface and, if you like, its size; the
+ * SurfaceView takes care of placing the surface at the correct location on the
+ * screen
+ * 
+ * <p>The surface is Z ordered so that it is behind the window holding its
+ * SurfaceView; the SurfaceView punches a hole in its window to allow its
+ * surface to be displayed.  The view hierarchy will take care of correctly
+ * compositing with the Surface any siblings of the SurfaceView that would
+ * normally appear on top of it.  This can be used to place overlays such as
+ * buttons on top of the Surface, though note however that it can have an
+ * impact on performance since a full alpha-blended composite will be performed
+ * each time the Surface changes.
+ * 
+ * <p>Access to the underlying surface is provided via the SurfaceHolder interface,
+ * which can be retrieved by calling {@link #getHolder}.
+ * 
+ * <p>The Surface will be created for you while the SurfaceView's window is
+ * visible; you should implement {@link SurfaceHolder.Callback#surfaceCreated}
+ * and {@link SurfaceHolder.Callback#surfaceDestroyed} to discover when the
+ * Surface is created and destroyed as the window is shown and hidden.
+ * 
+ * <p>One of the purposes of this class is to provide a surface in which a
+ * secondary thread can render in to the screen.  If you are going to use it
+ * this way, you need to be aware of some threading semantics:
+ * 
+ * <ul>
+ * <li> All SurfaceView and
+ * {@link SurfaceHolder.Callback SurfaceHolder.Callback} methods will be called
+ * from the thread running the SurfaceView's window (typically the main thread
+ * of the application).  They thus need to correctly synchronize with any
+ * state that is also touched by the drawing thread.
+ * <li> You must ensure that the drawing thread only touches the underlying
+ * Surface while it is valid -- between
+ * {@link SurfaceHolder.Callback#surfaceCreated SurfaceHolder.Callback.surfaceCreated()}
+ * and
+ * {@link SurfaceHolder.Callback#surfaceDestroyed SurfaceHolder.Callback.surfaceDestroyed()}.
+ * </ul>
+ */
+public class SurfaceView extends View {
+    static private final String TAG = "SurfaceView";
+    static private final boolean DEBUG = false;
+    static private final boolean localLOGV = DEBUG ? true : Config.LOGV;
+
+    final ArrayList<SurfaceHolder.Callback> mCallbacks
+            = new ArrayList<SurfaceHolder.Callback>();
+
+    final int[] mLocation = new int[2];
+    
+    final ReentrantLock mSurfaceLock = new ReentrantLock();
+    final Surface mSurface = new Surface();
+    boolean mDrawingStopped = true;
+
+    final WindowManager.LayoutParams mLayout
+            = new WindowManager.LayoutParams();
+    IWindowSession mSession;
+    MyWindow mWindow;
+    final Rect mVisibleInsets = new Rect();
+    final Rect mWinFrame = new Rect();
+    final Rect mContentInsets = new Rect();
+
+    static final int KEEP_SCREEN_ON_MSG = 1;
+    static final int GET_NEW_SURFACE_MSG = 2;
+    
+    boolean mIsCreating = false;
+
+    final Handler mHandler = new Handler() {
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case KEEP_SCREEN_ON_MSG: {
+                    setKeepScreenOn(msg.arg1 != 0);
+                } break;
+                case GET_NEW_SURFACE_MSG: {
+                    handleGetNewSurface();
+                } break;
+            }
+        }
+    };
+    
+    boolean mRequestedVisible = false;
+    int mRequestedWidth = -1;
+    int mRequestedHeight = -1;
+    int mRequestedFormat = PixelFormat.OPAQUE;
+    int mRequestedType = -1;
+
+    boolean mHaveFrame = false;
+    boolean mDestroyReportNeeded = false;
+    boolean mNewSurfaceNeeded = false;
+    long mLastLockTime = 0;
+    
+    boolean mVisible = false;
+    int mLeft = -1;
+    int mTop = -1;
+    int mWidth = -1;
+    int mHeight = -1;
+    int mFormat = -1;
+    int mType = -1;
+    final Rect mSurfaceFrame = new Rect();
+
+    public SurfaceView(Context context) {
+        super(context);
+        setWillNotDraw(true);
+    }
+    
+    public SurfaceView(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setWillNotDraw(true);
+    }
+
+    public SurfaceView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        setWillNotDraw(true);
+    }
+    
+    /**
+     * Return the SurfaceHolder providing access and control over this
+     * SurfaceView's underlying surface.
+     * 
+     * @return SurfaceHolder The holder of the surface.
+     */
+    public SurfaceHolder getHolder() {
+        return mSurfaceHolder;
+    }
+
+    @Override
+    protected void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        mParent.requestTransparentRegion(this);
+        mSession = getWindowSession();
+        mLayout.token = getWindowToken();
+        mLayout.setTitle("SurfaceView");
+    }
+
+    @Override
+    protected void onWindowVisibilityChanged(int visibility) {
+        super.onWindowVisibilityChanged(visibility);
+        mRequestedVisible = visibility == VISIBLE;
+        updateWindow(false);
+    }
+    
+    @Override
+    protected void onDetachedFromWindow() {
+        mRequestedVisible = false;
+        updateWindow(false);
+        mHaveFrame = false;
+        if (mWindow != null) {
+            try {
+                mSession.remove(mWindow);
+            } catch (RemoteException ex) {
+            }
+            mWindow = null;
+        }
+        mSession = null;
+        mLayout.token = null;
+
+        super.onDetachedFromWindow();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        int width = getDefaultSize(mRequestedWidth, widthMeasureSpec);
+        int height = getDefaultSize(mRequestedHeight, heightMeasureSpec);
+        setMeasuredDimension(width, height);
+    }
+    
+    @Override
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        super.onScrollChanged(l, t, oldl, oldt);
+        updateWindow(false);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+        updateWindow(false);
+    }
+
+    @Override
+    public boolean gatherTransparentRegion(Region region) {
+        boolean opaque = true;
+        if ((mPrivateFlags & SKIP_DRAW) == 0) {
+            // this view draws, remove it from the transparent region
+            opaque = super.gatherTransparentRegion(region);
+        } else if (region != null) {
+            int w = getWidth();
+            int h = getHeight();
+            if (w>0 && h>0) {
+                getLocationInWindow(mLocation);
+                // otherwise, punch a hole in the whole hierarchy
+                int l = mLocation[0];
+                int t = mLocation[1];
+                region.op(l, t, l+w, t+h, Region.Op.UNION);
+            }
+        }
+        if (PixelFormat.formatHasAlpha(mRequestedFormat)) {
+            opaque = false;
+        }
+        return opaque;
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+        // draw() is not called when SKIP_DRAW is set
+        if ((mPrivateFlags & SKIP_DRAW) == 0) {
+            // punch a whole in the view-hierarchy below us
+            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        }
+        super.draw(canvas);
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        // if SKIP_DRAW is cleared, draw() has already punched a hole
+        if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+            // punch a whole in the view-hierarchy below us
+            canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+        }
+        // reposition ourselves where the surface is 
+        mHaveFrame = true;
+        updateWindow(false);
+        super.dispatchDraw(canvas);
+    }
+
+    private void updateWindow(boolean force) {
+        if (!mHaveFrame) {
+            return;
+        }
+        
+        int myWidth = mRequestedWidth;
+        if (myWidth <= 0) myWidth = getWidth();
+        int myHeight = mRequestedHeight;
+        if (myHeight <= 0) myHeight = getHeight();
+        
+        getLocationInWindow(mLocation);
+        final boolean creating = mWindow == null;
+        final boolean formatChanged = mFormat != mRequestedFormat;
+        final boolean sizeChanged = mWidth != myWidth || mHeight != myHeight;
+        final boolean visibleChanged = mVisible != mRequestedVisible
+                || mNewSurfaceNeeded;
+        final boolean typeChanged = mType != mRequestedType;
+        if (force || creating || formatChanged || sizeChanged || visibleChanged
+            || typeChanged || mLeft != mLocation[0] || mTop != mLocation[1]) {
+
+            if (localLOGV) Log.i(TAG, "Changes: creating=" + creating
+                    + " format=" + formatChanged + " size=" + sizeChanged
+                    + " visible=" + visibleChanged
+                    + " left=" + (mLeft != mLocation[0])
+                    + " top=" + (mTop != mLocation[1]));
+            
+            try {
+                final boolean visible = mVisible = mRequestedVisible;
+                mLeft = mLocation[0];
+                mTop = mLocation[1];
+                mWidth = myWidth;
+                mHeight = myHeight;
+                mFormat = mRequestedFormat;
+                mType = mRequestedType;
+
+                mLayout.x = mLeft;
+                mLayout.y = mTop;
+                mLayout.width = getWidth();
+                mLayout.height = getHeight();
+                mLayout.format = mRequestedFormat;
+                mLayout.flags |=WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS
+                              | WindowManager.LayoutParams.FLAG_SCALED
+                              | WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE
+                              | WindowManager.LayoutParams.FLAG_NOT_TOUCHABLE
+                              ;
+
+                mLayout.memoryType = mRequestedType;
+
+                if (mWindow == null) {
+                    mWindow = new MyWindow(this);
+                    mLayout.type = WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA;
+                    mLayout.gravity = Gravity.LEFT|Gravity.TOP;
+                    mSession.add(mWindow, mLayout,
+                            mVisible ? VISIBLE : GONE, mContentInsets);
+                }
+                
+                if (visibleChanged && (!visible || mNewSurfaceNeeded)) {
+                    reportSurfaceDestroyed();
+                }
+                
+                mNewSurfaceNeeded = false;
+                
+                mSurfaceLock.lock();
+                mDrawingStopped = !visible;
+                final int relayoutResult = mSession.relayout(
+                        mWindow, mLayout, mWidth, mHeight,
+                        visible ? VISIBLE : GONE, false, mWinFrame, mContentInsets,
+                        mVisibleInsets, mSurface);
+                if (localLOGV) Log.i(TAG, "New surface: " + mSurface
+                        + ", vis=" + visible + ", frame=" + mWinFrame);
+                mSurfaceFrame.left = 0;
+                mSurfaceFrame.top = 0;
+                mSurfaceFrame.right = mWinFrame.width();
+                mSurfaceFrame.bottom = mWinFrame.height();
+                mSurfaceLock.unlock();
+
+                try {
+                    if (visible) {
+                        mDestroyReportNeeded = true;
+
+                        SurfaceHolder.Callback callbacks[];
+                        synchronized (mCallbacks) {
+                            callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+                            mCallbacks.toArray(callbacks);
+                        }            
+
+                        if (visibleChanged) {
+                            mIsCreating = true;
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceCreated(mSurfaceHolder);
+                            }
+                        }
+                        if (creating || formatChanged || sizeChanged
+                                || visibleChanged) {
+                            for (SurfaceHolder.Callback c : callbacks) {
+                                c.surfaceChanged(mSurfaceHolder, mFormat, mWidth, mHeight);
+                            }
+                        }
+                    }
+                } finally {
+                    mIsCreating = false;
+                    if (creating || (relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                        mSession.finishDrawing(mWindow);
+                    }
+                }
+            } catch (RemoteException ex) {
+            }
+            if (localLOGV) Log.v(
+                TAG, "Layout: x=" + mLayout.x + " y=" + mLayout.y +
+                " w=" + mLayout.width + " h=" + mLayout.height +
+                ", frame=" + mSurfaceFrame);
+        }
+    }
+
+    private void reportSurfaceDestroyed() {
+        if (mDestroyReportNeeded) {
+            mDestroyReportNeeded = false;
+            SurfaceHolder.Callback callbacks[];
+            synchronized (mCallbacks) {
+                callbacks = new SurfaceHolder.Callback[mCallbacks.size()];
+                mCallbacks.toArray(callbacks);
+            }            
+            for (SurfaceHolder.Callback c : callbacks) {
+                c.surfaceDestroyed(mSurfaceHolder);
+            }
+        }
+        super.onDetachedFromWindow();
+    }
+
+    void handleGetNewSurface() {
+        mNewSurfaceNeeded = true;
+        updateWindow(false);
+    }
+
+    private static class MyWindow extends IWindow.Stub {
+        private WeakReference<SurfaceView> mSurfaceView;
+
+        public MyWindow(SurfaceView surfaceView) {
+            mSurfaceView = new WeakReference<SurfaceView>(surfaceView);
+        }
+
+        public void resized(int w, int h, Rect coveredInsets,
+                Rect visibleInsets, boolean reportDraw) {
+            SurfaceView surfaceView = mSurfaceView.get();
+            if (surfaceView != null) {
+                if (localLOGV) Log.v(
+                        "SurfaceView", surfaceView + " got resized: w=" +
+                                w + " h=" + h + ", cur w=" + mCurWidth + " h=" + mCurHeight);
+                synchronized (this) {
+                    if (mCurWidth != w || mCurHeight != h) {
+                        mCurWidth = w;
+                        mCurHeight = h;
+                    }
+                    if (reportDraw) {
+                        try {
+                            surfaceView.mSession.finishDrawing(surfaceView.mWindow);
+                        } catch (RemoteException e) {
+                        }
+                    }
+                }
+            }
+        }
+
+        public void dispatchKey(KeyEvent event) {
+            SurfaceView surfaceView = mSurfaceView.get();
+            if (surfaceView != null) {
+                //Log.w("SurfaceView", "Unexpected key event in surface: " + event);
+                if (surfaceView.mSession != null && surfaceView.mSurface != null) {
+                    try {
+                        surfaceView.mSession.finishKey(surfaceView.mWindow);
+                    } catch (RemoteException ex) {
+                    }
+                }
+            }
+        }
+
+        public void dispatchPointer(MotionEvent event, long eventTime) {
+            Log.w("SurfaceView", "Unexpected pointer event in surface: " + event);
+            //if (mSession != null && mSurface != null) {
+            //    try {
+            //        //mSession.finishKey(mWindow);
+            //    } catch (RemoteException ex) {
+            //    }
+            //}
+        }
+
+        public void dispatchTrackball(MotionEvent event, long eventTime) {
+            Log.w("SurfaceView", "Unexpected trackball event in surface: " + event);
+            //if (mSession != null && mSurface != null) {
+            //    try {
+            //        //mSession.finishKey(mWindow);
+            //    } catch (RemoteException ex) {
+            //    }
+            //}
+        }
+
+        public void dispatchAppVisibility(boolean visible) {
+            // The point of SurfaceView is to let the app control the surface.
+        }
+
+        public void dispatchGetNewSurface() {
+            SurfaceView surfaceView = mSurfaceView.get();
+            if (surfaceView != null) {
+                Message msg = surfaceView.mHandler.obtainMessage(GET_NEW_SURFACE_MSG);
+                surfaceView.mHandler.sendMessage(msg);
+            }
+        }
+
+        public void windowFocusChanged(boolean hasFocus, boolean touchEnabled) {
+            Log.w("SurfaceView", "Unexpected focus in surface: focus=" + hasFocus + ", touchEnabled=" + touchEnabled);
+        }
+
+        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+        }
+
+        int mCurWidth = -1;
+        int mCurHeight = -1;
+    }
+
+    private SurfaceHolder mSurfaceHolder = new SurfaceHolder() {
+        
+        private static final String LOG_TAG = "SurfaceHolder";
+        
+        public boolean isCreating() {
+            return mIsCreating;
+        }
+
+        public void addCallback(Callback callback) {
+            synchronized (mCallbacks) {
+                // This is a linear search, but in practice we'll 
+                // have only a couple callbacks, so it doesn't matter.
+                if (mCallbacks.contains(callback) == false) {      
+                    mCallbacks.add(callback);
+                }
+            }
+        }
+
+        public void removeCallback(Callback callback) {
+            synchronized (mCallbacks) {
+                mCallbacks.remove(callback);
+            }
+        }
+        
+        public void setFixedSize(int width, int height) {
+            if (mRequestedWidth != width || mRequestedHeight != height) {
+                mRequestedWidth = width;
+                mRequestedHeight = height;
+                requestLayout();
+            }
+        }
+
+        public void setSizeFromLayout() {
+            if (mRequestedWidth != -1 || mRequestedHeight != -1) {
+                mRequestedWidth = mRequestedHeight = -1;
+                requestLayout();
+            }
+        }
+
+        public void setFormat(int format) {
+            mRequestedFormat = format;
+            if (mWindow != null) {
+                updateWindow(false);
+            }
+        }
+
+        public void setType(int type) {
+            switch (type) {
+            case SURFACE_TYPE_NORMAL:
+            case SURFACE_TYPE_HARDWARE:
+            case SURFACE_TYPE_GPU:
+            case SURFACE_TYPE_PUSH_BUFFERS:
+                mRequestedType = type;
+                if (mWindow != null) {
+                    updateWindow(false);
+                }
+                break;
+            }
+        }
+
+        public void setKeepScreenOn(boolean screenOn) {
+            Message msg = mHandler.obtainMessage(KEEP_SCREEN_ON_MSG);
+            msg.arg1 = screenOn ? 1 : 0;
+            mHandler.sendMessage(msg);
+        }
+        
+        public Canvas lockCanvas() {
+            return internalLockCanvas(null);
+        }
+
+        public Canvas lockCanvas(Rect dirty) {
+            return internalLockCanvas(dirty);
+        }
+
+        private final Canvas internalLockCanvas(Rect dirty) {
+            if (mType == SURFACE_TYPE_PUSH_BUFFERS) {
+                throw new BadSurfaceTypeException(
+                        "Surface type is SURFACE_TYPE_PUSH_BUFFERS");
+            }
+            mSurfaceLock.lock();
+
+            if (localLOGV) Log.i(TAG, "Locking canvas... stopped="
+                    + mDrawingStopped + ", win=" + mWindow);
+
+            Canvas c = null;
+            if (!mDrawingStopped && mWindow != null) {
+                Rect frame = dirty != null ? dirty : mSurfaceFrame;
+                try {
+                    c = mSurface.lockCanvas(frame);
+                } catch (Exception e) {
+                    Log.e(LOG_TAG, "Exception locking surface", e);
+                }
+            }
+
+            if (localLOGV) Log.i(TAG, "Returned canvas: " + c);
+            if (c != null) {
+                mLastLockTime = SystemClock.uptimeMillis();
+                return c;
+            }
+            
+            // If the Surface is not ready to be drawn, then return null,
+            // but throttle calls to this function so it isn't called more
+            // than every 100ms.
+            long now = SystemClock.uptimeMillis();
+            long nextTime = mLastLockTime + 100;
+            if (nextTime > now) {
+                try {
+                    Thread.sleep(nextTime-now);
+                } catch (InterruptedException e) {
+                }
+                now = SystemClock.uptimeMillis();
+            }
+            mLastLockTime = now;
+            mSurfaceLock.unlock();
+            
+            return null;
+        }
+
+        public void unlockCanvasAndPost(Canvas canvas) {
+            mSurface.unlockCanvasAndPost(canvas);
+            mSurfaceLock.unlock();
+        }
+
+        public Surface getSurface() {
+            return mSurface;
+        }
+
+        public Rect getSurfaceFrame() {
+            return mSurfaceFrame;
+        }
+    };
+}
+
diff --git a/core/java/android/view/TouchDelegate.java b/core/java/android/view/TouchDelegate.java
new file mode 100644
index 0000000..27b49db
--- /dev/null
+++ b/core/java/android/view/TouchDelegate.java
@@ -0,0 +1,153 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+
+/**
+ * Helper class to handle situations where you want a view to have a larger touch area than its
+ * actual view bounds. The view whose touch area is changed is called the delegate view. This
+ * class should be used by an ancestor of the delegate. To use a TouchDelegate, first create an
+ * instance that specifies the bounds that should be mapped to the delegate and the delegate
+ * view itself.
+ * <p>
+ * The ancestor should then forward all of its touch events received in its
+ * {@link android.view.View#onTouchEvent(MotionEvent)} to {@link #onTouchEvent(MotionEvent)}.
+ * </p>
+ */
+public class TouchDelegate {
+    
+    /**
+     * View that should receive forwarded touch events 
+     */
+    private View mDelegateView;
+    
+    /**
+     * Bounds in local coordinates of the containing view that should be mapped to the delegate
+     * view. This rect is used for initial hit testing.
+     */
+    private Rect mBounds;
+    
+    /**
+     * mBounds inflated to include some slop. This rect is to track whether the motion events
+     * should be considered to be be within the delegate view.
+     */
+    private Rect mSlopBounds;
+    
+    /**
+     * True if the delegate had been targeted on a down event (intersected mBounds).
+     */
+    private boolean mDelegateTargeted;
+
+    /**
+     * The touchable region of the View extends above its actual extent.
+     */
+    public static final int ABOVE = 1;
+
+    /**
+     * The touchable region of the View extends below its actual extent.
+     */
+    public static final int BELOW = 2;
+
+    /**
+     * The touchable region of the View extends to the left of its
+     * actual extent.
+     */
+    public static final int TO_LEFT = 4;
+
+    /**
+     * The touchable region of the View extends to the right of its
+     * actual extent.
+     */
+    public static final int TO_RIGHT = 8;
+
+    private int mSlop;
+
+    /**
+     * Constructor
+     * 
+     * @param bounds Bounds in local coordinates of the containing view that should be mapped to
+     *        the delegate view
+     * @param delegateView The view that should receive motion events
+     */
+    public TouchDelegate(Rect bounds, View delegateView) {
+        mBounds = bounds;
+
+        mSlop = ViewConfiguration.get(delegateView.getContext()).getScaledTouchSlop();
+        mSlopBounds = new Rect(bounds);
+        mSlopBounds.inset(-mSlop, -mSlop);
+        mDelegateView = delegateView;
+    }
+
+    /**
+     * Will forward touch events to the delegate view if the event is within the bounds
+     * specified in the constructor.
+     * 
+     * @param event The touch event to forward
+     * @return True if the event was forwarded to the delegate, false otherwise.
+     */
+    public boolean onTouchEvent(MotionEvent event) {
+        int x = (int)event.getX();
+        int y = (int)event.getY();
+        boolean sendToDelegate = false;
+        boolean hit = true;
+        boolean handled = false;
+
+        switch (event.getAction()) {
+        case MotionEvent.ACTION_DOWN:
+            Rect bounds = mBounds;
+
+            if (bounds.contains(x, y)) {
+                mDelegateTargeted = true;
+                sendToDelegate = true;
+            }
+            break;
+        case MotionEvent.ACTION_UP:
+        case MotionEvent.ACTION_MOVE:
+            sendToDelegate = mDelegateTargeted;
+            if (sendToDelegate) {
+                Rect slopBounds = mSlopBounds;
+                if (!slopBounds.contains(x, y)) {
+                    hit = false;
+                }
+            }
+            break;
+        case MotionEvent.ACTION_CANCEL:
+            sendToDelegate = mDelegateTargeted;
+            mDelegateTargeted = false;
+            break;
+        }
+        if (sendToDelegate) {
+            final View delegateView = mDelegateView;
+            
+            if (hit) {
+                // Offset event coordinates to be inside the target view
+                event.setLocation(delegateView.getWidth() / 2, delegateView.getHeight() / 2);
+            } else {
+                // Offset event coordinates to be outside the target view (in case it does
+                // something like tracking pressed state)
+                int slop = mSlop;
+                event.setLocation(-(slop * 2), -(slop * 2));
+            }
+            handled = delegateView.dispatchTouchEvent(event);
+        }
+        return handled;
+    }
+}
diff --git a/core/java/android/view/VelocityTracker.java b/core/java/android/view/VelocityTracker.java
new file mode 100644
index 0000000..c80167e
--- /dev/null
+++ b/core/java/android/view/VelocityTracker.java
@@ -0,0 +1,215 @@
+/*
+ * Copyright (C) 2006 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.util.Config;
+import android.util.Log;
+
+/**
+ * Helper for tracking the velocity of touch events, for implementing
+ * flinging and other such gestures.  Use {@link #obtain} to retrieve a
+ * new instance of the class when you are going to begin tracking, put
+ * the motion events you receive into it with {@link #addMovement(MotionEvent)},
+ * and when you want to determine the velocity call
+ * {@link #computeCurrentVelocity(int)} and then {@link #getXVelocity()}
+ * and {@link #getXVelocity()}.
+ */
+public final class VelocityTracker {
+    static final String TAG = "VelocityTracker";
+    static final boolean DEBUG = false;
+    static final boolean localLOGV = DEBUG || Config.LOGV;
+    
+    static final int NUM_PAST = 10;
+    static final int LONGEST_PAST_TIME = 200;
+    
+    static final VelocityTracker[] mPool = new VelocityTracker[1];
+    
+    final float mPastX[] = new float[NUM_PAST];
+    final float mPastY[] = new float[NUM_PAST];
+    final long mPastTime[] = new long[NUM_PAST];
+   
+    float mYVelocity;
+    float mXVelocity;
+    
+    /**
+     * Retrieve a new VelocityTracker object to watch the velocity of a
+     * motion.  Be sure to call {@link #recycle} when done.  You should
+     * generally only maintain an active object while tracking a movement,
+     * so that the VelocityTracker can be re-used elsewhere.
+     * 
+     * @return Returns a new VelocityTracker.
+     */
+    static public VelocityTracker obtain() {
+        synchronized (mPool) {
+            VelocityTracker vt = mPool[0];
+            if (vt != null) {
+                vt.clear();
+                return vt;
+            }
+            return new VelocityTracker();
+        }
+    }
+    
+    /**
+     * Return a VelocityTracker object back to be re-used by others.  You must
+     * not touch the object after calling this function.
+     */
+    public void recycle() {
+        synchronized (mPool) {
+            mPool[0] = this;
+        }
+    }
+    
+    private VelocityTracker() {
+    }
+    
+    /**
+     * Reset the velocity tracker back to its initial state.
+     */
+    public void clear() {
+        mPastTime[0] = 0;
+    }
+    
+    /**
+     * Add a user's movement to the tracker.  You should call this for the
+     * initial {@link MotionEvent#ACTION_DOWN}, the following
+     * {@link MotionEvent#ACTION_MOVE} events that you receive, and the
+     * final {@link MotionEvent#ACTION_UP}.  You can, however, call this
+     * for whichever events you desire.
+     * 
+     * @param ev The MotionEvent you received and would like to track.
+     */
+    public void addMovement(MotionEvent ev) {
+        long time = ev.getEventTime();
+        final int N = ev.getHistorySize();
+        for (int i=0; i<N; i++) {
+            addPoint(ev.getHistoricalX(i), ev.getHistoricalY(i),
+                    ev.getHistoricalEventTime(i));
+        }
+        addPoint(ev.getX(), ev.getY(), time);
+    }
+
+    private void addPoint(float x, float y, long time) {
+        int drop = -1;
+        int i;
+        if (localLOGV) Log.v(TAG, "Adding past y=" + y + " time=" + time);
+        final long[] pastTime = mPastTime;
+        for (i=0; i<NUM_PAST; i++) {
+            if (pastTime[i] == 0) {
+                break;
+            } else if (pastTime[i] < time-LONGEST_PAST_TIME) {
+                if (localLOGV) Log.v(TAG, "Dropping past too old at "
+                        + i + " time=" + pastTime[i]);
+                drop = i;
+            }
+        }
+        if (localLOGV) Log.v(TAG, "Add index: " + i);
+        if (i == NUM_PAST && drop < 0) {
+            drop = 0;
+        }
+        if (drop == i) drop--;
+        final float[] pastX = mPastX;
+        final float[] pastY = mPastY;
+        if (drop >= 0) {
+            if (localLOGV) Log.v(TAG, "Dropping up to #" + drop);
+            final int start = drop+1;
+            final int count = NUM_PAST-drop-1;
+            System.arraycopy(pastX, start, pastX, 0, count);
+            System.arraycopy(pastY, start, pastY, 0, count);
+            System.arraycopy(pastTime, start, pastTime, 0, count);
+            i -= (drop+1);
+        }
+        pastX[i] = x;
+        pastY[i] = y;
+        pastTime[i] = time;
+        i++;
+        if (i < NUM_PAST) {
+            pastTime[i] = 0;
+        }
+    }
+    
+    /**
+     * Compute the current velocity based on the points that have been
+     * collected.  Only call this when you actually want to retrieve velocity
+     * information, as it is relatively expensive.  You can then retrieve
+     * the velocity with {@link #getXVelocity()} and
+     * {@link #getYVelocity()}.
+     * 
+     * @param units The units you would like the velocity in.  A value of 1
+     * provides pixels per millisecond, 1000 provides pixels per second, etc.
+     */
+    public void computeCurrentVelocity(int units) {
+        final float[] pastX = mPastX;
+        final float[] pastY = mPastY;
+        final long[] pastTime = mPastTime;
+        
+        // Kind-of stupid.
+        final float oldestX = pastX[0];
+        final float oldestY = pastY[0];
+        final long oldestTime = pastTime[0];
+        float accumX = 0;
+        float accumY = 0;
+        int N=0;
+        while (N < NUM_PAST) {
+            if (pastTime[N] == 0) {
+                break;
+            }
+            N++;
+        }
+        // Skip the last received event, since it is probably pretty noisy.
+        if (N > 3) N--;
+        
+        for (int i=1; i < N; i++) {
+            final int dur = (int)(pastTime[i] - oldestTime);
+            if (dur == 0) continue;
+            float dist = pastX[i] - oldestX;
+            float vel = (dist/dur) * units;   // pixels/frame.
+            if (accumX == 0) accumX = vel;
+            else accumX = (accumX + vel) * .5f;
+            
+            dist = pastY[i] - oldestY;
+            vel = (dist/dur) * units;   // pixels/frame.
+            if (accumY == 0) accumY = vel;
+            else accumY = (accumY + vel) * .5f;
+        }
+        mXVelocity = accumX;
+        mYVelocity = accumY;
+        
+        if (localLOGV) Log.v(TAG, "Y velocity=" + mYVelocity +" X velocity="
+                + mXVelocity + " N=" + N);
+    }
+    
+    /**
+     * Retrieve the last computed X velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @return The previously computed X velocity.
+     */
+    public float getXVelocity() {
+        return mXVelocity;
+    }
+    
+    /**
+     * Retrieve the last computed Y velocity.  You must first call
+     * {@link #computeCurrentVelocity(int)} before calling this function.
+     * 
+     * @return The previously computed Y velocity.
+     */
+    public float getYVelocity() {
+        return mYVelocity;
+    }
+}
diff --git a/core/java/android/view/View.java b/core/java/android/view/View.java
new file mode 100644
index 0000000..3e762f5
--- /dev/null
+++ b/core/java/android/view/View.java
@@ -0,0 +1,8076 @@
+/*
+ * Copyright (C) 2006 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.content.res.Resources;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.LinearGradient;
+import android.graphics.Matrix;
+import android.graphics.Paint;
+import android.graphics.PixelFormat;
+import android.graphics.PorterDuff;
+import android.graphics.PorterDuffXfermode;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.Shader;
+import android.graphics.Point;
+import android.graphics.drawable.ColorDrawable;
+import android.graphics.drawable.Drawable;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Message;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.os.RemoteException;
+import android.os.SystemClock;
+import android.os.SystemProperties;
+import android.util.AttributeSet;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.ContextMenu.ContextMenuInfo;
+import android.view.animation.Animation;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.view.inputmethod.EditorInfo;
+import android.widget.ScrollBarDrawable;
+
+import com.android.internal.R;
+import com.android.internal.view.menu.MenuBuilder;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.lang.ref.SoftReference;
+
+/**
+ * <p>
+ * This class represents the basic building block for user interface components. A View
+ * occupies a rectangular area on the screen and is responsible for drawing and
+ * event handling. View is the base class for <em>widgets</em>, which are
+ * used to create interactive UI components (buttons, text fields, etc.). The 
+ * {@link android.view.ViewGroup} subclass is the base class for <em>layouts</em>, which
+ * are invisible containers that hold other Views (or other ViewGroups) and define
+ * their layout properties.
+ * </p>
+ *
+ * <div class="special">
+ * <p>For an introduction to using this class to develop your 
+ * application's user interface, read the Developer Guide documentation on 
+ * <strong><a href="{@docRoot}guide/topics/ui/index.html">User Interface</a></strong>. Special topics
+ * include: 
+ * <br/><a href="{@docRoot}guide/topics/ui/declaring-layout.html">Declaring Layout</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/menus.html">Creating Menus</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/layout-objects.html">Common Layout Objects</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/binding.html">Binding to Data with AdapterView</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/ui-events.html">Handling UI Events</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/themes.html">Applying Styles and Themes</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/custom-components.html">Building Custom Components</a>
+ * <br/><a href="{@docRoot}guide/topics/ui/how-android-draws.html">How Android Draws Views</a>.
+ * </p>
+ * </div>
+ * 
+ * <a name="Using"></a>
+ * <h3>Using Views</h3>
+ * <p>
+ * All of the views in a window are arranged in a single tree. You can add views
+ * either from code or by specifying a tree of views in one or more XML layout
+ * files. There are many specialized subclasses of views that act as controls or
+ * are capable of displaying text, images, or other content.
+ * </p>
+ * <p>
+ * Once you have created a tree of views, there are typically a few types of
+ * common operations you may wish to perform:
+ * <ul>
+ * <li><strong>Set properties:</strong> for example setting the text of a
+ * {@link android.widget.TextView}. The available properties and the methods
+ * that set them will vary among the different subclasses of views. Note that
+ * properties that are known at build time can be set in the XML layout
+ * files.</li>
+ * <li><strong>Set focus:</strong> The framework will handled moving focus in
+ * response to user input. To force focus to a specific view, call
+ * {@link #requestFocus}.</li>
+ * <li><strong>Set up listeners:</strong> Views allow clients to set listeners
+ * that will be notified when something interesting happens to the view. For
+ * example, all views will let you set a listener to be notified when the view
+ * gains or loses focus. You can register such a listener using
+ * {@link #setOnFocusChangeListener}. Other view subclasses offer more
+ * specialized listeners. For example, a Button exposes a listener to notify
+ * clients when the button is clicked.</li>
+ * <li><strong>Set visibility:</strong> You can hide or show views using
+ * {@link #setVisibility}.</li>
+ * </ul>
+ * </p>
+ * <p><em>
+ * Note: The Android framework is responsible for measuring, laying out and
+ * drawing views. You should not call methods that perform these actions on
+ * views yourself unless you are actually implementing a
+ * {@link android.view.ViewGroup}.
+ * </em></p>
+ *
+ * <a name="Lifecycle"></a>
+ * <h3>Implementing a Custom View</h3>
+ *
+ * <p>
+ * To implement a custom view, you will usually begin by providing overrides for
+ * some of the standard methods that the framework calls on all views. You do
+ * not need to override all of these methods. In fact, you can start by just
+ * overriding {@link #onDraw(android.graphics.Canvas)}.
+ * <table border="2" width="85%" align="center" cellpadding="5">
+ *     <thead>
+ *         <tr><th>Category</th> <th>Methods</th> <th>Description</th></tr>
+ *     </thead>
+ *
+ *     <tbody>
+ *     <tr>
+ *         <td rowspan="2">Creation</td>
+ *         <td>Constructors</td>
+ *         <td>There is a form of the constructor that are called when the view
+ *         is created from code and a form that is called when the view is
+ *         inflated from a layout file. The second form should parse and apply
+ *         any attributes defined in the layout file.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onFinishInflate()}</code></td>
+ *         <td>Called after a view and all of its children has been inflated
+ *         from XML.</td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td rowspan="3">Layout</td>
+ *         <td><code>{@link #onMeasure}</code></td>
+ *         <td>Called to determine the size requirements for this view and all
+ *         of its children.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onLayout}</code></td>
+ *         <td>Called when this view should assign a size and position to all
+ *         of its children.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onSizeChanged}</code></td>
+ *         <td>Called when the size of this view has changed.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td>Drawing</td>
+ *         <td><code>{@link #onDraw}</code></td>
+ *         <td>Called when the view should render its content.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td rowspan="4">Event processing</td>
+ *         <td><code>{@link #onKeyDown}</code></td>
+ *         <td>Called when a new key event occurs.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onKeyUp}</code></td>
+ *         <td>Called when a key up event occurs.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onTrackballEvent}</code></td>
+ *         <td>Called when a trackball motion event occurs.
+ *         </td>
+ *     </tr>
+ *     <tr>
+ *         <td><code>{@link #onTouchEvent}</code></td>
+ *         <td>Called when a touch screen motion event occurs.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td rowspan="2">Focus</td>
+ *         <td><code>{@link #onFocusChanged}</code></td>
+ *         <td>Called when the view gains or loses focus.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td><code>{@link #onWindowFocusChanged}</code></td>
+ *         <td>Called when the window containing the view gains or loses focus.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td rowspan="3">Attaching</td>
+ *         <td><code>{@link #onAttachedToWindow()}</code></td>
+ *         <td>Called when the view is attached to a window.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td><code>{@link #onDetachedFromWindow}</code></td>
+ *         <td>Called when the view is detached from its window.
+ *         </td>
+ *     </tr>
+ *
+ *     <tr>
+ *         <td><code>{@link #onWindowVisibilityChanged}</code></td>
+ *         <td>Called when the visibility of the window containing the view
+ *         has changed.
+ *         </td>
+ *     </tr>
+ *     </tbody>
+ *
+ * </table>
+ * </p>
+ *
+ * <a name="IDs"></a>
+ * <h3>IDs</h3>
+ * Views may have an integer id associated with them. These ids are typically
+ * assigned in the layout XML files, and are used to find specific views within
+ * the view tree. A common pattern is to:
+ * <ul>
+ * <li>Define a Button in the layout file and assign it a unique ID.
+ * <pre>
+ * &lt;Button id="@+id/my_button"
+ *     android:layout_width="wrap_content"
+ *     android:layout_height="wrap_content"
+ *     android:text="@string/my_button_text"/&gt;
+ * </pre></li>
+ * <li>From the onCreate method of an Activity, find the Button
+ * <pre class="prettyprint">
+ *      Button myButton = (Button) findViewById(R.id.my_button);
+ * </pre></li>
+ * </ul>
+ * <p>
+ * View IDs need not be unique throughout the tree, but it is good practice to
+ * ensure that they are at least unique within the part of the tree you are
+ * searching.
+ * </p>
+ *
+ * <a name="Position"></a>
+ * <h3>Position</h3>
+ * <p>
+ * The geometry of a view is that of a rectangle. A view has a location,
+ * expressed as a pair of <em>left</em> and <em>top</em> coordinates, and
+ * two dimensions, expressed as a width and a height. The unit for location
+ * and dimensions is the pixel.
+ * </p>
+ *
+ * <p>
+ * It is possible to retrieve the location of a view by invoking the methods
+ * {@link #getLeft()} and {@link #getTop()}. The former returns the left, or X,
+ * coordinate of the rectangle representing the view. The latter returns the
+ * top, or Y, coordinate of the rectangle representing the view. These methods
+ * both return the location of the view relative to its parent. For instance,
+ * when getLeft() returns 20, that means the view is located 20 pixels to the
+ * right of the left edge of its direct parent.
+ * </p>
+ *
+ * <p>
+ * In addition, several convenience methods are offered to avoid unnecessary
+ * computations, namely {@link #getRight()} and {@link #getBottom()}.
+ * These methods return the coordinates of the right and bottom edges of the
+ * rectangle representing the view. For instance, calling {@link #getRight()}
+ * is similar to the following computation: <code>getLeft() + getWidth()</code>
+ * (see <a href="#SizePaddingMargins">Size</a> for more information about the width.)
+ * </p>
+ *
+ * <a name="SizePaddingMargins"></a>
+ * <h3>Size, padding and margins</h3>
+ * <p>
+ * The size of a view is expressed with a width and a height. A view actually
+ * possess two pairs of width and height values.
+ * </p>
+ *
+ * <p>
+ * The first pair is known as <em>measured width</em> and
+ * <em>measured height</em>. These dimensions define how big a view wants to be
+ * within its parent (see <a href="#Layout">Layout</a> for more details.) The
+ * measured dimensions can be obtained by calling {@link #getMeasuredWidth()}
+ * and {@link #getMeasuredHeight()}.
+ * </p>
+ *
+ * <p>
+ * The second pair is simply known as <em>width</em> and <em>height</em>, or
+ * sometimes <em>drawing width</em> and <em>drawing height</em>. These
+ * dimensions define the actual size of the view on screen, at drawing time and
+ * after layout. These values may, but do not have to, be different from the
+ * measured width and height. The width and height can be obtained by calling
+ * {@link #getWidth()} and {@link #getHeight()}.
+ * </p>
+ *
+ * <p>
+ * To measure its dimensions, a view takes into account its padding. The padding
+ * is expressed in pixels for the left, top, right and bottom parts of the view.
+ * Padding can be used to offset the content of the view by a specific amount of
+ * pixels. For instance, a left padding of 2 will push the view's content by
+ * 2 pixels to the right of the left edge. Padding can be set using the
+ * {@link #setPadding(int, int, int, int)} method and queried by calling
+ * {@link #getPaddingLeft()}, {@link #getPaddingTop()},
+ * {@link #getPaddingRight()} and {@link #getPaddingBottom()}.
+ * </p>
+ *
+ * <p>
+ * Even though a view can define a padding, it does not provide any support for
+ * margins. However, view groups provide such a support. Refer to
+ * {@link android.view.ViewGroup} and
+ * {@link android.view.ViewGroup.MarginLayoutParams} for further information.
+ * </p>
+ *
+ * <a name="Layout"></a>
+ * <h3>Layout</h3>
+ * <p>
+ * Layout is a two pass process: a measure pass and a layout pass. The measuring
+ * pass is implemented in {@link #measure(int, int)} and is a top-down traversal
+ * of the view tree. Each view pushes dimension specifications down the tree
+ * during the recursion. At the end of the measure pass, every view has stored
+ * its measurements. The second pass happens in
+ * {@link #layout(int,int,int,int)} and is also top-down. During
+ * this pass each parent is responsible for positioning all of its children
+ * using the sizes computed in the measure pass.
+ * </p>
+ *
+ * <p>
+ * When a view's measure() method returns, its {@link #getMeasuredWidth()} and
+ * {@link #getMeasuredHeight()} values must be set, along with those for all of
+ * that view's descendants. A view's measured width and measured height values
+ * must respect the constraints imposed by the view's parents. This guarantees
+ * that at the end of the measure pass, all parents accept all of their
+ * children's measurements. A parent view may call measure() more than once on
+ * its children. For example, the parent may measure each child once with
+ * unspecified dimensions to find out how big they want to be, then call
+ * measure() on them again with actual numbers if the sum of all the children's
+ * unconstrained sizes is too big or too small.
+ * </p>
+ *
+ * <p>
+ * The measure pass uses two classes to communicate dimensions. The
+ * {@link MeasureSpec} class is used by views to tell their parents how they
+ * want to be measured and positioned. The base LayoutParams class just
+ * describes how big the view wants to be for both width and height. For each
+ * dimension, it can specify one of:
+ * <ul>
+ * <li> an exact number
+ * <li>FILL_PARENT, which means the view wants to be as big as its parent
+ * (minus padding)
+ * <li> WRAP_CONTENT, which means that the view wants to be just big enough to
+ * enclose its content (plus padding).
+ * </ul>
+ * There are subclasses of LayoutParams for different subclasses of ViewGroup.
+ * For example, AbsoluteLayout has its own subclass of LayoutParams which adds
+ * an X and Y value.
+ * </p>
+ *
+ * <p>
+ * MeasureSpecs are used to push requirements down the tree from parent to
+ * child. A MeasureSpec can be in one of three modes:
+ * <ul>
+ * <li>UNSPECIFIED: This is used by a parent to determine the desired dimension
+ * of a child view. For example, a LinearLayout may call measure() on its child
+ * with the height set to UNSPECIFIED and a width of EXACTLY 240 to find out how
+ * tall the child view wants to be given a width of 240 pixels.
+ * <li>EXACTLY: This is used by the parent to impose an exact size on the
+ * child. The child must use this size, and guarantee that all of its
+ * descendants will fit within this size.
+ * <li>AT_MOST: This is used by the parent to impose a maximum size on the
+ * child. The child must gurantee that it and all of its descendants will fit
+ * within this size.
+ * </ul>
+ * </p>
+ *
+ * <p>
+ * To intiate a layout, call {@link #requestLayout}. This method is typically
+ * called by a view on itself when it believes that is can no longer fit within
+ * its current bounds.
+ * </p>
+ *
+ * <a name="Drawing"></a>
+ * <h3>Drawing</h3>
+ * <p>
+ * Drawing is handled by walking the tree and rendering each view that
+ * intersects the the invalid region. Because the tree is traversed in-order,
+ * this means that parents will draw before (i.e., behind) their children, with
+ * siblings drawn in the order they appear in the tree.
+ * If you set a background drawable for a View, then the View will draw it for you
+ * before calling back to its <code>onDraw()</code> method.
+ * </p>
+ *
+ * <p>
+ * Note that the framework will not draw views that are not in the invalid region. 
+ * </p>
+ *
+ * <p>
+ * To force a view to draw, call {@link #invalidate()}.
+ * </p>
+ *
+ * <a name="EventHandlingThreading"></a>
+ * <h3>Event Handling and Threading</h3>
+ * <p>
+ * The basic cycle of a view is as follows:
+ * <ol>
+ * <li>An event comes in and is dispatched to the appropriate view. The view
+ * handles the event and notifies any listeners.</li>
+ * <li>If in the course of processing the event, the view's bounds may need
+ * to be changed, the view will call {@link #requestLayout()}.</li>
+ * <li>Similarly, if in the course of processing the event the view's appearance
+ * may need to be changed, the view will call {@link #invalidate()}.</li>
+ * <li>If either {@link #requestLayout()} or {@link #invalidate()} were called,
+ * the framework will take care of measuring, laying out, and drawing the tree
+ * as appropriate.</li>
+ * </ol>
+ * </p>
+ *
+ * <p><em>Note: The entire view tree is single threaded. You must always be on
+ * the UI thread when calling any method on any view.</em>
+ * If you are doing work on other threads and want to update the state of a view
+ * from that thread, you should use a {@link Handler}.
+ * </p>
+ *
+ * <a name="FocusHandling"></a>
+ * <h3>Focus Handling</h3>
+ * <p>
+ * The framework will handle routine focus movement in response to user input.
+ * This includes changing the focus as views are removed or hidden, or as new
+ * views become available. Views indicate their willingness to take focus
+ * through the {@link #isFocusable} method. To change whether a view can take
+ * focus, call {@link #setFocusable(boolean)}.  When in touch mode (see notes below)
+ * views indicate whether they still would like focus via {@link #isFocusableInTouchMode}
+ * and can change this via {@link #setFocusableInTouchMode(boolean)}.
+ * </p>
+ * <p>
+ * Focus movement is based on an algorithm which finds the nearest neighbor in a
+ * given direction. In rare cases, the default algorithm may not match the
+ * intended behavior of the developer. In these situations, you can provide
+ * explicit overrides by using these XML attributes in the layout file:
+ * <pre>
+ * nextFocusDown
+ * nextFocusLeft
+ * nextFocusRight
+ * nextFocusUp
+ * </pre>
+ * </p>
+ *
+ *
+ * <p>
+ * To get a particular view to take focus, call {@link #requestFocus()}.
+ * </p>
+ *
+ * <a name="TouchMode"></a>
+ * <h3>Touch Mode</h3>
+ * <p>
+ * When a user is navigating a user interface via directional keys such as a D-pad, it is
+ * necessary to give focus to actionable items such as buttons so the user can see
+ * what will take input.  If the device has touch capabilities, however, and the user
+ * begins interacting with the interface by touching it, it is no longer necessary to
+ * always highlight, or give focus to, a particular view.  This motivates a mode
+ * for interaction named 'touch mode'.
+ * </p>
+ * <p>
+ * For a touch capable device, once the user touches the screen, the device
+ * will enter touch mode.  From this point onward, only views for which
+ * {@link #isFocusableInTouchMode} is true will be focusable, such as text editing widgets.
+ * Other views that are touchable, like buttons, will not take focus when touched; they will
+ * only fire the on click listeners.
+ * </p>
+ * <p>
+ * Any time a user hits a directional key, such as a D-pad direction, the view device will
+ * exit touch mode, and find a view to take focus, so that the user may resume interacting
+ * with the user interface without touching the screen again.
+ * </p>
+ * <p>
+ * The touch mode state is maintained across {@link android.app.Activity}s.  Call
+ * {@link #isInTouchMode} to see whether the device is currently in touch mode.
+ * </p>
+ *
+ * <a name="Scrolling"></a>
+ * <h3>Scrolling</h3>
+ * <p>
+ * The framework provides basic support for views that wish to internally
+ * scroll their content. This includes keeping track of the X and Y scroll
+ * offset as well as mechanisms for drawing scrollbars. See
+ * {@link #scrollBy(int, int)}, {@link #scrollTo(int, int)} for more details.
+ * </p>
+ *
+ * <a name="Tags"></a>
+ * <h3>Tags</h3>
+ * <p>
+ * Unlike IDs, tags are not used to identify views. Tags are essentially an
+ * extra piece of information that can be associated with a view. They are most
+ * often used as a convenience to store data related to views in the views
+ * themselves rather than by putting them in a separate structure.
+ * </p>
+ *
+ * <a name="Animation"></a>
+ * <h3>Animation</h3>
+ * <p>
+ * You can attach an {@link Animation} object to a view using
+ * {@link #setAnimation(Animation)} or
+ * {@link #startAnimation(Animation)}. The animation can alter the scale,
+ * rotation, translation and alpha of a view over time. If the animation is
+ * attached to a view that has children, the animation will affect the entire
+ * subtree rooted by that node. When an animation is started, the framework will
+ * take care of redrawing the appropriate views until the animation completes.
+ * </p>
+ *
+ * @attr ref android.R.styleable#View_fitsSystemWindows
+ * @attr ref android.R.styleable#View_nextFocusDown
+ * @attr ref android.R.styleable#View_nextFocusLeft
+ * @attr ref android.R.styleable#View_nextFocusRight
+ * @attr ref android.R.styleable#View_nextFocusUp
+ * @attr ref android.R.styleable#View_scrollX
+ * @attr ref android.R.styleable#View_scrollY
+ * @attr ref android.R.styleable#View_scrollbarTrackHorizontal
+ * @attr ref android.R.styleable#View_scrollbarThumbHorizontal
+ * @attr ref android.R.styleable#View_scrollbarSize
+ * @attr ref android.R.styleable#View_scrollbars
+ * @attr ref android.R.styleable#View_scrollbarThumbVertical
+ * @attr ref android.R.styleable#View_scrollbarTrackVertical
+ * @attr ref android.R.styleable#View_scrollbarAlwaysDrawHorizontalTrack
+ * @attr ref android.R.styleable#View_scrollbarAlwaysDrawVerticalTrack
+ *
+ * @see android.view.ViewGroup
+ */
+public class View implements Drawable.Callback, KeyEvent.Callback {
+    private static final boolean DBG = false;
+
+    /**
+     * The logging tag used by this class with android.util.Log.
+     */
+    protected static final String VIEW_LOG_TAG = "View";
+
+    /**
+     * Used to mark a View that has no ID.
+     */
+    public static final int NO_ID = -1;
+
+    /**
+     * This view does not want keystrokes. Use with TAKES_FOCUS_MASK when
+     * calling setFlags.
+     */
+    private static final int NOT_FOCUSABLE = 0x00000000;
+
+    /**
+     * This view wants keystrokes. Use with TAKES_FOCUS_MASK when calling
+     * setFlags.
+     */
+    private static final int FOCUSABLE = 0x00000001;
+
+    /**
+     * Mask for use with setFlags indicating bits used for focus.
+     */
+    private static final int FOCUSABLE_MASK = 0x00000001;
+
+    /**
+     * This view will adjust its padding to fit sytem windows (e.g. status bar)
+     */
+    private static final int FITS_SYSTEM_WINDOWS = 0x00000002;
+
+    /**
+     * This view is visible.  Use with {@link #setVisibility}.
+     */
+    public static final int VISIBLE = 0x00000000;
+
+    /**
+     * This view is invisible, but it still takes up space for layout purposes.
+     * Use with {@link #setVisibility}.
+     */
+    public static final int INVISIBLE = 0x00000004;
+
+    /**
+     * This view is invisible, and it doesn't take any space for layout
+     * purposes. Use with {@link #setVisibility}.
+     */
+    public static final int GONE = 0x00000008;
+
+    /**
+     * Mask for use with setFlags indicating bits used for visibility.
+     * {@hide}
+     */
+    static final int VISIBILITY_MASK = 0x0000000C;
+
+    private static final int[] VISIBILITY_FLAGS = {VISIBLE, INVISIBLE, GONE};
+
+    /**
+     * This view is enabled. Intrepretation varies by subclass.
+     * Use with ENABLED_MASK when calling setFlags.
+     * {@hide}
+     */
+    static final int ENABLED = 0x00000000;
+
+    /**
+     * This view is disabled. Intrepretation varies by subclass.
+     * Use with ENABLED_MASK when calling setFlags.
+     * {@hide}
+     */
+    static final int DISABLED = 0x00000020;
+
+   /**
+    * Mask for use with setFlags indicating bits used for indicating whether
+    * this view is enabled
+    * {@hide}
+    */
+    static final int ENABLED_MASK = 0x00000020;
+
+    /**
+     * This view won't draw. {@link #onDraw} won't be called and further
+     * optimizations
+     * will be performed. It is okay to have this flag set and a background.
+     * Use with DRAW_MASK when calling setFlags.
+     * {@hide}
+     */
+    static final int WILL_NOT_DRAW = 0x00000080;
+
+    /**
+     * Mask for use with setFlags indicating bits used for indicating whether
+     * this view is will draw
+     * {@hide}
+     */
+    static final int DRAW_MASK = 0x00000080;
+
+    /**
+     * <p>This view doesn't show scrollbars.</p>
+     * {@hide}
+     */
+    static final int SCROLLBARS_NONE = 0x00000000;
+
+    /**
+     * <p>This view shows horizontal scrollbars.</p>
+     * {@hide}
+     */
+    static final int SCROLLBARS_HORIZONTAL = 0x00000100;
+
+    /**
+     * <p>This view shows vertical scrollbars.</p>
+     * {@hide}
+     */
+    static final int SCROLLBARS_VERTICAL = 0x00000200;
+
+    /**
+     * <p>Mask for use with setFlags indicating bits used for indicating which
+     * scrollbars are enabled.</p>
+     * {@hide}
+     */
+    static final int SCROLLBARS_MASK = 0x00000300;
+
+    // note 0x00000400 and 0x00000800 are now available for next flags...
+
+    /**
+     * <p>This view doesn't show fading edges.</p>
+     * {@hide}
+     */
+    static final int FADING_EDGE_NONE = 0x00000000;
+
+    /**
+     * <p>This view shows horizontal fading edges.</p>
+     * {@hide}
+     */
+    static final int FADING_EDGE_HORIZONTAL = 0x00001000;
+
+    /**
+     * <p>This view shows vertical fading edges.</p>
+     * {@hide}
+     */
+    static final int FADING_EDGE_VERTICAL = 0x00002000;
+
+    /**
+     * <p>Mask for use with setFlags indicating bits used for indicating which
+     * fading edges are enabled.</p>
+     * {@hide}
+     */
+    static final int FADING_EDGE_MASK = 0x00003000;
+
+    /**
+     * <p>Indicates this view can be clicked. When clickable, a View reacts
+     * to clicks by notifying the OnClickListener.<p>
+     * {@hide}
+     */
+    static final int CLICKABLE = 0x00004000;
+
+    /**
+     * <p>Indicates this view is caching its drawing into a bitmap.</p>
+     * {@hide}
+     */
+    static final int DRAWING_CACHE_ENABLED = 0x00008000;
+
+    /**
+     * <p>Indicates that no icicle should be saved for this view.<p>
+     * {@hide}
+     */
+    static final int SAVE_DISABLED = 0x000010000;
+
+    /**
+     * <p>Mask for use with setFlags indicating bits used for the saveEnabled
+     * property.</p>
+     * {@hide}
+     */
+    static final int SAVE_DISABLED_MASK = 0x000010000;
+
+    /**
+     * <p>Indicates that no drawing cache should ever be created for this view.<p>
+     * {@hide}
+     */
+    static final int WILL_NOT_CACHE_DRAWING = 0x000020000;
+
+    /**
+     * <p>Indicates this view can take / keep focus when int touch mode.</p>
+     * {@hide}
+     */
+    static final int FOCUSABLE_IN_TOUCH_MODE = 0x00040000;
+
+    /**
+     * <p>Enables low quality mode for the drawing cache.</p>
+     */
+    public static final int DRAWING_CACHE_QUALITY_LOW = 0x00080000;
+
+    /**
+     * <p>Enables high quality mode for the drawing cache.</p>
+     */
+    public static final int DRAWING_CACHE_QUALITY_HIGH = 0x00100000;
+
+    /**
+     * <p>Enables automatic quality mode for the drawing cache.</p>
+     */
+    public static final int DRAWING_CACHE_QUALITY_AUTO = 0x00000000;
+
+    private static final int[] DRAWING_CACHE_QUALITY_FLAGS = {
+            DRAWING_CACHE_QUALITY_AUTO, DRAWING_CACHE_QUALITY_LOW, DRAWING_CACHE_QUALITY_HIGH
+    };
+
+    /**
+     * <p>Mask for use with setFlags indicating bits used for the cache
+     * quality property.</p>
+     * {@hide}
+     */
+    static final int DRAWING_CACHE_QUALITY_MASK = 0x00180000;
+
+    /**
+     * <p>
+     * Indicates this view can be long clicked. When long clickable, a View
+     * reacts to long clicks by notifying the OnLongClickListener or showing a
+     * context menu.
+     * </p>
+     * {@hide}
+     */
+    static final int LONG_CLICKABLE = 0x00200000;
+
+    /**
+     * <p>Indicates that this view gets its drawable states from its direct parent
+     * and ignores its original internal states.</p>
+     *
+     * @hide
+     */
+    static final int DUPLICATE_PARENT_STATE = 0x00400000;
+
+    /**
+     * The scrollbar style to display the scrollbars inside the content area,
+     * without increasing the padding. The scrollbars will be overlaid with
+     * translucency on the view's content.
+     */
+    public static final int SCROLLBARS_INSIDE_OVERLAY = 0;
+
+    /**
+     * The scrollbar style to display the scrollbars inside the padded area,
+     * increasing the padding of the view. The scrollbars will not overlap the
+     * content area of the view.
+     */
+    public static final int SCROLLBARS_INSIDE_INSET = 0x01000000;
+
+    /**
+     * The scrollbar style to display the scrollbars at the edge of the view,
+     * without increasing the padding. The scrollbars will be overlaid with
+     * translucency.
+     */
+    public static final int SCROLLBARS_OUTSIDE_OVERLAY = 0x02000000;
+
+    /**
+     * The scrollbar style to display the scrollbars at the edge of the view,
+     * increasing the padding of the view. The scrollbars will only overlap the
+     * background, if any.
+     */
+    public static final int SCROLLBARS_OUTSIDE_INSET = 0x03000000;
+
+    /**
+     * Mask to check if the scrollbar style is overlay or inset.
+     * {@hide}
+     */
+    static final int SCROLLBARS_INSET_MASK = 0x01000000;
+
+    /**
+     * Mask to check if the scrollbar style is inside or outside.
+     * {@hide}
+     */
+    static final int SCROLLBARS_OUTSIDE_MASK = 0x02000000;
+
+    /**
+     * Mask for scrollbar style.
+     * {@hide}
+     */
+    static final int SCROLLBARS_STYLE_MASK = 0x03000000;
+
+    /**
+     * View flag indicating that the screen should remain on while the
+     * window containing this view is visible to the user.  This effectively
+     * takes care of automatically setting the WindowManager's
+     * {@link WindowManager.LayoutParams#FLAG_KEEP_SCREEN_ON}.
+     */
+    public static final int KEEP_SCREEN_ON = 0x04000000;
+
+    /**
+     * View flag indicating whether this view should have sound effects enabled
+     * for events such as clicking and touching.
+     */
+    public static final int SOUND_EFFECTS_ENABLED = 0x08000000;
+
+    /**
+     * View flag indicating whether this view should have haptic feedback
+     * enabled for events such as long presses.
+     */
+    public static final int HAPTIC_FEEDBACK_ENABLED = 0x10000000;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus to the previous selectable
+     * item.
+     */
+    public static final int FOCUS_BACKWARD = 0x00000001;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus to the next selectable
+     * item.
+     */
+    public static final int FOCUS_FORWARD = 0x00000002;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus to the left.
+     */
+    public static final int FOCUS_LEFT = 0x00000011;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus up.
+     */
+    public static final int FOCUS_UP = 0x00000021;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus to the right.
+     */
+    public static final int FOCUS_RIGHT = 0x00000042;
+
+    /**
+     * Use with {@link #focusSearch}. Move focus down.
+     */
+    public static final int FOCUS_DOWN = 0x00000082;
+
+    /**
+     * Base View state sets
+     */
+    // Singles
+    /**
+     * Indicates the view has no states set. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     */
+    protected static final int[] EMPTY_STATE_SET = {};
+    /**
+     * Indicates the view is enabled. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     */
+    protected static final int[] ENABLED_STATE_SET = {R.attr.state_enabled};
+    /**
+     * Indicates the view is focused. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     */
+    protected static final int[] FOCUSED_STATE_SET = {R.attr.state_focused};
+    /**
+     * Indicates the view is selected. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     */
+    protected static final int[] SELECTED_STATE_SET = {R.attr.state_selected};
+    /**
+     * Indicates the view is pressed. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     * @hide
+     */
+    protected static final int[] PRESSED_STATE_SET = {R.attr.state_pressed};
+    /**
+     * Indicates the view's window has focus. States are used with
+     * {@link android.graphics.drawable.Drawable} to change the drawing of the
+     * view depending on its state.
+     *
+     * @see android.graphics.drawable.Drawable
+     * @see #getDrawableState()
+     */
+    protected static final int[] WINDOW_FOCUSED_STATE_SET =
+            {R.attr.state_window_focused};
+    // Doubles
+    /**
+     * Indicates the view is enabled and has the focus.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     */
+    protected static final int[] ENABLED_FOCUSED_STATE_SET =
+            stateSetUnion(ENABLED_STATE_SET, FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is enabled and selected.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     */
+    protected static final int[] ENABLED_SELECTED_STATE_SET =
+            stateSetUnion(ENABLED_STATE_SET, SELECTED_STATE_SET);
+    /**
+     * Indicates the view is enabled and that its window has focus.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] ENABLED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is focused and selected.
+     *
+     * @see #FOCUSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     */
+    protected static final int[] FOCUSED_SELECTED_STATE_SET =
+            stateSetUnion(FOCUSED_STATE_SET, SELECTED_STATE_SET);
+    /**
+     * Indicates the view has the focus and that its window has the focus.
+     *
+     * @see #FOCUSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] FOCUSED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is selected and that its window has the focus.
+     *
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    // Triples
+    /**
+     * Indicates the view is enabled, focused and selected.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     */
+    protected static final int[] ENABLED_FOCUSED_SELECTED_STATE_SET =
+            stateSetUnion(ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+    /**
+     * Indicates the view is enabled, focused and its window has the focus.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is enabled, selected and its window has the focus.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is focused, selected and its window has the focus.
+     *
+     * @see #FOCUSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+    /**
+     * Indicates the view is enabled, focused, selected and its window
+     * has the focus.
+     *
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(ENABLED_FOCUSED_SELECTED_STATE_SET,
+                          WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed and its window has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed and selected.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     */
+    protected static final int[] PRESSED_SELECTED_STATE_SET =
+            stateSetUnion(PRESSED_STATE_SET, SELECTED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, selected and its window has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed and focused.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_STATE_SET, FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, focused and its window has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, focused and selected.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_FOCUSED_SELECTED_STATE_SET =
+            stateSetUnion(PRESSED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, focused, selected and its window has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed and enabled.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_STATE_SET =
+            stateSetUnion(PRESSED_STATE_SET, ENABLED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled and its window has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled and selected.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_SELECTED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_STATE_SET, SELECTED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled, selected and its window has the
+     * focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled and focused.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_STATE_SET, FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled, focused and its window has the
+     * focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled, focused and selected.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_FOCUSED_STATE_SET, SELECTED_STATE_SET);
+
+    /**
+     * Indicates the view is pressed, enabled, focused, selected and its window
+     * has the focus.
+     *
+     * @see #PRESSED_STATE_SET
+     * @see #ENABLED_STATE_SET
+     * @see #SELECTED_STATE_SET
+     * @see #FOCUSED_STATE_SET
+     * @see #WINDOW_FOCUSED_STATE_SET
+     */
+    protected static final int[] PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET =
+            stateSetUnion(PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET, WINDOW_FOCUSED_STATE_SET);
+
+    /**
+     * The order here is very important to {@link #getDrawableState()}
+     */
+    private static final int[][] VIEW_STATE_SETS = {
+        EMPTY_STATE_SET,                                           // 0 0 0 0 0
+        WINDOW_FOCUSED_STATE_SET,                                  // 0 0 0 0 1
+        SELECTED_STATE_SET,                                        // 0 0 0 1 0
+        SELECTED_WINDOW_FOCUSED_STATE_SET,                         // 0 0 0 1 1
+        FOCUSED_STATE_SET,                                         // 0 0 1 0 0
+        FOCUSED_WINDOW_FOCUSED_STATE_SET,                          // 0 0 1 0 1
+        FOCUSED_SELECTED_STATE_SET,                                // 0 0 1 1 0
+        FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 0 1 1 1
+        ENABLED_STATE_SET,                                         // 0 1 0 0 0
+        ENABLED_WINDOW_FOCUSED_STATE_SET,                          // 0 1 0 0 1
+        ENABLED_SELECTED_STATE_SET,                                // 0 1 0 1 0
+        ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 0 1 0 1 1
+        ENABLED_FOCUSED_STATE_SET,                                 // 0 1 1 0 0
+        ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 0 1 1 0 1
+        ENABLED_FOCUSED_SELECTED_STATE_SET,                        // 0 1 1 1 0
+        ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 0 1 1 1 1
+        PRESSED_STATE_SET,                                         // 1 0 0 0 0
+        PRESSED_WINDOW_FOCUSED_STATE_SET,                          // 1 0 0 0 1
+        PRESSED_SELECTED_STATE_SET,                                // 1 0 0 1 0
+        PRESSED_SELECTED_WINDOW_FOCUSED_STATE_SET,                 // 1 0 0 1 1
+        PRESSED_FOCUSED_STATE_SET,                                 // 1 0 1 0 0
+        PRESSED_FOCUSED_WINDOW_FOCUSED_STATE_SET,                  // 1 0 1 0 1
+        PRESSED_FOCUSED_SELECTED_STATE_SET,                        // 1 0 1 1 0
+        PRESSED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 0 1 1 1
+        PRESSED_ENABLED_STATE_SET,                                 // 1 1 0 0 0
+        PRESSED_ENABLED_WINDOW_FOCUSED_STATE_SET,                  // 1 1 0 0 1
+        PRESSED_ENABLED_SELECTED_STATE_SET,                        // 1 1 0 1 0
+        PRESSED_ENABLED_SELECTED_WINDOW_FOCUSED_STATE_SET,         // 1 1 0 1 1
+        PRESSED_ENABLED_FOCUSED_STATE_SET,                         // 1 1 1 0 0
+        PRESSED_ENABLED_FOCUSED_WINDOW_FOCUSED_STATE_SET,          // 1 1 1 0 1
+        PRESSED_ENABLED_FOCUSED_SELECTED_STATE_SET,                // 1 1 1 1 0
+        PRESSED_ENABLED_FOCUSED_SELECTED_WINDOW_FOCUSED_STATE_SET, // 1 1 1 1 1
+    };
+
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is showing the last item.
+     * @hide
+     */
+    protected static final int[] LAST_STATE_SET = {R.attr.state_last};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is showing the first item.
+     * @hide
+     */
+    protected static final int[] FIRST_STATE_SET = {R.attr.state_first};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is showing the middle item.
+     * @hide
+     */
+    protected static final int[] MIDDLE_STATE_SET = {R.attr.state_middle};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is showing only one item.
+     * @hide
+     */
+    protected static final int[] SINGLE_STATE_SET = {R.attr.state_single};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is pressed and showing the last item.
+     * @hide
+     */
+    protected static final int[] PRESSED_LAST_STATE_SET = {R.attr.state_last, R.attr.state_pressed};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is pressed and showing the first item.
+     * @hide
+     */
+    protected static final int[] PRESSED_FIRST_STATE_SET = {R.attr.state_first, R.attr.state_pressed};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is pressed and showing the middle item.
+     * @hide
+     */
+    protected static final int[] PRESSED_MIDDLE_STATE_SET = {R.attr.state_middle, R.attr.state_pressed};
+    /**
+     * Used by views that contain lists of items. This state indicates that
+     * the view is pressed and showing only one item.
+     * @hide
+     */
+    protected static final int[] PRESSED_SINGLE_STATE_SET = {R.attr.state_single, R.attr.state_pressed};
+
+    /**
+     * Temporary Rect currently for use in setBackground().  This will probably
+     * be extended in the future to hold our own class with more than just
+     * a Rect. :)
+     */
+    static final ThreadLocal<Rect> sThreadLocal = new ThreadLocal<Rect>();
+    
+    /**
+     * The animation currently associated with this view.
+     * @hide
+     */
+    protected Animation mCurrentAnimation = null;
+
+    /**
+     * Width as measured during measure pass.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mMeasuredWidth;
+
+    /**
+     * Height as measured during measure pass.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mMeasuredHeight;
+
+    /**
+     * The view's identifier.
+     * {@hide}
+     *
+     * @see #setId(int)
+     * @see #getId()
+     */
+    @ViewDebug.ExportedProperty(resolveId = true)
+    int mID = NO_ID;
+
+    /**
+     * The view's tag.
+     * {@hide}
+     *
+     * @see #setTag(Object)
+     * @see #getTag()
+     */
+    protected Object mTag;
+
+    // for mPrivateFlags:
+    /** {@hide} */
+    static final int WANTS_FOCUS                    = 0x00000001;
+    /** {@hide} */
+    static final int FOCUSED                        = 0x00000002;
+    /** {@hide} */
+    static final int SELECTED                       = 0x00000004;
+    /** {@hide} */
+    static final int IS_ROOT_NAMESPACE              = 0x00000008;
+    /** {@hide} */
+    static final int HAS_BOUNDS                     = 0x00000010;
+    /** {@hide} */
+    static final int DRAWN                          = 0x00000020;
+    /**
+     * When this flag is set, this view is running an animation on behalf of its
+     * children and should therefore not cancel invalidate requests, even if they
+     * lie outside of this view's bounds.
+     *
+     * {@hide}
+     */
+    static final int DRAW_ANIMATION                 = 0x00000040;
+    /** {@hide} */
+    static final int SKIP_DRAW                      = 0x00000080;
+    /** {@hide} */
+    static final int ONLY_DRAWS_BACKGROUND          = 0x00000100;
+    /** {@hide} */
+    static final int REQUEST_TRANSPARENT_REGIONS    = 0x00000200;
+    /** {@hide} */
+    static final int DRAWABLE_STATE_DIRTY           = 0x00000400;
+    /** {@hide} */
+    static final int MEASURED_DIMENSION_SET         = 0x00000800;
+    /** {@hide} */
+    static final int FORCE_LAYOUT                   = 0x00001000;
+
+    private static final int LAYOUT_REQUIRED        = 0x00002000;
+
+    private static final int PRESSED                = 0x00004000;
+
+    /** {@hide} */
+    static final int DRAWING_CACHE_VALID            = 0x00008000;
+    /**
+     * Flag used to indicate that this view should be drawn once more (and only once
+     * more) after its animation has completed.
+     * {@hide}
+     */
+    static final int ANIMATION_STARTED              = 0x00010000;
+
+    private static final int SAVE_STATE_CALLED      = 0x00020000;
+
+    /**
+     * Indicates that the View returned true when onSetAlpha() was called and that
+     * the alpha must be restored.
+     * {@hide}
+     */
+    static final int ALPHA_SET                      = 0x00040000;
+
+    /**
+     * Set by {@link #setScrollContainer(boolean)}.
+     */
+    static final int SCROLL_CONTAINER               = 0x00080000;
+
+    /**
+     * Set by {@link #setScrollContainer(boolean)}.
+     */
+    static final int SCROLL_CONTAINER_ADDED         = 0x00100000;
+
+    /**
+     * The parent this view is attached to.
+     * {@hide}
+     *
+     * @see #getParent()
+     */
+    protected ViewParent mParent;
+
+    /**
+     * {@hide}
+     */
+    AttachInfo mAttachInfo;
+
+    /**
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    int mPrivateFlags;
+
+    /**
+     * Count of how many windows this view has been attached to.
+     */
+    int mWindowAttachCount;
+
+    /**
+     * The layout parameters associated with this view and used by the parent
+     * {@link android.view.ViewGroup} to determine how this view should be
+     * laid out.
+     * {@hide}
+     */
+    protected ViewGroup.LayoutParams mLayoutParams;
+
+    /**
+     * The view flags hold various views states.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    int mViewFlags;
+
+    /**
+     * The distance in pixels from the left edge of this view's parent
+     * to the left edge of this view.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mLeft;
+    /**
+     * The distance in pixels from the left edge of this view's parent
+     * to the right edge of this view.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mRight;
+    /**
+     * The distance in pixels from the top edge of this view's parent
+     * to the top edge of this view.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mTop;
+    /**
+     * The distance in pixels from the top edge of this view's parent
+     * to the bottom edge of this view.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mBottom;
+
+    /**
+     * The offset, in pixels, by which the content of this view is scrolled
+     * horizontally.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mScrollX;
+    /**
+     * The offset, in pixels, by which the content of this view is scrolled
+     * vertically.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mScrollY;
+
+    /**
+     * The left padding in pixels, that is the distance in pixels between the
+     * left edge of this view and the left edge of its content.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mPaddingLeft;
+    /**
+     * The right padding in pixels, that is the distance in pixels between the
+     * right edge of this view and the right edge of its content.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mPaddingRight;
+    /**
+     * The top padding in pixels, that is the distance in pixels between the
+     * top edge of this view and the top edge of its content.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mPaddingTop;
+    /**
+     * The bottom padding in pixels, that is the distance in pixels between the
+     * bottom edge of this view and the bottom edge of its content.
+     * {@hide}
+     */
+    @ViewDebug.ExportedProperty
+    protected int mPaddingBottom;
+
+    /**
+     * Cache the paddingRight set by the user to append to the scrollbar's size.
+     */
+    @ViewDebug.ExportedProperty
+    int mUserPaddingRight;
+
+    /**
+     * Cache the paddingBottom set by the user to append to the scrollbar's size.
+     */
+    @ViewDebug.ExportedProperty
+    int mUserPaddingBottom;
+
+    private int mOldWidthMeasureSpec = Integer.MIN_VALUE;
+    private int mOldHeightMeasureSpec = Integer.MIN_VALUE;
+
+    private Resources mResources = null;
+
+    private Drawable mBGDrawable;
+
+    private int mBackgroundResource;
+    private boolean mBackgroundSizeChanged;
+
+    /**
+     * Listener used to dispatch focus change events.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected OnFocusChangeListener mOnFocusChangeListener;
+
+    /**
+     * Listener used to dispatch click events.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected OnClickListener mOnClickListener;
+
+    /**
+     * Listener used to dispatch long click events.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected OnLongClickListener mOnLongClickListener;
+
+    /**
+     * Listener used to build the context menu.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected OnCreateContextMenuListener mOnCreateContextMenuListener;
+
+    private OnKeyListener mOnKeyListener;
+
+    private OnTouchListener mOnTouchListener;
+
+    /**
+     * The application environment this view lives in.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected Context mContext;
+
+    private ScrollabilityCache mScrollCache;
+
+    private int[] mDrawableState = null;
+
+    private SoftReference<Bitmap> mDrawingCache;
+
+    /**
+     * When this view has focus and the next focus is {@link #FOCUS_LEFT},
+     * the user may specify which view to go to next.
+     */
+    private int mNextFocusLeftId = View.NO_ID;
+
+    /**
+     * When this view has focus and the next focus is {@link #FOCUS_RIGHT},
+     * the user may specify which view to go to next.
+     */
+    private int mNextFocusRightId = View.NO_ID;
+
+    /**
+     * When this view has focus and the next focus is {@link #FOCUS_UP},
+     * the user may specify which view to go to next.
+     */
+    private int mNextFocusUpId = View.NO_ID;
+
+    /**
+     * When this view has focus and the next focus is {@link #FOCUS_DOWN},
+     * the user may specify which view to go to next.
+     */
+    private int mNextFocusDownId = View.NO_ID;
+
+    private CheckForLongPress mPendingCheckForLongPress;
+    private UnsetPressedState mUnsetPressedState;
+
+    /**
+     * Whether the long press's action has been invoked.  The tap's action is invoked on the
+     * up event while a long press is invoked as soon as the long press duration is reached, so
+     * a long press could be performed before the tap is checked, in which case the tap's action
+     * should not be invoked.
+     */
+    private boolean mHasPerformedLongPress;
+
+    /**
+     * The minimum height of the view. We'll try our best to have the height
+     * of this view to at least this amount.
+     */
+    @ViewDebug.ExportedProperty
+    private int mMinHeight;
+
+    /**
+     * The minimum width of the view. We'll try our best to have the width
+     * of this view to at least this amount.
+     */
+    @ViewDebug.ExportedProperty
+    private int mMinWidth;
+
+    /**
+     * The delegate to handle touch events that are physically in this view
+     * but should be handled by another view.
+     */
+    private TouchDelegate mTouchDelegate = null;
+
+    /**
+     * Solid color to use as a background when creating the drawing cache. Enables
+     * the cache to use 16 bit bitmaps instead of 32 bit.
+     */
+    private int mDrawingCacheBackgroundColor = 0;
+
+    /**
+     * Special tree observer used when mAttachInfo is null.
+     */
+    private ViewTreeObserver mFloatingTreeObserver;
+
+    // Used for debug only
+    static long sInstanceCount = 0;
+
+    /**
+     * Simple constructor to use when creating a view from code.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     */
+    public View(Context context) {
+        mContext = context;
+        mResources = context != null ? context.getResources() : null;
+        mViewFlags = SOUND_EFFECTS_ENABLED|HAPTIC_FEEDBACK_ENABLED;
+        ++sInstanceCount;
+    }
+
+    /**
+     * Constructor that is called when inflating a view from XML. This is called
+     * when a view is being constructed from an XML file, supplying attributes
+     * that were specified in the XML file. This version uses a default style of
+     * 0, so the only attribute values applied are those in the Context's Theme
+     * and the given AttributeSet.
+     *
+     * <p>
+     * The method onFinishInflate() will be called after all children have been
+     * added.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @see #View(Context, AttributeSet, int)
+     */
+    public View(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    /**
+     * Perform inflation from XML and apply a class-specific base style. This
+     * constructor of View allows subclasses to use their own base style when
+     * they are inflating. For example, a Button class's constructor would call
+     * this version of the super class constructor and supply
+     * <code>R.attr.buttonStyle</code> for <var>defStyle</var>; this allows
+     * the theme's button style to modify all of the base view attributes (in
+     * particular its background) as well as the Button class's attributes.
+     *
+     * @param context The Context the view is running in, through which it can
+     *        access the current theme, resources, etc.
+     * @param attrs The attributes of the XML tag that is inflating the view.
+     * @param defStyle The default style to apply to this view. If 0, no style
+     *        will be applied (beyond what is included in the theme). This may
+     *        either be an attribute resource, whose value will be retrieved
+     *        from the current theme, or an explicit style resource.
+     * @see #View(Context, AttributeSet)
+     */
+    public View(Context context, AttributeSet attrs, int defStyle) {
+        this(context);
+
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View,
+                defStyle, 0);
+
+        Drawable background = null;
+
+        int leftPadding = -1;
+        int topPadding = -1;
+        int rightPadding = -1;
+        int bottomPadding = -1;
+
+        int padding = -1;
+
+        int viewFlagValues = 0;
+        int viewFlagMasks = 0;
+
+        boolean setScrollContainer = false;
+        
+        int x = 0;
+        int y = 0;
+
+        int scrollbarStyle = SCROLLBARS_INSIDE_OVERLAY;
+
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case com.android.internal.R.styleable.View_background:
+                    background = a.getDrawable(attr);
+                    break;
+                case com.android.internal.R.styleable.View_padding:
+                    padding = a.getDimensionPixelSize(attr, -1);
+                    break;
+                 case com.android.internal.R.styleable.View_paddingLeft:
+                    leftPadding = a.getDimensionPixelSize(attr, -1);
+                    break;
+                case com.android.internal.R.styleable.View_paddingTop:
+                    topPadding = a.getDimensionPixelSize(attr, -1);
+                    break;
+                case com.android.internal.R.styleable.View_paddingRight:
+                    rightPadding = a.getDimensionPixelSize(attr, -1);
+                    break;
+                case com.android.internal.R.styleable.View_paddingBottom:
+                    bottomPadding = a.getDimensionPixelSize(attr, -1);
+                    break;
+                case com.android.internal.R.styleable.View_scrollX:
+                    x = a.getDimensionPixelOffset(attr, 0);
+                    break;
+                case com.android.internal.R.styleable.View_scrollY:
+                    y = a.getDimensionPixelOffset(attr, 0);
+                    break;
+                case com.android.internal.R.styleable.View_id:
+                    mID = a.getResourceId(attr, NO_ID);
+                    break;
+                case com.android.internal.R.styleable.View_tag:
+                    mTag = a.getText(attr);
+                    break;
+                case com.android.internal.R.styleable.View_fitsSystemWindows:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= FITS_SYSTEM_WINDOWS;
+                        viewFlagMasks |= FITS_SYSTEM_WINDOWS;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_focusable:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= FOCUSABLE;
+                        viewFlagMasks |= FOCUSABLE_MASK;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_focusableInTouchMode:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE;
+                        viewFlagMasks |= FOCUSABLE_IN_TOUCH_MODE | FOCUSABLE_MASK;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_clickable:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= CLICKABLE;
+                        viewFlagMasks |= CLICKABLE;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_longClickable:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= LONG_CLICKABLE;
+                        viewFlagMasks |= LONG_CLICKABLE;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_saveEnabled:
+                    if (!a.getBoolean(attr, true)) {
+                        viewFlagValues |= SAVE_DISABLED;
+                        viewFlagMasks |= SAVE_DISABLED_MASK;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_duplicateParentState:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= DUPLICATE_PARENT_STATE;
+                        viewFlagMasks |= DUPLICATE_PARENT_STATE;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_visibility:
+                    final int visibility = a.getInt(attr, 0);
+                    if (visibility != 0) {
+                        viewFlagValues |= VISIBILITY_FLAGS[visibility];
+                        viewFlagMasks |= VISIBILITY_MASK;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_drawingCacheQuality:
+                    final int cacheQuality = a.getInt(attr, 0);
+                    if (cacheQuality != 0) {
+                        viewFlagValues |= DRAWING_CACHE_QUALITY_FLAGS[cacheQuality];
+                        viewFlagMasks |= DRAWING_CACHE_QUALITY_MASK;
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_soundEffectsEnabled:
+                    if (!a.getBoolean(attr, true)) {
+                        viewFlagValues &= ~SOUND_EFFECTS_ENABLED;
+                        viewFlagMasks |= SOUND_EFFECTS_ENABLED;
+                    }
+                case com.android.internal.R.styleable.View_hapticFeedbackEnabled:
+                    if (!a.getBoolean(attr, true)) {
+                        viewFlagValues &= ~HAPTIC_FEEDBACK_ENABLED;
+                        viewFlagMasks |= HAPTIC_FEEDBACK_ENABLED;
+                    }
+                case R.styleable.View_scrollbars:
+                    final int scrollbars = a.getInt(attr, SCROLLBARS_NONE);
+                    if (scrollbars != SCROLLBARS_NONE) {
+                        viewFlagValues |= scrollbars;
+                        viewFlagMasks |= SCROLLBARS_MASK;
+                        initializeScrollbars(a);
+                    }
+                    break;
+                case R.styleable.View_fadingEdge:
+                    final int fadingEdge = a.getInt(attr, FADING_EDGE_NONE);
+                    if (fadingEdge != FADING_EDGE_NONE) {
+                        viewFlagValues |= fadingEdge;
+                        viewFlagMasks |= FADING_EDGE_MASK;
+                        initializeFadingEdge(a);
+                    }
+                    break;
+                case R.styleable.View_scrollbarStyle:
+                    scrollbarStyle = a.getInt(attr, SCROLLBARS_INSIDE_OVERLAY);
+                    if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
+                        viewFlagValues |= scrollbarStyle & SCROLLBARS_STYLE_MASK;
+                        viewFlagMasks |= SCROLLBARS_STYLE_MASK;
+                    }
+                    break;
+                case R.styleable.View_isScrollContainer:
+                    setScrollContainer = true;
+                    if (a.getBoolean(attr, false)) {
+                        setScrollContainer(true);
+                    }
+                    break;
+                case com.android.internal.R.styleable.View_keepScreenOn:
+                    if (a.getBoolean(attr, false)) {
+                        viewFlagValues |= KEEP_SCREEN_ON;
+                        viewFlagMasks |= KEEP_SCREEN_ON;
+                    }
+                    break;
+                case R.styleable.View_nextFocusLeft:
+                    mNextFocusLeftId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_nextFocusRight:
+                    mNextFocusRightId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_nextFocusUp:
+                    mNextFocusUpId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_nextFocusDown:
+                    mNextFocusDownId = a.getResourceId(attr, View.NO_ID);
+                    break;
+                case R.styleable.View_minWidth:
+                    mMinWidth = a.getDimensionPixelSize(attr, 0);
+                    break;
+                case R.styleable.View_minHeight:
+                    mMinHeight = a.getDimensionPixelSize(attr, 0);
+                    break;
+            }
+        }
+
+        if (background != null) {
+            setBackgroundDrawable(background);
+        }
+
+        if (padding >= 0) {
+            leftPadding = padding;
+            topPadding = padding;
+            rightPadding = padding;
+            bottomPadding = padding;
+        }
+
+        // If the user specified the padding (either with android:padding or
+        // android:paddingLeft/Top/Right/Bottom), use this padding, otherwise
+        // use the default padding or the padding from the background drawable
+        // (stored at this point in mPadding*)
+        setPadding(leftPadding >= 0 ? leftPadding : mPaddingLeft,
+                topPadding >= 0 ? topPadding : mPaddingTop,
+                rightPadding >= 0 ? rightPadding : mPaddingRight,
+                bottomPadding >= 0 ? bottomPadding : mPaddingBottom);
+
+        if (viewFlagMasks != 0) {
+            setFlags(viewFlagValues, viewFlagMasks);
+        }
+
+        // Needs to be called after mViewFlags is set
+        if (scrollbarStyle != SCROLLBARS_INSIDE_OVERLAY) {
+            recomputePadding();
+        }
+
+        if (x != 0 || y != 0) {
+            scrollTo(x, y);
+        }
+
+        if (!setScrollContainer && (viewFlagValues&SCROLLBARS_VERTICAL) != 0) {
+            setScrollContainer(true);
+        }
+  
+        a.recycle();
+    }
+
+    /**
+     * Non-public constructor for use in testing
+     */
+    View() {
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        --sInstanceCount;
+    }
+
+    /**
+     * <p>
+     * Initializes the fading edges from a given set of styled attributes. This
+     * method should be called by subclasses that need fading edges and when an
+     * instance of these subclasses is created programmatically rather than
+     * being inflated from XML. This method is automatically called when the XML
+     * is inflated.
+     * </p>
+     *
+     * @param a the styled attributes set to initialize the fading edges from
+     */
+    protected void initializeFadingEdge(TypedArray a) {
+        initScrollCache();
+
+        mScrollCache.fadingEdgeLength = a.getDimensionPixelSize(
+                R.styleable.View_fadingEdgeLength,
+                ViewConfiguration.get(mContext).getScaledFadingEdgeLength());
+    }
+
+    /**
+     * Returns the size of the vertical faded edges used to indicate that more
+     * content in this view is visible.
+     *
+     * @return The size in pixels of the vertical faded edge or 0 if vertical
+     *         faded edges are not enabled for this view.
+     * @attr ref android.R.styleable#View_fadingEdgeLength
+     */
+    public int getVerticalFadingEdgeLength() {
+        if (isVerticalFadingEdgeEnabled()) {
+            ScrollabilityCache cache = mScrollCache;
+            if (cache != null) {
+                return cache.fadingEdgeLength;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Set the size of the faded edge used to indicate that more content in this
+     * view is available.  Will not change whether the fading edge is enabled; use
+     * {@link #setVerticalFadingEdgeEnabled} or {@link #setHorizontalFadingEdgeEnabled}
+     * to enable the fading edge for the vertical or horizontal fading edges.
+     *
+     * @param length The size in pixels of the faded edge used to indicate that more
+     *        content in this view is visible.
+     */
+    public void setFadingEdgeLength(int length) {
+        initScrollCache();
+        mScrollCache.fadingEdgeLength = length;
+    }
+
+    /**
+     * Returns the size of the horizontal faded edges used to indicate that more
+     * content in this view is visible.
+     *
+     * @return The size in pixels of the horizontal faded edge or 0 if horizontal
+     *         faded edges are not enabled for this view.
+     * @attr ref android.R.styleable#View_fadingEdgeLength
+     */
+    public int getHorizontalFadingEdgeLength() {
+        if (isHorizontalFadingEdgeEnabled()) {
+            ScrollabilityCache cache = mScrollCache;
+            if (cache != null) {
+                return cache.fadingEdgeLength;
+            }
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the width of the vertical scrollbar.
+     *
+     * @return The width in pixels of the vertical scrollbar or 0 if there
+     *         is no vertical scrollbar.
+     */
+    public int getVerticalScrollbarWidth() {
+        ScrollabilityCache cache = mScrollCache;
+        if (cache != null) {
+            ScrollBarDrawable scrollBar = cache.scrollBar;
+            if (scrollBar != null) {
+                int size = scrollBar.getSize(true);
+                if (size <= 0) {
+                    size = cache.scrollBarSize;
+                }
+                return size;
+            }
+            return 0;
+        }
+        return 0;
+    }
+
+    /**
+     * Returns the height of the horizontal scrollbar.
+     *
+     * @return The height in pixels of the horizontal scrollbar or 0 if
+     *         there is no horizontal scrollbar.
+     */
+    protected int getHorizontalScrollbarHeight() {
+        ScrollabilityCache cache = mScrollCache;
+        if (cache != null) {
+            ScrollBarDrawable scrollBar = cache.scrollBar;
+            if (scrollBar != null) {
+                int size = scrollBar.getSize(false);
+                if (size <= 0) {
+                    size = cache.scrollBarSize;
+                }
+                return size;
+            }
+            return 0;
+        }
+        return 0;
+    }
+
+    /**
+     * <p>
+     * Initializes the scrollbars from a given set of styled attributes. This
+     * method should be called by subclasses that need scrollbars and when an
+     * instance of these subclasses is created programmatically rather than
+     * being inflated from XML. This method is automatically called when the XML
+     * is inflated.
+     * </p>
+     *
+     * @param a the styled attributes set to initialize the scrollbars from
+     */
+    protected void initializeScrollbars(TypedArray a) {
+        initScrollCache();
+
+        if (mScrollCache.scrollBar == null) {
+            mScrollCache.scrollBar = new ScrollBarDrawable();
+        }
+
+        final ScrollabilityCache scrollabilityCache = mScrollCache;
+
+        scrollabilityCache.scrollBarSize = a.getDimensionPixelSize(
+                com.android.internal.R.styleable.View_scrollbarSize,
+                ViewConfiguration.get(mContext).getScaledScrollBarSize());
+
+        Drawable track = a.getDrawable(R.styleable.View_scrollbarTrackHorizontal);
+        scrollabilityCache.scrollBar.setHorizontalTrackDrawable(track);
+
+        Drawable thumb = a.getDrawable(R.styleable.View_scrollbarThumbHorizontal);
+        if (thumb != null) {
+            scrollabilityCache.scrollBar.setHorizontalThumbDrawable(thumb);
+        }
+
+        boolean alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawHorizontalTrack,
+                false);
+        if (alwaysDraw) {
+            scrollabilityCache.scrollBar.setAlwaysDrawHorizontalTrack(true);
+        }
+
+        track = a.getDrawable(R.styleable.View_scrollbarTrackVertical);
+        scrollabilityCache.scrollBar.setVerticalTrackDrawable(track);
+
+        thumb = a.getDrawable(R.styleable.View_scrollbarThumbVertical);
+        if (thumb != null) {
+            scrollabilityCache.scrollBar.setVerticalThumbDrawable(thumb);
+        }
+
+        alwaysDraw = a.getBoolean(R.styleable.View_scrollbarAlwaysDrawVerticalTrack,
+                false);
+        if (alwaysDraw) {
+            scrollabilityCache.scrollBar.setAlwaysDrawVerticalTrack(true);
+        }
+
+        // Re-apply user/background padding so that scrollbar(s) get added
+        recomputePadding();
+    }
+
+    /**
+     * <p>
+     * Initalizes the scrollability cache if necessary.
+     * </p>
+     */
+    private void initScrollCache() {
+        if (mScrollCache == null) {
+            mScrollCache = new ScrollabilityCache(ViewConfiguration.get(mContext));
+        }
+    }
+
+    /**
+     * Register a callback to be invoked when focus of this view changed.
+     *
+     * @param l The callback that will run.
+     */
+    public void setOnFocusChangeListener(OnFocusChangeListener l) {
+        mOnFocusChangeListener = l;
+    }
+
+    /**
+     * Returns the focus-change callback registered for this view.
+     *
+     * @return The callback, or null if one is not registered.
+     */
+    public OnFocusChangeListener getOnFocusChangeListener() {
+        return mOnFocusChangeListener;
+    }
+
+    /**
+     * Register a callback to be invoked when this view is clicked. If this view is not
+     * clickable, it becomes clickable.
+     *
+     * @param l The callback that will run
+     *
+     * @see #setClickable(boolean)
+     */
+    public void setOnClickListener(OnClickListener l) {
+        if (!isClickable()) {
+            setClickable(true);
+        }
+        mOnClickListener = l;
+    }
+
+    /**
+     * Register a callback to be invoked when this view is clicked and held. If this view is not
+     * long clickable, it becomes long clickable.
+     *
+     * @param l The callback that will run
+     *
+     * @see #setLongClickable(boolean)
+     */
+    public void setOnLongClickListener(OnLongClickListener l) {
+        if (!isLongClickable()) {
+            setLongClickable(true);
+        }
+        mOnLongClickListener = l;
+    }
+
+    /**
+     * Register a callback to be invoked when the context menu for this view is
+     * being built. If this view is not long clickable, it becomes long clickable.
+     *
+     * @param l The callback that will run
+     *
+     */
+    public void setOnCreateContextMenuListener(OnCreateContextMenuListener l) {
+        if (!isLongClickable()) {
+            setLongClickable(true);
+        }
+        mOnCreateContextMenuListener = l;
+    }
+
+    /**
+     * Call this view's OnClickListener, if it is defined.
+     *
+     * @return True there was an assigned OnClickListener that was called, false
+     *         otherwise is returned.
+     */
+    public boolean performClick() {
+        if (mOnClickListener != null) {
+            playSoundEffect(SoundEffectConstants.CLICK);
+            mOnClickListener.onClick(this);
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Call this view's OnLongClickListener, if it is defined. Invokes the context menu
+     * if the OnLongClickListener did not consume the event.
+     *
+     * @return True there was an assigned OnLongClickListener that was called, false
+     *         otherwise is returned.
+     */
+    public boolean performLongClick() {
+        boolean handled = false;
+        if (mOnLongClickListener != null) {
+            handled = mOnLongClickListener.onLongClick(View.this);
+        }
+        if (!handled) {
+            handled = showContextMenu();
+        }
+        if (handled) {
+            performHapticFeedback(HapticFeedbackConstants.LONG_PRESS);
+        }
+        return handled;
+    }
+
+    /**
+     * Bring up the context menu for this view.
+     *
+     * @return Whether a context menu was displayed.
+     */
+    public boolean showContextMenu() {
+        return getParent().showContextMenuForChild(this);
+    }
+
+    /**
+     * Register a callback to be invoked when a key is pressed in this view.
+     * @param l the key listener to attach to this view
+     */
+    public void setOnKeyListener(OnKeyListener l) {
+        mOnKeyListener = l;
+    }
+
+    /**
+     * Register a callback to be invoked when a touch event is sent to this view.
+     * @param l the touch listener to attach to this view
+     */
+    public void setOnTouchListener(OnTouchListener l) {
+        mOnTouchListener = l;
+    }
+
+    /**
+     * Give this view focus. This will cause {@link #onFocusChanged} to be called.
+     *
+     * Note: this does not check whether this {@link View} should get focus, it just
+     * gives it focus no matter what.  It should only be called internally by framework
+     * code that knows what it is doing, namely {@link #requestFocus(int, Rect)}.
+     *
+     * @param direction values are View.FOCUS_UP, View.FOCUS_DOWN,
+     *        View.FOCUS_LEFT or View.FOCUS_RIGHT. This is the direction which
+     *        focus moved when requestFocus() is called. It may not always
+     *        apply, in which case use the default View.FOCUS_DOWN.
+     * @param previouslyFocusedRect The rectangle of the view that had focus
+     *        prior in this View's coordinate system.
+     */
+    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
+        if (DBG) {
+            System.out.println(this + " requestFocus()");
+        }
+
+        if ((mPrivateFlags & FOCUSED) == 0) {
+            mPrivateFlags |= FOCUSED;
+
+            if (mParent != null) {
+                mParent.requestChildFocus(this, this);
+            }
+
+            onFocusChanged(true, direction, previouslyFocusedRect);
+            refreshDrawableState();
+        }
+    }
+
+    /**
+     * Request that a rectangle of this view be visible on the screen,
+     * scrolling if necessary just enough.
+     *
+     * <p>A View should call this if it maintains some notion of which part
+     * of its content is interesting.  For example, a text editing view
+     * should call this when its cursor moves.
+     *
+     * @param rectangle The rectangle.
+     * @return Whether any parent scrolled.
+     */
+    public boolean requestRectangleOnScreen(Rect rectangle) {
+        return requestRectangleOnScreen(rectangle, false);
+    }
+
+    /**
+     * Request that a rectangle of this view be visible on the screen,
+     * scrolling if necessary just enough.
+     *
+     * <p>A View should call this if it maintains some notion of which part
+     * of its content is interesting.  For example, a text editing view
+     * should call this when its cursor moves.
+     *
+     * <p>When <code>immediate</code> is set to true, scrolling will not be
+     * animated.
+     *
+     * @param rectangle The rectangle.
+     * @param immediate True to forbid animated scrolling, false otherwise
+     * @return Whether any parent scrolled.
+     */
+    public boolean requestRectangleOnScreen(Rect rectangle, boolean immediate) {
+        View child = this;
+        ViewParent parent = mParent;
+        boolean scrolled = false;
+        while (parent != null) {
+            scrolled |= parent.requestChildRectangleOnScreen(child,
+                    rectangle, immediate);
+
+            // offset rect so next call has the rectangle in the
+            // coordinate system of its direct child.
+            rectangle.offset(child.getLeft(), child.getTop());
+            rectangle.offset(-child.getScrollX(), -child.getScrollY());
+
+            if (!(parent instanceof View)) {
+                break;
+            }
+            
+            child = (View) parent;
+            parent = child.getParent();
+        }
+        return scrolled;
+    }
+
+    /**
+     * Called when this view wants to give up focus. This will cause
+     * {@link #onFocusChanged} to be called.
+     */
+    public void clearFocus() {
+        if (DBG) {
+            System.out.println(this + " clearFocus()");
+        }
+
+        if ((mPrivateFlags & FOCUSED) != 0) {
+            mPrivateFlags &= ~FOCUSED;
+
+            if (mParent != null) {
+                mParent.clearChildFocus(this);
+            }
+
+            onFocusChanged(false, 0, null);
+            refreshDrawableState();
+        }
+    }
+
+    /**
+     * Called to clear the focus of a view that is about to be removed.
+     * Doesn't call clearChildFocus, which prevents this view from taking
+     * focus again before it has been removed from the parent
+     */
+    void clearFocusForRemoval() {
+        if ((mPrivateFlags & FOCUSED) != 0) {
+            mPrivateFlags &= ~FOCUSED;
+
+            onFocusChanged(false, 0, null);
+            refreshDrawableState();
+        }
+    }
+
+    /**
+     * Called internally by the view system when a new view is getting focus.
+     * This is what clears the old focus.
+     */
+    void unFocus() {
+        if (DBG) {
+            System.out.println(this + " unFocus()");
+        }
+
+        if ((mPrivateFlags & FOCUSED) != 0) {
+            mPrivateFlags &= ~FOCUSED;
+
+            onFocusChanged(false, 0, null);
+            refreshDrawableState();
+        }
+    }
+
+    /**
+     * Returns true if this view has focus iteself, or is the ancestor of the
+     * view that has focus.
+     *
+     * @return True if this view has or contains focus, false otherwise.
+     */
+    @ViewDebug.ExportedProperty
+    public boolean hasFocus() {
+        return (mPrivateFlags & FOCUSED) != 0;
+    }
+
+    /**
+     * Returns true if this view is focusable or if it contains a reachable View
+     * for which {@link #hasFocusable()} returns true. A "reachable hasFocusable()"
+     * is a View whose parents do not block descendants focus.
+     *
+     * Only {@link #VISIBLE} views are considered focusable.
+     *
+     * @return True if the view is focusable or if the view contains a focusable
+     *         View, false otherwise.
+     *
+     * @see ViewGroup#FOCUS_BLOCK_DESCENDANTS
+     */
+    public boolean hasFocusable() {
+        return (mViewFlags & VISIBILITY_MASK) == VISIBLE && isFocusable();
+    }
+
+    /**
+     * Called by the view system when the focus state of this view changes.
+     * When the focus change event is caused by directional navigation, direction
+     * and previouslyFocusedRect provide insight into where the focus is coming from.
+     * When overriding, be sure to call up through to the super class so that
+     * the standard focus handling will occur.
+     * 
+     * @param gainFocus True if the View has focus; false otherwise.
+     * @param direction The direction focus has moved when requestFocus()
+     *                  is called to give this view focus. Values are
+     *                  View.FOCUS_UP, View.FOCUS_DOWN, View.FOCUS_LEFT or
+     *                  View.FOCUS_RIGHT. It may not always apply, in which
+     *                  case use the default.
+     * @param previouslyFocusedRect The rectangle, in this view's coordinate
+     *        system, of the previously focused view.  If applicable, this will be
+     *        passed in as finer grained information about where the focus is coming
+     *        from (in addition to direction).  Will be <code>null</code> otherwise.
+     */
+    protected void onFocusChanged(boolean gainFocus, int direction, Rect previouslyFocusedRect) {
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (!gainFocus) {
+            if (isPressed()) {
+                setPressed(false);
+            }
+            if (imm != null && mAttachInfo != null
+                    && mAttachInfo.mHasWindowFocus) {
+                imm.focusOut(this);
+            }
+        } else if (imm != null && mAttachInfo != null
+                && mAttachInfo.mHasWindowFocus) {
+            imm.focusIn(this);
+        }
+        
+        invalidate();
+        if (mOnFocusChangeListener != null) {
+            mOnFocusChangeListener.onFocusChange(this, gainFocus);
+        }
+    }
+
+    /**
+     * Returns true if this view has focus
+     *
+     * @return True if this view has focus, false otherwise.
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isFocused() {
+        return (mPrivateFlags & FOCUSED) != 0;
+    }
+
+    /**
+     * Find the view in the hierarchy rooted at this view that currently has
+     * focus.
+     *
+     * @return The view that currently has focus, or null if no focused view can
+     *         be found.
+     */
+    public View findFocus() {
+        return (mPrivateFlags & FOCUSED) != 0 ? this : null;
+    }
+
+    /**
+     * Change whether this view is one of the set of scrollable containers in
+     * its window.  This will be used to determine whether the window can
+     * resize or must pan when a soft input area is open -- scrollable
+     * containers allow the window to use resize mode since the container
+     * will appropriately shrink.
+     */
+    public void setScrollContainer(boolean isScrollContainer) {
+        if (isScrollContainer) {
+            if (mAttachInfo != null && (mPrivateFlags&SCROLL_CONTAINER_ADDED) == 0) {
+                mAttachInfo.mScrollContainers.add(this);
+                mPrivateFlags |= SCROLL_CONTAINER_ADDED;
+            }
+            mPrivateFlags |= SCROLL_CONTAINER;
+        } else {
+            if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) {
+                mAttachInfo.mScrollContainers.remove(this);
+            }
+            mPrivateFlags &= ~(SCROLL_CONTAINER|SCROLL_CONTAINER_ADDED);
+        }
+    }
+
+    /**
+     * Returns the quality of the drawing cache.
+     *
+     * @return One of {@link #DRAWING_CACHE_QUALITY_AUTO},
+     *         {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH}
+     *
+     * @see #setDrawingCacheQuality(int)
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #isDrawingCacheEnabled()
+     *
+     * @attr ref android.R.styleable#View_drawingCacheQuality
+     */
+    public int getDrawingCacheQuality() {
+        return mViewFlags & DRAWING_CACHE_QUALITY_MASK;
+    }
+
+    /**
+     * Set the drawing cache quality of this view. This value is used only when the
+     * drawing cache is enabled
+     *
+     * @param quality One of {@link #DRAWING_CACHE_QUALITY_AUTO},
+     *        {@link #DRAWING_CACHE_QUALITY_LOW}, or {@link #DRAWING_CACHE_QUALITY_HIGH}
+     *
+     * @see #getDrawingCacheQuality()
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #isDrawingCacheEnabled()
+     *
+     * @attr ref android.R.styleable#View_drawingCacheQuality
+     */
+    public void setDrawingCacheQuality(int quality) {
+        setFlags(quality, DRAWING_CACHE_QUALITY_MASK);
+    }
+
+    /**
+     * Returns whether the screen should remain on, corresponding to the current
+     * value of {@link #KEEP_SCREEN_ON}.
+     *
+     * @return Returns true if {@link #KEEP_SCREEN_ON} is set.
+     *
+     * @see #setKeepScreenOn(boolean)
+     *
+     * @attr ref android.R.styleable#View_keepScreenOn
+     */
+    public boolean getKeepScreenOn() {
+        return (mViewFlags & KEEP_SCREEN_ON) != 0;
+    }
+
+    /**
+     * Controls whether the screen should remain on, modifying the
+     * value of {@link #KEEP_SCREEN_ON}.
+     *
+     * @param keepScreenOn Supply true to set {@link #KEEP_SCREEN_ON}.
+     *
+     * @see #getKeepScreenOn()
+     *
+     * @attr ref android.R.styleable#View_keepScreenOn
+     */
+    public void setKeepScreenOn(boolean keepScreenOn) {
+        setFlags(keepScreenOn ? KEEP_SCREEN_ON : 0, KEEP_SCREEN_ON);
+    }
+
+    /**
+     * @return The user specified next focus ID.
+     *
+     * @attr ref android.R.styleable#View_nextFocusLeft
+     */
+    public int getNextFocusLeftId() {
+        return mNextFocusLeftId;
+    }
+
+    /**
+     * Set the id of the view to use for the next focus
+     *
+     * @param nextFocusLeftId
+     *
+     * @attr ref android.R.styleable#View_nextFocusLeft
+     */
+    public void setNextFocusLeftId(int nextFocusLeftId) {
+        mNextFocusLeftId = nextFocusLeftId;
+    }
+
+    /**
+     * @return The user specified next focus ID.
+     *
+     * @attr ref android.R.styleable#View_nextFocusRight
+     */
+    public int getNextFocusRightId() {
+        return mNextFocusRightId;
+    }
+
+    /**
+     * Set the id of the view to use for the next focus
+     *
+     * @param nextFocusRightId
+     *
+     * @attr ref android.R.styleable#View_nextFocusRight
+     */
+    public void setNextFocusRightId(int nextFocusRightId) {
+        mNextFocusRightId = nextFocusRightId;
+    }
+
+    /**
+     * @return The user specified next focus ID.
+     *
+     * @attr ref android.R.styleable#View_nextFocusUp
+     */
+    public int getNextFocusUpId() {
+        return mNextFocusUpId;
+    }
+
+    /**
+     * Set the id of the view to use for the next focus
+     *
+     * @param nextFocusUpId
+     *
+     * @attr ref android.R.styleable#View_nextFocusUp
+     */
+    public void setNextFocusUpId(int nextFocusUpId) {
+        mNextFocusUpId = nextFocusUpId;
+    }
+
+    /**
+     * @return The user specified next focus ID.
+     *
+     * @attr ref android.R.styleable#View_nextFocusDown
+     */
+    public int getNextFocusDownId() {
+        return mNextFocusDownId;
+    }
+
+    /**
+     * Set the id of the view to use for the next focus
+     *
+     * @param nextFocusDownId
+     *
+     * @attr ref android.R.styleable#View_nextFocusDown
+     */
+    public void setNextFocusDownId(int nextFocusDownId) {
+        mNextFocusDownId = nextFocusDownId;
+    }
+
+    /**
+     * Returns the visibility of this view and all of its ancestors
+     *
+     * @return True if this view and all of its ancestors are {@link #VISIBLE}
+     */
+    public boolean isShown() {
+        View current = this;
+        //noinspection ConstantConditions
+        do {
+            if ((current.mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+                return false;
+            }
+            ViewParent parent = current.mParent;
+            if (parent == null) {
+                return false; // We are not attached to the view root
+            }
+            if (!(parent instanceof View)) {
+                return true;
+            }
+            current = (View) parent;
+        } while (current != null);
+
+        return false;
+    }
+
+    /**
+     * Apply the insets for system windows to this view, if the FITS_SYSTEM_WINDOWS flag
+     * is set
+     *
+     * @param insets Insets for system windows
+     *
+     * @return True if this view applied the insets, false otherwise
+     */
+    protected boolean fitSystemWindows(Rect insets) {
+        if ((mViewFlags & FITS_SYSTEM_WINDOWS) == FITS_SYSTEM_WINDOWS) {
+            mPaddingLeft = insets.left;
+            mPaddingTop = insets.top;
+            mPaddingRight = insets.right;
+            mPaddingBottom = insets.bottom;
+            requestLayout();
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns the visibility status for this view.
+     *
+     * @return One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+     * @attr ref android.R.styleable#View_visibility
+     */
+    @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
+        @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
+        @ViewDebug.IntToString(from = 8, to = "GONE")
+    })
+    public int getVisibility() {
+        return mViewFlags & VISIBILITY_MASK;
+    }
+
+    /**
+     * Set the enabled state of this view.
+     *
+     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+     * @attr ref android.R.styleable#View_visibility
+     */
+    @RemotableViewMethod
+    public void setVisibility(int visibility) {
+        setFlags(visibility, VISIBILITY_MASK);
+        if (mBGDrawable != null) mBGDrawable.setVisible(visibility == VISIBLE, false);
+    }
+
+    /**
+     * Returns the enabled status for this view. The interpretation of the
+     * enabled state varies by subclass.
+     *
+     * @return True if this view is enabled, false otherwise.
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isEnabled() {
+        return (mViewFlags & ENABLED_MASK) == ENABLED;
+    }
+
+    /**
+     * Set the enabled state of this view. The interpretation of the enabled
+     * state varies by subclass.
+     *
+     * @param enabled True if this view is enabled, false otherwise.
+     */
+    public void setEnabled(boolean enabled) {
+        setFlags(enabled ? ENABLED : DISABLED, ENABLED_MASK);
+
+        /*
+         * The View most likely has to change its appearance, so refresh
+         * the drawable state.
+         */
+        refreshDrawableState();
+
+        // Invalidate too, since the default behavior for views is to be
+        // be drawn at 50% alpha rather than to change the drawable.
+        invalidate();
+    }
+
+    /**
+     * Set whether this view can receive the focus.
+     *
+     * Setting this to false will also ensure that this view is not focusable
+     * in touch mode.
+     *
+     * @param focusable If true, this view can receive the focus.
+     *
+     * @see #setFocusableInTouchMode(boolean)
+     * @attr ref android.R.styleable#View_focusable
+     */
+    public void setFocusable(boolean focusable) {
+        if (!focusable) {
+            setFlags(0, FOCUSABLE_IN_TOUCH_MODE);
+        }
+        setFlags(focusable ? FOCUSABLE : NOT_FOCUSABLE, FOCUSABLE_MASK);
+    }
+
+    /**
+     * Set whether this view can receive focus while in touch mode.
+     *
+     * Setting this to true will also ensure that this view is focusable.
+     *
+     * @param focusableInTouchMode If true, this view can receive the focus while
+     *   in touch mode.
+     *
+     * @see #setFocusable(boolean)
+     * @attr ref android.R.styleable#View_focusableInTouchMode
+     */
+    public void setFocusableInTouchMode(boolean focusableInTouchMode) {
+        // Focusable in touch mode should always be set before the focusable flag
+        // otherwise, setting the focusable flag will trigger a focusableViewAvailable()
+        // which, in touch mode, will not successfully request focus on this view
+        // because the focusable in touch mode flag is not set
+        setFlags(focusableInTouchMode ? FOCUSABLE_IN_TOUCH_MODE : 0, FOCUSABLE_IN_TOUCH_MODE);
+        if (focusableInTouchMode) {
+            setFlags(FOCUSABLE, FOCUSABLE_MASK);
+        }
+    }
+
+    /**
+     * Set whether this view should have sound effects enabled for events such as
+     * clicking and touching.
+     *
+     * <p>You may wish to disable sound effects for a view if you already play sounds,
+     * for instance, a dial key that plays dtmf tones.
+     *
+     * @param soundEffectsEnabled whether sound effects are enabled for this view.
+     * @see #isSoundEffectsEnabled()
+     * @see #playSoundEffect(int)
+     * @attr ref android.R.styleable#View_soundEffectsEnabled
+     */
+    public void setSoundEffectsEnabled(boolean soundEffectsEnabled) {
+        setFlags(soundEffectsEnabled ? SOUND_EFFECTS_ENABLED: 0, SOUND_EFFECTS_ENABLED);
+    }
+
+    /**
+     * @return whether this view should have sound effects enabled for events such as
+     *     clicking and touching.
+     *
+     * @see #setSoundEffectsEnabled(boolean)
+     * @see #playSoundEffect(int)
+     * @attr ref android.R.styleable#View_soundEffectsEnabled
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isSoundEffectsEnabled() {
+        return SOUND_EFFECTS_ENABLED == (mViewFlags & SOUND_EFFECTS_ENABLED);
+    }
+
+    /**
+     * Set whether this view should have haptic feedback for events such as
+     * long presses.
+     *
+     * <p>You may wish to disable haptic feedback if your view already controls
+     * its own haptic feedback.
+     *
+     * @param hapticFeedbackEnabled whether haptic feedback enabled for this view.
+     * @see #isHapticFeedbackEnabled()
+     * @see #performHapticFeedback(int)
+     * @attr ref android.R.styleable#View_hapticFeedbackEnabled
+     */
+    public void setHapticFeedbackEnabled(boolean hapticFeedbackEnabled) {
+        setFlags(hapticFeedbackEnabled ? HAPTIC_FEEDBACK_ENABLED: 0, HAPTIC_FEEDBACK_ENABLED);
+    }
+
+    /**
+     * @return whether this view should have haptic feedback enabled for events
+     * long presses.
+     *
+     * @see #setHapticFeedbackEnabled(boolean)
+     * @see #performHapticFeedback(int)
+     * @attr ref android.R.styleable#View_hapticFeedbackEnabled
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isHapticFeedbackEnabled() {
+        return HAPTIC_FEEDBACK_ENABLED == (mViewFlags & HAPTIC_FEEDBACK_ENABLED);
+    }
+
+    /**
+     * If this view doesn't do any drawing on its own, set this flag to
+     * allow further optimizations. By default, this flag is not set on
+     * View, but could be set on some View subclasses such as ViewGroup.
+     *
+     * Typically, if you override {@link #onDraw} you should clear this flag.
+     *
+     * @param willNotDraw whether or not this View draw on its own
+     */
+    public void setWillNotDraw(boolean willNotDraw) {
+        setFlags(willNotDraw ? WILL_NOT_DRAW : 0, DRAW_MASK);
+    }
+
+    /**
+     * Returns whether or not this View draws on its own.
+     *
+     * @return true if this view has nothing to draw, false otherwise
+     */
+    @ViewDebug.ExportedProperty
+    public boolean willNotDraw() {
+        return (mViewFlags & DRAW_MASK) == WILL_NOT_DRAW;
+    }
+
+    /**
+     * When a View's drawing cache is enabled, drawing is redirected to an
+     * offscreen bitmap. Some views, like an ImageView, must be able to
+     * bypass this mechanism if they already draw a single bitmap, to avoid
+     * unnecessary usage of the memory.
+     *
+     * @param willNotCacheDrawing true if this view does not cache its
+     *        drawing, false otherwise
+     */
+    public void setWillNotCacheDrawing(boolean willNotCacheDrawing) {
+        setFlags(willNotCacheDrawing ? WILL_NOT_CACHE_DRAWING : 0, WILL_NOT_CACHE_DRAWING);
+    }
+
+    /**
+     * Returns whether or not this View can cache its drawing or not.
+     *
+     * @return true if this view does not cache its drawing, false otherwise
+     */
+    @ViewDebug.ExportedProperty
+    public boolean willNotCacheDrawing() {
+        return (mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING;
+    }
+
+    /**
+     * Indicates whether this view reacts to click events or not.
+     *
+     * @return true if the view is clickable, false otherwise
+     *
+     * @see #setClickable(boolean)
+     * @attr ref android.R.styleable#View_clickable
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isClickable() {
+        return (mViewFlags & CLICKABLE) == CLICKABLE;
+    }
+
+    /**
+     * Enables or disables click events for this view. When a view
+     * is clickable it will change its state to "pressed" on every click.
+     * Subclasses should set the view clickable to visually react to
+     * user's clicks.
+     *
+     * @param clickable true to make the view clickable, false otherwise
+     *
+     * @see #isClickable()
+     * @attr ref android.R.styleable#View_clickable
+     */
+    public void setClickable(boolean clickable) {
+        setFlags(clickable ? CLICKABLE : 0, CLICKABLE);
+    }
+
+    /**
+     * Indicates whether this view reacts to long click events or not.
+     *
+     * @return true if the view is long clickable, false otherwise
+     *
+     * @see #setLongClickable(boolean)
+     * @attr ref android.R.styleable#View_longClickable
+     */
+    public boolean isLongClickable() {
+        return (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE;
+    }
+
+    /**
+     * Enables or disables long click events for this view. When a view is long
+     * clickable it reacts to the user holding down the button for a longer
+     * duration than a tap. This event can either launch the listener or a
+     * context menu.
+     *
+     * @param longClickable true to make the view long clickable, false otherwise
+     * @see #isLongClickable()
+     * @attr ref android.R.styleable#View_longClickable
+     */
+    public void setLongClickable(boolean longClickable) {
+        setFlags(longClickable ? LONG_CLICKABLE : 0, LONG_CLICKABLE);
+    }
+
+    /**
+     * Sets the pressed that for this view.
+     *
+     * @see #isClickable()
+     * @see #setClickable(boolean)
+     *
+     * @param pressed Pass true to set the View's internal state to "pressed", or false to reverts
+     *        the View's internal state from a previously set "pressed" state.
+     */
+    public void setPressed(boolean pressed) {
+        if (pressed) {
+            mPrivateFlags |= PRESSED;
+        } else {
+            mPrivateFlags &= ~PRESSED;
+        }
+        refreshDrawableState();
+        dispatchSetPressed(pressed);
+    }
+
+    /**
+     * Dispatch setPressed to all of this View's children.
+     *
+     * @see #setPressed(boolean)
+     *
+     * @param pressed The new pressed state
+     */
+    protected void dispatchSetPressed(boolean pressed) {
+    }
+
+    /**
+     * Indicates whether the view is currently in pressed state. Unless
+     * {@link #setPressed(boolean)} is explicitly called, only clickable views can enter
+     * the pressed state.
+     *
+     * @see #setPressed
+     * @see #isClickable()
+     * @see #setClickable(boolean)
+     *
+     * @return true if the view is currently pressed, false otherwise
+     */
+    public boolean isPressed() {
+        return (mPrivateFlags & PRESSED) == PRESSED;
+    }
+
+    /**
+     * Indicates whether this view will save its state (that is,
+     * whether its {@link #onSaveInstanceState} method will be called).
+     *
+     * @return Returns true if the view state saving is enabled, else false.
+     *
+     * @see #setSaveEnabled(boolean)
+     * @attr ref android.R.styleable#View_saveEnabled
+     */
+    public boolean isSaveEnabled() {
+        return (mViewFlags & SAVE_DISABLED_MASK) != SAVE_DISABLED;
+    }
+
+    /**
+     * Controls whether the saving of this view's state is
+     * enabled (that is, whether its {@link #onSaveInstanceState} method
+     * will be called).  Note that even if freezing is enabled, the
+     * view still must have an id assigned to it (via {@link #setId setId()})
+     * for its state to be saved.  This flag can only disable the
+     * saving of this view; any child views may still have their state saved.
+     *
+     * @param enabled Set to false to <em>disable</em> state saving, or true
+     * (the default) to allow it.
+     *
+     * @see #isSaveEnabled()
+     * @see #setId(int)
+     * @see #onSaveInstanceState()
+     * @attr ref android.R.styleable#View_saveEnabled
+     */
+    public void setSaveEnabled(boolean enabled) {
+        setFlags(enabled ? 0 : SAVE_DISABLED, SAVE_DISABLED_MASK);
+    }
+
+
+    /**
+     * Returns whether this View is able to take focus.
+     *
+     * @return True if this view can take focus, or false otherwise.
+     * @attr ref android.R.styleable#View_focusable
+     */
+    @ViewDebug.ExportedProperty
+    public final boolean isFocusable() {
+        return FOCUSABLE == (mViewFlags & FOCUSABLE_MASK);
+    }
+
+    /**
+     * When a view is focusable, it may not want to take focus when in touch mode.
+     * For example, a button would like focus when the user is navigating via a D-pad
+     * so that the user can click on it, but once the user starts touching the screen,
+     * the button shouldn't take focus
+     * @return Whether the view is focusable in touch mode.
+     * @attr ref android.R.styleable#View_focusableInTouchMode
+     */
+    @ViewDebug.ExportedProperty
+    public final boolean isFocusableInTouchMode() {
+        return FOCUSABLE_IN_TOUCH_MODE == (mViewFlags & FOCUSABLE_IN_TOUCH_MODE);
+    }
+
+    /**
+     * Find the nearest view in the specified direction that can take focus.
+     * This does not actually give focus to that view.
+     *
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     *
+     * @return The nearest focusable in the specified direction, or null if none
+     *         can be found.
+     */
+    public View focusSearch(int direction) {
+        if (mParent != null) {
+            return mParent.focusSearch(this, direction);
+        } else {
+            return null;
+        }
+    }
+
+    /**
+     * This method is the last chance for the focused view and its ancestors to
+     * respond to an arrow key. This is called when the focused view did not
+     * consume the key internally, nor could the view system find a new view in
+     * the requested direction to give focus to.
+     *
+     * @param focused The currently focused view.
+     * @param direction The direction focus wants to move. One of FOCUS_UP,
+     *        FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT.
+     * @return True if the this view consumed this unhandled move.
+     */
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        return false;
+    }
+
+    /**
+     * If a user manually specified the next view id for a particular direction,
+     * use the root to look up the view.  Once a view is found, it is cached
+     * for future lookups.
+     * @param root The root view of the hierarchy containing this view.
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     * @return The user specified next view, or null if there is none.
+     */
+    View findUserSetNextFocus(View root, int direction) {
+        switch (direction) {
+            case FOCUS_LEFT:
+                if (mNextFocusLeftId == View.NO_ID) return null;
+                return findViewShouldExist(root, mNextFocusLeftId);
+            case FOCUS_RIGHT:
+                if (mNextFocusRightId == View.NO_ID) return null;
+                return findViewShouldExist(root, mNextFocusRightId);
+            case FOCUS_UP:
+                if (mNextFocusUpId == View.NO_ID) return null;
+                return findViewShouldExist(root, mNextFocusUpId);
+            case FOCUS_DOWN:
+                if (mNextFocusDownId == View.NO_ID) return null;
+                return findViewShouldExist(root, mNextFocusDownId);
+        }
+        return null;
+    }
+
+    private static View findViewShouldExist(View root, int childViewId) {
+        View result = root.findViewById(childViewId);
+        if (result == null) {
+            Log.w(VIEW_LOG_TAG, "couldn't find next focus view specified "
+                    + "by user for id " + childViewId);
+        }
+        return result;
+    }
+
+    /**
+     * Find and return all focusable views that are descendants of this view,
+     * possibly including this view if it is focusable itself.
+     *
+     * @param direction The direction of the focus
+     * @return A list of focusable views
+     */
+    public ArrayList<View> getFocusables(int direction) {
+        ArrayList<View> result = new ArrayList<View>(24);
+        addFocusables(result, direction);
+        return result;
+    }
+
+    /**
+     * Add any focusable views that are descendants of this view (possibly
+     * including this view if it is focusable itself) to views.  If we are in touch mode,
+     * only add views that are also focusable in touch mode.
+     *
+     * @param views Focusable views found so far
+     * @param direction The direction of the focus
+     */
+    public void addFocusables(ArrayList<View> views, int direction) {
+        if (!isFocusable()) return;
+
+        if (isInTouchMode() && !isFocusableInTouchMode()) return;
+
+        views.add(this);
+    }
+
+    /**
+     * Find and return all touchable views that are descendants of this view,
+     * possibly including this view if it is touchable itself.
+     *
+     * @return A list of touchable views
+     */
+    public ArrayList<View> getTouchables() {
+        ArrayList<View> result = new ArrayList<View>();
+        addTouchables(result);
+        return result;
+    }
+
+    /**
+     * Add any touchable views that are descendants of this view (possibly
+     * including this view if it is touchable itself) to views.
+     *
+     * @param views Touchable views found so far
+     */
+    public void addTouchables(ArrayList<View> views) {
+        final int viewFlags = mViewFlags;
+
+        if (((viewFlags & CLICKABLE) == CLICKABLE || (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)
+                && (viewFlags & ENABLED_MASK) == ENABLED) {
+            views.add(this);
+        }
+    }
+
+    /**
+     * Call this to try to give focus to a specific view or to one of its
+     * descendants.
+     *
+     * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns false),
+     * or if it is focusable and it is not focusable in touch mode ({@link #isFocusableInTouchMode})
+     * while the device is in touch mode.
+     *
+     * See also {@link #focusSearch}, which is what you call to say that you
+     * have focus, and you want your parent to look for the next one.
+     *
+     * This is equivalent to calling {@link #requestFocus(int, Rect)} with arguments
+     * {@link #FOCUS_DOWN} and <code>null</code>.
+     *
+     * @return Whether this view or one of its descendants actually took focus.
+     */
+    public final boolean requestFocus() {
+        return requestFocus(View.FOCUS_DOWN);
+    }
+
+
+    /**
+     * Call this to try to give focus to a specific view or to one of its
+     * descendants and give it a hint about what direction focus is heading.
+     *
+     * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns false),
+     * or if it is focusable and it is not focusable in touch mode ({@link #isFocusableInTouchMode})
+     * while the device is in touch mode.
+     *
+     * See also {@link #focusSearch}, which is what you call to say that you
+     * have focus, and you want your parent to look for the next one.
+     *
+     * This is equivalent to calling {@link #requestFocus(int, Rect)} with
+     * <code>null</code> set for the previously focused rectangle.
+     *
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     * @return Whether this view or one of its descendants actually took focus.
+     */
+    public final boolean requestFocus(int direction) {
+        return requestFocus(direction, null);
+    }
+
+    /**
+     * Call this to try to give focus to a specific view or to one of its descendants
+     * and give it hints about the direction and a specific rectangle that the focus
+     * is coming from.  The rectangle can help give larger views a finer grained hint
+     * about where focus is coming from, and therefore, where to show selection, or
+     * forward focus change internally.
+     *
+     * A view will not actually take focus if it is not focusable ({@link #isFocusable} returns false),
+     * or if it is focusable and it is not focusable in touch mode ({@link #isFocusableInTouchMode})
+     * while the device is in touch mode.
+     *
+     * A View will not take focus if it is not visible.
+     *
+     * A View will not take focus if one of its parents has {@link android.view.ViewGroup#getDescendantFocusability()}
+     * equal to {@link ViewGroup#FOCUS_BLOCK_DESCENDANTS}.
+     *
+     * See also {@link #focusSearch}, which is what you call to say that you
+     * have focus, and you want your parent to look for the next one.
+     *
+     * You may wish to override this method if your custom {@link View} has an internal
+     * {@link View} that it wishes to forward the request to.
+     *
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
+     *        to give a finer grained hint about where focus is coming from.  May be null
+     *        if there is no hint.
+     * @return Whether this view or one of its descendants actually took focus.
+     */
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        // need to be focusable
+        if ((mViewFlags & FOCUSABLE_MASK) != FOCUSABLE ||
+                (mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+            return false;
+        }
+
+        // need to be focusable in touch mode if in touch mode
+        if (isInTouchMode() &&
+                (FOCUSABLE_IN_TOUCH_MODE != (mViewFlags & FOCUSABLE_IN_TOUCH_MODE))) {
+            return false;
+        }
+
+        // need to not have any parents blocking us
+        if (hasAncestorThatBlocksDescendantFocus()) {
+            return false;
+        }
+
+        handleFocusGainInternal(direction, previouslyFocusedRect);
+        return true;
+    }
+
+    /**
+     * Call this to try to give focus to a specific view or to one of its descendants. This is a
+     * special variant of {@link #requestFocus() } that will allow views that are not focuable in
+     * touch mode to request focus when they are touched.
+     *
+     * @return Whether this view or one of its descendants actually took focus.
+     *
+     * @see #isInTouchMode()
+     *
+     */
+    public final boolean requestFocusFromTouch() {
+        // Leave touch mode if we need to
+        if (isInTouchMode()) {
+            View root = getRootView();
+            if (root != null) {
+               ViewRoot viewRoot = (ViewRoot)root.getParent();
+               if (viewRoot != null) {
+                   viewRoot.ensureTouchMode(false);
+               }
+            }
+        }
+        return requestFocus(View.FOCUS_DOWN);
+    }
+
+    /**
+     * @return Whether any ancestor of this view blocks descendant focus.
+     */
+    private boolean hasAncestorThatBlocksDescendantFocus() {
+        ViewParent ancestor = mParent;
+        while (ancestor instanceof ViewGroup) {
+            final ViewGroup vgAncestor = (ViewGroup) ancestor;
+            if (vgAncestor.getDescendantFocusability() == ViewGroup.FOCUS_BLOCK_DESCENDANTS) {
+                return true;
+            } else {
+                ancestor = vgAncestor.getParent();
+            }
+        }
+        return false;
+    }
+
+    /**
+     * This is called when a container is going to temporarily detach a child
+     * that currently has focus, with
+     * {@link ViewGroup#detachViewFromParent(View) ViewGroup.detachViewFromParent}.
+     * It will either be followed by {@link #onFinishTemporaryDetach()} or
+     * {@link #onDetachedFromWindow()} when the container is done.  Generally
+     * this is currently only done ListView for a view with focus.
+     */
+    public void onStartTemporaryDetach() {
+    }
+    
+    /**
+     * Called after {@link #onStartTemporaryDetach} when the container is done
+     * changing the view.
+     */
+    public void onFinishTemporaryDetach() {
+    }
+    
+    /**
+     * capture information of this view for later analysis: developement only
+     * check dynamic switch to make sure we only dump view
+     * when ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW) is set
+     */
+    private static void captureViewInfo(String subTag, View v) {
+        if (v == null ||
+           SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_VIEW, 0) == 0) {
+            return;
+        }
+        ViewDebug.dumpCapturedView(subTag, v);
+    }
+
+    /**
+     * Dispatch a key event before it is processed by any input method
+     * associated with the view hierarchy.  This can be used to intercept
+     * key events in special situations before the IME consumes them; a
+     * typical example would be handling the BACK key to update the application's
+     * UI instead of allowing the IME to see it and close itself.
+     *
+     * @param event The key event to be dispatched.
+     * @return True if the event was handled, false otherwise.
+     */
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        return onKeyPreIme(event.getKeyCode(), event);
+    }
+
+    /**
+     * Dispatch a key event to the next view on the focus path. This path runs
+     * from the top of the view tree down to the currently focused view. If this
+     * view has focus, it will dispatch to itself. Otherwise it will dispatch
+     * the next node down the focus path. This method also fires any key
+     * listeners.
+     *
+     * @param event The key event to be dispatched.
+     * @return True if the event was handled, false otherwise.
+     */
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        // If any attached key listener a first crack at the event.
+        //noinspection SimplifiableIfStatement
+
+        if (android.util.Config.LOGV) {
+            captureViewInfo("captureViewKeyEvent", this);
+        }
+
+        if (mOnKeyListener != null && (mViewFlags & ENABLED_MASK) == ENABLED
+                && mOnKeyListener.onKey(this, event.getKeyCode(), event)) {
+            return true;
+        }
+
+        return event.dispatch(this);
+    }
+
+    /**
+     * Dispatches a key shortcut event.
+     *
+     * @param event The key event to be dispatched.
+     * @return True if the event was handled by the view, false otherwise.
+     */
+    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+        return onKeyShortcut(event.getKeyCode(), event);
+    }
+
+    /**
+     * Pass the touch screen motion event down to the target view, or this
+     * view if it is the target.
+     *
+     * @param event The motion event to be dispatched.
+     * @return True if the event was handled by the view, false otherwise.
+     */
+    public boolean dispatchTouchEvent(MotionEvent event) {
+        if (mOnTouchListener != null && (mViewFlags & ENABLED_MASK) == ENABLED &&
+                mOnTouchListener.onTouch(this, event)) {
+            return true;
+        }
+        return onTouchEvent(event);
+    }
+
+    /**
+     * Pass a trackball motion event down to the focused view.
+     *
+     * @param event The motion event to be dispatched.
+     * @return True if the event was handled by the view, false otherwise.
+     */
+    public boolean dispatchTrackballEvent(MotionEvent event) {
+        //Log.i("view", "view=" + this + ", " + event.toString());
+        return onTrackballEvent(event);
+    }
+
+    /**
+     * Called when the window containing this view gains or loses window focus.
+     * ViewGroups should override to route to their children.
+     *
+     * @param hasFocus True if the window containing this view now has focus,
+     *        false otherwise.
+     */
+    public void dispatchWindowFocusChanged(boolean hasFocus) {
+        onWindowFocusChanged(hasFocus);
+    }
+
+    /**
+     * Called when the window containing this view gains or loses focus.  Note
+     * that this is separate from view focus: to receive key events, both
+     * your view and its window must have focus.  If a window is displayed
+     * on top of yours that takes input focus, then your own window will lose
+     * focus but the view focus will remain unchanged.
+     *
+     * @param hasWindowFocus True if the window containing this view now has
+     *        focus, false otherwise.
+     */
+    public void onWindowFocusChanged(boolean hasWindowFocus) {
+        InputMethodManager imm = InputMethodManager.peekInstance();
+        if (!hasWindowFocus) {
+            if (isPressed()) {
+                setPressed(false);
+            }
+            if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
+                imm.focusOut(this);
+            }
+        } else if (imm != null && (mPrivateFlags & FOCUSED) != 0) {
+            imm.focusIn(this);
+        }
+        refreshDrawableState();
+    }
+
+    /**
+     * Returns true if this view is in a window that currently has window focus.
+     * Note that this is not the same as the view itself having focus.
+     *
+     * @return True if this view is in a window that currently has window focus.
+     */
+    public boolean hasWindowFocus() {
+        return mAttachInfo != null && mAttachInfo.mHasWindowFocus;
+    }
+
+    /**
+     * Dispatch a window visibility change down the view hierarchy.
+     * ViewGroups should override to route to their children.
+     *
+     * @param visibility The new visibility of the window.
+     *
+     * @see #onWindowVisibilityChanged
+     */
+    public void dispatchWindowVisibilityChanged(int visibility) {
+        onWindowVisibilityChanged(visibility);
+    }
+
+    /**
+     * Called when the window containing has change its visibility
+     * (between {@link #GONE}, {@link #INVISIBLE}, and {@link #VISIBLE}).  Note
+     * that this tells you whether or not your window is being made visible
+     * to the window manager; this does <em>not</em> tell you whether or not
+     * your window is obscured by other windows on the screen, even if it
+     * is itself visible.
+     *
+     * @param visibility The new visibility of the window.
+     */
+    protected void onWindowVisibilityChanged(int visibility) {
+    }
+
+    /**
+     * Returns the current visibility of the window this view is attached to
+     * (either {@link #GONE}, {@link #INVISIBLE}, or {@link #VISIBLE}).
+     *
+     * @return Returns the current visibility of the view's window.
+     */
+    public int getWindowVisibility() {
+        return mAttachInfo != null ? mAttachInfo.mWindowVisibility : GONE;
+    }
+
+    /**
+     * Retrieve the overall visible display size in which the window this view is
+     * attached to has been positioned in.  This takes into account screen
+     * decorations above the window, for both cases where the window itself
+     * is being position inside of them or the window is being placed under
+     * then and covered insets are used for the window to position its content
+     * inside.  In effect, this tells you the available area where content can
+     * be placed and remain visible to users.
+     *
+     * <p>This function requires an IPC back to the window manager to retrieve
+     * the requested information, so should not be used in performance critical
+     * code like drawing.
+     *
+     * @param outRect Filled in with the visible display frame.  If the view
+     * is not attached to a window, this is simply the raw display size.
+     */
+    public void getWindowVisibleDisplayFrame(Rect outRect) {
+        if (mAttachInfo != null) {
+            try {
+                mAttachInfo.mSession.getDisplayFrame(mAttachInfo.mWindow, outRect);
+            } catch (RemoteException e) {
+                return;
+            }
+            // XXX This is really broken, and probably all needs to be done
+            // in the window manager, and we need to know more about whether
+            // we want the area behind or in front of the IME.
+            final Rect insets = mAttachInfo.mVisibleInsets;
+            outRect.left += insets.left;
+            outRect.top += insets.top;
+            outRect.right -= insets.right;
+            outRect.bottom -= insets.bottom;
+            return;
+        }
+        Display d = WindowManagerImpl.getDefault().getDefaultDisplay();
+        outRect.set(0, 0, d.getWidth(), d.getHeight());
+    }
+
+    /**
+     * Private function to aggregate all per-view attributes in to the view
+     * root.
+     */
+    void dispatchCollectViewAttributes(int visibility) {
+        performCollectViewAttributes(visibility);
+    }
+
+    void performCollectViewAttributes(int visibility) {
+        //noinspection PointlessBitwiseExpression
+        if (((visibility | mViewFlags) & (VISIBILITY_MASK | KEEP_SCREEN_ON))
+                == (VISIBLE | KEEP_SCREEN_ON)) {
+            mAttachInfo.mKeepScreenOn = true;
+        }
+    }
+
+    void needGlobalAttributesUpdate(boolean force) {
+        AttachInfo ai = mAttachInfo;
+        if (ai != null) {
+            if (ai.mKeepScreenOn || force) {
+                ai.mRecomputeGlobalAttributes = true;
+            }
+        }
+    }
+
+    /**
+     * Returns whether the device is currently in touch mode.  Touch mode is entered
+     * once the user begins interacting with the device by touch, and affects various
+     * things like whether focus is always visible to the user.
+     *
+     * @return Whether the device is in touch mode.
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isInTouchMode() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mInTouchMode;
+        } else {
+            return ViewRoot.isInTouchMode();
+        }
+    }
+
+    /**
+     * Returns the context the view is running in, through which it can
+     * access the current theme, resources, etc.
+     *
+     * @return The view's Context.
+     */
+    @ViewDebug.CapturedViewProperty
+    public final Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Handle a key event before it is processed by any input method
+     * associated with the view hierarchy.  This can be used to intercept
+     * key events in special situations before the IME consumes them; a
+     * typical example would be handling the BACK key to update the application's
+     * UI instead of allowing the IME to see it and close itself.
+     *
+     * @param keyCode The value in event.getKeyCode().
+     * @param event Description of the key event.
+     * @return If you handled the event, return true. If you want to allow the
+     *         event to be handled by the next receiver, return false.
+     */
+    public boolean onKeyPreIme(int keyCode, KeyEvent event) {
+        return false;
+    }
+
+    /**
+     * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+     * KeyEvent.Callback.onKeyMultiple()}: perform press of the view
+     * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or {@link KeyEvent#KEYCODE_ENTER}
+     * is released, if the view is enabled and clickable.
+     *
+     * @param keyCode A key code that represents the button pressed, from
+     *                {@link android.view.KeyEvent}.
+     * @param event   The KeyEvent object that defines the button action.
+     */
+    public boolean onKeyDown(int keyCode, KeyEvent event) {
+        boolean result = false;
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_ENTER: {
+                if ((mViewFlags & ENABLED_MASK) == DISABLED) {
+                    return true;
+                }
+                // Long clickable items don't necessarily have to be clickable
+                if (((mViewFlags & CLICKABLE) == CLICKABLE ||
+                        (mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) &&
+                        (event.getRepeatCount() == 0)) {
+                    setPressed(true);
+                    if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
+                        postCheckForLongClick();
+                    }
+                    return true;
+                }
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+     * KeyEvent.Callback.onKeyMultiple()}: perform clicking of the view
+     * when {@link KeyEvent#KEYCODE_DPAD_CENTER} or
+     * {@link KeyEvent#KEYCODE_ENTER} is released.
+     *
+     * @param keyCode A key code that represents the button pressed, from
+     *                {@link android.view.KeyEvent}.
+     * @param event   The KeyEvent object that defines the button action.
+     */
+    public boolean onKeyUp(int keyCode, KeyEvent event) {
+        boolean result = false;
+
+        switch (keyCode) {
+            case KeyEvent.KEYCODE_DPAD_CENTER:
+            case KeyEvent.KEYCODE_ENTER: {
+                if ((mViewFlags & ENABLED_MASK) == DISABLED) {
+                    return true;
+                }
+                if ((mViewFlags & CLICKABLE) == CLICKABLE && isPressed()) {
+                    setPressed(false);
+
+                    if (!mHasPerformedLongPress) {
+                        // This is a tap, so remove the longpress check
+                        if (mPendingCheckForLongPress != null) {
+                            removeCallbacks(mPendingCheckForLongPress);
+                        }
+
+                        result = performClick();
+                    }
+                }
+                break;
+            }
+        }
+        return result;
+    }
+
+    /**
+     * Default implementation of {@link KeyEvent.Callback#onKeyMultiple(int, int, KeyEvent)
+     * KeyEvent.Callback.onKeyMultiple()}: always returns false (doesn't handle
+     * the event).
+     *
+     * @param keyCode     A key code that represents the button pressed, from
+     *                    {@link android.view.KeyEvent}.
+     * @param repeatCount The number of times the action was made.
+     * @param event       The KeyEvent object that defines the button action.
+     */
+    public boolean onKeyMultiple(int keyCode, int repeatCount, KeyEvent event) {
+        return false;
+    }
+
+    /**
+     * Called when an unhandled key shortcut event occurs.
+     *
+     * @param keyCode The value in event.getKeyCode().
+     * @param event Description of the key event.
+     * @return If you handled the event, return true. If you want to allow the
+     *         event to be handled by the next receiver, return false.
+     */
+    public boolean onKeyShortcut(int keyCode, KeyEvent event) {
+        return false;
+    }
+
+    /**
+     * Check whether the called view is a text editor, in which case it
+     * would make sense to automatically display a soft input window for
+     * it.  Subclasses should override this if they implement
+     * {@link #onCreateInputConnection(EditorInfo)} to return true if
+     * a call on that method would return a non-null InputConnection.  The
+     * default implementation always returns false.
+     * 
+     * @return Returns true if this view is a text editor, else false.
+     */
+    public boolean onCheckIsTextEditor() {
+        return false;
+    }
+    
+    /**
+     * Create a new InputConnection for an InputMethod to interact
+     * with the view.  The default implementation returns null, since it doesn't
+     * support input methods.  You can override this to implement such support.
+     * This is only needed for views that take focus and text input.
+     * 
+     * <p>When implementing this, you probably also want to implement
+     * {@link #onCheckIsTextEditor()} to indicate you will return a
+     * non-null InputConnection.
+     *
+     * @param outAttrs Fill in with attribute information about the connection.
+     */
+    public InputConnection onCreateInputConnection(EditorInfo outAttrs) {
+        return null;
+    }
+
+    /**
+     * Show the context menu for this view. It is not safe to hold on to the
+     * menu after returning from this method.
+     *
+     * @param menu The context menu to populate
+     */
+    public void createContextMenu(ContextMenu menu) {
+        ContextMenuInfo menuInfo = getContextMenuInfo();
+
+        // Sets the current menu info so all items added to menu will have
+        // my extra info set.
+        ((MenuBuilder)menu).setCurrentMenuInfo(menuInfo);
+
+        onCreateContextMenu(menu);
+        if (mOnCreateContextMenuListener != null) {
+            mOnCreateContextMenuListener.onCreateContextMenu(menu, this, menuInfo);
+        }
+
+        // Clear the extra information so subsequent items that aren't mine don't
+        // have my extra info.
+        ((MenuBuilder)menu).setCurrentMenuInfo(null);
+
+        if (mParent != null) {
+            mParent.createContextMenu(menu);
+        }
+    }
+
+    /**
+     * Views should implement this if they have extra information to associate
+     * with the context menu. The return result is supplied as a parameter to
+     * the {@link OnCreateContextMenuListener#onCreateContextMenu(ContextMenu, View, ContextMenuInfo)}
+     * callback.
+     *
+     * @return Extra information about the item for which the context menu
+     *         should be shown. This information will vary across different
+     *         subclasses of View.
+     */
+    protected ContextMenuInfo getContextMenuInfo() {
+        return null;
+    }
+
+    /**
+     * Views should implement this if the view itself is going to add items to
+     * the context menu.
+     *
+     * @param menu the context menu to populate
+     */
+    protected void onCreateContextMenu(ContextMenu menu) {
+    }
+
+    /**
+     * Implement this method to handle trackball motion events.  The
+     * <em>relative</em> movement of the trackball since the last event
+     * can be retrieve with {@link MotionEvent#getX MotionEvent.getX()} and
+     * {@link MotionEvent#getY MotionEvent.getY()}.  These are normalized so
+     * that a movement of 1 corresponds to the user pressing one DPAD key (so
+     * they will often be fractional values, representing the more fine-grained
+     * movement information available from a trackball).
+     *
+     * @param event The motion event.
+     * @return True if the event was handled, false otherwise.
+     */
+    public boolean onTrackballEvent(MotionEvent event) {
+        return false;
+    }
+
+    /**
+     * Implement this method to handle touch screen motion events.
+     *
+     * @param event The motion event.
+     * @return True if the event was handled, false otherwise.
+     */
+    public boolean onTouchEvent(MotionEvent event) {
+        final int viewFlags = mViewFlags;
+
+        if ((viewFlags & ENABLED_MASK) == DISABLED) {
+            // A disabled view that is clickable still consumes the touch
+            // events, it just doesn't respond to them.
+            return (((viewFlags & CLICKABLE) == CLICKABLE ||
+                    (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE));
+        }
+
+        if (mTouchDelegate != null) {
+            if (mTouchDelegate.onTouchEvent(event)) {
+                return true;
+            }
+        }
+
+        if (((viewFlags & CLICKABLE) == CLICKABLE ||
+                (viewFlags & LONG_CLICKABLE) == LONG_CLICKABLE)) {
+            switch (event.getAction()) {
+                case MotionEvent.ACTION_UP:
+                    if ((mPrivateFlags & PRESSED) != 0) {
+                        // take focus if we don't have it already and we should in
+                        // touch mode.
+                        boolean focusTaken = false;
+                        if (isFocusable() && isFocusableInTouchMode() && !isFocused()) {
+                            focusTaken = requestFocus();
+                        }
+
+                        if (!mHasPerformedLongPress) {
+                            // This is a tap, so remove the longpress check
+                            if (mPendingCheckForLongPress != null) {
+                                removeCallbacks(mPendingCheckForLongPress);
+                            }
+
+                            // Only perform take click actions if we were in the pressed state
+                            if (!focusTaken) {
+                                performClick();
+                            }
+                        }
+
+                        if (mUnsetPressedState == null) {
+                            mUnsetPressedState = new UnsetPressedState();
+                        }
+
+                        if (!post(mUnsetPressedState)) {
+                            // If the post failed, unpress right now
+                            mUnsetPressedState.run();
+                        }
+                    }
+                    break;
+
+                case MotionEvent.ACTION_DOWN:
+                    mPrivateFlags |= PRESSED;
+                    refreshDrawableState();
+                    if ((mViewFlags & LONG_CLICKABLE) == LONG_CLICKABLE) {
+                        postCheckForLongClick();
+                    }
+                    break;
+
+                case MotionEvent.ACTION_CANCEL:
+                    mPrivateFlags &= ~PRESSED;
+                    refreshDrawableState();
+                    break;
+
+                case MotionEvent.ACTION_MOVE:
+                    final int x = (int) event.getX();
+                    final int y = (int) event.getY();
+
+                    // Be lenient about moving outside of buttons
+                    int slop = ViewConfiguration.get(mContext).getScaledTouchSlop();
+                    if ((x < 0 - slop) || (x >= getWidth() + slop) ||
+                            (y < 0 - slop) || (y >= getHeight() + slop)) {
+                        // Outside button
+                        if ((mPrivateFlags & PRESSED) != 0) {
+                            // Remove any future long press checks
+                            if (mPendingCheckForLongPress != null) {
+                                removeCallbacks(mPendingCheckForLongPress);
+                            }
+
+                            // Need to switch from pressed to not pressed
+                            mPrivateFlags &= ~PRESSED;
+                            refreshDrawableState();
+                        }
+                    } else {
+                        // Inside button
+                        if ((mPrivateFlags & PRESSED) == 0) {
+                            // Need to switch from not pressed to pressed
+                            mPrivateFlags |= PRESSED;
+                            refreshDrawableState();
+                        }
+                    }
+                    break;
+            }
+            return true;
+        }
+
+        return false;
+    }
+
+    /**
+     * Cancels a pending long press.  Your subclass can use this if you
+     * want the context menu to come up if the user presses and holds
+     * at the same place, but you don't want it to come up if they press
+     * and then move around enough to cause scrolling.
+     */
+    public void cancelLongPress() {
+        if (mPendingCheckForLongPress != null) {
+            removeCallbacks(mPendingCheckForLongPress);
+        }
+    }
+
+    /**
+     * Sets the TouchDelegate for this View.
+     */
+    public void setTouchDelegate(TouchDelegate delegate) {
+        mTouchDelegate = delegate;
+    }
+
+    /**
+     * Gets the TouchDelegate for this View.
+     */
+    public TouchDelegate getTouchDelegate() {
+        return mTouchDelegate;
+    }
+
+    /**
+     * Set flags controlling behavior of this view.
+     *
+     * @param flags Constant indicating the value which should be set
+     * @param mask Constant indicating the bit range that should be changed
+     */
+    void setFlags(int flags, int mask) {
+        int old = mViewFlags;
+        mViewFlags = (mViewFlags & ~mask) | (flags & mask);
+
+        int changed = mViewFlags ^ old;
+        if (changed == 0) {
+            return;
+        }
+        int privateFlags = mPrivateFlags;
+
+        /* Check if the FOCUSABLE bit has changed */
+        if (((changed & FOCUSABLE_MASK) != 0) &&
+                ((privateFlags & HAS_BOUNDS) !=0)) {
+            if (((old & FOCUSABLE_MASK) == FOCUSABLE)
+                    && ((privateFlags & FOCUSED) != 0)) {
+                /* Give up focus if we are no longer focusable */
+                clearFocus();
+            } else if (((old & FOCUSABLE_MASK) == NOT_FOCUSABLE)
+                    && ((privateFlags & FOCUSED) == 0)) {
+                /*
+                 * Tell the view system that we are now available to take focus
+                 * if no one else already has it.
+                 */
+                if (mParent != null) mParent.focusableViewAvailable(this);
+            }
+        }
+
+        if ((flags & VISIBILITY_MASK) == VISIBLE) {
+            if ((changed & VISIBILITY_MASK) != 0) {
+                /*
+                 * If this view is becoming visible, set the DRAWN flag so that
+                 * the next invalidate() will not be skipped.
+                 */
+                mPrivateFlags |= DRAWN;
+
+                needGlobalAttributesUpdate(true);
+
+                // a view becoming visible is worth notifying the parent
+                // about in case nothing has focus.  even if this specific view
+                // isn't focusable, it may contain something that is, so let
+                // the root view try to give this focus if nothing else does.
+                if ((mParent != null) && (mBottom > mTop) && (mRight > mLeft)) {
+                    mParent.focusableViewAvailable(this);
+                }
+            }
+        }
+
+        /* Check if the GONE bit has changed */
+        if ((changed & GONE) != 0) {
+            needGlobalAttributesUpdate(false);
+            requestLayout();
+            invalidate();
+
+            if (((mViewFlags & VISIBILITY_MASK) == GONE) && hasFocus()) {
+                clearFocus();
+            }
+            if (mAttachInfo != null) {
+                mAttachInfo.mViewVisibilityChanged = true;
+            }
+        }
+
+        /* Check if the VISIBLE bit has changed */
+        if ((changed & INVISIBLE) != 0) {
+            needGlobalAttributesUpdate(false);
+            invalidate();
+
+            if (((mViewFlags & VISIBILITY_MASK) == INVISIBLE) && hasFocus()) {
+                // root view becoming invisible shouldn't clear focus
+                if (getRootView() != this) {
+                    clearFocus();
+                }
+            }
+            if (mAttachInfo != null) {
+                mAttachInfo.mViewVisibilityChanged = true;
+            }
+        }
+
+        if ((changed & WILL_NOT_CACHE_DRAWING) != 0) {
+            destroyDrawingCache();
+        }
+
+        if ((changed & DRAWING_CACHE_ENABLED) != 0) {
+            destroyDrawingCache();
+            mPrivateFlags &= ~DRAWING_CACHE_VALID;
+        }
+
+        if ((changed & DRAWING_CACHE_QUALITY_MASK) != 0) {
+            destroyDrawingCache();
+            mPrivateFlags &= ~DRAWING_CACHE_VALID;
+        }
+
+        if ((changed & DRAW_MASK) != 0) {
+            if ((mViewFlags & WILL_NOT_DRAW) != 0) {
+                if (mBGDrawable != null) {
+                    mPrivateFlags &= ~SKIP_DRAW;
+                    mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
+                } else {
+                    mPrivateFlags |= SKIP_DRAW;
+                }
+            } else {
+                mPrivateFlags &= ~SKIP_DRAW;
+            }
+            requestLayout();
+            invalidate();
+        }
+
+        if ((changed & KEEP_SCREEN_ON) != 0) {
+            if (mParent != null) {
+                mParent.recomputeViewAttributes(this);
+            }
+        }
+    }
+
+    /**
+     * Change the view's z order in the tree, so it's on top of other sibling
+     * views
+     */
+    public void bringToFront() {
+        if (mParent != null) {
+            mParent.bringChildToFront(this);
+        }
+    }
+
+    /**
+     * This is called in response to an internal scroll in this view (i.e., the
+     * view scrolled its own contents). This is typically as a result of
+     * {@link #scrollBy(int, int)} or {@link #scrollTo(int, int)} having been
+     * called.
+     *
+     * @param l Current horizontal scroll origin.
+     * @param t Current vertical scroll origin.
+     * @param oldl Previous horizontal scroll origin.
+     * @param oldt Previous vertical scroll origin.
+     */
+    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
+        mBackgroundSizeChanged = true;
+
+        final AttachInfo ai = mAttachInfo;
+        if (ai != null) {
+            ai.mViewScrollChanged = true;
+        }
+    }
+
+    /**
+     * This is called during layout when the size of this view has changed. If
+     * you were just added to the view hierarchy, you're called with the old
+     * values of 0.
+     *
+     * @param w Current width of this view.
+     * @param h Current height of this view.
+     * @param oldw Old width of this view.
+     * @param oldh Old height of this view.
+     */
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+    }
+
+    /**
+     * Called by draw to draw the child views. This may be overridden
+     * by derived classes to gain control just before its children are drawn
+     * (but after its own view has been drawn).
+     * @param canvas the canvas on which to draw the view
+     */
+    protected void dispatchDraw(Canvas canvas) {
+    }
+
+    /**
+     * Gets the parent of this view. Note that the parent is a
+     * ViewParent and not necessarily a View.
+     *
+     * @return Parent of this view.
+     */
+    public final ViewParent getParent() {
+        return mParent;
+    }
+
+    /**
+     * Return the scrolled left position of this view. This is the left edge of
+     * the displayed part of your view. You do not need to draw any pixels
+     * farther left, since those are outside of the frame of your view on
+     * screen.
+     *
+     * @return The left edge of the displayed part of your view, in pixels.
+     */
+    public final int getScrollX() {
+        return mScrollX;
+    }
+
+    /**
+     * Return the scrolled top position of this view. This is the top edge of
+     * the displayed part of your view. You do not need to draw any pixels above
+     * it, since those are outside of the frame of your view on screen.
+     *
+     * @return The top edge of the displayed part of your view, in pixels.
+     */
+    public final int getScrollY() {
+        return mScrollY;
+    }
+
+    /**
+     * Return the width of the your view.
+     *
+     * @return The width of your view, in pixels.
+     */
+    @ViewDebug.ExportedProperty
+    public final int getWidth() {
+        return mRight - mLeft;
+    }
+
+    /**
+     * Return the height of your view.
+     *
+     * @return The height of your view, in pixels.
+     */
+    @ViewDebug.ExportedProperty
+    public final int getHeight() {
+        return mBottom - mTop;
+    }
+
+    /**
+     * Return the visible drawing bounds of your view. Fills in the output
+     * rectangle with the values from getScrollX(), getScrollY(),
+     * getWidth(), and getHeight().
+     *
+     * @param outRect The (scrolled) drawing bounds of the view.
+     */
+    public void getDrawingRect(Rect outRect) {
+        outRect.left = mScrollX;
+        outRect.top = mScrollY;
+        outRect.right = mScrollX + (mRight - mLeft);
+        outRect.bottom = mScrollY + (mBottom - mTop);
+    }
+
+    /**
+     * The width of this view as measured in the most recent call to measure().
+     * This should be used during measurement and layout calculations only. Use
+     * {@link #getWidth()} to see how wide a view is after layout.
+     *
+     * @return The measured width of this view.
+     */
+    public final int getMeasuredWidth() {
+        return mMeasuredWidth;
+    }
+
+    /**
+     * The height of this view as measured in the most recent call to measure().
+     * This should be used during measurement and layout calculations only. Use
+     * {@link #getHeight()} to see how tall a view is after layout.
+     *
+     * @return The measured height of this view.
+     */
+    public final int getMeasuredHeight() {
+        return mMeasuredHeight;
+    }
+
+    /**
+     * Top position of this view relative to its parent.
+     *
+     * @return The top of this view, in pixels.
+     */
+    @ViewDebug.CapturedViewProperty
+    public final int getTop() {
+        return mTop;
+    }
+
+    /**
+     * Bottom position of this view relative to its parent.
+     *
+     * @return The bottom of this view, in pixels.
+     */
+    @ViewDebug.CapturedViewProperty
+    public final int getBottom() {
+        return mBottom;
+    }
+
+    /**
+     * Left position of this view relative to its parent.
+     *
+     * @return The left edge of this view, in pixels.
+     */
+    @ViewDebug.CapturedViewProperty
+    public final int getLeft() {
+        return mLeft;
+    }
+
+    /**
+     * Right position of this view relative to its parent.
+     *
+     * @return The right edge of this view, in pixels.
+     */
+    @ViewDebug.CapturedViewProperty
+    public final int getRight() {
+        return mRight;
+    }
+
+    /**
+     * Hit rectangle in parent's coordinates
+     *
+     * @param outRect The hit rectangle of the view.
+     */
+    public void getHitRect(Rect outRect) {
+        outRect.set(mLeft, mTop, mRight, mBottom);
+    }
+
+    /**
+     * When a view has focus and the user navigates away from it, the next view is searched for
+     * starting from the rectangle filled in by this method.
+     *
+     * By default, the rectange is the {@link #getDrawingRect})of the view.  However, if your
+     * view maintains some idea of internal selection, such as a cursor, or a selected row
+     * or column, you should override this method and fill in a more specific rectangle.
+     *
+     * @param r The rectangle to fill in, in this view's coordinates.
+     */
+    public void getFocusedRect(Rect r) {
+        getDrawingRect(r);
+    }
+
+    /**
+     * If some part of this view is not clipped by any of its parents, then
+     * return that area in r in global (root) coordinates. To convert r to local
+     * coordinates, offset it by -globalOffset (e.g. r.offset(-globalOffset.x,
+     * -globalOffset.y)) If the view is completely clipped or translated out,
+     * return false.
+     *
+     * @param r If true is returned, r holds the global coordinates of the
+     *        visible portion of this view.
+     * @param globalOffset If true is returned, globalOffset holds the dx,dy
+     *        between this view and its root. globalOffet may be null.
+     * @return true if r is non-empty (i.e. part of the view is visible at the
+     *         root level.
+     */
+    public boolean getGlobalVisibleRect(Rect r, Point globalOffset) {
+        int width = mRight - mLeft;
+        int height = mBottom - mTop;
+        if (width > 0 && height > 0) {
+            r.set(0, 0, width, height);
+            if (globalOffset != null) {
+                globalOffset.set(-mScrollX, -mScrollY);
+            }
+            return mParent == null || mParent.getChildVisibleRect(this, r, globalOffset);
+        }
+        return false;
+    }
+
+    public final boolean getGlobalVisibleRect(Rect r) {
+        return getGlobalVisibleRect(r, null);
+    }
+
+    public final boolean getLocalVisibleRect(Rect r) {
+        Point offset = new Point();
+        if (getGlobalVisibleRect(r, offset)) {
+            r.offset(-offset.x, -offset.y); // make r local
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Offset this view's vertical location by the specified number of pixels.
+     *
+     * @param offset the number of pixels to offset the view by
+     */
+    public void offsetTopAndBottom(int offset) {
+        mTop += offset;
+        mBottom += offset;
+    }
+
+    /**
+     * Offset this view's horizontal location by the specified amount of pixels.
+     *
+     * @param offset the numer of pixels to offset the view by
+     */
+    public void offsetLeftAndRight(int offset) {
+        mLeft += offset;
+        mRight += offset;
+    }
+
+    /**
+     * Get the LayoutParams associated with this view. All views should have
+     * layout parameters. These supply parameters to the <i>parent</i> of this
+     * view specifying how it should be arranged. There are many subclasses of
+     * ViewGroup.LayoutParams, and these correspond to the different subclasses
+     * of ViewGroup that are responsible for arranging their children.
+     * @return The LayoutParams associated with this view
+     */
+    @ViewDebug.ExportedProperty(deepExport = true, prefix = "layout_")
+    public ViewGroup.LayoutParams getLayoutParams() {
+        return mLayoutParams;
+    }
+
+    /**
+     * Set the layout parameters associated with this view. These supply
+     * parameters to the <i>parent</i> of this view specifying how it should be
+     * arranged. There are many subclasses of ViewGroup.LayoutParams, and these
+     * correspond to the different subclasses of ViewGroup that are responsible
+     * for arranging their children.
+     *
+     * @param params the layout parameters for this view
+     */
+    public void setLayoutParams(ViewGroup.LayoutParams params) {
+        if (params == null) {
+            throw new NullPointerException("params == null");
+        }
+        mLayoutParams = params;
+        requestLayout();
+    }
+
+    /**
+     * Set the scrolled position of your view. This will cause a call to
+     * {@link #onScrollChanged(int, int, int, int)} and the view will be
+     * invalidated.
+     * @param x the x position to scroll to
+     * @param y the y position to scroll to
+     */
+    public void scrollTo(int x, int y) {
+        if (mScrollX != x || mScrollY != y) {
+            int oldX = mScrollX;
+            int oldY = mScrollY;
+            mScrollX = x;
+            mScrollY = y;
+            onScrollChanged(mScrollX, mScrollY, oldX, oldY);
+            invalidate();
+        }
+    }
+
+    /**
+     * Move the scrolled position of your view. This will cause a call to
+     * {@link #onScrollChanged(int, int, int, int)} and the view will be
+     * invalidated.
+     * @param x the amount of pixels to scroll by horizontally
+     * @param y the amount of pixels to scroll by vertically
+     */
+    public void scrollBy(int x, int y) {
+        scrollTo(mScrollX + x, mScrollY + y);
+    }
+
+    /**
+     * Mark the the area defined by dirty as needing to be drawn. If the view is
+     * visible, {@link #onDraw} will be called at some point in the future.
+     * This must be called from a UI thread. To call from a non-UI thread, call
+     * {@link #postInvalidate()}.
+     *
+     * WARNING: This method is destructive to dirty.
+     * @param dirty the rectangle representing the bounds of the dirty region
+     */
+    public void invalidate(Rect dirty) {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
+        }
+
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
+            mPrivateFlags &= ~DRAWING_CACHE_VALID;
+            final ViewParent p = mParent;
+            final AttachInfo ai = mAttachInfo;
+            if (p != null && ai != null) {
+                final int scrollX = mScrollX;
+                final int scrollY = mScrollY;
+                final Rect r = ai.mTmpInvalRect;
+                r.set(dirty.left - scrollX, dirty.top - scrollY,
+                        dirty.right - scrollX, dirty.bottom - scrollY);
+                mParent.invalidateChild(this, r);
+            }
+        }
+    }
+
+    /**
+     * Mark the the area defined by the rect (l,t,r,b) as needing to be drawn.
+     * The coordinates of the dirty rect are relative to the view.
+     * If the view is visible, {@link #onDraw} will be called at some point
+     * in the future. This must be called from a UI thread. To call
+     * from a non-UI thread, call {@link #postInvalidate()}.
+     * @param l the left position of the dirty region
+     * @param t the top position of the dirty region
+     * @param r the right position of the dirty region
+     * @param b the bottom position of the dirty region
+     */
+    public void invalidate(int l, int t, int r, int b) {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
+        }
+
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
+            mPrivateFlags &= ~DRAWING_CACHE_VALID;
+            final ViewParent p = mParent;
+            final AttachInfo ai = mAttachInfo;
+            if (p != null && ai != null && l < r && t < b) {
+                final int scrollX = mScrollX;
+                final int scrollY = mScrollY;
+                final Rect tmpr = ai.mTmpInvalRect;
+                tmpr.set(l - scrollX, t - scrollY, r - scrollX, b - scrollY);
+                p.invalidateChild(this, tmpr);
+            }
+        }
+    }
+
+    /**
+     * Invalidate the whole view. If the view is visible, {@link #onDraw} will
+     * be called at some point in the future. This must be called from a
+     * UI thread. To call from a non-UI thread, call {@link #postInvalidate()}.
+     */
+    public void invalidate() {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE);
+        }
+
+        if ((mPrivateFlags & (DRAWN | HAS_BOUNDS)) == (DRAWN | HAS_BOUNDS)) {
+            mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
+            final ViewParent p = mParent;
+            final AttachInfo ai = mAttachInfo;
+            if (p != null && ai != null) {
+                final Rect r = ai.mTmpInvalRect;
+                r.set(0, 0, mRight - mLeft, mBottom - mTop);
+                // Don't call invalidate -- we don't want to internally scroll
+                // our own bounds
+                p.invalidateChild(this, r);
+            }
+        }
+    }
+
+    /**
+     * @return A handler associated with the thread running the View. This
+     * handler can be used to pump events in the UI events queue.
+     */
+    public Handler getHandler() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mHandler;
+        }
+        return null;
+    }
+
+    /**
+     * Causes the Runnable to be added to the message queue.
+     * The runnable will be run on the user interface thread.
+     *
+     * @param action The Runnable that will be executed.
+     *
+     * @return Returns true if the Runnable was successfully placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.
+     */
+    public boolean post(Runnable action) {
+        Handler handler;
+        if (mAttachInfo != null) {
+            handler = mAttachInfo.mHandler;
+        } else {
+            // Assume that post will succeed later
+            ViewRoot.getRunQueue().post(action);
+            return true;
+        }
+
+        return handler.post(action);
+    }
+
+    /**
+     * Causes the Runnable to be added to the message queue, to be run
+     * after the specified amount of time elapses.
+     * The runnable will be run on the user interface thread.
+     *
+     * @param action The Runnable that will be executed.
+     * @param delayMillis The delay (in milliseconds) until the Runnable
+     *        will be executed.
+     *
+     * @return true if the Runnable was successfully placed in to the
+     *         message queue.  Returns false on failure, usually because the
+     *         looper processing the message queue is exiting.  Note that a
+     *         result of true does not mean the Runnable will be processed --
+     *         if the looper is quit before the delivery time of the message
+     *         occurs then the message will be dropped.
+     */
+    public boolean postDelayed(Runnable action, long delayMillis) {
+        Handler handler;
+        if (mAttachInfo != null) {
+            handler = mAttachInfo.mHandler;
+        } else {
+            // Assume that post will succeed later
+            ViewRoot.getRunQueue().postDelayed(action, delayMillis);
+            return true;
+        }
+
+        return handler.postDelayed(action, delayMillis);
+    }
+
+    /**
+     * Removes the specified Runnable from the message queue.
+     *
+     * @param action The Runnable to remove from the message handling queue
+     *
+     * @return true if this view could ask the Handler to remove the Runnable,
+     *         false otherwise. When the returned value is true, the Runnable
+     *         may or may not have been actually removed from the message queue
+     *         (for instance, if the Runnable was not in the queue already.)
+     */
+    public boolean removeCallbacks(Runnable action) {
+        Handler handler;
+        if (mAttachInfo != null) {
+            handler = mAttachInfo.mHandler;
+        } else {
+            // Assume that post will succeed later
+            ViewRoot.getRunQueue().removeCallbacks(action);
+            return true;
+        }
+
+        handler.removeCallbacks(action);
+        return true;
+    }
+
+    /**
+     * Cause an invalidate to happen on a subsequent cycle through the event loop.
+     * Use this to invalidate the View from a non-UI thread.
+     *
+     * @see #invalidate()
+     */
+    public void postInvalidate() {
+        postInvalidateDelayed(0);
+    }
+
+    /**
+     * Cause an invalidate of the specified area to happen on a subsequent cycle
+     * through the event loop. Use this to invalidate the View from a non-UI thread.
+     *
+     * @param left The left coordinate of the rectangle to invalidate.
+     * @param top The top coordinate of the rectangle to invalidate.
+     * @param right The right coordinate of the rectangle to invalidate.
+     * @param bottom The bottom coordinate of the rectangle to invalidate.
+     *
+     * @see #invalidate(int, int, int, int)
+     * @see #invalidate(Rect)
+     */
+    public void postInvalidate(int left, int top, int right, int bottom) {
+        postInvalidateDelayed(0, left, top, right, bottom);
+    }
+
+    /**
+     * Cause an invalidate to happen on a subsequent cycle through the event
+     * loop. Waits for the specified amount of time.
+     *
+     * @param delayMilliseconds the duration in milliseconds to delay the
+     *         invalidation by
+     */
+    public void postInvalidateDelayed(long delayMilliseconds) {
+        // We try only with the AttachInfo because there's no point in invalidating
+        // if we are not attached to our window
+        if (mAttachInfo != null) {
+            Message msg = Message.obtain();
+            msg.what = AttachInfo.INVALIDATE_MSG;
+            msg.obj = this;
+            mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+        }
+    }
+
+    /**
+     * Cause an invalidate of the specified area to happen on a subsequent cycle
+     * through the event loop. Waits for the specified amount of time.
+     *
+     * @param delayMilliseconds the duration in milliseconds to delay the
+     *         invalidation by
+     * @param left The left coordinate of the rectangle to invalidate.
+     * @param top The top coordinate of the rectangle to invalidate.
+     * @param right The right coordinate of the rectangle to invalidate.
+     * @param bottom The bottom coordinate of the rectangle to invalidate.
+     */
+    public void postInvalidateDelayed(long delayMilliseconds, int left, int top,
+            int right, int bottom) {
+
+        // We try only with the AttachInfo because there's no point in invalidating
+        // if we are not attached to our window
+        if (mAttachInfo != null) {
+            final AttachInfo.InvalidateInfo info = AttachInfo.InvalidateInfo.acquire();
+            info.target = this;
+            info.left = left;
+            info.top = top;
+            info.right = right;
+            info.bottom = bottom;
+
+            final Message msg = Message.obtain();
+            msg.what = AttachInfo.INVALIDATE_RECT_MSG;
+            msg.obj = info;
+            mAttachInfo.mHandler.sendMessageDelayed(msg, delayMilliseconds);
+        }
+    }
+
+    /**
+     * Called by a parent to request that a child update its values for mScrollX
+     * and mScrollY if necessary. This will typically be done if the child is
+     * animating a scroll using a {@link android.widget.Scroller Scroller}
+     * object.
+     */
+    public void computeScroll() {
+    }
+
+    /**
+     * <p>Indicate whether the horizontal edges are faded when the view is
+     * scrolled horizontally.</p>
+     *
+     * @return true if the horizontal edges should are faded on scroll, false
+     *         otherwise
+     *
+     * @see #setHorizontalFadingEdgeEnabled(boolean)
+     * @attr ref android.R.styleable#View_fadingEdge
+     */
+    public boolean isHorizontalFadingEdgeEnabled() {
+        return (mViewFlags & FADING_EDGE_HORIZONTAL) == FADING_EDGE_HORIZONTAL;
+    }
+
+    /**
+     * <p>Define whether the horizontal edges should be faded when this view
+     * is scrolled horizontally.</p>
+     *
+     * @param horizontalFadingEdgeEnabled true if the horizontal edges should
+     *                                    be faded when the view is scrolled
+     *                                    horizontally
+     *
+     * @see #isHorizontalFadingEdgeEnabled()
+     * @attr ref android.R.styleable#View_fadingEdge
+     */
+    public void setHorizontalFadingEdgeEnabled(boolean horizontalFadingEdgeEnabled) {
+        if (isHorizontalFadingEdgeEnabled() != horizontalFadingEdgeEnabled) {
+            if (horizontalFadingEdgeEnabled) {
+                initScrollCache();
+            }
+
+            mViewFlags ^= FADING_EDGE_HORIZONTAL;
+        }
+    }
+
+    /**
+     * <p>Indicate whether the vertical edges are faded when the view is
+     * scrolled horizontally.</p>
+     *
+     * @return true if the vertical edges should are faded on scroll, false
+     *         otherwise
+     *
+     * @see #setVerticalFadingEdgeEnabled(boolean)
+     * @attr ref android.R.styleable#View_fadingEdge
+     */
+    public boolean isVerticalFadingEdgeEnabled() {
+        return (mViewFlags & FADING_EDGE_VERTICAL) == FADING_EDGE_VERTICAL;
+    }
+
+    /**
+     * <p>Define whether the vertical edges should be faded when this view
+     * is scrolled vertically.</p>
+     *
+     * @param verticalFadingEdgeEnabled true if the vertical edges should
+     *                                  be faded when the view is scrolled
+     *                                  vertically
+     *
+     * @see #isVerticalFadingEdgeEnabled()
+     * @attr ref android.R.styleable#View_fadingEdge
+     */
+    public void setVerticalFadingEdgeEnabled(boolean verticalFadingEdgeEnabled) {
+        if (isVerticalFadingEdgeEnabled() != verticalFadingEdgeEnabled) {
+            if (verticalFadingEdgeEnabled) {
+                initScrollCache();
+            }
+
+            mViewFlags ^= FADING_EDGE_VERTICAL;
+        }
+    }
+
+    /**
+     * Returns the strength, or intensity, of the top faded edge. The strength is
+     * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
+     * returns 0.0 or 1.0 but no value in between.
+     *
+     * Subclasses should override this method to provide a smoother fade transition
+     * when scrolling occurs.
+     *
+     * @return the intensity of the top fade as a float between 0.0f and 1.0f
+     */
+    protected float getTopFadingEdgeStrength() {
+        return computeVerticalScrollOffset() > 0 ? 1.0f : 0.0f;
+    }
+
+    /**
+     * Returns the strength, or intensity, of the bottom faded edge. The strength is
+     * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
+     * returns 0.0 or 1.0 but no value in between.
+     *
+     * Subclasses should override this method to provide a smoother fade transition
+     * when scrolling occurs.
+     *
+     * @return the intensity of the bottom fade as a float between 0.0f and 1.0f
+     */
+    protected float getBottomFadingEdgeStrength() {
+        return computeVerticalScrollOffset() + computeVerticalScrollExtent() <
+                computeVerticalScrollRange() ? 1.0f : 0.0f;
+    }
+
+    /**
+     * Returns the strength, or intensity, of the left faded edge. The strength is
+     * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
+     * returns 0.0 or 1.0 but no value in between.
+     *
+     * Subclasses should override this method to provide a smoother fade transition
+     * when scrolling occurs.
+     *
+     * @return the intensity of the left fade as a float between 0.0f and 1.0f
+     */
+    protected float getLeftFadingEdgeStrength() {
+        return computeHorizontalScrollOffset() > 0 ? 1.0f : 0.0f;
+    }
+
+    /**
+     * Returns the strength, or intensity, of the right faded edge. The strength is
+     * a value between 0.0 (no fade) and 1.0 (full fade). The default implementation
+     * returns 0.0 or 1.0 but no value in between.
+     *
+     * Subclasses should override this method to provide a smoother fade transition
+     * when scrolling occurs.
+     *
+     * @return the intensity of the right fade as a float between 0.0f and 1.0f
+     */
+    protected float getRightFadingEdgeStrength() {
+        return computeHorizontalScrollOffset() + computeHorizontalScrollExtent() <
+                computeHorizontalScrollRange() ? 1.0f : 0.0f;
+    }
+
+    /**
+     * <p>Indicate whether the horizontal scrollbar should be drawn or not. The
+     * scrollbar is not drawn by default.</p>
+     *
+     * @return true if the horizontal scrollbar should be painted, false
+     *         otherwise
+     *
+     * @see #setHorizontalScrollBarEnabled(boolean)
+     */
+    public boolean isHorizontalScrollBarEnabled() {
+        return (mViewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
+    }
+
+    /**
+     * <p>Define whether the horizontal scrollbar should be drawn or not. The
+     * scrollbar is not drawn by default.</p>
+     *
+     * @param horizontalScrollBarEnabled true if the horizontal scrollbar should
+     *                                   be painted
+     *
+     * @see #isHorizontalScrollBarEnabled()
+     */
+    public void setHorizontalScrollBarEnabled(boolean horizontalScrollBarEnabled) {
+        if (isHorizontalScrollBarEnabled() != horizontalScrollBarEnabled) {
+            mViewFlags ^= SCROLLBARS_HORIZONTAL;
+            recomputePadding();
+        }
+    }
+
+    /**
+     * <p>Indicate whether the vertical scrollbar should be drawn or not. The
+     * scrollbar is not drawn by default.</p>
+     *
+     * @return true if the vertical scrollbar should be painted, false
+     *         otherwise
+     *
+     * @see #setVerticalScrollBarEnabled(boolean)
+     */
+    public boolean isVerticalScrollBarEnabled() {
+        return (mViewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL;
+    }
+
+    /**
+     * <p>Define whether the vertical scrollbar should be drawn or not. The
+     * scrollbar is not drawn by default.</p>
+     *
+     * @param verticalScrollBarEnabled true if the vertical scrollbar should
+     *                                 be painted
+     *
+     * @see #isVerticalScrollBarEnabled()
+     */
+    public void setVerticalScrollBarEnabled(boolean verticalScrollBarEnabled) {
+        if (isVerticalScrollBarEnabled() != verticalScrollBarEnabled) {
+            mViewFlags ^= SCROLLBARS_VERTICAL;
+            recomputePadding();
+        }
+    }
+
+    private void recomputePadding() {
+        setPadding(mPaddingLeft, mPaddingTop, mUserPaddingRight, mUserPaddingBottom);
+    }
+
+    /**
+     * <p>Specify the style of the scrollbars. The scrollbars can be overlaid or
+     * inset. When inset, they add to the padding of the view. And the scrollbars
+     * can be drawn inside the padding area or on the edge of the view. For example,
+     * if a view has a background drawable and you want to draw the scrollbars
+     * inside the padding specified by the drawable, you can use
+     * SCROLLBARS_INSIDE_OVERLAY or SCROLLBARS_INSIDE_INSET. If you want them to
+     * appear at the edge of the view, ignoring the padding, then you can use
+     * SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.</p>
+     * @param style the style of the scrollbars. Should be one of
+     * SCROLLBARS_INSIDE_OVERLAY, SCROLLBARS_INSIDE_INSET,
+     * SCROLLBARS_OUTSIDE_OVERLAY or SCROLLBARS_OUTSIDE_INSET.
+     * @see #SCROLLBARS_INSIDE_OVERLAY
+     * @see #SCROLLBARS_INSIDE_INSET
+     * @see #SCROLLBARS_OUTSIDE_OVERLAY
+     * @see #SCROLLBARS_OUTSIDE_INSET
+     */
+    public void setScrollBarStyle(int style) {
+        if (style != (mViewFlags & SCROLLBARS_STYLE_MASK)) {
+            mViewFlags = (mViewFlags & ~SCROLLBARS_STYLE_MASK) | (style & SCROLLBARS_STYLE_MASK);
+            recomputePadding();
+        }
+    }
+
+    /**
+     * <p>Returns the current scrollbar style.</p>
+     * @return the current scrollbar style
+     * @see #SCROLLBARS_INSIDE_OVERLAY
+     * @see #SCROLLBARS_INSIDE_INSET
+     * @see #SCROLLBARS_OUTSIDE_OVERLAY
+     * @see #SCROLLBARS_OUTSIDE_INSET
+     */
+    public int getScrollBarStyle() {
+        return mViewFlags & SCROLLBARS_STYLE_MASK;
+    }
+
+    /**
+     * <p>Compute the horizontal range that the horizontal scrollbar
+     * represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollExtent()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default range is the drawing width of this view.</p>
+     *
+     * @return the total horizontal range represented by the horizontal
+     *         scrollbar
+     *
+     * @see #computeHorizontalScrollExtent()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeHorizontalScrollRange() {
+        return getWidth();
+    }
+
+    /**
+     * <p>Compute the horizontal offset of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the horizontal offset of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeHorizontalScrollOffset() {
+        return mScrollX;
+    }
+
+    /**
+     * <p>Compute the horizontal extent of the horizontal scrollbar's thumb
+     * within the horizontal range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeHorizontalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing width of this view.</p>
+     *
+     * @return the horizontal extent of the scrollbar's thumb
+     *
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeHorizontalScrollExtent() {
+        return getWidth();
+    }
+
+    /**
+     * <p>Compute the vertical range that the vertical scrollbar represents.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollExtent()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * @return the total vertical range represented by the vertical scrollbar
+     *
+     * <p>The default range is the drawing height of this view.</p>
+     *
+     * @see #computeVerticalScrollExtent()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeVerticalScrollRange() {
+        return getHeight();
+    }
+
+    /**
+     * <p>Compute the vertical offset of the vertical scrollbar's thumb
+     * within the horizontal range. This value is used to compute the position
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeVerticalScrollRange()} and
+     * {@link #computeVerticalScrollExtent()}.</p>
+     *
+     * <p>The default offset is the scroll offset of this view.</p>
+     *
+     * @return the vertical offset of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollExtent()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeVerticalScrollOffset() {
+        return mScrollY;
+    }
+
+    /**
+     * <p>Compute the vertical extent of the horizontal scrollbar's thumb
+     * within the vertical range. This value is used to compute the length
+     * of the thumb within the scrollbar's track.</p>
+     *
+     * <p>The range is expressed in arbitrary units that must be the same as the
+     * units used by {@link #computeHorizontalScrollRange()} and
+     * {@link #computeVerticalScrollOffset()}.</p>
+     *
+     * <p>The default extent is the drawing height of this view.</p>
+     *
+     * @return the vertical extent of the scrollbar's thumb
+     *
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    protected int computeVerticalScrollExtent() {
+        return getHeight();
+    }
+
+    /**
+     * <p>Request the drawing of the horizontal and the vertical scrollbar. The
+     * scrollbars are painted only if they have been awakened first.</p>
+     *
+     * @param canvas the canvas on which to draw the scrollbars
+     */
+    private void onDrawScrollBars(Canvas canvas) {
+        // scrollbars are drawn only when the animation is running
+        final ScrollabilityCache cache = mScrollCache;
+        if (cache != null) {
+            final int viewFlags = mViewFlags;
+
+            final boolean drawHorizontalScrollBar =
+                (viewFlags & SCROLLBARS_HORIZONTAL) == SCROLLBARS_HORIZONTAL;
+            final boolean drawVerticalScrollBar =
+                (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL
+                && !isVerticalScrollBarHidden();
+
+            if (drawVerticalScrollBar || drawHorizontalScrollBar) {
+                final int width = mRight - mLeft;
+                final int height = mBottom - mTop;
+
+                final ScrollBarDrawable scrollBar = cache.scrollBar;
+                int size = scrollBar.getSize(false);
+                if (size <= 0) {
+                    size = cache.scrollBarSize;
+                }
+
+                if (drawHorizontalScrollBar) {
+                    onDrawHorizontalScrollBar(canvas, scrollBar, width, height, size);
+                }
+
+                if (drawVerticalScrollBar) {
+                    onDrawVerticalScrollBar(canvas, scrollBar, width, height, size);
+                }
+            }
+        }
+    }
+    
+    /**
+     * Override this if the vertical scrollbar needs to be hidden in a subclass, like when 
+     * FastScroller is visible.
+     * @return whether to temporarily hide the vertical scrollbar
+     * @hide
+     */
+    protected boolean isVerticalScrollBarHidden() {
+        return false;
+    }
+
+    /**
+     * <p>Draw the horizontal scrollbar if
+     * {@link #isHorizontalScrollBarEnabled()} returns true.</p>
+     *
+     * <p>The length of the scrollbar and its thumb is computed according to the
+     * values returned by {@link #computeHorizontalScrollRange()},
+     * {@link #computeHorizontalScrollExtent()} and
+     * {@link #computeHorizontalScrollOffset()}. Refer to
+     * {@link android.widget.ScrollBarDrawable} for more information about how
+     * these values relate to each other.</p>
+     *
+     * @param canvas the canvas on which to draw the scrollbar
+     * @param scrollBar the scrollbar's drawable
+     * @param width the width of the drawing surface
+     * @param height the height of the drawing surface
+     * @param size the size of the scrollbar
+     *
+     * @see #isHorizontalScrollBarEnabled()
+     * @see #computeHorizontalScrollRange()
+     * @see #computeHorizontalScrollExtent()
+     * @see #computeHorizontalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    private void onDrawHorizontalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width,
+            int height, int size) {
+
+        final int viewFlags = mViewFlags;
+        final int scrollX = mScrollX;
+        final int scrollY = mScrollY;
+        final int inside = (viewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+        final int top = scrollY + height - size - (mUserPaddingBottom & inside);
+
+        final int verticalScrollBarGap =
+            (viewFlags & SCROLLBARS_VERTICAL) == SCROLLBARS_VERTICAL ?
+                    getVerticalScrollbarWidth() : 0;
+
+        scrollBar.setBounds(scrollX + (mPaddingLeft & inside) + getScrollBarPaddingLeft(), top,
+                scrollX + width - (mUserPaddingRight & inside) - verticalScrollBarGap, top + size);
+        scrollBar.setParameters(
+                computeHorizontalScrollRange(),
+                computeHorizontalScrollOffset(),
+                computeHorizontalScrollExtent(), false);
+        scrollBar.draw(canvas);
+    }
+
+    /**
+     * <p>Draw the vertical scrollbar if {@link #isVerticalScrollBarEnabled()}
+     * returns true.</p>
+     *
+     * <p>The length of the scrollbar and its thumb is computed according to the
+     * values returned by {@link #computeVerticalScrollRange()},
+     * {@link #computeVerticalScrollExtent()} and
+     * {@link #computeVerticalScrollOffset()}. Refer to
+     * {@link android.widget.ScrollBarDrawable} for more information about how
+     * these values relate to each other.</p>
+     *
+     * @param canvas the canvas on which to draw the scrollbar
+     * @param scrollBar the scrollbar's drawable
+     * @param width the width of the drawing surface
+     * @param height the height of the drawing surface
+     * @param size the size of the scrollbar
+     *
+     * @see #isVerticalScrollBarEnabled()
+     * @see #computeVerticalScrollRange()
+     * @see #computeVerticalScrollExtent()
+     * @see #computeVerticalScrollOffset()
+     * @see android.widget.ScrollBarDrawable
+     */
+    private void onDrawVerticalScrollBar(Canvas canvas, ScrollBarDrawable scrollBar, int width,
+            int height, int size) {
+
+        final int scrollX = mScrollX;
+        final int scrollY = mScrollY;
+        final int inside = (mViewFlags & SCROLLBARS_OUTSIDE_MASK) == 0 ? ~0 : 0;
+        // TODO: Deal with RTL languages to position scrollbar on left
+        final int left = scrollX + width - size - (mUserPaddingRight & inside);
+
+        scrollBar.setBounds(left, scrollY + (mPaddingTop & inside),
+                left + size, scrollY + height - (mUserPaddingBottom & inside));
+        scrollBar.setParameters(
+                computeVerticalScrollRange(),
+                computeVerticalScrollOffset(),
+                computeVerticalScrollExtent(), true);
+        scrollBar.draw(canvas);
+    }
+
+    /**
+     * Implement this to do your drawing.
+     *
+     * @param canvas the canvas on which the background will be drawn
+     */
+    protected void onDraw(Canvas canvas) {
+    }
+
+    /*
+     * Caller is responsible for calling requestLayout if necessary.
+     * (This allows addViewInLayout to not request a new layout.)
+     */
+    void assignParent(ViewParent parent) {
+        if (mParent == null) {
+            mParent = parent;
+        } else if (parent == null) {
+            mParent = null;
+        } else {
+            throw new RuntimeException("view " + this + " being added, but"
+                    + " it already has a parent");
+        }
+    }
+
+    /**
+     * This is called when the view is attached to a window.  At this point it
+     * has a Surface and will start drawing.  Note that this function is
+     * guaranteed to be called before {@link #onDraw}, however it may be called
+     * any time before the first onDraw -- including before or after
+     * {@link #onMeasure}.
+     *
+     * @see #onDetachedFromWindow()
+     */
+    protected void onAttachedToWindow() {
+        if ((mPrivateFlags & REQUEST_TRANSPARENT_REGIONS) != 0) {
+            mParent.requestTransparentRegion(this);
+        }
+    }
+
+    /**
+     * This is called when the view is detached from a window.  At this point it
+     * no longer has a surface for drawing.
+     *
+     * @see #onAttachedToWindow()
+     */
+    protected void onDetachedFromWindow() {
+        if (mPendingCheckForLongPress != null) {
+            removeCallbacks(mPendingCheckForLongPress);
+        }
+        destroyDrawingCache();
+    }
+
+    /**
+     * @return The number of times this view has been attached to a window
+     */
+    protected int getWindowAttachCount() {
+        return mWindowAttachCount;
+    }
+
+    /**
+     * Retrieve a unique token identifying the window this view is attached to.
+     * @return Return the window's token for use in
+     * {@link WindowManager.LayoutParams#token WindowManager.LayoutParams.token}.
+     */
+    public IBinder getWindowToken() {
+        return mAttachInfo != null ? mAttachInfo.mWindowToken : null;
+    }
+
+    /**
+     * Retrieve a unique token identifying the top-level "real" window of
+     * the window that this view is attached to.  That is, this is like
+     * {@link #getWindowToken}, except if the window this view in is a panel
+     * window (attached to another containing window), then the token of
+     * the containing window is returned instead.
+     *
+     * @return Returns the associated window token, either
+     * {@link #getWindowToken()} or the containing window's token.
+     */
+    public IBinder getApplicationWindowToken() {
+        AttachInfo ai = mAttachInfo;
+        if (ai != null) {
+            IBinder appWindowToken = ai.mPanelParentWindowToken;
+            if (appWindowToken == null) {
+                appWindowToken = ai.mWindowToken;
+            }
+            return appWindowToken;
+        }
+        return null;
+    }
+
+    /**
+     * Retrieve private session object this view hierarchy is using to
+     * communicate with the window manager.
+     * @return the session object to communicate with the window manager
+     */
+    /*package*/ IWindowSession getWindowSession() {
+        return mAttachInfo != null ? mAttachInfo.mSession : null;
+    }
+
+    /**
+     * @param info the {@link android.view.View.AttachInfo} to associated with
+     *        this view
+     */
+    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
+        //System.out.println("Attached! " + this);
+        mAttachInfo = info;
+        mWindowAttachCount++;
+        if (mFloatingTreeObserver != null) {
+            info.mTreeObserver.merge(mFloatingTreeObserver);
+            mFloatingTreeObserver = null;
+        }
+        if ((mPrivateFlags&SCROLL_CONTAINER) != 0) {
+            mAttachInfo.mScrollContainers.add(this);
+            mPrivateFlags |= SCROLL_CONTAINER_ADDED;
+        }
+        performCollectViewAttributes(visibility);
+        onAttachedToWindow();
+        int vis = info.mWindowVisibility;
+        if (vis != GONE) {
+            onWindowVisibilityChanged(vis);
+        }
+    }
+
+    void dispatchDetachedFromWindow() {
+        //System.out.println("Detached! " + this);
+        AttachInfo info = mAttachInfo;
+        if (info != null) {
+            int vis = info.mWindowVisibility;
+            if (vis != GONE) {
+                onWindowVisibilityChanged(GONE);
+            }
+        }
+
+        onDetachedFromWindow();
+        if ((mPrivateFlags&SCROLL_CONTAINER_ADDED) != 0) {
+            mAttachInfo.mScrollContainers.remove(this);
+            mPrivateFlags &= ~SCROLL_CONTAINER_ADDED;
+        }
+        mAttachInfo = null;
+    }
+
+    /**
+     * Store this view hierarchy's frozen state into the given container.
+     *
+     * @param container The SparseArray in which to save the view's state.
+     *
+     * @see #restoreHierarchyState
+     * @see #dispatchSaveInstanceState
+     * @see #onSaveInstanceState
+     */
+    public void saveHierarchyState(SparseArray<Parcelable> container) {
+        dispatchSaveInstanceState(container);
+    }
+
+    /**
+     * Called by {@link #saveHierarchyState} to store the state for this view and its children.
+     * May be overridden to modify how freezing happens to a view's children; for example, some
+     * views may want to not store state for their children.
+     *
+     * @param container The SparseArray in which to save the view's state.
+     *
+     * @see #dispatchRestoreInstanceState
+     * @see #saveHierarchyState
+     * @see #onSaveInstanceState
+     */
+    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+        if (mID != NO_ID && (mViewFlags & SAVE_DISABLED_MASK) == 0) {
+            mPrivateFlags &= ~SAVE_STATE_CALLED;
+            Parcelable state = onSaveInstanceState();
+            if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) {
+                throw new IllegalStateException(
+                        "Derived class did not call super.onSaveInstanceState()");
+            }
+            if (state != null) {
+                // Log.i("View", "Freezing #" + Integer.toHexString(mID)
+                // + ": " + state);
+                container.put(mID, state);
+            }
+        }
+    }
+
+    /**
+     * Hook allowing a view to generate a representation of its internal state
+     * that can later be used to create a new instance with that same state.
+     * This state should only contain information that is not persistent or can
+     * not be reconstructed later. For example, you will never store your
+     * current position on screen because that will be computed again when a
+     * new instance of the view is placed in its view hierarchy.
+     * <p>
+     * Some examples of things you may store here: the current cursor position
+     * in a text view (but usually not the text itself since that is stored in a
+     * content provider or other persistent storage), the currently selected
+     * item in a list view.
+     *
+     * @return Returns a Parcelable object containing the view's current dynamic
+     *         state, or null if there is nothing interesting to save. The
+     *         default implementation returns null.
+     * @see #onRestoreInstanceState
+     * @see #saveHierarchyState
+     * @see #dispatchSaveInstanceState
+     * @see #setSaveEnabled(boolean)
+     */
+    protected Parcelable onSaveInstanceState() {
+        mPrivateFlags |= SAVE_STATE_CALLED;
+        return BaseSavedState.EMPTY_STATE;
+    }
+
+    /**
+     * Restore this view hierarchy's frozen state from the given container.
+     *
+     * @param container The SparseArray which holds previously frozen states.
+     *
+     * @see #saveHierarchyState
+     * @see #dispatchRestoreInstanceState
+     * @see #onRestoreInstanceState
+     */
+    public void restoreHierarchyState(SparseArray<Parcelable> container) {
+        dispatchRestoreInstanceState(container);
+    }
+
+    /**
+     * Called by {@link #restoreHierarchyState} to retrieve the state for this view and its
+     * children. May be overridden to modify how restoreing happens to a view's children; for
+     * example, some views may want to not store state for their children.
+     *
+     * @param container The SparseArray which holds previously saved state.
+     *
+     * @see #dispatchSaveInstanceState
+     * @see #restoreHierarchyState
+     * @see #onRestoreInstanceState
+     */
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        if (mID != NO_ID) {
+            Parcelable state = container.get(mID);
+            if (state != null) {
+                // Log.i("View", "Restoreing #" + Integer.toHexString(mID)
+                // + ": " + state);
+                mPrivateFlags &= ~SAVE_STATE_CALLED;
+                onRestoreInstanceState(state);
+                if ((mPrivateFlags & SAVE_STATE_CALLED) == 0) {
+                    throw new IllegalStateException(
+                            "Derived class did not call super.onRestoreInstanceState()");
+                }
+            }
+        }
+    }
+
+    /**
+     * Hook allowing a view to re-apply a representation of its internal state that had previously
+     * been generated by {@link #onSaveInstanceState}. This function will never be called with a
+     * null state.
+     *
+     * @param state The frozen state that had previously been returned by
+     *        {@link #onSaveInstanceState}.
+     *
+     * @see #onSaveInstanceState
+     * @see #restoreHierarchyState
+     * @see #dispatchRestoreInstanceState
+     */
+    protected void onRestoreInstanceState(Parcelable state) {
+        mPrivateFlags |= SAVE_STATE_CALLED;
+        if (state != BaseSavedState.EMPTY_STATE && state != null) {
+            throw new IllegalArgumentException("Wrong state class -- expecting View State");
+        }
+    }
+
+    /**
+     * <p>Return the time at which the drawing of the view hierarchy started.</p>
+     *
+     * @return the drawing start time in milliseconds
+     */
+    public long getDrawingTime() {
+        return mAttachInfo != null ? mAttachInfo.mDrawingTime : 0;
+    }
+
+    /**
+     * <p>Enables or disables the duplication of the parent's state into this view. When
+     * duplication is enabled, this view gets its drawable state from its parent rather
+     * than from its own internal properties.</p>
+     *
+     * <p>Note: in the current implementation, setting this property to true after the
+     * view was added to a ViewGroup might have no effect at all. This property should
+     * always be used from XML or set to true before adding this view to a ViewGroup.</p>
+     *
+     * <p>Note: if this view's parent addStateFromChildren property is enabled and this
+     * property is enabled, an exception will be thrown.</p>
+     *
+     * @param enabled True to enable duplication of the parent's drawable state, false
+     *                to disable it.
+     *
+     * @see #getDrawableState()
+     * @see #isDuplicateParentStateEnabled()
+     */
+    public void setDuplicateParentStateEnabled(boolean enabled) {
+        setFlags(enabled ? DUPLICATE_PARENT_STATE : 0, DUPLICATE_PARENT_STATE);
+    }
+
+    /**
+     * <p>Indicates whether this duplicates its drawable state from its parent.</p>
+     *
+     * @return True if this view's drawable state is duplicated from the parent,
+     *         false otherwise
+     *
+     * @see #getDrawableState()
+     * @see #setDuplicateParentStateEnabled(boolean)
+     */
+    public boolean isDuplicateParentStateEnabled() {
+        return (mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE;
+    }
+
+    /**
+     * <p>Enables or disables the drawing cache. When the drawing cache is enabled, the next call
+     * to {@link #getDrawingCache()} or {@link #buildDrawingCache()} will draw the view in a
+     * bitmap. Calling {@link #draw(android.graphics.Canvas)} will not draw from the cache when
+     * the cache is enabled. To benefit from the cache, you must request the drawing cache by
+     * calling {@link #getDrawingCache()} and draw it on screen if the returned bitmap is not
+     * null.</p>
+     *
+     * @param enabled true to enable the drawing cache, false otherwise
+     *
+     * @see #isDrawingCacheEnabled()
+     * @see #getDrawingCache()
+     * @see #buildDrawingCache()
+     */
+    public void setDrawingCacheEnabled(boolean enabled) {
+        setFlags(enabled ? DRAWING_CACHE_ENABLED : 0, DRAWING_CACHE_ENABLED);
+    }
+
+    /**
+     * <p>Indicates whether the drawing cache is enabled for this view.</p>
+     *
+     * @return true if the drawing cache is enabled
+     *
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #getDrawingCache()
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isDrawingCacheEnabled() {
+        return (mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED;
+    }
+
+    /**
+     * <p>Returns the bitmap in which this view drawing is cached. The returned bitmap
+     * is null when caching is disabled. If caching is enabled and the cache is not ready,
+     * this method will create it. Calling {@link #draw(android.graphics.Canvas)} will not
+     * draw from the cache when the cache is enabled. To benefit from the cache, you must
+     * request the drawing cache by calling this method and draw it on screen if the
+     * returned bitmap is not null.</p>
+     *
+     * @return a bitmap representing this view or null if cache is disabled
+     *
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #isDrawingCacheEnabled()
+     * @see #buildDrawingCache()
+     * @see #destroyDrawingCache()
+     */
+    public Bitmap getDrawingCache() {
+        if ((mViewFlags & WILL_NOT_CACHE_DRAWING) == WILL_NOT_CACHE_DRAWING) {
+            return null;
+        }
+        if ((mViewFlags & DRAWING_CACHE_ENABLED) == DRAWING_CACHE_ENABLED) {
+            buildDrawingCache();
+        }
+        return mDrawingCache == null ? null : mDrawingCache.get();
+    }
+
+    /**
+     * <p>Frees the resources used by the drawing cache. If you call
+     * {@link #buildDrawingCache()} manually without calling
+     * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
+     * should cleanup the cache with this method afterwards.</p>
+     *
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #buildDrawingCache()
+     * @see #getDrawingCache()
+     */
+    public void destroyDrawingCache() {
+        if (mDrawingCache != null) {
+            final Bitmap bitmap = mDrawingCache.get();
+            if (bitmap != null) bitmap.recycle();
+            mDrawingCache = null;
+        }
+    }
+
+    /**
+     * Setting a solid background color for the drawing cache's bitmaps will improve
+     * perfromance and memory usage. Note, though that this should only be used if this
+     * view will always be drawn on top of a solid color.
+     *
+     * @param color The background color to use for the drawing cache's bitmap
+     *
+     * @see #setDrawingCacheEnabled(boolean)
+     * @see #buildDrawingCache()
+     * @see #getDrawingCache()
+     */
+    public void setDrawingCacheBackgroundColor(int color) {
+        mDrawingCacheBackgroundColor = color;
+    }
+
+    /**
+     * @see #setDrawingCacheBackgroundColor(int)
+     *
+     * @return The background color to used for the drawing cache's bitmap
+     */
+    public int getDrawingCacheBackgroundColor() {
+        return mDrawingCacheBackgroundColor;
+    }
+
+    /**
+     * <p>Forces the drawing cache to be built if the drawing cache is invalid.</p>
+     *
+     * <p>If you call {@link #buildDrawingCache()} manually without calling
+     * {@link #setDrawingCacheEnabled(boolean) setDrawingCacheEnabled(true)}, you
+     * should cleanup the cache by calling {@link #destroyDrawingCache()} afterwards.</p>
+     *
+     * @see #getDrawingCache()
+     * @see #destroyDrawingCache()
+     */
+    public void buildDrawingCache() {
+        if ((mPrivateFlags & DRAWING_CACHE_VALID) == 0 || mDrawingCache == null ||
+                mDrawingCache.get() == null) {
+
+            if (ViewDebug.TRACE_HIERARCHY) {
+                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.BUILD_CACHE);
+            }
+            if (ViewRoot.PROFILE_DRAWING) {
+                EventLog.writeEvent(60002, hashCode());
+            }
+
+            final int width = mRight - mLeft;
+            final int height = mBottom - mTop;
+
+            final int drawingCacheBackgroundColor = mDrawingCacheBackgroundColor;
+            final boolean opaque = drawingCacheBackgroundColor != 0 ||
+                (mBGDrawable != null && mBGDrawable.getOpacity() == PixelFormat.OPAQUE);
+
+            if (width <= 0 || height <= 0 ||
+                    (width * height * (opaque ? 2 : 4) >= // Projected bitmap size in bytes
+                            ViewConfiguration.get(mContext).getScaledMaximumDrawingCacheSize())) {
+                destroyDrawingCache();
+                return;
+            }
+
+            boolean clear = true;
+            Bitmap bitmap = mDrawingCache == null ? null : mDrawingCache.get();
+
+            if (bitmap == null || bitmap.getWidth() != width || bitmap.getHeight() != height) {
+
+                Bitmap.Config quality;
+                if (!opaque) {
+                    switch (mViewFlags & DRAWING_CACHE_QUALITY_MASK) {
+                        case DRAWING_CACHE_QUALITY_AUTO:
+                            quality = Bitmap.Config.ARGB_8888;
+                            break;
+                        case DRAWING_CACHE_QUALITY_LOW:
+                            quality = Bitmap.Config.ARGB_4444;
+                            break;
+                        case DRAWING_CACHE_QUALITY_HIGH:
+                            quality = Bitmap.Config.ARGB_8888;
+                            break;
+                        default:
+                            quality = Bitmap.Config.ARGB_8888;
+                            break;
+                    }
+                } else {
+                    quality = Bitmap.Config.RGB_565;
+                }
+
+                // Try to cleanup memory
+                if (bitmap != null) bitmap.recycle();
+
+                try {
+                    bitmap = Bitmap.createBitmap(width, height, quality);
+                    mDrawingCache = new SoftReference<Bitmap>(bitmap);
+                } catch (OutOfMemoryError e) {
+                    // If there is not enough memory to create the bitmap cache, just
+                    // ignore the issue as bitmap caches are not required to draw the
+                    // view hierarchy
+                    mDrawingCache = null;
+                    return;
+                }
+
+                clear = drawingCacheBackgroundColor != 0;
+            }
+
+            Canvas canvas;
+            final AttachInfo attachInfo = mAttachInfo;
+            if (attachInfo != null) {
+                canvas = attachInfo.mCanvas;
+                if (canvas == null) {
+                    canvas = new Canvas();
+                }
+                canvas.setBitmap(bitmap);
+                // Temporarily clobber the cached Canvas in case one of our children
+                // is also using a drawing cache. Without this, the children would
+                // steal the canvas by attaching their own bitmap to it and bad, bad
+                // thing would happen (invisible views, corrupted drawings, etc.)
+                attachInfo.mCanvas = null;
+            } else {
+                // This case should hopefully never or seldom happen
+                canvas = new Canvas(bitmap);
+            }
+
+            if (clear) {
+                bitmap.eraseColor(drawingCacheBackgroundColor);
+            }
+
+            computeScroll();
+            final int restoreCount = canvas.save();
+            canvas.translate(-mScrollX, -mScrollY);
+
+            mPrivateFlags |= DRAWN;
+
+            // Fast path for layouts with no backgrounds
+            if ((mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                if (ViewDebug.TRACE_HIERARCHY) {
+                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+                }
+                dispatchDraw(canvas);
+            } else {
+                draw(canvas);
+            }
+
+            canvas.restoreToCount(restoreCount);
+
+            if (attachInfo != null) {
+                // Restore the cached Canvas for our siblings
+                attachInfo.mCanvas = canvas;
+            }
+            mPrivateFlags |= DRAWING_CACHE_VALID;
+        }
+    }
+
+    /**
+     * Indicates whether this View is currently in edit mode. A View is usually
+     * in edit mode when displayed within a developer tool. For instance, if
+     * this View is being drawn by a visual user interface builder, this method
+     * should return true.
+     *
+     * Subclasses should check the return value of this method to provide
+     * different behaviors if their normal behavior might interfere with the
+     * host environment. For instance: the class spawns a thread in its
+     * constructor, the drawing code relies on device-specific features, etc.
+     *
+     * This method is usually checked in the drawing code of custom widgets.
+     *
+     * @return True if this View is in edit mode, false otherwise.
+     */
+    public boolean isInEditMode() {
+        return false;
+    }
+
+    /**
+     * If the View draws content inside its padding and enables fading edges,
+     * it needs to support padding offsets. Padding offsets are added to the
+     * fading edges to extend the length of the fade so that it covers pixels
+     * drawn inside the padding.
+     *
+     * Subclasses of this class should override this method if they need
+     * to draw content inside the padding.
+     *
+     * @return True if padding offset must be applied, false otherwise.
+     *
+     * @see #getLeftPaddingOffset()
+     * @see #getRightPaddingOffset()
+     * @see #getTopPaddingOffset()
+     * @see #getBottomPaddingOffset()
+     *
+     * @since CURRENT
+     */
+    protected boolean isPaddingOffsetRequired() {
+        return false;
+    }
+
+    /**
+     * Amount by which to extend the left fading region. Called only when
+     * {@link #isPaddingOffsetRequired()} returns true.
+     *
+     * @return The left padding offset in pixels.
+     *
+     * @see #isPaddingOffsetRequired()
+     *
+     * @since CURRENT
+     */
+    protected int getLeftPaddingOffset() {
+        return 0;
+    }
+
+    /**
+     * Amount by which to extend the right fading region. Called only when
+     * {@link #isPaddingOffsetRequired()} returns true.
+     *
+     * @return The right padding offset in pixels.
+     *
+     * @see #isPaddingOffsetRequired()
+     *
+     * @since CURRENT
+     */
+    protected int getRightPaddingOffset() {
+        return 0;
+    }
+
+    /**
+     * Amount by which to extend the top fading region. Called only when
+     * {@link #isPaddingOffsetRequired()} returns true.
+     *
+     * @return The top padding offset in pixels.
+     *
+     * @see #isPaddingOffsetRequired()
+     *
+     * @since CURRENT
+     */
+    protected int getTopPaddingOffset() {
+        return 0;
+    }
+
+    /**
+     * Amount by which to extend the bottom fading region. Called only when
+     * {@link #isPaddingOffsetRequired()} returns true.
+     *
+     * @return The bottom padding offset in pixels.
+     *
+     * @see #isPaddingOffsetRequired()
+     *
+     * @since CURRENT
+     */
+    protected int getBottomPaddingOffset() {
+        return 0;
+    }
+
+    /**
+     * Manually render this view (and all of its children) to the given Canvas.
+     * The view must have already done a full layout before this function is
+     * called.  When implementing a view, do not override this method; instead,
+     * you should implement {@link #onDraw}.
+     *
+     * @param canvas The Canvas to which the View is rendered.
+     */
+    public void draw(Canvas canvas) {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+        }
+
+        mPrivateFlags |= DRAWN;                    
+
+        /*
+         * Draw traversal performs several drawing steps which must be executed
+         * in the appropriate order:
+         *
+         *      1. Draw the background
+         *      2. If necessary, save the canvas' layers to prepare for fading
+         *      3. Draw view's content
+         *      4. Draw children
+         *      5. If necessary, draw the fading edges and restore layers
+         *      6. Draw decorations (scrollbars for instance)
+         */
+
+        // Step 1, draw the background, if needed
+        int saveCount;
+
+        final Drawable background = mBGDrawable;
+        if (background != null) {
+            final int scrollX = mScrollX;
+            final int scrollY = mScrollY;
+
+            if (mBackgroundSizeChanged) {
+                background.setBounds(0, 0,  mRight - mLeft, mBottom - mTop);
+                mBackgroundSizeChanged = false;
+            }
+
+            if ((scrollX | scrollY) == 0) {
+                background.draw(canvas);
+            } else {
+                canvas.translate(scrollX, scrollY);
+                background.draw(canvas);
+                canvas.translate(-scrollX, -scrollY);
+            }
+        }
+
+        // skip step 2 & 5 if possible (common case)
+        final int viewFlags = mViewFlags;
+        boolean horizontalEdges = (viewFlags & FADING_EDGE_HORIZONTAL) != 0;
+        boolean verticalEdges = (viewFlags & FADING_EDGE_VERTICAL) != 0;
+        if (!verticalEdges && !horizontalEdges) {
+            // Step 3, draw the content
+            onDraw(canvas);
+
+            // Step 4, draw the children
+            dispatchDraw(canvas);
+
+            // Step 6, draw decorations (scrollbars)
+            onDrawScrollBars(canvas);
+
+            // we're done...
+            return;
+        }
+
+        /*
+         * Here we do the full fledged routine...
+         * (this is an uncommon case where speed matters less,
+         * this is why we repeat some of the tests that have been
+         * done above)
+         */
+
+        boolean drawTop = false;
+        boolean drawBottom = false;
+        boolean drawLeft = false;
+        boolean drawRight = false;
+
+        float topFadeStrength = 0.0f;
+        float bottomFadeStrength = 0.0f;
+        float leftFadeStrength = 0.0f;
+        float rightFadeStrength = 0.0f;
+
+        // Step 2, save the canvas' layers
+        int paddingLeft = mPaddingLeft;
+        int paddingTop = mPaddingTop;
+
+        final boolean offsetRequired = isPaddingOffsetRequired();
+        if (offsetRequired) {
+            paddingLeft += getLeftPaddingOffset();
+            paddingTop += getTopPaddingOffset();
+        }
+
+        int left = mScrollX + paddingLeft;
+        int right = left + mRight - mLeft - mPaddingRight - paddingLeft;
+        int top = mScrollY + paddingTop;
+        int bottom = top + mBottom - mTop - mPaddingBottom - paddingTop;
+
+        if (offsetRequired) {
+            right += getRightPaddingOffset();
+            bottom += getBottomPaddingOffset();
+        }
+
+        final ScrollabilityCache scrollabilityCache = mScrollCache;
+        int length = scrollabilityCache.fadingEdgeLength;
+
+        // clip the fade length if top and bottom fades overlap
+        // overlapping fades produce odd-looking artifacts
+        if (verticalEdges && (top + length > bottom - length)) {
+            length = (bottom - top) / 2;
+        }
+
+        // also clip horizontal fades if necessary
+        if (horizontalEdges && (left + length > right - length)) {
+            length = (right - left) / 2;
+        }
+
+        if (verticalEdges) {
+            topFadeStrength = Math.max(0.0f, Math.min(1.0f, getTopFadingEdgeStrength()));
+            drawTop = topFadeStrength >= 0.0f;
+            bottomFadeStrength = Math.max(0.0f, Math.min(1.0f, getBottomFadingEdgeStrength()));
+            drawBottom = bottomFadeStrength >= 0.0f;
+        }
+
+        if (horizontalEdges) {
+            leftFadeStrength = Math.max(0.0f, Math.min(1.0f, getLeftFadingEdgeStrength()));
+            drawLeft = leftFadeStrength >= 0.0f;
+            rightFadeStrength = Math.max(0.0f, Math.min(1.0f, getRightFadingEdgeStrength()));
+            drawRight = rightFadeStrength >= 0.0f;
+        }
+
+        saveCount = canvas.getSaveCount();
+
+        int solidColor = getSolidColor();
+        if (solidColor == 0) {
+            final int flags = Canvas.HAS_ALPHA_LAYER_SAVE_FLAG;
+
+            if (drawTop) {
+                canvas.saveLayer(left, top, right, top + length, null, flags);
+            }
+
+            if (drawBottom) {
+                canvas.saveLayer(left, bottom - length, right, bottom, null, flags);
+            }
+
+            if (drawLeft) {
+                canvas.saveLayer(left, top, left + length, bottom, null, flags);
+            }
+
+            if (drawRight) {
+                canvas.saveLayer(right - length, top, right, bottom, null, flags);
+            }
+        } else {
+            scrollabilityCache.setFadeColor(solidColor);
+        }
+
+        // Step 3, draw the content
+        onDraw(canvas);
+
+        // Step 4, draw the children
+        dispatchDraw(canvas);
+
+        // Step 5, draw the fade effect and restore layers
+        final Paint p = scrollabilityCache.paint;
+        final Matrix matrix = scrollabilityCache.matrix;
+        final Shader fade = scrollabilityCache.shader;
+        final float fadeHeight = scrollabilityCache.fadingEdgeLength;
+
+        if (drawTop) {
+            matrix.setScale(1, fadeHeight * topFadeStrength);
+            matrix.postTranslate(left, top);
+            fade.setLocalMatrix(matrix);
+            canvas.drawRect(left, top, right, top + length, p);
+        }
+
+        if (drawBottom) {
+            matrix.setScale(1, fadeHeight * bottomFadeStrength);
+            matrix.postRotate(180);
+            matrix.postTranslate(left, bottom);
+            fade.setLocalMatrix(matrix);
+            canvas.drawRect(left, bottom - length, right, bottom, p);
+        }
+
+        if (drawLeft) {
+            matrix.setScale(1, fadeHeight * leftFadeStrength);
+            matrix.postRotate(-90);
+            matrix.postTranslate(left, top);
+            fade.setLocalMatrix(matrix);
+            canvas.drawRect(left, top, left + length, bottom, p);
+        }
+
+        if (drawRight) {
+            matrix.setScale(1, fadeHeight * rightFadeStrength);
+            matrix.postRotate(90);
+            matrix.postTranslate(right, top);
+            fade.setLocalMatrix(matrix);
+            canvas.drawRect(right - length, top, right, bottom, p);
+        }
+
+        canvas.restoreToCount(saveCount);
+
+        // Step 6, draw decorations (scrollbars)
+        onDrawScrollBars(canvas);
+    }
+
+    /**
+     * Override this if your view is known to always be drawn on top of a solid color background,
+     * and needs to draw fading edges. Returning a non-zero color enables the view system to
+     * optimize the drawing of the fading edges. If you do return a non-zero color, the alpha
+     * should be set to 0xFF.
+     *
+     * @see #setVerticalFadingEdgeEnabled
+     * @see #setHorizontalFadingEdgeEnabled
+     *
+     * @return The known solid color background for this view, or 0 if the color may vary
+     */
+    public int getSolidColor() {
+        return 0;
+    }
+
+    /**
+     * Build a human readable string representation of the specified view flags.
+     *
+     * @param flags the view flags to convert to a string
+     * @return a String representing the supplied flags
+     */
+    private static String printFlags(int flags) {
+        String output = "";
+        int numFlags = 0;
+        if ((flags & FOCUSABLE_MASK) == FOCUSABLE) {
+            output += "TAKES_FOCUS";
+            numFlags++;
+        }
+
+        switch (flags & VISIBILITY_MASK) {
+        case INVISIBLE:
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "INVISIBLE";
+            // USELESS HERE numFlags++;
+            break;
+        case GONE:
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "GONE";
+            // USELESS HERE numFlags++;
+            break;
+        default:
+            break;
+        }
+        return output;
+    }
+
+    /**
+     * Build a human readable string representation of the specified private
+     * view flags.
+     *
+     * @param privateFlags the private view flags to convert to a string
+     * @return a String representing the supplied flags
+     */
+    private static String printPrivateFlags(int privateFlags) {
+        String output = "";
+        int numFlags = 0;
+
+        if ((privateFlags & WANTS_FOCUS) == WANTS_FOCUS) {
+            output += "WANTS_FOCUS";
+            numFlags++;
+        }
+
+        if ((privateFlags & FOCUSED) == FOCUSED) {
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "FOCUSED";
+            numFlags++;
+        }
+
+        if ((privateFlags & SELECTED) == SELECTED) {
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "SELECTED";
+            numFlags++;
+        }
+
+        if ((privateFlags & IS_ROOT_NAMESPACE) == IS_ROOT_NAMESPACE) {
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "IS_ROOT_NAMESPACE";
+            numFlags++;
+        }
+
+        if ((privateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "HAS_BOUNDS";
+            numFlags++;
+        }
+
+        if ((privateFlags & DRAWN) == DRAWN) {
+            if (numFlags > 0) {
+                output += " ";
+            }
+            output += "DRAWN";
+            // USELESS HERE numFlags++;
+        }
+        return output;
+    }
+
+    /**
+     * <p>Indicates whether or not this view's layout will be requested during
+     * the next hierarchy layout pass.</p>
+     *
+     * @return true if the layout will be forced during next layout pass
+     */
+    public boolean isLayoutRequested() {
+        return (mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT;
+    }
+
+    /**
+     * Assign a size and position to a view and all of its
+     * descendants
+     *
+     * <p>This is the second phase of the layout mechanism.
+     * (The first is measuring). In this phase, each parent calls
+     * layout on all of its children to position them.
+     * This is typically done using the child measurements
+     * that were stored in the measure pass().
+     *
+     * Derived classes with children should override
+     * onLayout. In that method, they should
+     * call layout on each of their their children.
+     *
+     * @param l Left position, relative to parent
+     * @param t Top position, relative to parent
+     * @param r Right position, relative to parent
+     * @param b Bottom position, relative to parent
+     */
+    public final void layout(int l, int t, int r, int b) {
+        boolean changed = setFrame(l, t, r, b);
+        if (changed || (mPrivateFlags & LAYOUT_REQUIRED) == LAYOUT_REQUIRED) {
+            if (ViewDebug.TRACE_HIERARCHY) {
+                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_LAYOUT);
+            }
+
+            onLayout(changed, l, t, r, b);
+            mPrivateFlags &= ~LAYOUT_REQUIRED;
+        }
+        mPrivateFlags &= ~FORCE_LAYOUT;
+    }
+
+    /**
+     * Called from layout when this view should
+     * assign a size and position to each of its children.
+     *
+     * Derived classes with children should override
+     * this method and call layout on each of
+     * their their children.
+     * @param changed This is a new size or position for this view
+     * @param left Left position, relative to parent
+     * @param top Top position, relative to parent
+     * @param right Right position, relative to parent
+     * @param bottom Bottom position, relative to parent
+     */
+    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
+    }
+
+    /**
+     * Assign a size and position to this view.
+     *
+     * This is called from layout.
+     *
+     * @param left Left position, relative to parent
+     * @param top Top position, relative to parent
+     * @param right Right position, relative to parent
+     * @param bottom Bottom position, relative to parent
+     * @return true if the new size and position are different than the
+     *         previous ones
+     * {@hide}
+     */
+    protected boolean setFrame(int left, int top, int right, int bottom) {
+        boolean changed = false;
+
+        if (DBG) {
+            System.out.println(this + " View.setFrame(" + left + "," + top + ","
+                    + right + "," + bottom + ")");
+        }
+
+        if (mLeft != left || mRight != right || mTop != top || mBottom != bottom) {
+            changed = true;
+
+            // Remember our drawn bit
+            int drawn = mPrivateFlags & DRAWN;
+
+            // Invalidate our old position
+            invalidate();
+
+
+            int oldWidth = mRight - mLeft;
+            int oldHeight = mBottom - mTop;
+
+            mLeft = left;
+            mTop = top;
+            mRight = right;
+            mBottom = bottom;
+
+            mPrivateFlags |= HAS_BOUNDS;
+
+            int newWidth = right - left;
+            int newHeight = bottom - top;
+
+            if (newWidth != oldWidth || newHeight != oldHeight) {
+                onSizeChanged(newWidth, newHeight, oldWidth, oldHeight);
+            }
+
+            if ((mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                // If we are visible, force the DRAWN bit to on so that
+                // this invalidate will go through (at least to our parent).
+                // This is because someone may have invalidated this view
+                // before this call to setFrame came in, therby clearing
+                // the DRAWN bit.
+                mPrivateFlags |= DRAWN;
+                invalidate();
+            }
+
+            // Reset drawn bit to original value (invalidate turns it off)
+            mPrivateFlags |= drawn;
+
+            mBackgroundSizeChanged = true;
+        }
+        return changed;
+    }
+
+    /**
+     * Finalize inflating a view from XML.  This is called as the last phase
+     * of inflation, after all child views have been added.
+     *
+     * <p>Even if the subclass overrides onFinishInflate, they should always be
+     * sure to call the super method, so that we get called.
+     */
+    protected void onFinishInflate() {
+    }
+
+    /**
+     * Returns the resources associated with this view.
+     *
+     * @return Resources object.
+     */
+    public Resources getResources() {
+        return mResources;
+    }
+
+    /**
+     * Invalidates the specified Drawable.
+     *
+     * @param drawable the drawable to invalidate
+     */
+    public void invalidateDrawable(Drawable drawable) {
+        if (verifyDrawable(drawable)) {
+            final Rect dirty = drawable.getBounds();
+            final int scrollX = mScrollX;
+            final int scrollY = mScrollY;
+
+            invalidate(dirty.left + scrollX, dirty.top + scrollY,
+                    dirty.right + scrollX, dirty.bottom + scrollY);
+        }
+    }
+
+    /**
+     * Schedules an action on a drawable to occur at a specified time.
+     *
+     * @param who the recipient of the action
+     * @param what the action to run on the drawable
+     * @param when the time at which the action must occur. Uses the
+     *        {@link SystemClock#uptimeMillis} timebase.
+     */
+    public void scheduleDrawable(Drawable who, Runnable what, long when) {
+        if (verifyDrawable(who) && what != null && mAttachInfo != null) {
+            mAttachInfo.mHandler.postAtTime(what, who, when);
+        }
+    }
+
+    /**
+     * Cancels a scheduled action on a drawable.
+     *
+     * @param who the recipient of the action
+     * @param what the action to cancel
+     */
+    public void unscheduleDrawable(Drawable who, Runnable what) {
+        if (verifyDrawable(who) && what != null && mAttachInfo != null) {
+            mAttachInfo.mHandler.removeCallbacks(what, who);
+        }
+    }
+
+    /**
+     * Unschedule any events associated with the given Drawable.  This can be
+     * used when selecting a new Drawable into a view, so that the previous
+     * one is completely unscheduled.
+     *
+     * @param who The Drawable to unschedule.
+     *
+     * @see #drawableStateChanged
+     */
+    public void unscheduleDrawable(Drawable who) {
+        if (mAttachInfo != null) {
+            mAttachInfo.mHandler.removeCallbacksAndMessages(who);
+        }
+    }
+
+    /**
+     * If your view subclass is displaying its own Drawable objects, it should
+     * override this function and return true for any Drawable it is
+     * displaying.  This allows animations for those drawables to be
+     * scheduled.
+     *
+     * <p>Be sure to call through to the super class when overriding this
+     * function.
+     *
+     * @param who The Drawable to verify.  Return true if it is one you are
+     *            displaying, else return the result of calling through to the
+     *            super class.
+     *
+     * @return boolean If true than the Drawable is being displayed in the
+     *         view; else false and it is not allowed to animate.
+     *
+     * @see #unscheduleDrawable
+     * @see #drawableStateChanged
+     */
+    protected boolean verifyDrawable(Drawable who) {
+        return who == mBGDrawable;
+    }
+
+    /**
+     * This function is called whenever the state of the view changes in such
+     * a way that it impacts the state of drawables being shown.
+     *
+     * <p>Be sure to call through to the superclass when overriding this
+     * function.
+     *
+     * @see Drawable#setState
+     */
+    protected void drawableStateChanged() {
+        Drawable d = mBGDrawable;
+        if (d != null && d.isStateful()) {
+            d.setState(getDrawableState());
+        }
+    }
+
+    /**
+     * Call this to force a view to update its drawable state. This will cause
+     * drawableStateChanged to be called on this view. Views that are interested
+     * in the new state should call getDrawableState.
+     *
+     * @see #drawableStateChanged
+     * @see #getDrawableState
+     */
+    public void refreshDrawableState() {
+        mPrivateFlags |= DRAWABLE_STATE_DIRTY;
+        drawableStateChanged();
+
+        ViewParent parent = mParent;
+        if (parent != null) {
+            parent.childDrawableStateChanged(this);
+        }
+    }
+
+    /**
+     * Return an array of resource IDs of the drawable states representing the
+     * current state of the view.
+     *
+     * @return The current drawable state
+     *
+     * @see Drawable#setState
+     * @see #drawableStateChanged
+     * @see #onCreateDrawableState
+     */
+    public final int[] getDrawableState() {
+        if ((mDrawableState != null) && ((mPrivateFlags & DRAWABLE_STATE_DIRTY) == 0)) {
+            return mDrawableState;
+        } else {
+            mDrawableState = onCreateDrawableState(0);
+            mPrivateFlags &= ~DRAWABLE_STATE_DIRTY;
+            return mDrawableState;
+        }
+    }
+
+    /**
+     * Generate the new {@link android.graphics.drawable.Drawable} state for
+     * this view. This is called by the view
+     * system when the cached Drawable state is determined to be invalid.  To
+     * retrieve the current state, you should use {@link #getDrawableState}.
+     *
+     * @param extraSpace if non-zero, this is the number of extra entries you
+     * would like in the returned array in which you can place your own
+     * states.
+     *
+     * @return Returns an array holding the current {@link Drawable} state of
+     * the view.
+     *
+     * @see #mergeDrawableStates
+     */
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if ((mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE &&
+                mParent instanceof View) {
+            return ((View) mParent).onCreateDrawableState(extraSpace);
+        }
+
+        int[] drawableState;
+
+        int privateFlags = mPrivateFlags;
+
+        int viewStateIndex = (((privateFlags & PRESSED) != 0) ? 1 : 0);
+
+        viewStateIndex = (viewStateIndex << 1)
+                + (((mViewFlags & ENABLED_MASK) == ENABLED) ? 1 : 0);
+
+        viewStateIndex = (viewStateIndex << 1) + (isFocused() ? 1 : 0);
+
+        viewStateIndex = (viewStateIndex << 1)
+                + (((privateFlags & SELECTED) != 0) ? 1 : 0);
+
+        final boolean hasWindowFocus = hasWindowFocus();
+        viewStateIndex = (viewStateIndex << 1) + (hasWindowFocus ? 1 : 0);
+
+        drawableState = VIEW_STATE_SETS[viewStateIndex];
+
+        //noinspection ConstantIfStatement
+        if (false) {
+            Log.i("View", "drawableStateIndex=" + viewStateIndex);
+            Log.i("View", toString()
+                    + " pressed=" + ((privateFlags & PRESSED) != 0)
+                    + " en=" + ((mViewFlags & ENABLED_MASK) == ENABLED)
+                    + " fo=" + hasFocus()
+                    + " sl=" + ((privateFlags & SELECTED) != 0)
+                    + " wf=" + hasWindowFocus
+                    + ": " + Arrays.toString(drawableState));
+        }
+
+        if (extraSpace == 0) {
+            return drawableState;
+        }
+
+        final int[] fullState;
+        if (drawableState != null) {
+            fullState = new int[drawableState.length + extraSpace];
+            System.arraycopy(drawableState, 0, fullState, 0, drawableState.length);
+        } else {
+            fullState = new int[extraSpace];
+        }
+
+        return fullState;
+    }
+
+    /**
+     * Merge your own state values in <var>additionalState</var> into the base
+     * state values <var>baseState</var> that were returned by
+     * {@link #onCreateDrawableState}.
+     *
+     * @param baseState The base state values returned by
+     * {@link #onCreateDrawableState}, which will be modified to also hold your
+     * own additional state values.
+     *
+     * @param additionalState The additional state values you would like
+     * added to <var>baseState</var>; this array is not modified.
+     *
+     * @return As a convenience, the <var>baseState</var> array you originally
+     * passed into the function is returned.
+     *
+     * @see #onCreateDrawableState
+     */
+    protected static int[] mergeDrawableStates(int[] baseState, int[] additionalState) {
+        final int N = baseState.length;
+        int i = N - 1;
+        while (i >= 0 && baseState[i] == 0) {
+            i--;
+        }
+        System.arraycopy(additionalState, 0, baseState, i + 1, additionalState.length);
+        return baseState;
+    }
+
+    /**
+     * Sets the background color for this view.
+     * @param color the color of the background
+     */
+    public void setBackgroundColor(int color) {
+        setBackgroundDrawable(new ColorDrawable(color));
+    }
+
+    /**
+     * Set the background to a given resource. The resource should refer to
+     * a Drawable object.
+     * @param resid The identifier of the resource.
+     * @attr ref android.R.styleable#View_background
+     */
+    public void setBackgroundResource(int resid) {
+        if (resid != 0 && resid == mBackgroundResource) {
+            return;
+        }
+
+        Drawable d= null;
+        if (resid != 0) {
+            d = mResources.getDrawable(resid);
+        }
+        setBackgroundDrawable(d);
+
+        mBackgroundResource = resid;
+    }
+
+    /**
+     * Set the background to a given Drawable, or remove the background. If the
+     * background has padding, this View's padding is set to the background's
+     * padding. However, when a background is removed, this View's padding isn't
+     * touched. If setting the padding is desired, please use
+     * {@link #setPadding(int, int, int, int)}.
+     *
+     * @param d The Drawable to use as the background, or null to remove the
+     *        background
+     */
+    public void setBackgroundDrawable(Drawable d) {
+        boolean requestLayout = false;
+
+        mBackgroundResource = 0;
+
+        /*
+         * Regardless of whether we're setting a new background or not, we want
+         * to clear the previous drawable.
+         */
+        if (mBGDrawable != null) {
+            mBGDrawable.setCallback(null);
+            unscheduleDrawable(mBGDrawable);
+        }
+
+        if (d != null) {
+            Rect padding = sThreadLocal.get();
+            if (padding == null) {
+                padding = new Rect();
+                sThreadLocal.set(padding);
+            }
+            if (d.getPadding(padding)) {
+                setPadding(padding.left, padding.top, padding.right, padding.bottom);
+            }
+
+            // Compare the minimum sizes of the old Drawable and the new.  If there isn't an old or
+            // if it has a different minimum size, we should layout again
+            if (mBGDrawable == null || mBGDrawable.getMinimumHeight() != d.getMinimumHeight() ||
+                    mBGDrawable.getMinimumWidth() != d.getMinimumWidth()) {
+                requestLayout = true;
+            }
+
+            d.setCallback(this);
+            if (d.isStateful()) {
+                d.setState(getDrawableState());
+            }
+            d.setVisible(getVisibility() == VISIBLE, false);
+            mBGDrawable = d;
+
+            if ((mPrivateFlags & SKIP_DRAW) != 0) {
+                mPrivateFlags &= ~SKIP_DRAW;
+                mPrivateFlags |= ONLY_DRAWS_BACKGROUND;
+                requestLayout = true;
+            }
+        } else {
+            /* Remove the background */
+            mBGDrawable = null;
+
+            if ((mPrivateFlags & ONLY_DRAWS_BACKGROUND) != 0) {
+                /*
+                 * This view ONLY drew the background before and we're removing
+                 * the background, so now it won't draw anything
+                 * (hence we SKIP_DRAW)
+                 */
+                mPrivateFlags &= ~ONLY_DRAWS_BACKGROUND;
+                mPrivateFlags |= SKIP_DRAW;
+            }
+
+            /*
+             * When the background is set, we try to apply its padding to this
+             * View. When the background is removed, we don't touch this View's
+             * padding. This is noted in the Javadocs. Hence, we don't need to
+             * requestLayout(), the invalidate() below is sufficient.
+             */
+
+            // The old background's minimum size could have affected this
+            // View's layout, so let's requestLayout
+            requestLayout = true;
+        }
+
+        if (requestLayout) {
+            requestLayout();
+        }
+
+        mBackgroundSizeChanged = true;
+        invalidate();
+    }
+
+    /**
+     * Gets the background drawable
+     * @return The drawable used as the background for this view, if any.
+     */
+    public Drawable getBackground() {
+        return mBGDrawable;
+    }
+
+    private int getScrollBarPaddingLeft() {
+        // TODO: Deal with RTL languages
+        return 0;
+    }
+
+    /*
+     * Returns the pixels occupied by the vertical scrollbar, if not overlaid
+     */
+    private int getScrollBarPaddingRight() {
+        // TODO: Deal with RTL languages
+        if ((mViewFlags & SCROLLBARS_VERTICAL) == 0) {
+            return 0;
+        }
+        return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getVerticalScrollbarWidth();
+    }
+
+    /*
+     * Returns the pixels occupied by the horizontal scrollbar, if not overlaid
+     */
+    private int getScrollBarPaddingBottom() {
+        if ((mViewFlags & SCROLLBARS_HORIZONTAL) == 0) {
+            return 0;
+        }
+        return (mViewFlags & SCROLLBARS_INSET_MASK) == 0 ? 0 : getHorizontalScrollbarHeight();
+    }
+
+    /**
+     * Sets the padding. The view may add on the space required to display
+     * the scrollbars, depending on the style and visibility of the scrollbars.
+     * So the values returned from {@link #getPaddingLeft}, {@link #getPaddingTop},
+     * {@link #getPaddingRight} and {@link #getPaddingBottom} may be different
+     * from the values set in this call.
+     *
+     * @attr ref android.R.styleable#View_padding
+     * @attr ref android.R.styleable#View_paddingBottom
+     * @attr ref android.R.styleable#View_paddingLeft
+     * @attr ref android.R.styleable#View_paddingRight
+     * @attr ref android.R.styleable#View_paddingTop
+     * @param left the left padding in pixels
+     * @param top the top padding in pixels
+     * @param right the right padding in pixels
+     * @param bottom the bottom padding in pixels
+     */
+    public void setPadding(int left, int top, int right, int bottom) {
+        boolean changed = false;
+
+        mUserPaddingRight = right;
+        mUserPaddingBottom = bottom;
+
+        if (mPaddingLeft != left + getScrollBarPaddingLeft()) {
+            changed = true;
+            mPaddingLeft = left;
+        }
+        if (mPaddingTop != top) {
+            changed = true;
+            mPaddingTop = top;
+        }
+        if (mPaddingRight != right + getScrollBarPaddingRight()) {
+            changed = true;
+            mPaddingRight = right + getScrollBarPaddingRight();
+        }
+        if (mPaddingBottom != bottom + getScrollBarPaddingBottom()) {
+            changed = true;
+            mPaddingBottom = bottom + getScrollBarPaddingBottom();
+        }
+
+        if (changed) {
+            requestLayout();
+        }
+    }
+
+    /**
+     * Returns the top padding of this view.
+     *
+     * @return the top padding in pixels
+     */
+    public int getPaddingTop() {
+        return mPaddingTop;
+    }
+
+    /**
+     * Returns the bottom padding of this view. If there are inset and enabled
+     * scrollbars, this value may include the space required to display the
+     * scrollbars as well.
+     *
+     * @return the bottom padding in pixels
+     */
+    public int getPaddingBottom() {
+        return mPaddingBottom;
+    }
+
+    /**
+     * Returns the left padding of this view. If there are inset and enabled
+     * scrollbars, this value may include the space required to display the
+     * scrollbars as well.
+     *
+     * @return the left padding in pixels
+     */
+    public int getPaddingLeft() {
+        return mPaddingLeft;
+    }
+
+    /**
+     * Returns the right padding of this view. If there are inset and enabled
+     * scrollbars, this value may include the space required to display the
+     * scrollbars as well.
+     *
+     * @return the right padding in pixels
+     */
+    public int getPaddingRight() {
+        return mPaddingRight;
+    }
+
+    /**
+     * Changes the selection state of this view. A view can be selected or not.
+     * Note that selection is not the same as focus. Views are typically
+     * selected in the context of an AdapterView like ListView or GridView;
+     * the selected view is the view that is highlighted.
+     *
+     * @param selected true if the view must be selected, false otherwise
+     */
+    public void setSelected(boolean selected) {
+        if (((mPrivateFlags & SELECTED) != 0) != selected) {
+            mPrivateFlags = (mPrivateFlags & ~SELECTED) | (selected ? SELECTED : 0);
+            invalidate();
+            refreshDrawableState();
+            dispatchSetSelected(selected);
+        }
+    }
+
+    /**
+     * Dispatch setSelected to all of this View's children.
+     *
+     * @see #setSelected(boolean)
+     *
+     * @param selected The new selected state
+     */
+    protected void dispatchSetSelected(boolean selected) {
+    }
+
+    /**
+     * Indicates the selection state of this view.
+     *
+     * @return true if the view is selected, false otherwise
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isSelected() {
+        return (mPrivateFlags & SELECTED) != 0;
+    }
+
+    /**
+     * Returns the ViewTreeObserver for this view's hierarchy. The view tree
+     * observer can be used to get notifications when global events, like
+     * layout, happen.
+     *
+     * The returned ViewTreeObserver observer is not guaranteed to remain
+     * valid for the lifetime of this View. If the caller of this method keeps
+     * a long-lived reference to ViewTreeObserver, it should always check for
+     * the return value of {@link ViewTreeObserver#isAlive()}.
+     *
+     * @return The ViewTreeObserver for this view's hierarchy.
+     */
+    public ViewTreeObserver getViewTreeObserver() {
+        if (mAttachInfo != null) {
+            return mAttachInfo.mTreeObserver;
+        }
+        if (mFloatingTreeObserver == null) {
+            mFloatingTreeObserver = new ViewTreeObserver();
+        }
+        return mFloatingTreeObserver;
+    }
+
+    /**
+     * <p>Finds the topmost view in the current view hierarchy.</p>
+     *
+     * @return the topmost view containing this view
+     */
+    public View getRootView() {
+        if (mAttachInfo != null) {
+            final View v = mAttachInfo.mRootView;
+            if (v != null) {
+                return v;
+            }
+        }
+        
+        View parent = this;
+
+        while (parent.mParent != null && parent.mParent instanceof View) {
+            parent = (View) parent.mParent;
+        }
+
+        return parent;
+    }
+
+    /**
+     * <p>Computes the coordinates of this view on the screen. The argument
+     * must be an array of two integers. After the method returns, the array
+     * contains the x and y location in that order.</p>
+     *
+     * @param location an array of two integers in which to hold the coordinates
+     */
+    public void getLocationOnScreen(int[] location) {
+        getLocationInWindow(location);
+
+        final AttachInfo info = mAttachInfo;
+        location[0] += info.mWindowLeft;
+        location[1] += info.mWindowTop;
+    }
+
+    /**
+     * <p>Computes the coordinates of this view in its window. The argument
+     * must be an array of two integers. After the method returns, the array
+     * contains the x and y location in that order.</p>
+     *
+     * @param location an array of two integers in which to hold the coordinates
+     */
+    public void getLocationInWindow(int[] location) {
+        if (location == null || location.length < 2) {
+            throw new IllegalArgumentException("location must be an array of "
+                    + "two integers");
+        }
+
+        location[0] = mLeft;
+        location[1] = mTop;
+
+        ViewParent viewParent = mParent;
+        while (viewParent instanceof View) {
+            final View view = (View)viewParent;
+            location[0] += view.mLeft - view.mScrollX;
+            location[1] += view.mTop - view.mScrollY;
+            viewParent = view.mParent;
+        }
+        
+        if (viewParent instanceof ViewRoot) {
+            // *cough*
+            final ViewRoot vr = (ViewRoot)viewParent;
+            location[1] -= vr.mCurScrollY;
+        }
+    }
+
+    /**
+     * {@hide}
+     * @param id the id of the view to be found
+     * @return the view of the specified id, null if cannot be found
+     */
+    protected View findViewTraversal(int id) {
+        if (id == mID) {
+            return this;
+        }
+        return null;
+    }
+
+    /**
+     * {@hide}
+     * @param tag the tag of the view to be found
+     * @return the view of specified tag, null if cannot be found
+     */
+    protected View findViewWithTagTraversal(Object tag) {
+        if (tag != null && tag.equals(mTag)) {
+            return this;
+        }
+        return null;
+    }
+
+    /**
+     * Look for a child view with the given id.  If this view has the given
+     * id, return this view.
+     *
+     * @param id The id to search for.
+     * @return The view that has the given id in the hierarchy or null
+     */
+    public final View findViewById(int id) {
+        if (id < 0) {
+            return null;
+        }
+        return findViewTraversal(id);
+    }
+
+    /**
+     * Look for a child view with the given tag.  If this view has the given
+     * tag, return this view.
+     *
+     * @param tag The tag to search for, using "tag.equals(getTag())".
+     * @return The View that has the given tag in the hierarchy or null
+     */
+    public final View findViewWithTag(Object tag) {
+        if (tag == null) {
+            return null;
+        }
+        return findViewWithTagTraversal(tag);
+    }
+
+    /**
+     * Sets the identifier for this view. The identifier does not have to be
+     * unique in this view's hierarchy. The identifier should be a positive
+     * number.
+     *
+     * @see #NO_ID
+     * @see #getId
+     * @see #findViewById
+     *
+     * @param id a number used to identify the view
+     *
+     * @attr ref android.R.styleable#View_id
+     */
+    public void setId(int id) {
+        mID = id;
+    }
+
+    /**
+     * {@hide}
+     *
+     * @param isRoot true if the view belongs to the root namespace, false
+     *        otherwise
+     */
+    public void setIsRootNamespace(boolean isRoot) {
+        if (isRoot) {
+            mPrivateFlags |= IS_ROOT_NAMESPACE;
+        } else {
+            mPrivateFlags &= ~IS_ROOT_NAMESPACE;
+        }
+    }
+
+    /**
+     * {@hide}
+     *
+     * @return true if the view belongs to the root namespace, false otherwise
+     */
+    public boolean isRootNamespace() {
+        return (mPrivateFlags&IS_ROOT_NAMESPACE) != 0;
+    }
+
+    /**
+     * Returns this view's identifier.
+     *
+     * @return a positive integer used to identify the view or {@link #NO_ID}
+     *         if the view has no ID
+     *
+     * @see #setId
+     * @see #findViewById
+     * @attr ref android.R.styleable#View_id
+     */
+    @ViewDebug.CapturedViewProperty
+    public int getId() {
+        return mID;
+    }
+
+    /**
+     * Returns this view's tag.
+     *
+     * @return the Object stored in this view as a tag
+     */
+    @ViewDebug.ExportedProperty
+    public Object getTag() {
+        return mTag;
+    }
+
+    /**
+     * Sets the tag associated with this view. A tag can be used to mark
+     * a view in its hierarchy and does not have to be unique within the
+     * hierarchy. Tags can also be used to store data within a view without
+     * resorting to another data structure.
+     *
+     * @param tag an Object to tag the view with
+     */
+    public void setTag(final Object tag) {
+        mTag = tag;
+    }
+
+    /**
+     * Prints information about this view in the log output, with the tag
+     * {@link #VIEW_LOG_TAG}.
+     *
+     * @hide
+     */
+    public void debug() {
+        debug(0);
+    }
+
+    /**
+     * Prints information about this view in the log output, with the tag
+     * {@link #VIEW_LOG_TAG}. Each line in the output is preceded with an
+     * indentation defined by the <code>depth</code>.
+     *
+     * @param depth the indentation level
+     *
+     * @hide
+     */
+    protected void debug(int depth) {
+        String output = debugIndent(depth - 1);
+
+        output += "+ " + this;
+        int id = getId();
+        if (id != -1) {
+            output += " (id=" + id + ")";
+        }
+        Object tag = getTag();
+        if (tag != null) {
+            output += " (tag=" + tag + ")";
+        }
+        Log.d(VIEW_LOG_TAG, output);
+
+        if ((mPrivateFlags & FOCUSED) != 0) {
+            output = debugIndent(depth) + " FOCUSED";
+            Log.d(VIEW_LOG_TAG, output);
+        }
+
+        output = debugIndent(depth);
+        output += "frame={" + mLeft + ", " + mTop + ", " + mRight
+                + ", " + mBottom + "} scroll={" + mScrollX + ", " + mScrollY
+                + "} ";
+        Log.d(VIEW_LOG_TAG, output);
+
+        if (mPaddingLeft != 0 || mPaddingTop != 0 || mPaddingRight != 0
+                || mPaddingBottom != 0) {
+            output = debugIndent(depth);
+            output += "padding={" + mPaddingLeft + ", " + mPaddingTop
+                    + ", " + mPaddingRight + ", " + mPaddingBottom + "}";
+            Log.d(VIEW_LOG_TAG, output);
+        }
+
+        output = debugIndent(depth);
+        output += "mMeasureWidth=" + mMeasuredWidth +
+                " mMeasureHeight=" + mMeasuredHeight;
+        Log.d(VIEW_LOG_TAG, output);
+
+        output = debugIndent(depth);
+        if (mLayoutParams == null) {
+            output += "BAD! no layout params";
+        } else {
+            output = mLayoutParams.debug(output);
+        }
+        Log.d(VIEW_LOG_TAG, output);
+
+        output = debugIndent(depth);
+        output += "flags={";
+        output += View.printFlags(mViewFlags);
+        output += "}";
+        Log.d(VIEW_LOG_TAG, output);
+
+        output = debugIndent(depth);
+        output += "privateFlags={";
+        output += View.printPrivateFlags(mPrivateFlags);
+        output += "}";
+        Log.d(VIEW_LOG_TAG, output);
+    }
+
+    /**
+     * Creates an string of whitespaces used for indentation.
+     *
+     * @param depth the indentation level
+     * @return a String containing (depth * 2 + 3) * 2 white spaces
+     *
+     * @hide
+     */
+    protected static String debugIndent(int depth) {
+        StringBuilder spaces = new StringBuilder((depth * 2 + 3) * 2);
+        for (int i = 0; i < (depth * 2) + 3; i++) {
+            spaces.append(' ').append(' ');
+        }
+        return spaces.toString();
+    }
+
+    /**
+     * <p>Return the offset of the widget's text baseline from the widget's top
+     * boundary. If this widget does not support baseline alignment, this
+     * method returns -1. </p>
+     *
+     * @return the offset of the baseline within the widget's bounds or -1
+     *         if baseline alignment is not supported
+     */
+    @ViewDebug.ExportedProperty
+    public int getBaseline() {
+        return -1;
+    }
+
+    /**
+     * Call this when something has changed which has invalidated the
+     * layout of this view. This will schedule a layout pass of the view
+     * tree.
+     */
+    public void requestLayout() {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.REQUEST_LAYOUT);
+        }
+
+        mPrivateFlags |= FORCE_LAYOUT;
+
+        if (mParent != null && !mParent.isLayoutRequested()) {
+            mParent.requestLayout();
+        }
+    }
+
+    /**
+     * Forces this view to be laid out during the next layout pass.
+     * This method does not call requestLayout() or forceLayout()
+     * on the parent.
+     */
+    public void forceLayout() {
+        mPrivateFlags |= FORCE_LAYOUT;
+    }
+
+    /**
+     * <p>
+     * This is called to find out how big a view should be. The parent
+     * supplies constraint information in the width and height parameters.
+     * </p>
+     *
+     * <p>
+     * The actual mesurement work of a view is performed in
+     * {@link #onMeasure(int, int)}, called by this method. Therefore, only
+     * {@link #onMeasure(int, int)} can and must be overriden by subclasses.
+     * </p>
+     *
+     *
+     * @param widthMeasureSpec Horizontal space requirements as imposed by the
+     *        parent
+     * @param heightMeasureSpec Vertical space requirements as imposed by the
+     *        parent
+     *
+     * @see #onMeasure(int, int)
+     */
+    public final void measure(int widthMeasureSpec, int heightMeasureSpec) {
+        if ((mPrivateFlags & FORCE_LAYOUT) == FORCE_LAYOUT ||
+                widthMeasureSpec != mOldWidthMeasureSpec ||
+                heightMeasureSpec != mOldHeightMeasureSpec) {
+
+            // first clears the measured dimension flag
+            mPrivateFlags &= ~MEASURED_DIMENSION_SET;
+
+            if (ViewDebug.TRACE_HIERARCHY) {
+                ViewDebug.trace(this, ViewDebug.HierarchyTraceType.ON_MEASURE);
+            }
+
+            // measure ourselves, this should set the measured dimension flag back
+            onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            // flag not set, setMeasuredDimension() was not invoked, we raise
+            // an exception to warn the developer
+            if ((mPrivateFlags & MEASURED_DIMENSION_SET) != MEASURED_DIMENSION_SET) {
+                throw new IllegalStateException("onMeasure() did not set the"
+                        + " measured dimension by calling"
+                        + " setMeasuredDimension()");
+            }
+
+            mPrivateFlags |= LAYOUT_REQUIRED;
+        }
+
+        mOldWidthMeasureSpec = widthMeasureSpec;
+        mOldHeightMeasureSpec = heightMeasureSpec;
+    }
+
+    /**
+     * <p>
+     * Measure the view and its content to determine the measured width and the
+     * measured height. This method is invoked by {@link #measure(int, int)} and
+     * should be overriden by subclasses to provide accurate and efficient
+     * measurement of their contents.
+     * </p>
+     *
+     * <p>
+     * <strong>CONTRACT:</strong> When overriding this method, you
+     * <em>must</em> call {@link #setMeasuredDimension(int, int)} to store the
+     * measured width and height of this view. Failure to do so will trigger an
+     * <code>IllegalStateException</code>, thrown by
+     * {@link #measure(int, int)}. Calling the superclass'
+     * {@link #onMeasure(int, int)} is a valid use.
+     * </p>
+     *
+     * <p>
+     * The base class implementation of measure defaults to the background size,
+     * unless a larger size is allowed by the MeasureSpec. Subclasses should
+     * override {@link #onMeasure(int, int)} to provide better measurements of
+     * their content.
+     * </p>
+     *
+     * <p>
+     * If this method is overridden, it is the subclass's responsibility to make
+     * sure the measured height and width are at least the view's minimum height
+     * and width ({@link #getSuggestedMinimumHeight()} and
+     * {@link #getSuggestedMinimumWidth()}).
+     * </p>
+     *
+     * @param widthMeasureSpec horizontal space requirements as imposed by the parent.
+     *                         The requirements are encoded with
+     *                         {@link android.view.View.MeasureSpec}.
+     * @param heightMeasureSpec vertical space requirements as imposed by the parent.
+     *                         The requirements are encoded with
+     *                         {@link android.view.View.MeasureSpec}.
+     *
+     * @see #getMeasuredWidth()
+     * @see #getMeasuredHeight()
+     * @see #setMeasuredDimension(int, int)
+     * @see #getSuggestedMinimumHeight()
+     * @see #getSuggestedMinimumWidth()
+     * @see android.view.View.MeasureSpec#getMode(int)
+     * @see android.view.View.MeasureSpec#getSize(int)
+     */
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(getDefaultSize(getSuggestedMinimumWidth(), widthMeasureSpec),
+                getDefaultSize(getSuggestedMinimumHeight(), heightMeasureSpec));
+    }
+
+    /**
+     * <p>This mehod must be called by {@link #onMeasure(int, int)} to store the
+     * measured width and measured height. Failing to do so will trigger an
+     * exception at measurement time.</p>
+     *
+     * @param measuredWidth the measured width of this view
+     * @param measuredHeight the measured height of this view
+     */
+    protected final void setMeasuredDimension(int measuredWidth, int measuredHeight) {
+        mMeasuredWidth = measuredWidth;
+        mMeasuredHeight = measuredHeight;
+
+        mPrivateFlags |= MEASURED_DIMENSION_SET;
+    }
+
+    /**
+     * Utility to reconcile a desired size with constraints imposed by a MeasureSpec.
+     * Will take the desired size, unless a different size is imposed by the constraints.
+     *
+     * @param size How big the view wants to be
+     * @param measureSpec Constraints imposed by the parent
+     * @return The size this view should be.
+     */
+    public static int resolveSize(int size, int measureSpec) {
+        int result = size;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize =  MeasureSpec.getSize(measureSpec);
+        switch (specMode) {
+        case MeasureSpec.UNSPECIFIED:
+            result = size;
+            break;
+        case MeasureSpec.AT_MOST:
+            result = Math.min(size, specSize);
+            break;
+        case MeasureSpec.EXACTLY:
+            result = specSize;
+            break;
+        }
+        return result;
+    }
+
+    /**
+     * Utility to return a default size. Uses the supplied size if the
+     * MeasureSpec imposed no contraints. Will get larger if allowed
+     * by the MeasureSpec.
+     *
+     * @param size Default size for this view
+     * @param measureSpec Constraints imposed by the parent
+     * @return The size this view should be.
+     */
+    public static int getDefaultSize(int size, int measureSpec) {
+        int result = size;
+        int specMode = MeasureSpec.getMode(measureSpec);
+        int specSize =  MeasureSpec.getSize(measureSpec);
+
+        switch (specMode) {
+        case MeasureSpec.UNSPECIFIED:
+            result = size;
+            break;
+        case MeasureSpec.AT_MOST:
+        case MeasureSpec.EXACTLY:
+            result = specSize;
+            break;
+        }
+        return result;
+    }
+
+    /**
+     * Returns the suggested minimum height that the view should use. This
+     * returns the maximum of the view's minimum height
+     * and the background's minimum height
+     * ({@link android.graphics.drawable.Drawable#getMinimumHeight()}).
+     * <p>
+     * When being used in {@link #onMeasure(int, int)}, the caller should still
+     * ensure the returned height is within the requirements of the parent.
+     *
+     * @return The suggested minimum height of the view.
+     */
+    protected int getSuggestedMinimumHeight() {
+        int suggestedMinHeight = mMinHeight;
+
+        if (mBGDrawable != null) {
+            final int bgMinHeight = mBGDrawable.getMinimumHeight();
+            if (suggestedMinHeight < bgMinHeight) {
+                suggestedMinHeight = bgMinHeight;
+            }
+        }
+
+        return suggestedMinHeight;
+    }
+
+    /**
+     * Returns the suggested minimum width that the view should use. This
+     * returns the maximum of the view's minimum width)
+     * and the background's minimum width
+     *  ({@link android.graphics.drawable.Drawable#getMinimumWidth()}).
+     * <p>
+     * When being used in {@link #onMeasure(int, int)}, the caller should still
+     * ensure the returned width is within the requirements of the parent.
+     *
+     * @return The suggested minimum width of the view.
+     */
+    protected int getSuggestedMinimumWidth() {
+        int suggestedMinWidth = mMinWidth;
+
+        if (mBGDrawable != null) {
+            final int bgMinWidth = mBGDrawable.getMinimumWidth();
+            if (suggestedMinWidth < bgMinWidth) {
+                suggestedMinWidth = bgMinWidth;
+            }
+        }
+
+        return suggestedMinWidth;
+    }
+
+    /**
+     * Sets the minimum height of the view. It is not guaranteed the view will
+     * be able to achieve this minimum height (for example, if its parent layout
+     * constrains it with less available height).
+     *
+     * @param minHeight The minimum height the view will try to be.
+     */
+    public void setMinimumHeight(int minHeight) {
+        mMinHeight = minHeight;
+    }
+
+    /**
+     * Sets the minimum width of the view. It is not guaranteed the view will
+     * be able to achieve this minimum width (for example, if its parent layout
+     * constrains it with less available width).
+     *
+     * @param minWidth The minimum width the view will try to be.
+     */
+    public void setMinimumWidth(int minWidth) {
+        mMinWidth = minWidth;
+    }
+
+    /**
+     * Get the animation currently associated with this view.
+     *
+     * @return The animation that is currently playing or
+     *         scheduled to play for this view.
+     */
+    public Animation getAnimation() {
+        return mCurrentAnimation;
+    }
+
+    /**
+     * Start the specified animation now.
+     *
+     * @param animation the animation to start now
+     */
+    public void startAnimation(Animation animation) {
+        animation.setStartTime(Animation.START_ON_FIRST_FRAME);
+        setAnimation(animation);
+        invalidate();
+    }
+
+    /**
+     * Cancels any animations for this view.
+     */
+    public void clearAnimation() {
+        mCurrentAnimation = null;
+    }
+
+    /**
+     * Sets the next animation to play for this view.
+     * If you want the animation to play immediately, use
+     * startAnimation. This method provides allows fine-grained
+     * control over the start time and invalidation, but you
+     * must make sure that 1) the animation has a start time set, and
+     * 2) the view will be invalidated when the animation is supposed to
+     * start.
+     *
+     * @param animation The next animation, or null.
+     */
+    public void setAnimation(Animation animation) {
+        mCurrentAnimation = animation;
+        if (animation != null) {
+            animation.reset();
+        }
+    }
+
+    /**
+     * Invoked by a parent ViewGroup to notify the start of the animation
+     * currently associated with this view. If you override this method,
+     * always call super.onAnimationStart();
+     *
+     * @see #setAnimation(android.view.animation.Animation)
+     * @see #getAnimation()
+     */
+    protected void onAnimationStart() {
+        mPrivateFlags |= ANIMATION_STARTED;
+    }
+
+    /**
+     * Invoked by a parent ViewGroup to notify the end of the animation
+     * currently associated with this view. If you override this method,
+     * always call super.onAnimationEnd();
+     *
+     * @see #setAnimation(android.view.animation.Animation)
+     * @see #getAnimation()
+     */
+    protected void onAnimationEnd() {
+        mPrivateFlags &= ~ANIMATION_STARTED;
+    }
+
+    /**
+     * Invoked if there is a Transform that involves alpha. Subclass that can
+     * draw themselves with the specified alpha should return true, and then
+     * respect that alpha when their onDraw() is called. If this returns false
+     * then the view may be redirected to draw into an offscreen buffer to
+     * fulfill the request, which will look fine, but may be slower than if the
+     * subclass handles it internally. The default implementation returns false.
+     *
+     * @param alpha The alpha (0..255) to apply to the view's drawing
+     * @return true if the view can draw with the specified alpha.
+     */
+    protected boolean onSetAlpha(int alpha) {
+        return false;
+    }
+
+    /**
+     * This is used by the RootView to perform an optimization when
+     * the view hierarchy contains one or several SurfaceView.
+     * SurfaceView is always considered transparent, but its children are not,
+     * therefore all View objects remove themselves from the global transparent
+     * region (passed as a parameter to this function).
+     *
+     * @param region The transparent region for this ViewRoot (window).
+     *
+     * @return Returns true if the effective visibility of the view at this
+     * point is opaque, regardless of the transparent region; returns false
+     * if it is possible for underlying windows to be seen behind the view.
+     *
+     * {@hide}
+     */
+    public boolean gatherTransparentRegion(Region region) {
+        final AttachInfo attachInfo = mAttachInfo;
+        if (region != null && attachInfo != null) {
+            final int pflags = mPrivateFlags;
+            if ((pflags & SKIP_DRAW) == 0) {
+                // The SKIP_DRAW flag IS NOT set, so this view draws. We need to
+                // remove it from the transparent region.
+                final int[] location = attachInfo.mTransparentLocation;
+                getLocationInWindow(location);
+                region.op(location[0], location[1], location[0] + mRight - mLeft,
+                        location[1] + mBottom - mTop, Region.Op.DIFFERENCE);
+            } else if ((pflags & ONLY_DRAWS_BACKGROUND) != 0 && mBGDrawable != null) {
+                // The ONLY_DRAWS_BACKGROUND flag IS set and the background drawable
+                // exists, so we remove the background drawable's non-transparent
+                // parts from this transparent region.
+                applyDrawableToTransparentRegion(mBGDrawable, region);
+            }
+        }
+        return true;
+    }
+
+    /**
+     * Play a sound effect for this view.
+     *
+     * <p>The framework will play sound effects for some built in actions, such as
+     * clicking, but you may wish to play these effects in your widget,
+     * for instance, for internal navigation.
+     *
+     * <p>The sound effect will only be played if sound effects are enabled by the user, and
+     * {@link #isSoundEffectsEnabled()} is true.
+     *
+     * @param soundConstant One of the constants defined in {@link SoundEffectConstants}
+     */
+    public void playSoundEffect(int soundConstant) {
+        if (mAttachInfo == null || mAttachInfo.mRootCallbacks == null || !isSoundEffectsEnabled()) {
+            return;
+        }
+        mAttachInfo.mRootCallbacks.playSoundEffect(soundConstant);
+    }
+
+    /**
+     * Provide haptic feedback to the user for this view.
+     *
+     * <p>The framework will provide haptic feedback for some built in actions,
+     * such as long presses, but you may wish to provide feedback for your
+     * own widget.
+     *
+     * <p>The feedback will only be performed if
+     * {@link #isHapticFeedbackEnabled()} is true.
+     *
+     * @param feedbackConstant One of the constants defined in
+     * {@link HapticFeedbackConstants}
+     */
+    public boolean performHapticFeedback(int feedbackConstant) {
+        return performHapticFeedback(feedbackConstant, 0);
+    }
+
+    /**
+     * Like {@link #performHapticFeedback(int)}, with additional options.
+     *
+     * @param feedbackConstant One of the constants defined in
+     * {@link HapticFeedbackConstants}
+     * @param flags Additional flags as per {@link HapticFeedbackConstants}.
+     */
+    public boolean performHapticFeedback(int feedbackConstant, int flags) {
+        if (mAttachInfo == null) {
+            return false;
+        }
+        if ((flags&HapticFeedbackConstants.FLAG_IGNORE_VIEW_SETTING) == 0
+                && !isHapticFeedbackEnabled()) {
+            return false;
+        }
+        return mAttachInfo.mRootCallbacks.performHapticFeedback(
+                feedbackConstant,
+                (flags&HapticFeedbackConstants.FLAG_IGNORE_GLOBAL_SETTING) != 0);
+    }
+
+    /**
+     * Given a Drawable whose bounds have been set to draw into this view,
+     * update a Region being computed for {@link #gatherTransparentRegion} so
+     * that any non-transparent parts of the Drawable are removed from the
+     * given transparent region.
+     *
+     * @param dr The Drawable whose transparency is to be applied to the region.
+     * @param region A Region holding the current transparency information,
+     * where any parts of the region that are set are considered to be
+     * transparent.  On return, this region will be modified to have the
+     * transparency information reduced by the corresponding parts of the
+     * Drawable that are not transparent.
+     * {@hide}
+     */
+    public void applyDrawableToTransparentRegion(Drawable dr, Region region) {
+        if (DBG) {
+            Log.i("View", "Getting transparent region for: " + this);
+        }
+        final Region r = dr.getTransparentRegion();
+        final Rect db = dr.getBounds();
+        final AttachInfo attachInfo = mAttachInfo;
+        if (r != null && attachInfo != null) {
+            final int w = getRight()-getLeft();
+            final int h = getBottom()-getTop();
+            if (db.left > 0) {
+                //Log.i("VIEW", "Drawable left " + db.left + " > view 0");
+                r.op(0, 0, db.left, h, Region.Op.UNION);
+            }
+            if (db.right < w) {
+                //Log.i("VIEW", "Drawable right " + db.right + " < view " + w);
+                r.op(db.right, 0, w, h, Region.Op.UNION);
+            }
+            if (db.top > 0) {
+                //Log.i("VIEW", "Drawable top " + db.top + " > view 0");
+                r.op(0, 0, w, db.top, Region.Op.UNION);
+            }
+            if (db.bottom < h) {
+                //Log.i("VIEW", "Drawable bottom " + db.bottom + " < view " + h);
+                r.op(0, db.bottom, w, h, Region.Op.UNION);
+            }
+            final int[] location = attachInfo.mTransparentLocation;
+            getLocationInWindow(location);
+            r.translate(location[0], location[1]);
+            region.op(r, Region.Op.INTERSECT);
+        } else {
+            region.op(db, Region.Op.DIFFERENCE);
+        }
+    }
+
+    private void postCheckForLongClick() {
+        mHasPerformedLongPress = false;
+
+        if (mPendingCheckForLongPress == null) {
+            mPendingCheckForLongPress = new CheckForLongPress();
+        }
+        mPendingCheckForLongPress.rememberWindowAttachCount();
+        postDelayed(mPendingCheckForLongPress, ViewConfiguration.getLongPressTimeout());
+    }
+
+    private static int[] stateSetUnion(final int[] stateSet1,
+                                       final int[] stateSet2) {
+        final int stateSet1Length = stateSet1.length;
+        final int stateSet2Length = stateSet2.length;
+        final int[] newSet = new int[stateSet1Length + stateSet2Length];
+        int k = 0;
+        int i = 0;
+        int j = 0;
+        // This is a merge of the two input state sets and assumes that the
+        // input sets are sorted by the order imposed by ViewDrawableStates.
+        for (int viewState : R.styleable.ViewDrawableStates) {
+            if (i < stateSet1Length && stateSet1[i] == viewState) {
+                newSet[k++] = viewState;
+                i++;
+            } else if (j < stateSet2Length && stateSet2[j] == viewState) {
+                newSet[k++] = viewState;
+                j++;
+            }
+            if (k > 1) {
+                assert(newSet[k - 1] > newSet[k - 2]);
+            }
+        }
+        return newSet;
+    }
+
+    /**
+     * Inflate a view from an XML resource.  This convenience method wraps the {@link
+     * LayoutInflater} class, which provides a full range of options for view inflation.
+     *
+     * @param context The Context object for your activity or application.
+     * @param resource The resource ID to inflate
+     * @param root A view group that will be the parent.  Used to properly inflate the
+     * layout_* parameters.
+     * @see LayoutInflater
+     */
+    public static View inflate(Context context, int resource, ViewGroup root) {
+        LayoutInflater factory = LayoutInflater.from(context);
+        return factory.inflate(resource, root);
+    }
+
+    /**
+     * A MeasureSpec encapsulates the layout requirements passed from parent to child.
+     * Each MeasureSpec represents a requirement for either the width or the height.
+     * A MeasureSpec is comprised of a size and a mode. There are three possible
+     * modes:
+     * <dl>
+     * <dt>UNSPECIFIED</dt>
+     * <dd>
+     * The parent has not imposed any constraint on the child. It can be whatever size
+     * it wants.
+     * </dd>
+     *
+     * <dt>EXACTLY</dt>
+     * <dd>
+     * The parent has determined an exact size for the child. The child is going to be
+     * given those bounds regardless of how big it wants to be.
+     * </dd>
+     *
+     * <dt>AT_MOST</dt>
+     * <dd>
+     * The child can be as large as it wants up to the specified size.
+     * </dd>
+     * </dl>
+     *
+     * MeasureSpecs are implemented as ints to reduce object allocation. This class
+     * is provided to pack and unpack the &lt;size, mode&gt; tuple into the int.
+     */
+    public static class MeasureSpec {
+        private static final int MODE_SHIFT = 30;
+        private static final int MODE_MASK  = 0x3 << MODE_SHIFT;
+
+        /**
+         * Measure specification mode: The parent has not imposed any constraint
+         * on the child. It can be whatever size it wants.
+         */
+        public static final int UNSPECIFIED = 0 << MODE_SHIFT;
+
+        /**
+         * Measure specification mode: The parent has determined an exact size
+         * for the child. The child is going to be given those bounds regardless
+         * of how big it wants to be.
+         */
+        public static final int EXACTLY     = 1 << MODE_SHIFT;
+
+        /**
+         * Measure specification mode: The child can be as large as it wants up
+         * to the specified size.
+         */
+        public static final int AT_MOST     = 2 << MODE_SHIFT;
+
+        /**
+         * Creates a measure specification based on the supplied size and mode.
+         *
+         * The mode must always be one of the following:
+         * <ul>
+         *  <li>{@link android.view.View.MeasureSpec#UNSPECIFIED}</li>
+         *  <li>{@link android.view.View.MeasureSpec#EXACTLY}</li>
+         *  <li>{@link android.view.View.MeasureSpec#AT_MOST}</li>
+         * </ul>
+         *
+         * @param size the size of the measure specification
+         * @param mode the mode of the measure specification
+         * @return the measure specification based on size and mode
+         */
+        public static int makeMeasureSpec(int size, int mode) {
+            return size + mode;
+        }
+
+        /**
+         * Extracts the mode from the supplied measure specification.
+         *
+         * @param measureSpec the measure specification to extract the mode from
+         * @return {@link android.view.View.MeasureSpec#UNSPECIFIED},
+         *         {@link android.view.View.MeasureSpec#AT_MOST} or
+         *         {@link android.view.View.MeasureSpec#EXACTLY}
+         */
+        public static int getMode(int measureSpec) {
+            return (measureSpec & MODE_MASK);
+        }
+
+        /**
+         * Extracts the size from the supplied measure specification.
+         *
+         * @param measureSpec the measure specification to extract the size from
+         * @return the size in pixels defined in the supplied measure specification
+         */
+        public static int getSize(int measureSpec) {
+            return (measureSpec & ~MODE_MASK);
+        }
+
+        /**
+         * Returns a String representation of the specified measure
+         * specification.
+         *
+         * @param measureSpec the measure specification to convert to a String
+         * @return a String with the following format: "MeasureSpec: MODE SIZE"
+         */
+        public static String toString(int measureSpec) {
+            int mode = getMode(measureSpec);
+            int size = getSize(measureSpec);
+
+            StringBuilder sb = new StringBuilder("MeasureSpec: ");
+
+            if (mode == UNSPECIFIED)
+                sb.append("UNSPECIFIED ");
+            else if (mode == EXACTLY)
+                sb.append("EXACTLY ");
+            else if (mode == AT_MOST)
+                sb.append("AT_MOST ");
+            else
+                sb.append(mode).append(" ");
+
+            sb.append(size);
+            return sb.toString();
+        }
+    }
+
+    class CheckForLongPress implements Runnable {
+
+        private int mOriginalWindowAttachCount;
+
+        public void run() {
+            if (isPressed() && (mParent != null) && hasWindowFocus()
+                    && mOriginalWindowAttachCount == mWindowAttachCount) {
+                if (performLongClick()) {
+                    mHasPerformedLongPress = true;
+                }
+            }
+        }
+
+        public void rememberWindowAttachCount() {
+            mOriginalWindowAttachCount = mWindowAttachCount;
+        }
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a key event is
+     * dispatched to this view. The callback will be invoked before the key
+     * event is given to the view.
+     */
+    public interface OnKeyListener {
+        /**
+         * Called when a key is dispatched to a view. This allows listeners to
+         * get a chance to respond before the target view.
+         *
+         * @param v The view the key has been dispatched to.
+         * @param keyCode The code for the physical key that was pressed
+         * @param event The KeyEvent object containing full information about
+         *        the event.
+         * @return True if the listener has consumed the event, false otherwise.
+         */
+        boolean onKey(View v, int keyCode, KeyEvent event);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a touch event is
+     * dispatched to this view. The callback will be invoked before the touch
+     * event is given to the view.
+     */
+    public interface OnTouchListener {
+        /**
+         * Called when a touch event is dispatched to a view. This allows listeners to
+         * get a chance to respond before the target view.
+         *
+         * @param v The view the touch event has been dispatched to.
+         * @param event The MotionEvent object containing full information about
+         *        the event.
+         * @return True if the listener has consumed the event, false otherwise.
+         */
+        boolean onTouch(View v, MotionEvent event);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a view has been clicked and held.
+     */
+    public interface OnLongClickListener {
+        /**
+         * Called when a view has been clicked and held.
+         *
+         * @param v The view that was clicked and held.
+         *
+         * return True if the callback consumed the long click, false otherwise
+         */
+        boolean onLongClick(View v);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the focus state of
+     * a view changed.
+     */
+    public interface OnFocusChangeListener {
+        /**
+         * Called when the focus state of a view has changed.
+         *
+         * @param v The view whose state has changed.
+         * @param hasFocus The new focus state of v.
+         */
+        void onFocusChange(View v, boolean hasFocus);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when a view is clicked.
+     */
+    public interface OnClickListener {
+        /**
+         * Called when a view has been clicked.
+         *
+         * @param v The view that was clicked.
+         */
+        void onClick(View v);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the context menu
+     * for this view is being built.
+     */
+    public interface OnCreateContextMenuListener {
+        /**
+         * Called when the context menu for this view is being built. It is not
+         * safe to hold onto the menu after this method returns.
+         *
+         * @param menu The context menu that is being built
+         * @param v The view for which the context menu is being built
+         * @param menuInfo Extra information about the item for which the
+         *            context menu should be shown. This information will vary
+         *            depending on the class of v.
+         */
+        void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo);
+    }
+
+    private final class UnsetPressedState implements Runnable {
+        public void run() {
+            setPressed(false);
+        }
+    }
+
+    /**
+     * Base class for derived classes that want to save and restore their own
+     * state in {@link android.view.View#onSaveInstanceState()}.
+     */
+    public static class BaseSavedState extends AbsSavedState {
+        /**
+         * Constructor used when reading from a parcel. Reads the state of the superclass.
+         *
+         * @param source
+         */
+        public BaseSavedState(Parcel source) {
+            super(source);
+        }
+
+        /**
+         * Constructor called by derived classes when creating their SavedState objects
+         *
+         * @param superState The state of the superclass of this view
+         */
+        public BaseSavedState(Parcelable superState) {
+            super(superState);
+        }
+
+        public static final Parcelable.Creator<BaseSavedState> CREATOR =
+                new Parcelable.Creator<BaseSavedState>() {
+            public BaseSavedState createFromParcel(Parcel in) {
+                return new BaseSavedState(in);
+            }
+
+            public BaseSavedState[] newArray(int size) {
+                return new BaseSavedState[size];
+            }
+        };
+    }
+
+    /**
+     * A set of information given to a view when it is attached to its parent
+     * window.
+     */
+    static class AttachInfo {
+
+        interface Callbacks {
+            void playSoundEffect(int effectId);
+            boolean performHapticFeedback(int effectId, boolean always);
+        }
+
+        /**
+         * InvalidateInfo is used to post invalidate(int, int, int, int) messages
+         * to a Handler. This class contains the target (View) to invalidate and
+         * the coordinates of the dirty rectangle.
+         *
+         * For performance purposes, this class also implements a pool of up to
+         * POOL_LIMIT objects that get reused. This reduces memory allocations
+         * whenever possible.
+         *
+         * The pool is implemented as a linked list of InvalidateInfo object with
+         * the root pointing to the next available InvalidateInfo. If the root
+         * is null (i.e. when all instances from the pool have been acquired),
+         * then a new InvalidateInfo is created and returned to the caller.
+         *
+         * An InvalidateInfo is sent back to the pool by calling its release()
+         * method. If the pool is full the object is simply discarded.
+         *
+         * This implementation follows the object pool pattern used in the
+         * MotionEvent class.
+         */
+        static class InvalidateInfo {
+            private static final int POOL_LIMIT = 10;
+            private static final Object sLock = new Object();
+
+            private static int sAcquiredCount = 0;
+            private static InvalidateInfo sRoot;
+
+            private InvalidateInfo next;
+
+            View target;
+
+            int left;
+            int top;
+            int right;
+            int bottom;
+
+            static InvalidateInfo acquire() {
+                synchronized (sLock) {
+                    if (sRoot == null) {
+                        return new InvalidateInfo();
+                    }
+
+                    InvalidateInfo info = sRoot;
+                    sRoot = info.next;
+                    sAcquiredCount--;
+
+                    return info;
+                }
+            }
+
+            void release() {
+                synchronized (sLock) {
+                    if (sAcquiredCount < POOL_LIMIT) {
+                        sAcquiredCount++;
+                        next = sRoot;
+                        sRoot = this;
+                    }
+                }
+            }
+        }
+
+        final IWindowSession mSession;
+
+        final IWindow mWindow;
+
+        final IBinder mWindowToken;
+
+        final Callbacks mRootCallbacks;
+
+        /**
+         * The top view of the hierarchy.
+         */
+        View mRootView;
+        
+        IBinder mPanelParentWindowToken;
+        Surface mSurface;
+
+        /**
+         * Left position of this view's window
+         */
+        int mWindowLeft;
+
+        /**
+         * Top position of this view's window
+         */
+        int mWindowTop;
+
+        /**
+         * For windows that are full-screen but using insets to layout inside
+         * of the screen decorations, these are the current insets for the
+         * content of the window.
+         */
+        final Rect mContentInsets = new Rect();
+
+        /**
+         * For windows that are full-screen but using insets to layout inside
+         * of the screen decorations, these are the current insets for the
+         * actual visible parts of the window.
+         */
+        final Rect mVisibleInsets = new Rect();
+
+        /**
+         * The internal insets given by this window.  This value is
+         * supplied by the client (through
+         * {@link ViewTreeObserver.OnComputeInternalInsetsListener}) and will
+         * be given to the window manager when changed to be used in laying
+         * out windows behind it.
+         */
+        final ViewTreeObserver.InternalInsetsInfo mGivenInternalInsets
+                = new ViewTreeObserver.InternalInsetsInfo();
+
+        /**
+         * All views in the window's hierarchy that serve as scroll containers,
+         * used to determine if the window can be resized or must be panned
+         * to adjust for a soft input area.
+         */
+        final ArrayList<View> mScrollContainers = new ArrayList<View>();
+
+        /**
+         * Indicates whether the view's window currently has the focus.
+         */
+        boolean mHasWindowFocus;
+
+        /**
+         * The current visibility of the window.
+         */
+        int mWindowVisibility;
+
+        /**
+         * Indicates the time at which drawing started to occur.
+         */
+        long mDrawingTime;
+
+        /**
+         * Indicates whether the view's window is currently in touch mode.
+         */
+        boolean mInTouchMode;
+
+        /**
+         * Indicates that ViewRoot should trigger a global layout change
+         * the next time it performs a traversal
+         */
+        boolean mRecomputeGlobalAttributes;
+
+        /**
+         * Set to true when attributes (like mKeepScreenOn) need to be
+         * recomputed.
+         */
+        boolean mAttributesChanged;
+
+        /**
+         * Set during a traveral if any views want to keep the screen on.
+         */
+        boolean mKeepScreenOn;
+
+        /**
+         * Set if the visibility of any views has changed.
+         */
+        boolean mViewVisibilityChanged;
+
+        /**
+         * Set to true if a view has been scrolled.
+         */
+        boolean mViewScrollChanged;
+
+        /**
+         * Global to the view hierarchy used as a temporary for dealing with
+         * x/y points in the transparent region computations.
+         */
+        final int[] mTransparentLocation = new int[2];
+
+        /**
+         * Global to the view hierarchy used as a temporary for dealing with
+         * x/y points in the ViewGroup.invalidateChild implementation.
+         */
+        final int[] mInvalidateChildLocation = new int[2];
+
+        /**
+         * The view tree observer used to dispatch global events like
+         * layout, pre-draw, touch mode change, etc.
+         */
+        final ViewTreeObserver mTreeObserver = new ViewTreeObserver();
+
+        /**
+         * A Canvas used by the view hierarchy to perform bitmap caching.
+         */
+        Canvas mCanvas;
+
+        /**
+         * A Handler supplied by a view's {@link android.view.ViewRoot}. This
+         * handler can be used to pump events in the UI events queue.
+         */
+        final Handler mHandler;
+
+        /**
+         * Identifier for messages requesting the view to be invalidated.
+         * Such messages should be sent to {@link #mHandler}.
+         */
+        static final int INVALIDATE_MSG = 0x1;
+
+        /**
+         * Identifier for messages requesting the view to invalidate a region.
+         * Such messages should be sent to {@link #mHandler}.
+         */
+        static final int INVALIDATE_RECT_MSG = 0x2;
+
+        /**
+         * Temporary for use in computing invalidate rectangles while
+         * calling up the hierarchy.
+         */
+        final Rect mTmpInvalRect = new Rect();
+        
+        /**
+         * Creates a new set of attachment information with the specified
+         * events handler and thread.
+         *
+         * @param handler the events handler the view must use
+         */
+        AttachInfo(IWindowSession session, IWindow window,
+                Handler handler, Callbacks effectPlayer) {
+            mSession = session;
+            mWindow = window;
+            mWindowToken = window.asBinder();
+            mHandler = handler;
+            mRootCallbacks = effectPlayer;
+        }
+    }
+
+    /**
+     * <p>ScrollabilityCache holds various fields used by a View when scrolling
+     * is supported. This avoids keeping too many unused fields in most
+     * instances of View.</p>
+     */
+    private static class ScrollabilityCache {
+        public int fadingEdgeLength;
+
+        public int scrollBarSize;
+        public ScrollBarDrawable scrollBar;
+
+        public final Paint paint;
+        public final Matrix matrix;
+        public Shader shader;
+
+        private int mLastColor;
+
+        public ScrollabilityCache(ViewConfiguration configuration) {
+            fadingEdgeLength = configuration.getScaledFadingEdgeLength();
+            scrollBarSize = configuration.getScaledScrollBarSize();
+
+            paint = new Paint();
+            matrix = new Matrix();
+            // use use a height of 1, and then wack the matrix each time we
+            // actually use it.
+            shader = new LinearGradient(0, 0, 0, 1, 0xFF000000, 0, Shader.TileMode.CLAMP);
+            
+            paint.setShader(shader);
+            paint.setXfermode(new PorterDuffXfermode(PorterDuff.Mode.DST_OUT));
+        }
+        
+        public void setFadeColor(int color) {
+            if (color != 0 && color != mLastColor) {
+                mLastColor = color;
+                color |= 0xFF000000;
+                
+                shader = new LinearGradient(0, 0, 0, 1, color, 0, Shader.TileMode.CLAMP);
+                
+                paint.setShader(shader);
+                // Restore the default transfer mode (src_over)
+                paint.setXfermode(null);
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/ViewConfiguration.java b/core/java/android/view/ViewConfiguration.java
new file mode 100644
index 0000000..d3f48c6
--- /dev/null
+++ b/core/java/android/view/ViewConfiguration.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2006 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.SparseArray;
+
+/**
+ * Contains methods to standard constants used in the UI for timeouts, sizes, and distances.
+ */
+public class ViewConfiguration {
+    /**
+     * Defines the width of the horizontal scrollbar and the height of the vertical scrollbar in
+     * pixels
+     */
+    private static final int SCROLL_BAR_SIZE = 10;
+
+    /**
+     * Defines the length of the fading edges in pixels
+     */
+    private static final int FADING_EDGE_LENGTH = 12;
+
+    /**
+     * Defines the duration in milliseconds of the pressed state in child
+     * components.
+     */
+    private static final int PRESSED_STATE_DURATION = 85;
+    
+    /**
+     * Defines the duration in milliseconds before a press turns into
+     * a long press
+     */
+    private static final int LONG_PRESS_TIMEOUT = 500;
+    
+    /**
+     * Defines the duration in milliseconds a user needs to hold down the
+     * appropriate button to bring up the global actions dialog (power off,
+     * lock screen, etc).
+     */
+    private static final int GLOBAL_ACTIONS_KEY_TIMEOUT = 500;
+    
+    /**
+     * Defines the duration in milliseconds we will wait to see if a touch event 
+     * is a tap or a scroll. If the user does not move within this interval, it is
+     * considered to be a tap. 
+     */
+    private static final int TAP_TIMEOUT = 100;
+    
+    /**
+     * Defines the duration in milliseconds we will wait to see if a touch event 
+     * is a jump tap. If the user does not complete the jump tap within this interval, it is
+     * considered to be a tap. 
+     */
+    private static final int JUMP_TAP_TIMEOUT = 500;
+
+    /**
+     * Defines the duration in milliseconds between the first tap's up event and
+     * the second tap's down event for an interaction to be considered a
+     * double-tap.
+     */
+    private static final int DOUBLE_TAP_TIMEOUT = 300;
+    
+    /**
+     * Defines the duration in milliseconds we want to display zoom controls in response 
+     * to a user panning within an application.
+     */
+    private static final int ZOOM_CONTROLS_TIMEOUT = 3000;
+
+    /**
+     * Inset in pixels to look for touchable content when the user touches the edge of the screen
+     */
+    private static final int EDGE_SLOP = 12;
+    
+    /**
+     * Distance a touch can wander before we think the user is scrolling in pixels
+     */
+    private static final int TOUCH_SLOP = 25;
+    
+    /**
+     * Distance between the first touch and second touch to still be considered a double tap
+     */
+    private static final int DOUBLE_TAP_SLOP = 100;
+    
+    /**
+     * Distance a touch needs to be outside of a window's bounds for it to
+     * count as outside for purposes of dismissing the window.
+     */
+    private static final int WINDOW_TOUCH_SLOP = 16;
+
+    /**
+     * Minimum velocity to initiate a fling, as measured in pixels per second
+     */
+    private static final int MINIMUM_FLING_VELOCITY = 50;
+
+    /**
+     * The maximum size of View's drawing cache, expressed in bytes. This size
+     * should be at least equal to the size of the screen in ARGB888 format.
+     */
+    @Deprecated
+    private static final int MAXIMUM_DRAWING_CACHE_SIZE = 320 * 480 * 4; // HVGA screen, ARGB8888
+
+    /**
+     * The coefficient of friction applied to flings/scrolls.
+     */
+    private static float SCROLL_FRICTION = 0.015f;
+
+    private final int mEdgeSlop;
+    private final int mFadingEdgeLength;
+    private final int mMinimumFlingVelocity;
+    private final int mScrollbarSize;
+    private final int mTouchSlop;
+    private final int mDoubleTapSlop;
+    private final int mWindowTouchSlop;
+    private final int mMaximumDrawingCacheSize;
+
+    private static final SparseArray<ViewConfiguration> sConfigurations =
+            new SparseArray<ViewConfiguration>(2);
+
+    /**
+     * @deprecated Use {@link android.view.ViewConfiguration#get(android.content.Context)} instead.
+     */
+    @Deprecated
+    public ViewConfiguration() {
+        mEdgeSlop = EDGE_SLOP;
+        mFadingEdgeLength = FADING_EDGE_LENGTH;
+        mMinimumFlingVelocity = MINIMUM_FLING_VELOCITY;
+        mScrollbarSize = SCROLL_BAR_SIZE;
+        mTouchSlop = TOUCH_SLOP;
+        mDoubleTapSlop = DOUBLE_TAP_SLOP;
+        mWindowTouchSlop = WINDOW_TOUCH_SLOP;
+        //noinspection deprecation
+        mMaximumDrawingCacheSize = MAXIMUM_DRAWING_CACHE_SIZE;
+    }
+
+    /**
+     * Creates a new configuration for the specified context. The configuration depends on
+     * various parameters of the context, like the dimension of the display or the density
+     * of the display.
+     *
+     * @param context The application context used to initialize this view configuration.
+     *
+     * @see #get(android.content.Context) 
+     * @see android.util.DisplayMetrics
+     */
+    private ViewConfiguration(Context context) {
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final float density = metrics.density;
+
+        mEdgeSlop = (int) (density * EDGE_SLOP + 0.5f);
+        mFadingEdgeLength = (int) (density * FADING_EDGE_LENGTH + 0.5f);
+        mMinimumFlingVelocity = (int) (density * MINIMUM_FLING_VELOCITY + 0.5f);
+        mScrollbarSize = (int) (density * SCROLL_BAR_SIZE + 0.5f);
+        mTouchSlop = (int) (density * TOUCH_SLOP + 0.5f);
+        mDoubleTapSlop = (int) (density * DOUBLE_TAP_SLOP + 0.5f);
+        mWindowTouchSlop = (int) (density * WINDOW_TOUCH_SLOP + 0.5f);
+
+        // Size of the screen in bytes, in ARGB_8888 format
+        mMaximumDrawingCacheSize = 4 * metrics.widthPixels * metrics.heightPixels;
+    }
+
+    /**
+     * Returns a configuration for the specified context. The configuration depends on
+     * various parameters of the context, like the dimension of the display or the
+     * density of the display.
+     *
+     * @param context The application context used to initialize the view configuration.
+     */
+    public static ViewConfiguration get(Context context) {
+        final DisplayMetrics metrics = context.getResources().getDisplayMetrics();
+        final int density = (int) (100.0f * metrics.density);
+
+        ViewConfiguration configuration = sConfigurations.get(density);
+        if (configuration == null) {
+            configuration = new ViewConfiguration(context);
+            sConfigurations.put(density, configuration);
+        }
+
+        return configuration;
+    }
+
+    /**
+     * @return The width of the horizontal scrollbar and the height of the vertical
+     *         scrollbar in pixels
+     *
+     * @deprecated Use {@link #getScaledScrollBarSize()} instead.
+     */
+    @Deprecated
+    public static int getScrollBarSize() {
+        return SCROLL_BAR_SIZE;
+    }
+
+    /**
+     * @return The width of the horizontal scrollbar and the height of the vertical
+     *         scrollbar in pixels
+     */
+    public int getScaledScrollBarSize() {
+        return mScrollbarSize;
+    }
+
+    /**
+     * @return the length of the fading edges in pixels
+     *
+     * @deprecated Use {@link #getScaledFadingEdgeLength()} instead.
+     */
+    @Deprecated
+    public static int getFadingEdgeLength() {
+        return FADING_EDGE_LENGTH;
+    }
+
+    /**
+     * @return the length of the fading edges in pixels
+     */
+    public int getScaledFadingEdgeLength() {
+        return mFadingEdgeLength;
+    }
+
+    /**
+     * @return the duration in milliseconds of the pressed state in child
+     * components.
+     */
+    public static int getPressedStateDuration() {
+        return PRESSED_STATE_DURATION;
+    }
+    
+    /**
+     * @return the duration in milliseconds before a press turns into
+     * a long press
+     */
+    public static int getLongPressTimeout() {
+        return LONG_PRESS_TIMEOUT;
+    }
+    
+    /**
+     * @return the duration in milliseconds we will wait to see if a touch event
+     * is a tap or a scroll. If the user does not move within this interval, it is
+     * considered to be a tap. 
+     */
+    public static int getTapTimeout() {
+        return TAP_TIMEOUT;
+    }
+    
+    /**
+     * @return the duration in milliseconds we will wait to see if a touch event
+     * is a jump tap. If the user does not move within this interval, it is
+     * considered to be a tap. 
+     */
+    public static int getJumpTapTimeout() {
+        return JUMP_TAP_TIMEOUT;
+    }
+    
+    /**
+     * @return the duration in milliseconds between the first tap's up event and
+     * the second tap's down event for an interaction to be considered a
+     * double-tap.
+     * @hide pending API council
+     */
+    public static int getDoubleTapTimeout() {
+        return DOUBLE_TAP_TIMEOUT;
+    }
+    
+    /**
+     * @return Inset in pixels to look for touchable content when the user touches the edge of the
+     *         screen
+     *
+     * @deprecated Use {@link #getScaledEdgeSlop()} instead.
+     */
+    @Deprecated
+    public static int getEdgeSlop() {
+        return EDGE_SLOP;
+    }
+
+    /**
+     * @return Inset in pixels to look for touchable content when the user touches the edge of the
+     *         screen
+     */
+    public int getScaledEdgeSlop() {
+        return mEdgeSlop;
+    }
+
+    /**
+     * @return Distance a touch can wander before we think the user is scrolling in pixels
+     *
+     * @deprecated Use {@link #getScaledTouchSlop()} instead.
+     */
+    @Deprecated
+    public static int getTouchSlop() {
+        return TOUCH_SLOP;
+    }
+
+    /**
+     * @return Distance a touch can wander before we think the user is scrolling in pixels
+     */
+    public int getScaledTouchSlop() {
+        return mTouchSlop;
+    }
+
+    /**
+     * @return Distance between the first touch and second touch to still be
+     *         considered a double tap
+     * @deprecated Use {@link #getScaledDoubleTapSlop()} instead.
+     * @hide The only client of this should be GestureDetector, which needs this
+     *       for clients that still use its deprecated constructor.
+     */
+    @Deprecated
+    public static int getDoubleTapSlop() {
+        return DOUBLE_TAP_SLOP;
+    }
+    
+    /**
+     * @return Distance between the first touch and second touch to still be
+     *         considered a double tap
+     * @hide pending API council
+     */
+    public int getScaledDoubleTapSlop() {
+        return mDoubleTapSlop;
+    }
+
+    /**
+     * @return Distance a touch must be outside the bounds of a window for it
+     * to be counted as outside the window for purposes of dismissing that
+     * window.
+     *
+     * @deprecated Use {@link #getScaledWindowTouchSlop()} instead.
+     */
+    @Deprecated
+    public static int getWindowTouchSlop() {
+        return WINDOW_TOUCH_SLOP;
+    }
+
+    /**
+     * @return Distance a touch must be outside the bounds of a window for it
+     * to be counted as outside the window for purposes of dismissing that
+     * window.
+     */
+    public int getScaledWindowTouchSlop() {
+        return mWindowTouchSlop;
+    }
+    
+    /**
+     * @return Minimum velocity to initiate a fling, as measured in pixels per second.
+     *
+     * @deprecated Use {@link #getScaledMinimumFlingVelocity()} instead.
+     */
+    @Deprecated
+    public static int getMinimumFlingVelocity() {
+        return MINIMUM_FLING_VELOCITY;
+    }
+
+    /**
+     * @return Minimum velocity to initiate a fling, as measured in pixels per second.
+     */
+    public int getScaledMinimumFlingVelocity() {
+        return mMinimumFlingVelocity;
+    }
+
+    /**
+     * The maximum drawing cache size expressed in bytes.
+     *
+     * @return the maximum size of View's drawing cache expressed in bytes
+     *
+     * @deprecated Use {@link #getScaledMaximumDrawingCacheSize()} instead.
+     */
+    @Deprecated
+    public static int getMaximumDrawingCacheSize() {
+        //noinspection deprecation
+        return MAXIMUM_DRAWING_CACHE_SIZE;
+    }
+
+    /**
+     * The maximum drawing cache size expressed in bytes.
+     *
+     * @return the maximum size of View's drawing cache expressed in bytes
+     */
+    public int getScaledMaximumDrawingCacheSize() {
+        return mMaximumDrawingCacheSize;
+    }
+
+    /**
+     * The amount of time that the zoom controls should be
+     * displayed on the screen expressed in milliseconds.
+     * 
+     * @return the time the zoom controls should be visible expressed
+     * in milliseconds.
+     */
+    public static long getZoomControlsTimeout() {
+        return ZOOM_CONTROLS_TIMEOUT;
+    }
+
+    /**
+     * The amount of time a user needs to press the relevant key to bring up
+     * the global actions dialog.
+     *
+     * @return how long a user needs to press the relevant key to bring up
+     *   the global actions dialog.
+     */
+    public static long getGlobalActionKeyTimeout() {
+        return GLOBAL_ACTIONS_KEY_TIMEOUT;
+    }
+
+    /**
+     * The amount of friction applied to scrolls and flings.
+     * 
+     * @return A scalar dimensionless value representing the coefficient of
+     *         friction.
+     */
+    public static float getScrollFriction() {
+        return SCROLL_FRICTION;
+    }
+}
diff --git a/core/java/android/view/ViewDebug.java b/core/java/android/view/ViewDebug.java
new file mode 100644
index 0000000..883c7bd
--- /dev/null
+++ b/core/java/android/view/ViewDebug.java
@@ -0,0 +1,1128 @@
+/*
+ * Copyright (C) 2007 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.util.Log;
+import android.content.res.Resources;
+import android.graphics.Bitmap;
+import android.os.Environment;
+
+import java.io.File;
+import java.io.BufferedWriter;
+import java.io.FileWriter;
+import java.io.IOException;
+import java.io.FileOutputStream;
+import java.io.DataOutputStream;
+import java.io.OutputStreamWriter;
+import java.io.BufferedOutputStream;
+import java.io.OutputStream;
+import java.util.List;
+import java.util.LinkedList;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+import java.lang.annotation.Target;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.lang.reflect.InvocationTargetException;
+
+/**
+ * Various debugging/tracing tools related to {@link View} and the view hierarchy.
+ */
+public class ViewDebug {
+    /**
+     * Enables or disables view hierarchy tracing. Any invoker of
+     * {@link #trace(View, android.view.ViewDebug.HierarchyTraceType)} should first
+     * check that this value is set to true as not to affect performance.
+     */
+    public static final boolean TRACE_HIERARCHY = false;
+
+    /**
+     * Enables or disables view recycler tracing. Any invoker of
+     * {@link #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])} should first
+     * check that this value is set to true as not to affect performance.
+     */
+    public static final boolean TRACE_RECYCLER = false;
+    
+    /**
+     * The system property of dynamic switch for capturing view information
+     * when it is set, we dump interested fields and methods for the view on focus
+     */    
+    static final String SYSTEM_PROPERTY_CAPTURE_VIEW = "debug.captureview";
+        
+    /**
+     * The system property of dynamic switch for capturing event information
+     * when it is set, we log key events, touch/motion and trackball events
+     */    
+    static final String SYSTEM_PROPERTY_CAPTURE_EVENT = "debug.captureevent";
+    
+    /**
+     * This annotation can be used to mark fields and methods to be dumped by
+     * the view server. Only non-void methods with no arguments can be annotated
+     * by this annotation.
+     */
+    @Target({ ElementType.FIELD, ElementType.METHOD })
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface ExportedProperty {
+        /**
+         * When resolveId is true, and if the annotated field/method return value
+         * is an int, the value is converted to an Android's resource name.
+         *
+         * @return true if the property's value must be transformed into an Android
+         *         resource name, false otherwise
+         */
+        boolean resolveId() default false;
+
+        /**
+         * A mapping can be defined to map int values to specific strings. For
+         * instance, View.getVisibility() returns 0, 4 or 8. However, these values
+         * actually mean VISIBLE, INVISIBLE and GONE. A mapping can be used to see
+         * these human readable values:
+         *
+         * <pre>
+         * @ViewDebug.ExportedProperty(mapping = {
+         *     @ViewDebug.IntToString(from = 0, to = "VISIBLE"),
+         *     @ViewDebug.IntToString(from = 4, to = "INVISIBLE"),
+         *     @ViewDebug.IntToString(from = 8, to = "GONE")
+         * })
+         * public int getVisibility() { ...
+         * <pre>
+         *
+         * @return An array of int to String mappings
+         *
+         * @see android.view.ViewDebug.IntToString
+         */
+        IntToString[] mapping() default { };
+
+        /**
+         * When deep export is turned on, this property is not dumped. Instead, the
+         * properties contained in this property are dumped. Each child property
+         * is prefixed with the name of this property.
+         *
+         * @return true if the properties of this property should be dumped
+         *
+         * @see #prefix() 
+         */
+        boolean deepExport() default false;
+
+        /**
+         * The prefix to use on child properties when deep export is enabled
+         *
+         * @return a prefix as a String
+         *
+         * @see #deepExport()
+         */
+        String prefix() default "";
+    }
+
+    /**
+     * Defines a mapping from an int value to a String. Such a mapping can be used
+     * in a @ExportedProperty to provide more meaningful values to the end user.
+     *
+     * @see android.view.ViewDebug.ExportedProperty
+     */
+    @Target({ ElementType.TYPE })
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface IntToString {
+        /**
+         * The original int value to map to a String.
+         *
+         * @return An arbitrary int value.
+         */
+        int from();
+
+        /**
+         * The String to use in place of the original int value.
+         *
+         * @return An arbitrary non-null String.
+         */
+        String to();
+    }
+    
+    /**
+     * This annotation can be used to mark fields and methods to be dumped when
+     * the view is captured. Methods with this annotation must have no arguments
+     * and must return <some type of data>.
+     * 
+     * @hide pending API Council approval
+     */
+    @Target({ ElementType.FIELD, ElementType.METHOD })
+    @Retention(RetentionPolicy.RUNTIME)
+    public @interface CapturedViewProperty {
+        /**
+         * When retrieveReturn is true, we need to retrieve second level methods 
+         * e.g., we need myView.getFirstLevelMethod().getSecondLevelMethod()
+         * we will set retrieveReturn = true on the annotation of 
+         * myView.getFirstLevelMethod()
+         * @return true if we need the second level methods 
+         */
+        boolean retrieveReturn() default false;        
+    }
+        
+    private static HashMap<Class<?>, Method[]> mCapturedViewMethodsForClasses = null;
+    private static HashMap<Class<?>, Field[]> mCapturedViewFieldsForClasses = null;
+
+    // Maximum delay in ms after which we stop trying to capture a View's drawing
+    private static final int CAPTURE_TIMEOUT = 4000;
+
+    private static final String REMOTE_COMMAND_CAPTURE = "CAPTURE";
+    private static final String REMOTE_COMMAND_DUMP = "DUMP";
+    private static final String REMOTE_COMMAND_INVALIDATE = "INVALIDATE";
+    private static final String REMOTE_COMMAND_REQUEST_LAYOUT = "REQUEST_LAYOUT";
+
+    private static HashMap<Class<?>, Field[]> sFieldsForClasses;
+    private static HashMap<Class<?>, Method[]> sMethodsForClasses;
+        
+    /**
+     * Defines the type of hierarhcy trace to output to the hierarchy traces file.
+     */
+    public enum HierarchyTraceType {
+        INVALIDATE,
+        INVALIDATE_CHILD,
+        INVALIDATE_CHILD_IN_PARENT,
+        REQUEST_LAYOUT,
+        ON_LAYOUT,
+        ON_MEASURE,
+        DRAW,
+        BUILD_CACHE
+    }
+
+    private static BufferedWriter sHierarchyTraces;
+    private static ViewRoot sHierarhcyRoot;
+    private static String sHierarchyTracePrefix;
+
+    /**
+     * Defines the type of recycler trace to output to the recycler traces file.
+     */
+    public enum RecyclerTraceType {
+        NEW_VIEW,
+        BIND_VIEW,
+        RECYCLE_FROM_ACTIVE_HEAP,
+        RECYCLE_FROM_SCRAP_HEAP,
+        MOVE_TO_ACTIVE_HEAP,
+        MOVE_TO_SCRAP_HEAP,
+        MOVE_FROM_ACTIVE_TO_SCRAP_HEAP
+    }
+
+    private static class RecyclerTrace {
+        public int view;
+        public RecyclerTraceType type;
+        public int position;
+        public int indexOnScreen;
+    }
+
+    private static View sRecyclerOwnerView;
+    private static List<View> sRecyclerViews;
+    private static List<RecyclerTrace> sRecyclerTraces;
+    private static String sRecyclerTracePrefix;
+
+    /**
+     * Returns the number of instanciated Views.
+     *
+     * @return The number of Views instanciated in the current process.
+     *
+     * @hide
+     */
+    public static long getViewInstanceCount() {
+        return View.sInstanceCount;
+    }
+
+    /**
+     * Returns the number of instanciated ViewRoots.
+     *
+     * @return The number of ViewRoots instanciated in the current process.
+     *
+     * @hide
+     */
+    public static long getViewRootInstanceCount() {
+        return ViewRoot.getInstanceCount();
+    }    
+
+    /**
+     * Outputs a trace to the currently opened recycler traces. The trace records the type of
+     * recycler action performed on the supplied view as well as a number of parameters.
+     *
+     * @param view the view to trace
+     * @param type the type of the trace
+     * @param parameters parameters depending on the type of the trace
+     */
+    public static void trace(View view, RecyclerTraceType type, int... parameters) {
+        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
+            return;
+        }
+
+        if (!sRecyclerViews.contains(view)) {
+            sRecyclerViews.add(view);
+        }
+
+        final int index = sRecyclerViews.indexOf(view);
+
+        RecyclerTrace trace = new RecyclerTrace();
+        trace.view = index;
+        trace.type = type;
+        trace.position = parameters[0];
+        trace.indexOnScreen = parameters[1];
+
+        sRecyclerTraces.add(trace);
+    }
+
+    /**
+     * Starts tracing the view recycler of the specified view. The trace is identified by a prefix,
+     * used to build the traces files names: <code>/EXTERNAL/view-recycler/PREFIX.traces</code> and
+     * <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>.
+     *
+     * Only one view recycler can be traced at the same time. After calling this method, any
+     * other invocation will result in a <code>IllegalStateException</code> unless
+     * {@link #stopRecyclerTracing()} is invoked before.
+     *
+     * Traces files are created only after {@link #stopRecyclerTracing()} is invoked.
+     *
+     * This method will return immediately if TRACE_RECYCLER is false.
+     *
+     * @param prefix the traces files name prefix
+     * @param view the view whose recycler must be traced
+     *
+     * @see #stopRecyclerTracing()
+     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     */
+    public static void startRecyclerTracing(String prefix, View view) {
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (!TRACE_RECYCLER) {
+            return;
+        }
+
+        if (sRecyclerOwnerView != null) {
+            throw new IllegalStateException("You must call stopRecyclerTracing() before running" +
+                " a new trace!");
+        }
+
+        sRecyclerTracePrefix = prefix;
+        sRecyclerOwnerView = view;
+        sRecyclerViews = new ArrayList<View>();
+        sRecyclerTraces = new LinkedList<RecyclerTrace>();
+    }
+
+    /**
+     * Stops the current view recycer tracing.
+     *
+     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.traces</code>
+     * containing all the traces (or method calls) relative to the specified view's recycler.
+     *
+     * Calling this method creates the file <code>/EXTERNAL/view-recycler/PREFIX.recycler</code>
+     * containing all of the views used by the recycler of the view supplied to
+     * {@link #startRecyclerTracing(String, View)}.
+     *
+     * This method will return immediately if TRACE_RECYCLER is false.
+     *
+     * @see #startRecyclerTracing(String, View)
+     * @see #trace(View, android.view.ViewDebug.RecyclerTraceType, int[])
+     */
+    public static void stopRecyclerTracing() {
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (!TRACE_RECYCLER) {
+            return;
+        }
+
+        if (sRecyclerOwnerView == null || sRecyclerViews == null) {
+            throw new IllegalStateException("You must call startRecyclerTracing() before" +
+                " stopRecyclerTracing()!");
+        }
+
+        File recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
+        recyclerDump.mkdirs();
+
+        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".recycler");
+        try {
+            final BufferedWriter out = new BufferedWriter(new FileWriter(recyclerDump), 8 * 1024);
+
+            for (View view : sRecyclerViews) {
+                final String name = view.getClass().getName();
+                out.write(name);
+                out.newLine();
+            }
+
+            out.close();
+        } catch (IOException e) {
+            Log.e("View", "Could not dump recycler content");
+            return;
+        }
+
+        recyclerDump = new File(Environment.getExternalStorageDirectory(), "view-recycler/");
+        recyclerDump = new File(recyclerDump, sRecyclerTracePrefix + ".traces");
+        try {
+            final FileOutputStream file = new FileOutputStream(recyclerDump);
+            final DataOutputStream out = new DataOutputStream(file);
+
+            for (RecyclerTrace trace : sRecyclerTraces) {
+                out.writeInt(trace.view);
+                out.writeInt(trace.type.ordinal());
+                out.writeInt(trace.position);
+                out.writeInt(trace.indexOnScreen);
+                out.flush();
+            }
+
+            out.close();
+        } catch (IOException e) {
+            Log.e("View", "Could not dump recycler traces");
+            return;
+        }
+
+        sRecyclerViews.clear();
+        sRecyclerViews = null;
+
+        sRecyclerTraces.clear();
+        sRecyclerTraces = null;
+
+        sRecyclerOwnerView = null;
+    }
+
+    /**
+     * Outputs a trace to the currently opened traces file. The trace contains the class name
+     * and instance's hashcode of the specified view as well as the supplied trace type.
+     *
+     * @param view the view to trace
+     * @param type the type of the trace
+     */
+    public static void trace(View view, HierarchyTraceType type) {
+        if (sHierarchyTraces == null) {
+            return;
+        }
+
+        try {
+            sHierarchyTraces.write(type.name());
+            sHierarchyTraces.write(' ');
+            sHierarchyTraces.write(view.getClass().getName());
+            sHierarchyTraces.write('@');
+            sHierarchyTraces.write(Integer.toHexString(view.hashCode()));
+            sHierarchyTraces.newLine();
+        } catch (IOException e) {
+            Log.w("View", "Error while dumping trace of type " + type + " for view " + view);
+        }
+    }
+
+    /**
+     * Starts tracing the view hierarchy of the specified view. The trace is identified by a prefix,
+     * used to build the traces files names: <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code> and
+     * <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>.
+     *
+     * Only one view hierarchy can be traced at the same time. After calling this method, any
+     * other invocation will result in a <code>IllegalStateException</code> unless
+     * {@link #stopHierarchyTracing()} is invoked before.
+     *
+     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>
+     * containing all the traces (or method calls) relative to the specified view's hierarchy.
+     *
+     * This method will return immediately if TRACE_HIERARCHY is false.
+     *
+     * @param prefix the traces files name prefix
+     * @param view the view whose hierarchy must be traced
+     *
+     * @see #stopHierarchyTracing()
+     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     */
+    public static void startHierarchyTracing(String prefix, View view) {
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (!TRACE_HIERARCHY) {
+            return;
+        }
+
+        if (sHierarhcyRoot != null) {
+            throw new IllegalStateException("You must call stopHierarchyTracing() before running" +
+                " a new trace!");
+        }
+
+        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
+        hierarchyDump.mkdirs();
+
+        hierarchyDump = new File(hierarchyDump, prefix + ".traces");
+        sHierarchyTracePrefix = prefix;
+
+        try {
+            sHierarchyTraces = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
+        } catch (IOException e) {
+            Log.e("View", "Could not dump view hierarchy");
+            return;
+        }
+
+        sHierarhcyRoot = (ViewRoot) view.getRootView().getParent();
+    }
+
+    /**
+     * Stops the current view hierarchy tracing. This method closes the file
+     * <code>/EXTERNAL/view-hierarchy/PREFIX.traces</code>.
+     *
+     * Calling this method creates the file <code>/EXTERNAL/view-hierarchy/PREFIX.tree</code>
+     * containing the view hierarchy of the view supplied to
+     * {@link #startHierarchyTracing(String, View)}.
+     *
+     * This method will return immediately if TRACE_HIERARCHY is false.
+     *
+     * @see #startHierarchyTracing(String, View) 
+     * @see #trace(View, android.view.ViewDebug.HierarchyTraceType)
+     */
+    public static void stopHierarchyTracing() {
+        //noinspection PointlessBooleanExpression,ConstantConditions
+        if (!TRACE_HIERARCHY) {
+            return;
+        }
+
+        if (sHierarhcyRoot == null || sHierarchyTraces == null) {
+            throw new IllegalStateException("You must call startHierarchyTracing() before" +
+                " stopHierarchyTracing()!");
+        }
+
+        try {
+            sHierarchyTraces.close();
+        } catch (IOException e) {
+            Log.e("View", "Could not write view traces");
+        }
+        sHierarchyTraces = null;
+
+        File hierarchyDump = new File(Environment.getExternalStorageDirectory(), "view-hierarchy/");
+        hierarchyDump.mkdirs();
+        hierarchyDump = new File(hierarchyDump, sHierarchyTracePrefix + ".tree");
+
+        BufferedWriter out;
+        try {
+            out = new BufferedWriter(new FileWriter(hierarchyDump), 8 * 1024);
+        } catch (IOException e) {
+            Log.e("View", "Could not dump view hierarchy");
+            return;
+        }
+
+        View view = sHierarhcyRoot.getView();
+        if (view instanceof ViewGroup) {
+            ViewGroup group = (ViewGroup) view;
+            dumpViewHierarchy(group, out, 0);
+            try {
+                out.close();
+            } catch (IOException e) {
+                Log.e("View", "Could not dump view hierarchy");
+            }
+        }
+
+        sHierarhcyRoot = null;
+    }
+    
+    static void dispatchCommand(View view, String command, String parameters,
+            OutputStream clientStream) throws IOException {
+
+        // Paranoid but safe...
+        view = view.getRootView();
+
+        if (REMOTE_COMMAND_DUMP.equalsIgnoreCase(command)) {
+            dump(view, clientStream);
+        } else {
+            final String[] params = parameters.split(" ");
+            if (REMOTE_COMMAND_CAPTURE.equalsIgnoreCase(command)) {
+                capture(view, clientStream, params[0]);
+            } else if (REMOTE_COMMAND_INVALIDATE.equalsIgnoreCase(command)) {
+                invalidate(view, params[0]);
+            } else if (REMOTE_COMMAND_REQUEST_LAYOUT.equalsIgnoreCase(command)) {
+                requestLayout(view, params[0]);
+            }
+        }
+    }
+
+    private static View findViewByHashCode(View root, String parameter) {
+        final String[] ids = parameter.split("@");
+        final String className = ids[0];
+        final int hashCode = Integer.parseInt(ids[1], 16);
+
+        View view = root.getRootView();
+        if (view instanceof ViewGroup) {
+            return findView((ViewGroup) view, className, hashCode);
+        }
+
+        return null;
+    }
+
+    private static void invalidate(View root, String parameter) {
+        final View view = findViewByHashCode(root, parameter);
+        if (view != null) {
+            view.postInvalidate();
+        }
+    }
+
+    private static void requestLayout(View root, String parameter) {
+        final View view = findViewByHashCode(root, parameter);
+        if (view != null) {
+            root.post(new Runnable() {
+                public void run() {
+                    view.requestLayout();
+                }
+            });
+        }
+    }
+
+    private static void capture(View root, final OutputStream clientStream, String parameter)
+            throws IOException {
+
+        final CountDownLatch latch = new CountDownLatch(1);
+        final View captureView = findViewByHashCode(root, parameter);
+
+        if (captureView != null) {
+            final Bitmap[] cache = new Bitmap[1];
+
+            final boolean hasCache = captureView.isDrawingCacheEnabled();
+            final boolean willNotCache = captureView.willNotCacheDrawing();
+
+            if (willNotCache) {
+                captureView.setWillNotCacheDrawing(false);
+            }
+
+            root.post(new Runnable() {
+                public void run() {
+                    try {
+                        if (!hasCache) {
+                            captureView.buildDrawingCache();
+                        }
+
+                        cache[0] = captureView.getDrawingCache();
+                    } finally {
+                        latch.countDown();
+                    }
+                }
+            });
+
+            try {
+                latch.await(CAPTURE_TIMEOUT, TimeUnit.MILLISECONDS);
+
+                if (cache[0] != null) {
+                    BufferedOutputStream out = null;
+                    try {
+                        out = new BufferedOutputStream(clientStream, 32 * 1024);
+                        cache[0].compress(Bitmap.CompressFormat.PNG, 100, out);
+                        out.flush();
+                    } finally {
+                        if (out != null) {
+                            out.close();
+                        }
+                    }
+                }
+            } catch (InterruptedException e) {
+                Log.w("View", "Could not complete the capture of the view " + captureView);                
+            } finally {
+                if (willNotCache) {
+                    captureView.setWillNotCacheDrawing(true);
+                }
+                if (!hasCache) {
+                    captureView.destroyDrawingCache();
+                }
+            }
+        }
+    }
+
+    private static void dump(View root, OutputStream clientStream) throws IOException {
+        BufferedWriter out = null;
+        try {
+            out = new BufferedWriter(new OutputStreamWriter(clientStream), 32 * 1024);
+            View view = root.getRootView();
+            if (view instanceof ViewGroup) {
+                ViewGroup group = (ViewGroup) view;
+                dumpViewHierarchyWithProperties(group, out, 0);
+            }
+            out.write("DONE.");
+            out.newLine();
+        } finally {
+            if (out != null) {
+                out.close();
+            }
+        }
+    }
+
+    private static View findView(ViewGroup group, String className, int hashCode) {
+        if (isRequestedView(group, className, hashCode)) {
+            return group;
+        }
+
+        final int count = group.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = group.getChildAt(i);
+            if (view instanceof ViewGroup) {
+                final View found = findView((ViewGroup) view, className, hashCode);
+                if (found != null) {
+                    return found;
+                }
+            } else if (isRequestedView(view, className, hashCode)) {
+                return view;
+            }
+        }
+
+        return null;
+    }
+
+    private static boolean isRequestedView(View view, String className, int hashCode) {
+        return view.getClass().getName().equals(className) && view.hashCode() == hashCode;
+    }
+
+    private static void dumpViewHierarchyWithProperties(ViewGroup group,
+            BufferedWriter out, int level) {
+        if (!dumpViewWithProperties(group, out, level)) {
+            return;
+        }
+
+        final int count = group.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = group.getChildAt(i);
+            if (view instanceof ViewGroup) {
+                dumpViewHierarchyWithProperties((ViewGroup) view, out, level + 1);
+            } else {
+                dumpViewWithProperties(view, out, level + 1);
+            }
+        }
+    }
+
+    private static boolean dumpViewWithProperties(View view, BufferedWriter out, int level) {
+        try {
+            for (int i = 0; i < level; i++) {
+                out.write(' ');
+            }
+            out.write(view.getClass().getName());
+            out.write('@');
+            out.write(Integer.toHexString(view.hashCode()));
+            out.write(' ');
+            dumpViewProperties(view, out);
+            out.newLine();
+        } catch (IOException e) {
+            Log.w("View", "Error while dumping hierarchy tree");
+            return false;
+        }
+        return true;
+    }
+
+    private static Field[] getExportedPropertyFields(Class<?> klass) {
+        if (sFieldsForClasses == null) {
+            sFieldsForClasses = new HashMap<Class<?>, Field[]>();
+        }
+        final HashMap<Class<?>, Field[]> map = sFieldsForClasses;
+
+        Field[] fields = map.get(klass);
+        if (fields != null) {
+            return fields;
+        }
+
+        final ArrayList<Field> foundFields = new ArrayList<Field>();
+        fields = klass.getDeclaredFields();
+
+        int count = fields.length;
+        for (int i = 0; i < count; i++) {
+            final Field field = fields[i];
+            if (field.isAnnotationPresent(ExportedProperty.class)) {
+                field.setAccessible(true);
+                foundFields.add(field);
+            }
+        }
+
+        fields = foundFields.toArray(new Field[foundFields.size()]);
+        map.put(klass, fields);
+
+        return fields;
+    }
+
+    private static Method[] getExportedPropertyMethods(Class<?> klass) {
+        if (sMethodsForClasses == null) {
+            sMethodsForClasses = new HashMap<Class<?>, Method[]>();
+        }
+        final HashMap<Class<?>, Method[]> map = sMethodsForClasses;
+
+        Method[] methods = map.get(klass);
+        if (methods != null) {
+            return methods;
+        }
+
+        final ArrayList<Method> foundMethods = new ArrayList<Method>();
+        methods = klass.getDeclaredMethods();
+        
+        int count = methods.length;
+        for (int i = 0; i < count; i++) {
+            final Method method = methods[i];            
+            if (method.getParameterTypes().length == 0 &&
+                        method.isAnnotationPresent(ExportedProperty.class) &&
+                        method.getReturnType() != Void.class) {
+                method.setAccessible(true);
+                foundMethods.add(method);
+            }
+        }
+
+        methods = foundMethods.toArray(new Method[foundMethods.size()]);
+        map.put(klass, methods);
+
+        return methods;
+    }
+
+    private static void dumpViewProperties(Object view, BufferedWriter out) throws IOException {
+        dumpViewProperties(view, out, "");
+    }
+
+    private static void dumpViewProperties(Object view, BufferedWriter out, String prefix)
+            throws IOException {
+        Class<?> klass = view.getClass();
+
+        do {
+            exportFields(view, out, klass, prefix);
+            exportMethods(view, out, klass, prefix);
+            klass = klass.getSuperclass();
+        } while (klass != Object.class);
+    }
+    
+    private static void exportMethods(Object view, BufferedWriter out, Class<?> klass,
+            String prefix) throws IOException {
+
+        final Method[] methods = getExportedPropertyMethods(klass);
+
+        int count = methods.length;
+        for (int i = 0; i < count; i++) {
+            final Method method = methods[i];
+            //noinspection EmptyCatchBlock
+            try {
+                // TODO: This should happen on the UI thread
+                Object methodValue = method.invoke(view, (Object[]) null);
+                final Class<?> returnType = method.getReturnType();
+
+                if (returnType == int.class) {
+                    ExportedProperty property = method.getAnnotation(ExportedProperty.class);
+                    if (property.resolveId() && view instanceof View) {
+                        final Resources resources = ((View) view).getContext().getResources();
+                        final int id = (Integer) methodValue;
+                        if (id >= 0) {
+                            try {
+                                methodValue = resources.getResourceTypeName(id) + '/' +
+                                        resources.getResourceEntryName(id);
+                            } catch (Resources.NotFoundException e) {
+                                methodValue = "UNKNOWN";
+                            }
+                        } else {
+                            methodValue = "NO_ID";
+                        }
+                    } else {
+                        final IntToString[] mapping = property.mapping();
+                        if (mapping.length > 0) {
+                            final int intValue = (Integer) methodValue;
+                            boolean mapped = false;
+                            int mappingCount = mapping.length;
+                            for (int j = 0; j < mappingCount; j++) {
+                                final IntToString mapper = mapping[j];
+                                if (mapper.from() == intValue) {
+                                    methodValue = mapper.to();
+                                    mapped = true;
+                                    break;
+                                }
+                            }
+
+                            if (!mapped) {
+                                methodValue = intValue;
+                            }
+                        }
+                    }
+                } else if (!returnType.isPrimitive()) {
+                    ExportedProperty property = method.getAnnotation(ExportedProperty.class);
+                    if (property.deepExport()) {
+                        dumpViewProperties(methodValue, out, prefix + property.prefix());
+                        continue;
+                    }
+                }
+
+                out.write(prefix);
+                out.write(method.getName());
+                out.write("()=");
+
+                if (methodValue != null) {
+                    final String value = methodValue.toString().replace("\n", "\\n");
+                    out.write(String.valueOf(value.length()));
+                    out.write(",");
+                    out.write(value);
+                } else {
+                    out.write("4,null");
+                }
+
+                out.write(' ');
+            } catch (IllegalAccessException e) {
+            } catch (InvocationTargetException e) {
+            }
+        }
+    }
+
+    private static void exportFields(Object view, BufferedWriter out, Class<?> klass, String prefix)
+            throws IOException {
+        final Field[] fields = getExportedPropertyFields(klass);
+
+        int count = fields.length;
+        for (int i = 0; i < count; i++) {
+            final Field field = fields[i];
+
+            //noinspection EmptyCatchBlock
+            try {
+                Object fieldValue = null;
+                final Class<?> type = field.getType();
+
+                if (type == int.class) {
+                    ExportedProperty property = field.getAnnotation(ExportedProperty.class);
+                    if (property.resolveId() && view instanceof View) {
+                        final Resources resources = ((View) view).getContext().getResources();
+                        final int id = field.getInt(view);
+                        if (id >= 0) {
+                            try {
+                                fieldValue = resources.getResourceTypeName(id) + '/' +
+                                        resources.getResourceEntryName(id);
+                            } catch (Resources.NotFoundException e) {
+                                fieldValue = "UNKNOWN";
+                            }
+                        } else {
+                            fieldValue = "NO_ID";
+                        }
+                    } else {
+                        final IntToString[] mapping = property.mapping();
+                        if (mapping.length > 0) {
+                            final int intValue = field.getInt(view);
+                            int mappingCount = mapping.length;
+                            for (int j = 0; j < mappingCount; j++) {
+                                final IntToString mapped = mapping[j];
+                                if (mapped.from() == intValue) {
+                                    fieldValue = mapped.to();
+                                    break;
+                                }
+                            }
+
+                            if (fieldValue == null) {
+                                fieldValue = intValue;
+                            }
+                        }
+                    }
+                } else if (!type.isPrimitive()) {
+                    ExportedProperty property = field.getAnnotation(ExportedProperty.class);
+                    if (property.deepExport()) {
+                        dumpViewProperties(field.get(view), out, prefix + property.prefix());
+                        continue;
+                    }
+                }
+
+                if (fieldValue == null) {
+                    fieldValue = field.get(view);
+                }                
+
+                out.write(prefix);
+                out.write(field.getName());
+                out.write('=');
+
+                if (fieldValue != null) {
+                    final String value = fieldValue.toString().replace("\n", "\\n");
+                    out.write(String.valueOf(value.length()));
+                    out.write(",");
+                    out.write(value);
+                } else {
+                    out.write("4,null");
+                }
+
+                out.write(' ');
+            } catch (IllegalAccessException e) {
+            }
+        }
+    }
+
+    private static void dumpViewHierarchy(ViewGroup group, BufferedWriter out, int level) {
+        if (!dumpView(group, out, level)) {
+            return;
+        }
+
+        final int count = group.getChildCount();
+        for (int i = 0; i < count; i++) {
+            final View view = group.getChildAt(i);
+            if (view instanceof ViewGroup) {
+                dumpViewHierarchy((ViewGroup) view, out, level + 1);
+            } else {
+                dumpView(view, out, level + 1);
+            }
+        }
+    }
+
+    private static boolean dumpView(Object view, BufferedWriter out, int level) {
+        try {
+            for (int i = 0; i < level; i++) {
+                out.write(' ');
+            }
+            out.write(view.getClass().getName());
+            out.write('@');
+            out.write(Integer.toHexString(view.hashCode()));
+            out.newLine();
+        } catch (IOException e) {
+            Log.w("View", "Error while dumping hierarchy tree");
+            return false;
+        }
+        return true;
+    }
+
+    private static Field[] capturedViewGetPropertyFields(Class<?> klass) {
+        if (mCapturedViewFieldsForClasses == null) {
+            mCapturedViewFieldsForClasses = new HashMap<Class<?>, Field[]>();
+        }
+        final HashMap<Class<?>, Field[]> map = mCapturedViewFieldsForClasses;
+
+        Field[] fields = map.get(klass);
+        if (fields != null) {
+            return fields;
+        }
+
+        final ArrayList<Field> foundFields = new ArrayList<Field>();
+        fields = klass.getFields();
+
+        int count = fields.length;
+        for (int i = 0; i < count; i++) {
+            final Field field = fields[i];
+            if (field.isAnnotationPresent(CapturedViewProperty.class)) {
+                field.setAccessible(true);
+                foundFields.add(field);
+            }
+        }
+
+        fields = foundFields.toArray(new Field[foundFields.size()]);
+        map.put(klass, fields);
+
+        return fields;
+    }
+
+    private static Method[] capturedViewGetPropertyMethods(Class<?> klass) {
+        if (mCapturedViewMethodsForClasses == null) {
+            mCapturedViewMethodsForClasses = new HashMap<Class<?>, Method[]>();
+        }
+        final HashMap<Class<?>, Method[]> map = mCapturedViewMethodsForClasses;
+
+        Method[] methods = map.get(klass);
+        if (methods != null) {
+            return methods;
+        }
+
+        final ArrayList<Method> foundMethods = new ArrayList<Method>();
+        methods = klass.getMethods();
+        
+        int count = methods.length;
+        for (int i = 0; i < count; i++) {
+            final Method method = methods[i];            
+            if (method.getParameterTypes().length == 0 &&
+                    method.isAnnotationPresent(CapturedViewProperty.class) &&
+                    method.getReturnType() != Void.class) {
+                method.setAccessible(true);
+                foundMethods.add(method);
+            }
+        }
+
+        methods = foundMethods.toArray(new Method[foundMethods.size()]);
+        map.put(klass, methods);
+
+        return methods;
+    }
+              
+    private static String capturedViewExportMethods(Object obj, Class<?> klass, 
+            String prefix) {
+
+        if (obj == null) {
+            return "null";
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        final Method[] methods = capturedViewGetPropertyMethods(klass);
+
+        int count = methods.length;
+        for (int i = 0; i < count; i++) {
+            final Method method = methods[i];
+            try {
+                Object methodValue = method.invoke(obj, (Object[]) null);
+                final Class<?> returnType = method.getReturnType();
+                
+                CapturedViewProperty property = method.getAnnotation(CapturedViewProperty.class);
+                if (property.retrieveReturn()) {
+                    //we are interested in the second level data only
+                    sb.append(capturedViewExportMethods(methodValue, returnType, method.getName() + "#"));
+                } else {                    
+                    sb.append(prefix);
+                    sb.append(method.getName());
+                    sb.append("()=");
+                    
+                    if (methodValue != null) {
+                        final String value = methodValue.toString().replace("\n", "\\n");                        
+                        sb.append(value);                        
+                    } else {
+                        sb.append("null");
+                    }
+                    sb.append("; ");
+                }
+              } catch (IllegalAccessException e) {
+                  //Exception IllegalAccess, it is OK here 
+                  //we simply ignore this method
+              } catch (InvocationTargetException e) {
+                  //Exception InvocationTarget, it is OK here 
+                  //we simply ignore this method
+              }              
+        }        
+        return sb.toString();
+    }
+
+    private static String capturedViewExportFields(Object obj, Class<?> klass, String prefix) {
+        
+        if (obj == null) {
+            return "null";
+        }
+        
+        StringBuilder sb = new StringBuilder();
+        final Field[] fields = capturedViewGetPropertyFields(klass);
+
+        int count = fields.length;
+        for (int i = 0; i < count; i++) {
+            final Field field = fields[i];
+            try {
+                Object fieldValue = field.get(obj);
+
+                sb.append(prefix);
+                sb.append(field.getName());
+                sb.append("=");
+
+                if (fieldValue != null) {
+                    final String value = fieldValue.toString().replace("\n", "\\n");
+                    sb.append(value);
+                } else {
+                    sb.append("null");
+                }
+                sb.append(' ');
+            } catch (IllegalAccessException e) {
+                //Exception IllegalAccess, it is OK here 
+                //we simply ignore this field
+            }
+        }
+        return sb.toString();
+    }
+    
+    /**
+     * dump view info for id based instrument test generation 
+     * (and possibly further data analysis). The results are dumped
+     * to the log. 
+     * @param tag for log
+     * @param view for dump
+     * 
+     * @hide pending API Council approval
+     */
+    public static void dumpCapturedView(String tag, Object view) {        
+        Class<?> klass = view.getClass();
+        StringBuilder sb = new StringBuilder(klass.getName() + ": ");
+        sb.append(capturedViewExportFields(view, klass, ""));
+        sb.append(capturedViewExportMethods(view, klass, ""));        
+        Log.d(tag, sb.toString());        
+    }
+}
diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java
new file mode 100644
index 0000000..70cc2a9
--- /dev/null
+++ b/core/java/android/view/ViewGroup.java
@@ -0,0 +1,3478 @@
+/*
+ * Copyright (C) 2006 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 com.android.internal.R;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Bitmap;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.graphics.RectF;
+import android.os.Parcelable;
+import android.os.SystemClock;
+import android.util.AttributeSet;
+import android.util.EventLog;
+import android.util.Log;
+import android.util.SparseArray;
+import android.view.animation.Animation;
+import android.view.animation.AnimationUtils;
+import android.view.animation.LayoutAnimationController;
+import android.view.animation.Transformation;
+
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * A <code>ViewGroup</code> is a special view that can contain other views
+ * (called children.) The view group is the base class for layouts and views
+ * containers. This class also defines the
+ * {@link android.view.ViewGroup.LayoutParams} class which serves as the base
+ * class for layouts parameters.
+ * </p>
+ *
+ * <p>
+ * Also see {@link LayoutParams} for layout attributes.
+ * </p>
+ */
+public abstract class ViewGroup extends View implements ViewParent, ViewManager {
+    private static final boolean DBG = false;
+
+    /**
+     * Views which have been hidden or removed which need to be animated on
+     * their way out.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected ArrayList<View> mDisappearingChildren;
+
+    /**
+     * Listener used to propagate events indicating when children are added
+     * and/or removed from a view group.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected OnHierarchyChangeListener mOnHierarchyChangeListener;
+
+    // The view contained within this ViewGroup that has or contains focus.
+    private View mFocused;
+
+    // The current transformation to apply on the child being drawn
+    private Transformation mChildTransformation;
+    private RectF mInvalidateRegion;
+
+    // Target of Motion events
+    private View mMotionTarget;
+    private final Rect mTempRect = new Rect();
+
+    // Layout animation
+    private LayoutAnimationController mLayoutAnimationController;
+    private Animation.AnimationListener mAnimationListener;
+
+    /**
+     * Internal flags.
+     * 
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected int mGroupFlags;
+
+    // When set, ViewGroup invalidates only the child's rectangle
+    // Set by default
+    private static final int FLAG_CLIP_CHILDREN = 0x1;
+
+    // When set, ViewGroup excludes the padding area from the invalidate rectangle
+    // Set by default
+    private static final int FLAG_CLIP_TO_PADDING = 0x2;
+
+    // When set, dispatchDraw() will invoke invalidate(); this is set by drawChild() when
+    // a child needs to be invalidated and FLAG_OPTIMIZE_INVALIDATE is set
+    private static final int FLAG_INVALIDATE_REQUIRED  = 0x4;
+
+    // When set, dispatchDraw() will run the layout animation and unset the flag
+    private static final int FLAG_RUN_ANIMATION = 0x8;
+
+    // When set, there is either no layout animation on the ViewGroup or the layout
+    // animation is over
+    // Set by default
+    private static final int FLAG_ANIMATION_DONE = 0x10;
+
+    // If set, this ViewGroup has padding; if unset there is no padding and we don't need
+    // to clip it, even if FLAG_CLIP_TO_PADDING is set
+    private static final int FLAG_PADDING_NOT_NULL = 0x20;
+
+    // When set, this ViewGroup caches its children in a Bitmap before starting a layout animation
+    // Set by default
+    private static final int FLAG_ANIMATION_CACHE = 0x40;
+
+    // When set, this ViewGroup converts calls to invalidate(Rect) to invalidate() during a
+    // layout animation; this avoid clobbering the hierarchy
+    // Automatically set when the layout animation starts, depending on the animation's
+    // characteristics
+    private static final int FLAG_OPTIMIZE_INVALIDATE = 0x80;
+
+    // When set, the next call to drawChild() will clear mChildTransformation's matrix
+    private static final int FLAG_CLEAR_TRANSFORMATION = 0x100;
+
+    // When set, this ViewGroup invokes mAnimationListener.onAnimationEnd() and removes
+    // the children's Bitmap caches if necessary
+    // This flag is set when the layout animation is over (after FLAG_ANIMATION_DONE is set)
+    private static final int FLAG_NOTIFY_ANIMATION_LISTENER = 0x200;
+
+    /**
+     * When set, the drawing method will call {@link #getChildDrawingOrder(int, int)}
+     * to get the index of the child to draw for that iteration.
+     */
+    protected static final int FLAG_USE_CHILD_DRAWING_ORDER = 0x400;
+    
+    /**
+     * When set, this ViewGroup supports static transformations on children; this causes
+     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
+     * invoked when a child is drawn.
+     *
+     * Any subclass overriding
+     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
+     * set this flags in {@link #mGroupFlags}.
+     * 
+     * {@hide}
+     */
+    protected static final int FLAG_SUPPORT_STATIC_TRANSFORMATIONS = 0x800;
+
+    // When the previous drawChild() invocation used an alpha value that was lower than
+    // 1.0 and set it in mCachePaint
+    private static final int FLAG_ALPHA_LOWER_THAN_ONE = 0x1000;
+
+    /**
+     * When set, this ViewGroup's drawable states also include those
+     * of its children.
+     */
+    private static final int FLAG_ADD_STATES_FROM_CHILDREN = 0x2000;
+
+    /**
+     * When set, this ViewGroup tries to always draw its children using their drawing cache.
+     */
+    private static final int FLAG_ALWAYS_DRAWN_WITH_CACHE = 0x4000;
+
+    /**
+     * When set, and if FLAG_ALWAYS_DRAWN_WITH_CACHE is not set, this ViewGroup will try to
+     * draw its children with their drawing cache.
+     */
+    private static final int FLAG_CHILDREN_DRAWN_WITH_CACHE = 0x8000;
+
+    /**
+     * When set, this group will go through its list of children to notify them of
+     * any drawable state change.
+     */
+    private static final int FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE = 0x10000;
+
+    private static final int FLAG_MASK_FOCUSABILITY = 0x60000;
+
+    /**
+     * This view will get focus before any of its descendants.
+     */
+    public static final int FOCUS_BEFORE_DESCENDANTS = 0x20000;
+
+    /**
+     * This view will get focus only if none of its descendants want it.
+     */
+    public static final int FOCUS_AFTER_DESCENDANTS = 0x40000;
+
+    /**
+     * This view will block any of its descendants from getting focus, even
+     * if they are focusable.
+     */
+    public static final int FOCUS_BLOCK_DESCENDANTS = 0x60000;
+
+    /**
+     * Used to map between enum in attrubutes and flag values.
+     */
+    private static final int[] DESCENDANT_FOCUSABILITY_FLAGS =
+            {FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS,
+                    FOCUS_BLOCK_DESCENDANTS};
+
+    /**
+     * When set, this ViewGroup should not intercept touch events.
+     */
+    private static final int FLAG_DISALLOW_INTERCEPT = 0x80000;
+    
+    /**
+     * Indicates which types of drawing caches are to be kept in memory.
+     * This field should be made private, so it is hidden from the SDK.
+     * {@hide}
+     */
+    protected int mPersistentDrawingCache;
+
+    /**
+     * Used to indicate that no drawing cache should be kept in memory.
+     */
+    public static final int PERSISTENT_NO_CACHE = 0x0;
+
+    /**
+     * Used to indicate that the animation drawing cache should be kept in memory.
+     */
+    public static final int PERSISTENT_ANIMATION_CACHE = 0x1;
+
+    /**
+     * Used to indicate that the scrolling drawing cache should be kept in memory.
+     */
+    public static final int PERSISTENT_SCROLLING_CACHE = 0x2;
+
+    /**
+     * Used to indicate that all drawing caches should be kept in memory.
+     */
+    public static final int PERSISTENT_ALL_CACHES = 0x3;
+
+    /**
+     * We clip to padding when FLAG_CLIP_TO_PADDING and FLAG_PADDING_NOT_NULL
+     * are set at the same time.
+     */
+    protected static final int CLIP_TO_PADDING_MASK = FLAG_CLIP_TO_PADDING | FLAG_PADDING_NOT_NULL;
+
+    // Index of the child's left position in the mLocation array
+    private static final int CHILD_LEFT_INDEX = 0;
+    // Index of the child's top position in the mLocation array
+    private static final int CHILD_TOP_INDEX = 1;
+
+    // Child views of this ViewGroup
+    private View[] mChildren;
+    // Number of valid children in the mChildren array, the rest should be null or not
+    // considered as children
+    private int mChildrenCount;
+
+    private static final int ARRAY_INITIAL_CAPACITY = 12;
+    private static final int ARRAY_CAPACITY_INCREMENT = 12;
+
+    // Used to draw cached views
+    private final Paint mCachePaint = new Paint();
+
+    public ViewGroup(Context context) {
+        super(context);
+        initViewGroup();
+    }
+
+    public ViewGroup(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initViewGroup();
+        initFromAttributes(context, attrs);
+    }
+
+    public ViewGroup(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initViewGroup();
+        initFromAttributes(context, attrs);
+    }
+
+    private void initViewGroup() {
+        // ViewGroup doesn't draw by default
+        setFlags(WILL_NOT_DRAW, DRAW_MASK);
+        mGroupFlags |= FLAG_CLIP_CHILDREN;
+        mGroupFlags |= FLAG_CLIP_TO_PADDING;
+        mGroupFlags |= FLAG_ANIMATION_DONE;
+        mGroupFlags |= FLAG_ANIMATION_CACHE;
+        mGroupFlags |= FLAG_ALWAYS_DRAWN_WITH_CACHE;
+
+        setDescendantFocusability(FOCUS_BEFORE_DESCENDANTS);
+
+        mChildren = new View[ARRAY_INITIAL_CAPACITY];
+        mChildrenCount = 0;
+
+        mCachePaint.setDither(false);
+
+        mPersistentDrawingCache = PERSISTENT_SCROLLING_CACHE;
+    }
+
+    private void initFromAttributes(Context context, AttributeSet attrs) {
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                R.styleable.ViewGroup);
+
+        final int N = a.getIndexCount();
+        for (int i = 0; i < N; i++) {
+            int attr = a.getIndex(i);
+            switch (attr) {
+                case R.styleable.ViewGroup_clipChildren:
+                    setClipChildren(a.getBoolean(attr, true));
+                    break;
+                case R.styleable.ViewGroup_clipToPadding:
+                    setClipToPadding(a.getBoolean(attr, true));
+                    break;
+                case R.styleable.ViewGroup_animationCache:
+                    setAnimationCacheEnabled(a.getBoolean(attr, true));
+                    break;
+                case R.styleable.ViewGroup_persistentDrawingCache:
+                    setPersistentDrawingCache(a.getInt(attr, PERSISTENT_SCROLLING_CACHE));
+                    break;
+                case R.styleable.ViewGroup_addStatesFromChildren:
+                    setAddStatesFromChildren(a.getBoolean(attr, false));
+                    break;
+                case R.styleable.ViewGroup_alwaysDrawnWithCache:
+                    setAlwaysDrawnWithCacheEnabled(a.getBoolean(attr, true));
+                    break;
+                case R.styleable.ViewGroup_layoutAnimation:
+                    int id = a.getResourceId(attr, -1);
+                    if (id > 0) {
+                        setLayoutAnimation(AnimationUtils.loadLayoutAnimation(mContext, id));
+                    }
+                    break;
+                case R.styleable.ViewGroup_descendantFocusability:
+                    setDescendantFocusability(DESCENDANT_FOCUSABILITY_FLAGS[a.getInt(attr, 0)]);
+                    break;
+            }
+        }
+
+        a.recycle();
+    }
+
+    /**
+     * Gets the descendant focusability of this view group.  The descendant
+     * focusability defines the relationship between this view group and its
+     * descendants when looking for a view to take focus in
+     * {@link #requestFocus(int, android.graphics.Rect)}.
+     *
+     * @return one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
+     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
+     */
+    @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.IntToString(from = FOCUS_BEFORE_DESCENDANTS, to = "FOCUS_BEFORE_DESCENDANTS"),
+        @ViewDebug.IntToString(from = FOCUS_AFTER_DESCENDANTS, to = "FOCUS_AFTER_DESCENDANTS"),
+        @ViewDebug.IntToString(from = FOCUS_BLOCK_DESCENDANTS, to = "FOCUS_BLOCK_DESCENDANTS")
+    })
+    public int getDescendantFocusability() {
+        return mGroupFlags & FLAG_MASK_FOCUSABILITY;
+    }
+
+    /**
+     * Set the descendant focusability of this view group. This defines the relationship
+     * between this view group and its descendants when looking for a view to
+     * take focus in {@link #requestFocus(int, android.graphics.Rect)}.
+     *
+     * @param focusability one of {@link #FOCUS_BEFORE_DESCENDANTS}, {@link #FOCUS_AFTER_DESCENDANTS},
+     *   {@link #FOCUS_BLOCK_DESCENDANTS}.
+     */
+    public void setDescendantFocusability(int focusability) {
+        switch (focusability) {
+            case FOCUS_BEFORE_DESCENDANTS:
+            case FOCUS_AFTER_DESCENDANTS:
+            case FOCUS_BLOCK_DESCENDANTS:
+                break;
+            default:
+                throw new IllegalArgumentException("must be one of FOCUS_BEFORE_DESCENDANTS, "
+                        + "FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS");
+        }
+        mGroupFlags &= ~FLAG_MASK_FOCUSABILITY;
+        mGroupFlags |= (focusability & FLAG_MASK_FOCUSABILITY);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void handleFocusGainInternal(int direction, Rect previouslyFocusedRect) {
+        if (mFocused != null) {
+            mFocused.unFocus();
+            mFocused = null;
+        }
+        super.handleFocusGainInternal(direction, previouslyFocusedRect);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void requestChildFocus(View child, View focused) {
+        if (DBG) {
+            System.out.println(this + " requestChildFocus()");
+        }
+        if (getDescendantFocusability() == FOCUS_BLOCK_DESCENDANTS) {
+            return;
+        }
+
+        // Unfocus us, if necessary
+        super.unFocus();
+
+        // We had a previous notion of who had focus. Clear it.
+        if (mFocused != child) {
+            if (mFocused != null) {
+                mFocused.unFocus();
+            }
+
+            mFocused = child;
+        }
+        if (mParent != null) {
+            mParent.requestChildFocus(this, focused);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void focusableViewAvailable(View v) {
+        if (mParent != null
+                // shortcut: don't report a new focusable view if we block our descendants from
+                // getting focus
+                && (getDescendantFocusability() != FOCUS_BLOCK_DESCENDANTS)
+                // shortcut: don't report a new focusable view if we already are focused
+                // (and we don't prefer our descendants)
+                //
+                // note: knowing that mFocused is non-null is not a good enough reason
+                // to break the traversal since in that case we'd actually have to find
+                // the focused view and make sure it wasn't FOCUS_AFTER_DESCENDANTS and
+                // an ancestor of v; this will get checked for at ViewRoot
+                && !(isFocused() && getDescendantFocusability() != FOCUS_AFTER_DESCENDANTS)) {
+            mParent.focusableViewAvailable(v);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean showContextMenuForChild(View originalView) {
+        return mParent != null && mParent.showContextMenuForChild(originalView);
+    }
+
+    /**
+     * Find the nearest view in the specified direction that wants to take
+     * focus.
+     *
+     * @param focused The view that currently has focus
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and
+     *        FOCUS_RIGHT, or 0 for not applicable.
+     */
+    public View focusSearch(View focused, int direction) {
+        if (isRootNamespace()) {
+            // root namespace means we should consider ourselves the top of the
+            // tree for focus searching; otherwise we could be focus searching
+            // into other tabs.  see LocalActivityManager and TabHost for more info
+            return FocusFinder.getInstance().findNextFocus(this, focused, direction);
+        } else if (mParent != null) {
+            return mParent.focusSearch(focused, direction);
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean requestChildRectangleOnScreen(View child, Rect rectangle, boolean immediate) {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchUnhandledMove(View focused, int direction) {
+        return mFocused != null &&
+                mFocused.dispatchUnhandledMove(focused, direction);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void clearChildFocus(View child) {
+        if (DBG) {
+            System.out.println(this + " clearChildFocus()");
+        }
+
+        mFocused = null;
+        if (mParent != null) {
+            mParent.clearChildFocus(this);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void clearFocus() {
+        super.clearFocus();
+
+        // clear any child focus if it exists
+        if (mFocused != null) {
+            mFocused.clearFocus();
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void unFocus() {
+        if (DBG) {
+            System.out.println(this + " unFocus()");
+        }
+
+        super.unFocus();
+        if (mFocused != null) {
+            mFocused.unFocus();
+        }
+        mFocused = null;
+    }
+
+    /**
+     * Returns the focused child of this view, if any. The child may have focus
+     * or contain focus.
+     *
+     * @return the focused child or null.
+     */
+    public View getFocusedChild() {
+        return mFocused;
+    }
+
+    /**
+     * Returns true if this view has or contains focus
+     *
+     * @return true if this view has or contains focus
+     */
+    @Override
+    public boolean hasFocus() {
+        return (mPrivateFlags & FOCUSED) != 0 || mFocused != null;
+    }
+
+    /*
+     * (non-Javadoc)
+     *
+     * @see android.view.View#findFocus()
+     */
+    @Override
+    public View findFocus() {
+        if (DBG) {
+            System.out.println("Find focus in " + this + ": flags="
+                    + isFocused() + ", child=" + mFocused);
+        }
+
+        if (isFocused()) {
+            return this;
+        }
+
+        if (mFocused != null) {
+            return mFocused.findFocus();
+        }
+        return null;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean hasFocusable() {
+        if ((mViewFlags & VISIBILITY_MASK) != VISIBLE) {
+            return false;
+        }
+
+        if (isFocusable()) {
+            return true;
+        }
+
+        final int descendantFocusability = getDescendantFocusability();
+        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if (child.hasFocusable()) {
+                    return true;
+                }
+            }
+        }
+
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addFocusables(ArrayList<View> views, int direction) {
+        final int focusableCount = views.size();
+
+        final int descendantFocusability = getDescendantFocusability();
+
+        if (descendantFocusability != FOCUS_BLOCK_DESCENDANTS) {
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                    child.addFocusables(views, direction);
+                }
+            }
+        }
+
+        // we add ourselves (if focusable) in all cases except for when we are
+        // FOCUS_AFTER_DESCENDANTS and there are some descendants focusable.  this is
+        // to avoid the focus search finding layouts when a more precise search
+        // among the focusable children would be more interesting.
+        if (
+            descendantFocusability != FOCUS_AFTER_DESCENDANTS ||
+                // No focusable descendants
+                (focusableCount == views.size())) {
+            super.addFocusables(views, direction);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchWindowFocusChanged(boolean hasFocus) {
+        super.dispatchWindowFocusChanged(hasFocus);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchWindowFocusChanged(hasFocus);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void addTouchables(ArrayList<View> views) {
+        super.addTouchables(views);
+
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+
+        for (int i = 0; i < count; i++) {
+            final View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                child.addTouchables(views);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchWindowVisibilityChanged(int visibility) {
+        super.dispatchWindowVisibilityChanged(visibility);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchWindowVisibilityChanged(visibility);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void recomputeViewAttributes(View child) {
+        ViewParent parent = mParent;
+        if (parent != null) parent.recomputeViewAttributes(this);
+    }
+    
+    @Override
+    void dispatchCollectViewAttributes(int visibility) {
+        visibility |= mViewFlags&VISIBILITY_MASK;
+        super.dispatchCollectViewAttributes(visibility);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchCollectViewAttributes(visibility);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void bringChildToFront(View child) {
+        int index = indexOfChild(child);
+        if (index >= 0) {
+            removeFromArray(index);
+            addInArray(child, mChildrenCount);
+            child.mParent = this;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchKeyEventPreIme(KeyEvent event) {
+        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+            return super.dispatchKeyEventPreIme(event);
+        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            return mFocused.dispatchKeyEventPreIme(event);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchKeyEvent(KeyEvent event) {
+        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+            return super.dispatchKeyEvent(event);
+        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            return mFocused.dispatchKeyEvent(event);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchKeyShortcutEvent(KeyEvent event) {
+        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+            return super.dispatchKeyShortcutEvent(event);
+        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            return mFocused.dispatchKeyShortcutEvent(event);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchTrackballEvent(MotionEvent event) {
+        if ((mPrivateFlags & (FOCUSED | HAS_BOUNDS)) == (FOCUSED | HAS_BOUNDS)) {
+            return super.dispatchTrackballEvent(event);
+        } else if (mFocused != null && (mFocused.mPrivateFlags & HAS_BOUNDS) == HAS_BOUNDS) {
+            return mFocused.dispatchTrackballEvent(event);
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean dispatchTouchEvent(MotionEvent ev) {
+        final int action = ev.getAction();
+        final float xf = ev.getX();
+        final float yf = ev.getY();
+        final float scrolledXFloat = xf + mScrollX;
+        final float scrolledYFloat = yf + mScrollY;
+        final Rect frame = mTempRect;
+
+        boolean disallowIntercept = (mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0;
+
+        if (action == MotionEvent.ACTION_DOWN) {
+            if (mMotionTarget != null) {
+                // this is weird, we got a pen down, but we thought it was
+                // already down!
+                // XXX: We should probably send an ACTION_UP to the current
+                // target.
+                mMotionTarget = null;
+            }
+            // If we're disallowing intercept or if we're allowing and we didn't
+            // intercept
+            if (disallowIntercept || !onInterceptTouchEvent(ev)) {
+                // reset this event's action (just to protect ourselves)
+                ev.setAction(MotionEvent.ACTION_DOWN);
+                // We know we want to dispatch the event down, find a child
+                // who can handle it, start with the front-most child.
+                final int scrolledXInt = (int) scrolledXFloat;
+                final int scrolledYInt = (int) scrolledYFloat;
+                final View[] children = mChildren;
+                final int count = mChildrenCount;
+                for (int i = count - 1; i >= 0; i--) {
+                    final View child = children[i];
+                    if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE
+                            || child.getAnimation() != null) {
+                        child.getHitRect(frame);
+                        if (frame.contains(scrolledXInt, scrolledYInt)) {
+                            // offset the event to the view's coordinate system
+                            final float xc = scrolledXFloat - child.mLeft;
+                            final float yc = scrolledYFloat - child.mTop;
+                            ev.setLocation(xc, yc);
+                            if (child.dispatchTouchEvent(ev))  {
+                                // Event handled, we have a target now.
+                                mMotionTarget = child;
+                                return true;
+                            }
+                            // The event didn't get handled, try the next view.
+                            // Don't reset the event's location, it's not
+                            // necessary here.
+                        }
+                    }
+                }
+            }
+        }
+        
+        boolean isUpOrCancel = (action == MotionEvent.ACTION_UP) ||
+                (action == MotionEvent.ACTION_CANCEL); 
+
+        if (isUpOrCancel) {
+            // Note, we've already copied the previous state to our local
+            // variable, so this takes effect on the next event
+            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+        }
+        
+        // The event wasn't an ACTION_DOWN, dispatch it to our target if
+        // we have one.
+        final View target = mMotionTarget;
+        if (target == null) {
+            // We don't have a target, this means we're handling the
+            // event as a regular view.
+            ev.setLocation(xf, yf);
+            return super.dispatchTouchEvent(ev);
+        }
+
+        // if have a target, see if we're allowed to and want to intercept its
+        // events
+        if (!disallowIntercept && onInterceptTouchEvent(ev)) {
+            final float xc = scrolledXFloat - (float) target.mLeft;
+            final float yc = scrolledYFloat - (float) target.mTop;
+            ev.setAction(MotionEvent.ACTION_CANCEL);
+            ev.setLocation(xc, yc);
+            if (!target.dispatchTouchEvent(ev)) {
+                // target didn't handle ACTION_CANCEL. not much we can do
+                // but they should have.
+            }
+            // clear the target
+            mMotionTarget = null;
+            // Don't dispatch this event to our own view, because we already
+            // saw it when intercepting; we just want to give the following
+            // event to the normal onTouchEvent().
+            return true;
+        }
+
+        if (isUpOrCancel) {
+            mMotionTarget = null;
+        }
+
+        // finally offset the event to the target's coordinate system and
+        // dispatch the event.
+        final float xc = scrolledXFloat - (float) target.mLeft;
+        final float yc = scrolledYFloat - (float) target.mTop;
+        ev.setLocation(xc, yc);
+
+        return target.dispatchTouchEvent(ev);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        
+        if (disallowIntercept == ((mGroupFlags & FLAG_DISALLOW_INTERCEPT) != 0)) {
+            // We're already in this state, assume our ancestors are too
+            return;
+        }
+        
+        if (disallowIntercept) {
+            mGroupFlags |= FLAG_DISALLOW_INTERCEPT;
+        } else {
+            mGroupFlags &= ~FLAG_DISALLOW_INTERCEPT;
+        }
+        
+        // Pass it up to our parent
+        if (mParent != null) {
+            mParent.requestDisallowInterceptTouchEvent(disallowIntercept);
+        }
+    }
+
+    /**
+     * Implement this method to intercept all touch screen motion events.  This
+     * allows you to watch events as they are dispatched to your children, and
+     * take ownership of the current gesture at any point.
+     *
+     * <p>Using this function takes some care, as it has a fairly complicated
+     * interaction with {@link View#onTouchEvent(MotionEvent)
+     * View.onTouchEvent(MotionEvent)}, and using it requires implementing
+     * that method as well as this one in the correct way.  Events will be
+     * received in the following order:
+     *
+     * <ol>
+     * <li> You will receive the down event here.
+     * <li> The down event will be handled either by a child of this view
+     * group, or given to your own onTouchEvent() method to handle; this means
+     * you should implement onTouchEvent() to return true, so you will
+     * continue to see the rest of the gesture (instead of looking for
+     * a parent view to handle it).  Also, by returning true from
+     * onTouchEvent(), you will not receive any following
+     * events in onInterceptTouchEvent() and all touch processing must
+     * happen in onTouchEvent() like normal.
+     * <li> For as long as you return false from this function, each following
+     * event (up to and including the final up) will be delivered first here
+     * and then to the target's onTouchEvent().
+     * <li> If you return true from here, you will not receive any
+     * following events: the target view will receive the same event but
+     * with the action {@link MotionEvent#ACTION_CANCEL}, and all further
+     * events will be delivered to your onTouchEvent() method and no longer
+     * appear here.
+     * </ol>
+     *
+     * @param ev The motion event being dispatched down the hierarchy.
+     * @return Return true to steal motion events from the children and have
+     * them dispatched to this ViewGroup through onTouchEvent().
+     * The current target will receive an ACTION_CANCEL event, and no further
+     * messages will be delivered here.
+     */
+    public boolean onInterceptTouchEvent(MotionEvent ev) {
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * Looks for a view to give focus to respecting the setting specified by
+     * {@link #getDescendantFocusability()}.
+     *
+     * Uses {@link #onRequestFocusInDescendants(int, android.graphics.Rect)} to
+     * find focus within the children of this group when appropriate.
+     *
+     * @see #FOCUS_BEFORE_DESCENDANTS
+     * @see #FOCUS_AFTER_DESCENDANTS
+     * @see #FOCUS_BLOCK_DESCENDANTS
+     * @see #onRequestFocusInDescendants
+     */
+    @Override
+    public boolean requestFocus(int direction, Rect previouslyFocusedRect) {
+        if (DBG) {
+            System.out.println(this + " ViewGroup.requestFocus direction="
+                    + direction);
+        }
+        int descendantFocusability = getDescendantFocusability();
+
+        switch (descendantFocusability) {
+            case FOCUS_BLOCK_DESCENDANTS:
+                return super.requestFocus(direction, previouslyFocusedRect);
+            case FOCUS_BEFORE_DESCENDANTS: {
+                final boolean took = super.requestFocus(direction, previouslyFocusedRect);
+                return took ? took : onRequestFocusInDescendants(direction, previouslyFocusedRect);
+            }
+            case FOCUS_AFTER_DESCENDANTS: {
+                final boolean took = onRequestFocusInDescendants(direction, previouslyFocusedRect);
+                return took ? took : super.requestFocus(direction, previouslyFocusedRect);
+            }
+            default:
+                throw new IllegalStateException("descendant focusability must be "
+                        + "one of FOCUS_BEFORE_DESCENDANTS, FOCUS_AFTER_DESCENDANTS, FOCUS_BLOCK_DESCENDANTS "
+                        + "but is " + descendantFocusability);
+        }
+    }
+
+    /**
+     * Look for a descendant to call {@link View#requestFocus} on.
+     * Called by {@link ViewGroup#requestFocus(int, android.graphics.Rect)}
+     * when it wants to request focus within its children.  Override this to
+     * customize how your {@link ViewGroup} requests focus within its children.
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     * @param previouslyFocusedRect The rectangle (in this View's coordinate system)
+     *        to give a finer grained hint about where focus is coming from.  May be null
+     *        if there is no hint.
+     * @return Whether focus was taken.
+     */
+    @SuppressWarnings({"ConstantConditions"})
+    protected boolean onRequestFocusInDescendants(int direction,
+            Rect previouslyFocusedRect) {
+        int index;
+        int increment;
+        int end;
+        int count = mChildrenCount;
+        if ((direction & FOCUS_FORWARD) != 0) {
+            index = 0;
+            increment = 1;
+            end = count;
+        } else {
+            index = count - 1;
+            increment = -1;
+            end = -1;
+        }
+        final View[] children = mChildren;
+        for (int i = index; i != end; i += increment) {
+            View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                if (child.requestFocus(direction, previouslyFocusedRect)) {
+                    return true;
+                }
+            }
+        }
+        return false;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void dispatchAttachedToWindow(AttachInfo info, int visibility) {
+        super.dispatchAttachedToWindow(info, visibility);
+        visibility |= mViewFlags & VISIBILITY_MASK;
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchAttachedToWindow(info, visibility);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    void dispatchDetachedFromWindow() {
+        // If we still have a motion target, we are still in the process of
+        // dispatching motion events to a child; we need to get rid of that
+        // child to avoid dispatching events to it after the window is torn
+        // down. To make sure we keep the child in a consistent state, we
+        // first send it an ACTION_CANCEL motion event.
+        if (mMotionTarget != null) {
+            final long now = SystemClock.uptimeMillis();
+            final MotionEvent event = MotionEvent.obtain(now, now,
+                    MotionEvent.ACTION_CANCEL, 0.0f, 0.0f, 0);
+            mMotionTarget.dispatchTouchEvent(event);
+            event.recycle();
+            mMotionTarget = null;
+        }
+
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchDetachedFromWindow();
+        }
+        super.dispatchDetachedFromWindow();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void setPadding(int left, int top, int right, int bottom) {
+        super.setPadding(left, top, right, bottom);
+
+        if ((mPaddingLeft | mPaddingTop | mPaddingRight | mPaddingRight) != 0) {
+            mGroupFlags |= FLAG_PADDING_NOT_NULL;
+        } else {
+            mGroupFlags &= ~FLAG_PADDING_NOT_NULL;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void dispatchSaveInstanceState(SparseArray<Parcelable> container) {
+        super.dispatchSaveInstanceState(container);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchSaveInstanceState(container);
+        }
+    }
+
+    /**
+     * Perform dispatching of a {@link #saveHierarchyState freeze()} to only this view,
+     * not to its children.  For use when overriding
+     * {@link #dispatchSaveInstanceState dispatchFreeze()} to allow subclasses to freeze
+     * their own state but not the state of their children.
+     *
+     * @param container the container
+     */
+    protected void dispatchFreezeSelfOnly(SparseArray<Parcelable> container) {
+        super.dispatchSaveInstanceState(container);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void dispatchRestoreInstanceState(SparseArray<Parcelable> container) {
+        super.dispatchRestoreInstanceState(container);
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            children[i].dispatchRestoreInstanceState(container);
+        }
+    }
+
+    /**
+     * Perform dispatching of a {@link #restoreHierarchyState thaw()} to only this view,
+     * not to its children.  For use when overriding
+     * {@link #dispatchRestoreInstanceState dispatchThaw()} to allow subclasses to thaw
+     * their own state but not the state of their children.
+     *
+     * @param container the container
+     */
+    protected void dispatchThawSelfOnly(SparseArray<Parcelable> container) {
+        super.dispatchRestoreInstanceState(container);
+    }
+
+    /**
+     * Enables or disables the drawing cache for each child of this view group.
+     *
+     * @param enabled true to enable the cache, false to dispose of it
+     */
+    protected void setChildrenDrawingCacheEnabled(boolean enabled) {
+        if (enabled || (mPersistentDrawingCache & PERSISTENT_ALL_CACHES) != PERSISTENT_ALL_CACHES) {
+            final View[] children = mChildren;
+            final int count = mChildrenCount;
+            for (int i = 0; i < count; i++) {
+                children[i].setDrawingCacheEnabled(enabled);
+            }
+        }
+    }
+
+    @Override
+    protected void onAnimationStart() {
+        super.onAnimationStart();
+
+        // When this ViewGroup's animation starts, build the cache for the children
+        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                    child.setDrawingCacheEnabled(true);
+                    child.buildDrawingCache();
+                }
+            }
+
+            mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
+        }
+    }
+
+    @Override
+    protected void onAnimationEnd() {
+        super.onAnimationEnd();
+
+        // When this ViewGroup's animation ends, destroy the cache of the children
+        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
+            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
+
+            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
+                setChildrenDrawingCacheEnabled(false);
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        int flags = mGroupFlags;
+
+        if ((flags & FLAG_RUN_ANIMATION) != 0 && canAnimate()) {
+            final boolean cache = (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE) {
+                    final LayoutParams params = child.getLayoutParams();
+                    attachLayoutAnimationParameters(child, params, i, count);
+                    bindLayoutAnimation(child);
+                    if (cache) {
+                        child.setDrawingCacheEnabled(true);
+                        child.buildDrawingCache();
+                    }
+                }
+            }
+
+            final LayoutAnimationController controller = mLayoutAnimationController;
+            if (controller.willOverlap()) {
+                mGroupFlags |= FLAG_OPTIMIZE_INVALIDATE;
+            }
+
+            controller.start();
+
+            mGroupFlags &= ~FLAG_RUN_ANIMATION;
+            mGroupFlags &= ~FLAG_ANIMATION_DONE;
+
+            if (cache) {
+                mGroupFlags |= FLAG_CHILDREN_DRAWN_WITH_CACHE;
+            }
+
+            if (mAnimationListener != null) {
+                mAnimationListener.onAnimationStart(controller.getAnimation());
+            }
+        }
+
+        int saveCount = 0;
+        final boolean clipToPadding = (flags & CLIP_TO_PADDING_MASK) == CLIP_TO_PADDING_MASK;
+        if (clipToPadding) {
+            saveCount = canvas.save();
+            final int scrollX = mScrollX;
+            final int scrollY = mScrollY;
+            canvas.clipRect(scrollX + mPaddingLeft, scrollY + mPaddingTop,
+                    scrollX + mRight - mLeft - mPaddingRight,
+                    scrollY + mBottom - mTop - mPaddingBottom);
+
+        }
+
+        // We will draw our child's animation, let's reset the flag
+        mPrivateFlags &= ~DRAW_ANIMATION;
+        mGroupFlags &= ~FLAG_INVALIDATE_REQUIRED;
+
+        boolean more = false;
+        final long drawingTime = getDrawingTime();
+
+        if ((flags & FLAG_USE_CHILD_DRAWING_ORDER) == 0) {
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+                    more |= drawChild(canvas, child, drawingTime);
+                }
+            }
+        } else {
+            for (int i = 0; i < count; i++) {
+                final View child = children[getChildDrawingOrder(count, i)];
+                if ((child.mViewFlags & VISIBILITY_MASK) == VISIBLE || child.getAnimation() != null) {
+                    more |= drawChild(canvas, child, drawingTime);
+                }
+            }
+        }
+
+        // Draw any disappearing views that have animations
+        if (mDisappearingChildren != null) {
+            final ArrayList<View> disappearingChildren = mDisappearingChildren;
+            final int disappearingCount = disappearingChildren.size() - 1;
+            // Go backwards -- we may delete as animations finish
+            for (int i = disappearingCount; i >= 0; i--) {
+                final View child = disappearingChildren.get(i);
+                more |= drawChild(canvas, child, drawingTime);
+            }
+        }
+
+        if (clipToPadding) {
+            canvas.restoreToCount(saveCount);
+        }
+
+        // mGroupFlags might have been updated by drawChild()
+        flags = mGroupFlags;
+
+        if ((flags & FLAG_INVALIDATE_REQUIRED) == FLAG_INVALIDATE_REQUIRED) {
+            invalidate();
+        }
+
+        if ((flags & FLAG_ANIMATION_DONE) == 0 && (flags & FLAG_NOTIFY_ANIMATION_LISTENER) == 0 &&
+                mLayoutAnimationController.isDone() && !more) {
+            // We want to erase the drawing cache and notify the listener after the
+            // next frame is drawn because one extra invalidate() is caused by
+            // drawChild() after the animation is over
+            mGroupFlags |= FLAG_NOTIFY_ANIMATION_LISTENER;
+            final Runnable end = new Runnable() {
+               public void run() {
+                   notifyAnimationListener();
+               }
+            };
+            post(end);
+        }
+    }
+    
+    /**
+     * Returns the index of the child to draw for this iteration. Override this
+     * if you want to change the drawing order of children. By default, it
+     * returns i.
+     * <p>
+     * NOTE: In order for this method to be called, the
+     * {@link #FLAG_USE_CHILD_DRAWING_ORDER} must be set.
+     * 
+     * @param i The current iteration.
+     * @return The index of the child to draw this iteration.
+     */
+    protected int getChildDrawingOrder(int childCount, int i) {
+        return i;
+    }
+    
+    private void notifyAnimationListener() {
+        mGroupFlags &= ~FLAG_NOTIFY_ANIMATION_LISTENER;
+        mGroupFlags |= FLAG_ANIMATION_DONE;
+
+        if (mAnimationListener != null) {
+           final Runnable end = new Runnable() {
+               public void run() {
+                   mAnimationListener.onAnimationEnd(mLayoutAnimationController.getAnimation());
+               }
+           };
+           post(end);
+        }
+
+        if ((mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE) {
+            mGroupFlags &= ~FLAG_CHILDREN_DRAWN_WITH_CACHE;
+            if ((mPersistentDrawingCache & PERSISTENT_ANIMATION_CACHE) == 0) {
+                setChildrenDrawingCacheEnabled(false);
+            }
+        }
+
+        invalidate();
+    }
+
+    /**
+     * Draw one child of this View Group. This method is responsible for getting
+     * the canvas in the right state. This includes clipping, translating so
+     * that the child's scrolled origin is at 0, 0, and applying any animation
+     * transformations.
+     *
+     * @param canvas The canvas on which to draw the child
+     * @param child Who to draw
+     * @param drawingTime The time at which draw is occuring
+     * @return True if an invalidate() was issued
+     */
+    protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
+        boolean more = false;
+
+        final int cl = child.mLeft;
+        final int ct = child.mTop;
+        final int cr = child.mRight;
+        final int cb = child.mBottom;
+
+        final int flags = mGroupFlags;
+
+        if ((flags & FLAG_CLEAR_TRANSFORMATION) == FLAG_CLEAR_TRANSFORMATION) {
+            if (mChildTransformation != null) {
+                mChildTransformation.clear();
+            }
+            mGroupFlags &= ~FLAG_CLEAR_TRANSFORMATION;
+        }
+
+        Transformation transformToApply = null;
+        final Animation a = child.getAnimation();
+        boolean concatMatrix = false;
+
+        final int childWidth = cr - cl;
+        final int childHeight = cb - ct;
+
+        if (a != null) {
+            if (mInvalidateRegion == null) {
+                mInvalidateRegion = new RectF();
+            }
+            final RectF region = mInvalidateRegion;
+
+            final boolean initialized = a.isInitialized();
+            if (!initialized) {
+                a.initialize(childWidth, childHeight, getWidth(), getHeight());
+                a.initializeInvalidateRegion(0, 0, childWidth, childHeight);
+                child.onAnimationStart();
+            }
+
+            if (mChildTransformation == null) {
+                mChildTransformation = new Transformation();
+            }
+            more = a.getTransformation(drawingTime, mChildTransformation);
+            transformToApply = mChildTransformation;
+
+            concatMatrix = a.willChangeTransformationMatrix();
+
+            if (more) {
+                if (!a.willChangeBounds()) {
+                    if ((flags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) ==
+                            FLAG_OPTIMIZE_INVALIDATE) {
+                        mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+                    } else if ((flags & FLAG_INVALIDATE_REQUIRED) == 0) {
+                        // The child need to draw an animation, potentially offscreen, so
+                        // make sure we do not cancel invalidate requests
+                        mPrivateFlags |= DRAW_ANIMATION;
+                        invalidate(cl, ct, cr, cb);
+                    }
+                } else {
+                    a.getInvalidateRegion(0, 0, childWidth, childHeight, region, transformToApply);
+
+                    // The child need to draw an animation, potentially offscreen, so
+                    // make sure we do not cancel invalidate requests
+                    mPrivateFlags |= DRAW_ANIMATION;
+                    // Enlarge the invalidate region to account for rounding errors
+                    // in Animation#getInvalidateRegion(); Using 0.5f is unfortunately
+                    // not enough for some types of animations (e.g. scale down.)
+                    final int left = cl + (int) (region.left - 1.0f);
+                    final int top = ct + (int) (region.top - 1.0f);
+                    invalidate(left, top,
+                            left + (int) (region.width() + 1.0f),
+                            top + (int) (region.height() + 1.0f));
+                }
+            }
+        } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) ==
+                FLAG_SUPPORT_STATIC_TRANSFORMATIONS) {
+            if (mChildTransformation == null) {
+                mChildTransformation = new Transformation();
+            }
+            final boolean hasTransform = getChildStaticTransformation(child, mChildTransformation);
+            if (hasTransform) {
+                final int transformType = mChildTransformation.getTransformationType();
+                transformToApply = transformType != Transformation.TYPE_IDENTITY ?
+                        mChildTransformation : null;
+                concatMatrix = (transformType & Transformation.TYPE_MATRIX) != 0;
+            }
+        }
+
+        if (!concatMatrix && canvas.quickReject(cl, ct, cr, cb, Canvas.EdgeType.BW) &&
+                (child.mPrivateFlags & DRAW_ANIMATION) == 0) {
+            return more;
+        }
+
+        child.computeScroll();
+
+        final int sx = child.mScrollX;
+        final int sy = child.mScrollY;
+
+        Bitmap cache = null;
+        if ((flags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE ||
+                (flags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE) {
+            cache = child.getDrawingCache();
+        }
+
+        final boolean hasNoCache = cache == null;
+
+        final int restoreTo = canvas.save();
+        if (hasNoCache) {
+            canvas.translate(cl - sx, ct - sy);
+        } else {
+            canvas.translate(cl, ct);
+        }
+
+        float alpha = 1.0f;
+
+        if (transformToApply != null) {
+            if (concatMatrix) {
+                int transX = 0;
+                int transY = 0;
+                if (hasNoCache) {
+                    transX = -sx;
+                    transY = -sy;
+                }
+                // Undo the scroll translation, apply the transformation matrix,
+                // then redo the scroll translate to get the correct result.
+                canvas.translate(-transX, -transY);
+                canvas.concat(transformToApply.getMatrix());
+                canvas.translate(transX, transY);
+                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+            }
+
+            alpha = transformToApply.getAlpha();
+            if (alpha < 1.0f) {
+                mGroupFlags |= FLAG_CLEAR_TRANSFORMATION;
+            }
+
+            if (alpha < 1.0f && hasNoCache) {
+                final int multipliedAlpha = (int) (255 * alpha);
+                if (!child.onSetAlpha(multipliedAlpha)) {
+                    canvas.saveLayerAlpha(sx, sy, sx + cr - cl, sy + cb - ct, multipliedAlpha,
+                            Canvas.HAS_ALPHA_LAYER_SAVE_FLAG | Canvas.CLIP_TO_LAYER_SAVE_FLAG);
+                } else {
+                    child.mPrivateFlags |= ALPHA_SET;
+                }
+            }
+        } else if ((child.mPrivateFlags & ALPHA_SET) == ALPHA_SET) {
+            child.onSetAlpha(255);
+        }
+
+        if ((flags & FLAG_CLIP_CHILDREN) == FLAG_CLIP_CHILDREN) {
+            if (hasNoCache) {
+                canvas.clipRect(sx, sy, sx + childWidth, sy + childHeight);
+            } else {
+                canvas.clipRect(0, 0, childWidth, childHeight);
+            }
+        }
+
+        // Clear the flag as early as possible to allow draw() implementations
+        // to call invalidate() successfully when doing animations
+        child.mPrivateFlags |= DRAWN;
+
+        if (hasNoCache) {
+            // Fast path for layouts with no backgrounds
+            if ((child.mPrivateFlags & SKIP_DRAW) == SKIP_DRAW) {
+                if (ViewDebug.TRACE_HIERARCHY) {
+                    ViewDebug.trace(this, ViewDebug.HierarchyTraceType.DRAW);
+                }
+                child.dispatchDraw(canvas);
+            } else {
+                child.draw(canvas);
+            }
+        } else {
+            final Paint cachePaint = mCachePaint;
+            if (alpha < 1.0f) {
+                cachePaint.setAlpha((int) (alpha * 255));
+                mGroupFlags |= FLAG_ALPHA_LOWER_THAN_ONE;
+            } else if  ((flags & FLAG_ALPHA_LOWER_THAN_ONE) == FLAG_ALPHA_LOWER_THAN_ONE) {
+                cachePaint.setAlpha(255);
+                mGroupFlags &= ~FLAG_ALPHA_LOWER_THAN_ONE;
+            }
+            if (ViewRoot.PROFILE_DRAWING) {
+                EventLog.writeEvent(60003, hashCode());
+            }
+            canvas.drawBitmap(cache, 0.0f, 0.0f, cachePaint);
+        }
+
+        canvas.restoreToCount(restoreTo);
+
+        if (a != null && !more) {
+            child.onSetAlpha(255);
+            finishAnimatingView(child, a);
+        }
+
+        return more;
+    }
+
+    /**
+     * By default, children are clipped to their bounds before drawing. This
+     * allows view groups to override this behavior for animations, etc.
+     *
+     * @param clipChildren true to clip children to their bounds,
+     *        false otherwise
+     * @attr ref android.R.styleable#ViewGroup_clipChildren
+     */
+    public void setClipChildren(boolean clipChildren) {
+        setBooleanFlag(FLAG_CLIP_CHILDREN, clipChildren);
+    }
+
+    /**
+     * By default, children are clipped to the padding of the ViewGroup. This
+     * allows view groups to override this behavior
+     *
+     * @param clipToPadding true to clip children to the padding of the
+     *        group, false otherwise
+     * @attr ref android.R.styleable#ViewGroup_clipToPadding
+     */
+    public void setClipToPadding(boolean clipToPadding) {
+        setBooleanFlag(FLAG_CLIP_TO_PADDING, clipToPadding);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public void dispatchSetSelected(boolean selected) {
+        final View[] children = mChildren;
+        final int count = mChildrenCount;
+        for (int i = 0; i < count; i++) {
+            children[i].setSelected(selected);
+        }
+    }
+    
+    @Override
+    protected void dispatchSetPressed(boolean pressed) {
+        final View[] children = mChildren;
+        final int count = mChildrenCount;
+        for (int i = 0; i < count; i++) {
+            children[i].setPressed(pressed);
+        }
+    }
+
+    /**
+     * When this property is set to true, this ViewGroup supports static transformations on
+     * children; this causes
+     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} to be
+     * invoked when a child is drawn.
+     *
+     * Any subclass overriding
+     * {@link #getChildStaticTransformation(View, android.view.animation.Transformation)} should
+     * set this property to true.
+     *
+     * @param enabled True to enable static transformations on children, false otherwise.
+     *
+     * @see #FLAG_SUPPORT_STATIC_TRANSFORMATIONS
+     */
+    protected void setStaticTransformationsEnabled(boolean enabled) {
+        setBooleanFlag(FLAG_SUPPORT_STATIC_TRANSFORMATIONS, enabled);
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @see #setStaticTransformationsEnabled(boolean) 
+     */
+    protected boolean getChildStaticTransformation(View child, Transformation t) {
+        return false;
+    }
+
+    /**
+     * {@hide}
+     */
+    @Override
+    protected View findViewTraversal(int id) {
+        if (id == mID) {
+            return this;
+        }
+
+        final View[] where = mChildren;
+        final int len = mChildrenCount;
+
+        for (int i = 0; i < len; i++) {
+            View v = where[i];
+
+            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+                v = v.findViewById(id);
+
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * {@hide}
+     */
+    @Override
+    protected View findViewWithTagTraversal(Object tag) {
+        if (tag != null && tag.equals(mTag)) {
+            return this;
+        }
+
+        final View[] where = mChildren;
+        final int len = mChildrenCount;
+
+        for (int i = 0; i < len; i++) {
+            View v = where[i];
+
+            if ((v.mPrivateFlags & IS_ROOT_NAMESPACE) == 0) {
+                v = v.findViewWithTag(tag);
+
+                if (v != null) {
+                    return v;
+                }
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Adds a child view. If no layout parameters are already set on the child, the
+     * default parameters for this ViewGroup are set on the child.
+     *
+     * @param child the child view to add
+     *
+     * @see #generateDefaultLayoutParams()
+     */
+    public void addView(View child) {
+        addView(child, -1);
+    }
+
+    /**
+     * Adds a child view. If no layout parameters are already set on the child, the
+     * default parameters for this ViewGroup are set on the child.
+     *
+     * @param child the child view to add
+     * @param index the position at which to add the child
+     *
+     * @see #generateDefaultLayoutParams()
+     */
+    public void addView(View child, int index) {
+        LayoutParams params = child.getLayoutParams();
+        if (params == null) {
+            params = generateDefaultLayoutParams();
+            if (params == null) {
+                throw new IllegalArgumentException("generateDefaultLayoutParams() cannot return null");
+            }
+        }
+        addView(child, index, params);
+    }
+
+    /**
+     * Adds a child view with this ViewGroup's default layout parameters and the
+     * specified width and height.
+     *
+     * @param child the child view to add
+     */
+    public void addView(View child, int width, int height) {
+        final LayoutParams params = generateDefaultLayoutParams();
+        params.width = width;
+        params.height = height;
+        addView(child, -1, params);
+    }
+
+    /**
+     * Adds a child view with the specified layout parameters.
+     *
+     * @param child the child view to add
+     * @param params the layout parameters to set on the child
+     */
+    public void addView(View child, LayoutParams params) {
+        addView(child, -1, params);
+    }
+
+    /**
+     * Adds a child view with the specified layout parameters.
+     *
+     * @param child the child view to add
+     * @param index the position at which to add the child
+     * @param params the layout parameters to set on the child
+     */
+    public void addView(View child, int index, LayoutParams params) {
+        if (DBG) {
+            System.out.println(this + " addView");
+        }
+
+        // addViewInner() will call child.requestLayout() when setting the new LayoutParams
+        // therefore, we call requestLayout() on ourselves before, so that the child's request
+        // will be blocked at our level
+        requestLayout();
+        invalidate();
+        addViewInner(child, index, params, false);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+        if (!checkLayoutParams(params)) {
+            throw new IllegalArgumentException("Invalid LayoutParams supplied to " + this);
+        }
+        if (view.mParent != this) {
+            throw new IllegalArgumentException("Given view not a child of " + this);
+        }
+        view.setLayoutParams(params);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    protected boolean checkLayoutParams(ViewGroup.LayoutParams p) {
+        return  p != null;
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the hierarchy
+     * within this view changed. The hierarchy changes whenever a child is added
+     * to or removed from this view.
+     */
+    public interface OnHierarchyChangeListener {
+        /**
+         * Called when a new child is added to a parent view.
+         *
+         * @param parent the view in which a child was added
+         * @param child the new child view added in the hierarchy
+         */
+        void onChildViewAdded(View parent, View child);
+
+        /**
+         * Called when a child is removed from a parent view.
+         *
+         * @param parent the view from which the child was removed
+         * @param child the child removed from the hierarchy
+         */
+        void onChildViewRemoved(View parent, View child);
+    }
+
+    /**
+     * Register a callback to be invoked when a child is added to or removed
+     * from this view.
+     *
+     * @param listener the callback to invoke on hierarchy change
+     */
+    public void setOnHierarchyChangeListener(OnHierarchyChangeListener listener) {
+        mOnHierarchyChangeListener = listener;
+    }
+
+    /**
+     * Adds a view during layout. This is useful if in your onLayout() method,
+     * you need to add more views (as does the list view for example).
+     *
+     * If index is negative, it means put it at the end of the list.
+     *
+     * @param child the view to add to the group
+     * @param index the index at which the child must be added
+     * @param params the layout parameters to associate with the child
+     * @return true if the child was added, false otherwise
+     */
+    protected boolean addViewInLayout(View child, int index, LayoutParams params) {
+        return addViewInLayout(child, index, params, false);
+    }
+
+    /**
+     * Adds a view during layout. This is useful if in your onLayout() method,
+     * you need to add more views (as does the list view for example).
+     *
+     * If index is negative, it means put it at the end of the list.
+     *
+     * @param child the view to add to the group
+     * @param index the index at which the child must be added
+     * @param params the layout parameters to associate with the child
+     * @param preventRequestLayout if true, calling this method will not trigger a
+     *        layout request on child
+     * @return true if the child was added, false otherwise
+     */
+    protected boolean addViewInLayout(View child, int index, LayoutParams params,
+            boolean preventRequestLayout) {
+        child.mParent = null;
+        addViewInner(child, index, params, preventRequestLayout);
+        child.mPrivateFlags |= DRAWN;
+        return true;
+    }
+
+    /**
+     * Prevents the specified child to be laid out during the next layout pass.
+     *
+     * @param child the child on which to perform the cleanup
+     */
+    protected void cleanupLayoutState(View child) {
+        child.mPrivateFlags &= ~View.FORCE_LAYOUT;
+    }
+
+    private void addViewInner(View child, int index, LayoutParams params,
+            boolean preventRequestLayout) {
+
+        if (child.getParent() != null) {
+            throw new IllegalStateException("The specified child already has a parent. " +
+                    "You must call removeView() on the child's parent first.");
+        }
+
+        if (!checkLayoutParams(params)) {
+            params = generateLayoutParams(params);
+        }
+
+        if (preventRequestLayout) {
+            child.mLayoutParams = params;
+        } else {
+            child.setLayoutParams(params);
+        }
+
+        if (index < 0) {
+            index = mChildrenCount;
+        }
+
+        addInArray(child, index);
+
+        // tell our children
+        if (preventRequestLayout) {
+            child.assignParent(this);
+        } else {
+            child.mParent = this;
+        }
+
+        if (child.hasFocus()) {
+            requestChildFocus(child, child.findFocus());
+        }
+        
+        AttachInfo ai = mAttachInfo;
+        if (ai != null) {
+            boolean lastKeepOn = ai.mKeepScreenOn; 
+            ai.mKeepScreenOn = false;
+            child.dispatchAttachedToWindow(mAttachInfo, (mViewFlags&VISIBILITY_MASK));
+            if (ai.mKeepScreenOn) {
+                needGlobalAttributesUpdate(true);
+            }
+            ai.mKeepScreenOn = lastKeepOn;
+        }
+
+        if (mOnHierarchyChangeListener != null) {
+            mOnHierarchyChangeListener.onChildViewAdded(this, child);
+        }
+
+        if ((child.mViewFlags & DUPLICATE_PARENT_STATE) == DUPLICATE_PARENT_STATE) {
+            mGroupFlags |= FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE;
+        }
+    }
+
+    private void addInArray(View child, int index) {
+        View[] children = mChildren;
+        final int count = mChildrenCount;
+        final int size = children.length;
+        if (index == count) {
+            if (size == count) {
+                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
+                System.arraycopy(children, 0, mChildren, 0, size);
+                children = mChildren;
+            }
+            children[mChildrenCount++] = child;
+        } else if (index < count) {
+            if (size == count) {
+                mChildren = new View[size + ARRAY_CAPACITY_INCREMENT];
+                System.arraycopy(children, 0, mChildren, 0, index);
+                System.arraycopy(children, index, mChildren, index + 1, count - index);
+                children = mChildren;
+            } else {
+                System.arraycopy(children, index, children, index + 1, count - index);
+            }
+            children[index] = child;
+            mChildrenCount++;
+        } else {
+            throw new IndexOutOfBoundsException("index=" + index + " count=" + count);
+        }
+    }
+
+    // This method also sets the child's mParent to null
+    private void removeFromArray(int index) {
+        final View[] children = mChildren;
+        children[index].mParent = null;
+        final int count = mChildrenCount;
+        if (index == count - 1) {
+            children[--mChildrenCount] = null;
+        } else if (index >= 0 && index < count) {
+            System.arraycopy(children, index + 1, children, index, count - index - 1);
+            children[--mChildrenCount] = null;
+        } else {
+            throw new IndexOutOfBoundsException();
+        }
+    }
+
+    // This method also sets the children's mParent to null
+    private void removeFromArray(int start, int count) {
+        final View[] children = mChildren;
+        final int childrenCount = mChildrenCount;
+
+        start = Math.max(0, start);
+        final int end = Math.min(childrenCount, start + count);
+
+        if (start == end) {
+            return;
+        }
+
+        if (end == childrenCount) {
+            for (int i = start; i < end; i++) {
+                children[i].mParent = null;
+                children[i] = null;
+            }
+        } else {
+            for (int i = start; i < end; i++) {
+                children[i].mParent = null;
+            }
+
+            // Since we're looping above, we might as well do the copy, but is arraycopy()
+            // faster than the extra 2 bounds checks we would do in the loop?
+            System.arraycopy(children, end, children, start, childrenCount - end);
+
+            for (int i = childrenCount - (end - start); i < childrenCount; i++) {
+                children[i] = null;
+            }
+        }
+
+        mChildrenCount -= (end - start);
+    }
+
+    private void bindLayoutAnimation(View child) {
+        Animation a = mLayoutAnimationController.getAnimationForView(child);
+        child.setAnimation(a);
+    }
+
+    /**
+     * Subclasses should override this method to set layout animation
+     * parameters on the supplied child.
+     *
+     * @param child the child to associate with animation parameters
+     * @param params the child's layout parameters which hold the animation
+     *        parameters
+     * @param index the index of the child in the view group
+     * @param count the number of children in the view group
+     */
+    protected void attachLayoutAnimationParameters(View child,
+            LayoutParams params, int index, int count) {
+        LayoutAnimationController.AnimationParameters animationParams =
+                    params.layoutAnimationParameters;
+        if (animationParams == null) {
+            animationParams = new LayoutAnimationController.AnimationParameters();
+            params.layoutAnimationParameters = animationParams;
+        }
+
+        animationParams.count = count;
+        animationParams.index = index;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void removeView(View view) {
+        removeViewInternal(view);
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Removes a view during layout. This is useful if in your onLayout() method,
+     * you need to remove more views.
+     *
+     * @param view the view to remove from the group
+     */
+    public void removeViewInLayout(View view) {
+        removeViewInternal(view);
+    }
+
+    /**
+     * Removes a range of views during layout. This is useful if in your onLayout() method,
+     * you need to remove more views.
+     *
+     * @param start the index of the first view to remove from the group
+     * @param count the number of views to remove from the group
+     */
+    public void removeViewsInLayout(int start, int count) {
+        removeViewsInternal(start, count);
+    }
+
+    /**
+     * Removes the view at the specified position in the group.
+     *
+     * @param index the position in the group of the view to remove
+     */
+    public void removeViewAt(int index) {
+        removeViewInternal(index, getChildAt(index));
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Removes the specified range of views from the group.
+     *
+     * @param start the first position in the group of the range of views to remove
+     * @param count the number of views to remove
+     */
+    public void removeViews(int start, int count) {
+        removeViewsInternal(start, count);
+        requestLayout();
+        invalidate();
+    }
+
+    private void removeViewInternal(View view) {
+        final int index = indexOfChild(view);
+        if (index >= 0) {
+            removeViewInternal(index, view);
+        }
+    }
+
+    private void removeViewInternal(int index, View view) {
+        boolean clearChildFocus = false;
+        if (view == mFocused) {
+            view.clearFocusForRemoval();
+            clearChildFocus = true;
+        }
+
+        if (view.getAnimation() != null) {
+            addDisappearingView(view);
+        } else if (view.mAttachInfo != null) {
+           view.dispatchDetachedFromWindow();
+        }
+
+        if (mOnHierarchyChangeListener != null) {
+            mOnHierarchyChangeListener.onChildViewRemoved(this, view);
+        }
+
+        needGlobalAttributesUpdate(false);
+        
+        removeFromArray(index);
+
+        if (clearChildFocus) {
+            clearChildFocus(view);
+        }
+    }
+
+    private void removeViewsInternal(int start, int count) {
+        final OnHierarchyChangeListener onHierarchyChangeListener = mOnHierarchyChangeListener;
+        final boolean notifyListener = onHierarchyChangeListener != null;
+        final View focused = mFocused;
+        final boolean detach = mAttachInfo != null;
+        View clearChildFocus = null;
+
+        final View[] children = mChildren;
+        final int end = start + count;
+
+        for (int i = start; i < end; i++) {
+            final View view = children[i];
+
+            if (view == focused) {
+                view.clearFocusForRemoval();
+                clearChildFocus = view;
+            }
+
+            if (view.getAnimation() != null) {
+                addDisappearingView(view);
+            } else if (detach) {
+               view.dispatchDetachedFromWindow();
+            }
+
+            needGlobalAttributesUpdate(false);
+            
+            if (notifyListener) {
+                onHierarchyChangeListener.onChildViewRemoved(this, view);
+            }
+        }
+
+        removeFromArray(start, count);
+
+        if (clearChildFocus != null) {
+            clearChildFocus(clearChildFocus);
+        }
+    }
+
+    /**
+     * Call this method to remove all child views from the
+     * ViewGroup.
+     */
+    public void removeAllViews() {
+        removeAllViewsInLayout();
+        requestLayout();
+        invalidate();
+    }
+
+    /**
+     * Called by a ViewGroup subclass to remove child views from itself,
+     * when it must first know its size on screen before it can calculate how many
+     * child views it will render. An example is a Gallery or a ListView, which
+     * may "have" 50 children, but actually only render the number of children
+     * that can currently fit inside the object on screen. Do not call
+     * this method unless you are extending ViewGroup and understand the
+     * view measuring and layout pipeline.
+     */
+    public void removeAllViewsInLayout() {
+        final int count = mChildrenCount;
+        if (count <= 0) {
+            return;
+        }
+
+        final View[] children = mChildren;
+        mChildrenCount = 0;
+
+        final OnHierarchyChangeListener listener = mOnHierarchyChangeListener;
+        final boolean notify = listener != null;
+        final View focused = mFocused;
+        final boolean detach = mAttachInfo != null;
+        View clearChildFocus = null;
+
+        needGlobalAttributesUpdate(false);
+        
+        for (int i = count - 1; i >= 0; i--) {
+            final View view = children[i];
+
+            if (view == focused) {
+                view.clearFocusForRemoval();
+                clearChildFocus = view;
+            }
+
+            if (view.getAnimation() != null) {
+                addDisappearingView(view);
+            } else if (detach) {
+               view.dispatchDetachedFromWindow();
+            }
+
+            if (notify) {
+                listener.onChildViewRemoved(this, view);
+            }
+
+            view.mParent = null;
+            children[i] = null;
+        }
+
+        if (clearChildFocus != null) {
+            clearChildFocus(clearChildFocus);
+        }
+    }
+
+    /**
+     * Finishes the removal of a detached view. This method will dispatch the detached from
+     * window event and notify the hierarchy change listener.
+     *
+     * @param child the child to be definitely removed from the view hierarchy
+     * @param animate if true and the view has an animation, the view is placed in the
+     *                disappearing views list, otherwise, it is detached from the window
+     *
+     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
+     * @see #detachAllViewsFromParent()
+     * @see #detachViewFromParent(View)
+     * @see #detachViewFromParent(int)
+     */
+    protected void removeDetachedView(View child, boolean animate) {
+        if (child == mFocused) {
+            child.clearFocus();
+        }
+        
+        if (animate && child.getAnimation() != null) {
+            addDisappearingView(child);
+        } else if (child.mAttachInfo != null) {
+            child.dispatchDetachedFromWindow();
+        }
+
+        if (mOnHierarchyChangeListener != null) {
+            mOnHierarchyChangeListener.onChildViewRemoved(this, child);
+        }
+    }
+
+    /**
+     * Attaches a view to this view group. Attaching a view assigns this group as the parent,
+     * sets the layout parameters and puts the view in the list of children so it can be retrieved
+     * by calling {@link #getChildAt(int)}.
+     *
+     * This method should be called only for view which were detached from their parent.
+     *
+     * @param child the child to attach
+     * @param index the index at which the child should be attached
+     * @param params the layout parameters of the child
+     *
+     * @see #removeDetachedView(View, boolean)
+     * @see #detachAllViewsFromParent()
+     * @see #detachViewFromParent(View)
+     * @see #detachViewFromParent(int)
+     */
+    protected void attachViewToParent(View child, int index, LayoutParams params) {
+        child.mLayoutParams = params;
+
+        if (index < 0) {
+            index = mChildrenCount;
+        }
+
+        addInArray(child, index);
+
+        child.mParent = this;
+        child.mPrivateFlags |= DRAWN;
+
+        if (child.hasFocus()) {
+            requestChildFocus(child, child.findFocus());
+        }
+    }
+
+    /**
+     * Detaches a view from its parent. Detaching a view should be temporary and followed
+     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
+     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+     *
+     * @param child the child to detach
+     *
+     * @see #detachViewFromParent(int)
+     * @see #detachViewsFromParent(int, int)
+     * @see #detachAllViewsFromParent()
+     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
+     * @see #removeDetachedView(View, boolean)
+     */
+    protected void detachViewFromParent(View child) {
+        removeFromArray(indexOfChild(child));
+    }
+
+    /**
+     * Detaches a view from its parent. Detaching a view should be temporary and followed
+     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
+     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+     *
+     * @param index the index of the child to detach
+     *
+     * @see #detachViewFromParent(View)
+     * @see #detachAllViewsFromParent()
+     * @see #detachViewsFromParent(int, int)
+     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
+     * @see #removeDetachedView(View, boolean)
+     */
+    protected void detachViewFromParent(int index) {
+        removeFromArray(index);
+    }
+
+    /**
+     * Detaches a range of view from their parent. Detaching a view should be temporary and followed
+     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached, its
+     * parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+     *
+     * @param start the first index of the childrend range to detach
+     * @param count the number of children to detach
+     *
+     * @see #detachViewFromParent(View)
+     * @see #detachViewFromParent(int)
+     * @see #detachAllViewsFromParent()
+     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
+     * @see #removeDetachedView(View, boolean)
+     */
+    protected void detachViewsFromParent(int start, int count) {
+        removeFromArray(start, count);
+    }
+
+    /**
+     * Detaches all views from the parent. Detaching a view should be temporary and followed
+     * either by a call to {@link #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)}
+     * or a call to {@link #removeDetachedView(View, boolean)}. When a view is detached,
+     * its parent is null and cannot be retrieved by a call to {@link #getChildAt(int)}.
+     *
+     * @see #detachViewFromParent(View)
+     * @see #detachViewFromParent(int)
+     * @see #detachViewsFromParent(int, int)
+     * @see #attachViewToParent(View, int, android.view.ViewGroup.LayoutParams)
+     * @see #removeDetachedView(View, boolean)
+     */
+    protected void detachAllViewsFromParent() {
+        final int count = mChildrenCount;
+        if (count <= 0) {
+            return;
+        }
+
+        final View[] children = mChildren;
+        mChildrenCount = 0;
+
+        for (int i = count - 1; i >= 0; i--) {
+            children[i].mParent = null;
+            children[i] = null;
+        }
+    }
+
+    /**
+     * Don't call or override this method. It is used for the implementation of
+     * the view hierarchy.
+     */
+    public final void invalidateChild(View child, final Rect dirty) {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD);
+        }
+
+        ViewParent parent = this;
+
+        final AttachInfo attachInfo = mAttachInfo;
+        if (attachInfo != null) {
+            final int[] location = attachInfo.mInvalidateChildLocation;
+            location[CHILD_LEFT_INDEX] = child.mLeft;
+            location[CHILD_TOP_INDEX] = child.mTop;
+
+            // If the child is drawing an animation, we want to copy this flag onto
+            // ourselves and the parent to make sure the invalidate request goes
+            // through
+            final boolean drawAnimation = (child.mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION;
+    
+            do {
+                if (drawAnimation && parent instanceof View) {
+                    ((View) parent).mPrivateFlags |= DRAW_ANIMATION;
+                }
+                parent = parent.invalidateChildInParent(location, dirty);
+            } while (parent != null);
+        }
+    }
+
+    /**
+     * Don't call or override this method. It is used for the implementation of
+     * the view hierarchy.
+     *
+     * This implementation returns null if this ViewGroup does not have a parent,
+     * if this ViewGroup is already fully invalidated or if the dirty rectangle
+     * does not intersect with this ViewGroup's bounds.
+     */
+    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
+        if (ViewDebug.TRACE_HIERARCHY) {
+            ViewDebug.trace(this, ViewDebug.HierarchyTraceType.INVALIDATE_CHILD_IN_PARENT);
+        }
+
+        if ((mPrivateFlags & DRAWN) == DRAWN) {
+            if ((mGroupFlags & (FLAG_OPTIMIZE_INVALIDATE | FLAG_ANIMATION_DONE)) !=
+                        FLAG_OPTIMIZE_INVALIDATE) {
+                dirty.offset(location[CHILD_LEFT_INDEX] - mScrollX,
+                        location[CHILD_TOP_INDEX] - mScrollY);
+
+                final int left = mLeft;
+                final int top = mTop;
+
+                if (dirty.intersect(0, 0, mRight - left, mBottom - top) ||
+                        (mPrivateFlags & DRAW_ANIMATION) == DRAW_ANIMATION) {
+                    mPrivateFlags &= ~DRAWING_CACHE_VALID;
+
+                    location[CHILD_LEFT_INDEX] = left;
+                    location[CHILD_TOP_INDEX] = top;
+
+                    return mParent;
+                }
+            } else {
+                mPrivateFlags &= ~DRAWN & ~DRAWING_CACHE_VALID;
+
+                location[CHILD_LEFT_INDEX] = mLeft;
+                location[CHILD_TOP_INDEX] = mTop;
+
+                dirty.set(0, 0, mRight - location[CHILD_LEFT_INDEX],
+                        mBottom - location[CHILD_TOP_INDEX]);
+
+                return mParent;
+            }
+        }
+
+        return null;
+    }
+
+    /**
+     * Offset a rectangle that is in a descendant's coordinate
+     * space into our coordinate space.
+     * @param descendant A descendant of this view
+     * @param rect A rectangle defined in descendant's coordinate space.
+     */
+    public final void offsetDescendantRectToMyCoords(View descendant, Rect rect) {
+        offsetRectBetweenParentAndChild(descendant, rect, true, false);
+    }
+
+    /**
+     * Offset a rectangle that is in our coordinate space into an ancestor's
+     * coordinate space.
+     * @param descendant A descendant of this view
+     * @param rect A rectangle defined in descendant's coordinate space.
+     */
+    public final void offsetRectIntoDescendantCoords(View descendant, Rect rect) {
+        offsetRectBetweenParentAndChild(descendant, rect, false, false);
+    }
+
+    /**
+     * Helper method that offsets a rect either from parent to descendant or
+     * descendant to parent.
+     */
+    void offsetRectBetweenParentAndChild(View descendant, Rect rect,
+            boolean offsetFromChildToParent, boolean clipToBounds) {
+
+        // already in the same coord system :)
+        if (descendant == this) {
+            return;
+        }
+
+        ViewParent theParent = descendant.mParent;
+
+        // search and offset up to the parent
+        while ((theParent != null)
+                && (theParent instanceof View)
+                && (theParent != this)) {
+
+            if (offsetFromChildToParent) {
+                rect.offset(descendant.mLeft - descendant.mScrollX,
+                        descendant.mTop - descendant.mScrollY);
+                if (clipToBounds) {
+                    View p = (View) theParent;
+                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
+                }
+            } else {
+                if (clipToBounds) {
+                    View p = (View) theParent;
+                    rect.intersect(0, 0, p.mRight - p.mLeft, p.mBottom - p.mTop);
+                }
+                rect.offset(descendant.mScrollX - descendant.mLeft,
+                        descendant.mScrollY - descendant.mTop);
+            }
+
+            descendant = (View) theParent;
+            theParent = descendant.mParent;
+        }
+
+        // now that we are up to this view, need to offset one more time
+        // to get into our coordinate space
+        if (theParent == this) {
+            if (offsetFromChildToParent) {
+                rect.offset(descendant.mLeft - descendant.mScrollX,
+                        descendant.mTop - descendant.mScrollY);
+            } else {
+                rect.offset(descendant.mScrollX - descendant.mLeft,
+                        descendant.mScrollY - descendant.mTop);
+            }
+        } else {
+            throw new IllegalArgumentException("parameter must be a descendant of this view");
+        }
+    }
+
+    /**
+     * Offset the vertical location of all children of this view by the specified number of pixels.
+     *
+     * @param offset the number of pixels to offset
+     *
+     * @hide
+     */
+    public void offsetChildrenTopAndBottom(int offset) {
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+
+        for (int i = 0; i < count; i++) {
+            final View v = children[i];
+            v.mTop += offset;
+            v.mBottom += offset;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+        int dx = child.mLeft - mScrollX;
+        int dy = child.mTop - mScrollY;
+        if (offset != null) {
+            offset.x += dx;
+            offset.y += dy;
+        }
+        r.offset(dx, dy);
+        return r.intersect(0, 0, mRight - mLeft, mBottom - mTop) &&
+               (mParent == null || mParent.getChildVisibleRect(this, r, offset));
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected abstract void onLayout(boolean changed,
+            int l, int t, int r, int b);
+
+    /**
+     * Indicates whether the view group has the ability to animate its children
+     * after the first layout.
+     *
+     * @return true if the children can be animated, false otherwise
+     */
+    protected boolean canAnimate() {
+        return mLayoutAnimationController != null;
+    }
+
+    /**
+     * Runs the layout animation. Calling this method triggers a relayout of
+     * this view group.
+     */
+    public void startLayoutAnimation() {
+        if (mLayoutAnimationController != null) {
+            mGroupFlags |= FLAG_RUN_ANIMATION;
+            requestLayout();
+        }
+    }
+
+    /**
+     * Schedules the layout animation to be played after the next layout pass
+     * of this view group. This can be used to restart the layout animation
+     * when the content of the view group changes or when the activity is
+     * paused and resumed.
+     */
+    public void scheduleLayoutAnimation() {
+        mGroupFlags |= FLAG_RUN_ANIMATION;
+    }
+
+    /**
+     * Sets the layout animation controller used to animate the group's
+     * children after the first layout.
+     *
+     * @param controller the animation controller
+     */
+    public void setLayoutAnimation(LayoutAnimationController controller) {
+        mLayoutAnimationController = controller;
+        if (mLayoutAnimationController != null) {
+            mGroupFlags |= FLAG_RUN_ANIMATION;
+        }
+    }
+
+    /**
+     * Returns the layout animation controller used to animate the group's
+     * children.
+     *
+     * @return the current animation controller
+     */
+    public LayoutAnimationController getLayoutAnimation() {
+        return mLayoutAnimationController;
+    }
+
+    /**
+     * Indicates whether the children's drawing cache is used during a layout
+     * animation. By default, the drawing cache is enabled but this will prevent
+     * nested layout animations from working. To nest animations, you must disable
+     * the cache.
+     *
+     * @return true if the animation cache is enabled, false otherwise
+     *
+     * @see #setAnimationCacheEnabled(boolean)
+     * @see View#setDrawingCacheEnabled(boolean)
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isAnimationCacheEnabled() {
+        return (mGroupFlags & FLAG_ANIMATION_CACHE) == FLAG_ANIMATION_CACHE;
+    }
+
+    /**
+     * Enables or disables the children's drawing cache during a layout animation.
+     * By default, the drawing cache is enabled but this will prevent nested
+     * layout animations from working. To nest animations, you must disable the
+     * cache.
+     *
+     * @param enabled true to enable the animation cache, false otherwise
+     *
+     * @see #isAnimationCacheEnabled()
+     * @see View#setDrawingCacheEnabled(boolean)
+     */
+    public void setAnimationCacheEnabled(boolean enabled) {
+        setBooleanFlag(FLAG_ANIMATION_CACHE, enabled);
+    }
+
+    /**
+     * Indicates whether this ViewGroup will always try to draw its children using their
+     * drawing cache. By default this property is enabled.
+     *
+     * @return true if the animation cache is enabled, false otherwise
+     *
+     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
+     * @see #setChildrenDrawnWithCacheEnabled(boolean)
+     * @see View#setDrawingCacheEnabled(boolean)
+     */
+    @ViewDebug.ExportedProperty
+    public boolean isAlwaysDrawnWithCacheEnabled() {
+        return (mGroupFlags & FLAG_ALWAYS_DRAWN_WITH_CACHE) == FLAG_ALWAYS_DRAWN_WITH_CACHE;
+    }
+
+    /**
+     * Indicates whether this ViewGroup will always try to draw its children using their
+     * drawing cache. This property can be set to true when the cache rendering is
+     * slightly different from the children's normal rendering. Renderings can be different,
+     * for instance, when the cache's quality is set to low.
+     *
+     * When this property is disabled, the ViewGroup will use the drawing cache of its
+     * children only when asked to. It's usually the task of subclasses to tell ViewGroup
+     * when to start using the drawing cache and when to stop using it.
+     *
+     * @param always true to always draw with the drawing cache, false otherwise
+     *
+     * @see #isAlwaysDrawnWithCacheEnabled()
+     * @see #setChildrenDrawnWithCacheEnabled(boolean)
+     * @see View#setDrawingCacheEnabled(boolean)
+     * @see View#setDrawingCacheQuality(int)
+     */
+    public void setAlwaysDrawnWithCacheEnabled(boolean always) {
+        setBooleanFlag(FLAG_ALWAYS_DRAWN_WITH_CACHE, always);
+    }
+
+    /**
+     * Indicates whether the ViewGroup is currently drawing its children using
+     * their drawing cache.
+     *
+     * @return true if children should be drawn with their cache, false otherwise
+     *
+     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
+     * @see #setChildrenDrawnWithCacheEnabled(boolean)
+     */
+    @ViewDebug.ExportedProperty
+    protected boolean isChildrenDrawnWithCacheEnabled() {
+        return (mGroupFlags & FLAG_CHILDREN_DRAWN_WITH_CACHE) == FLAG_CHILDREN_DRAWN_WITH_CACHE;
+    }
+
+    /**
+     * Tells the ViewGroup to draw its children using their drawing cache. This property
+     * is ignored when {@link #isAlwaysDrawnWithCacheEnabled()} is true. A child's drawing cache
+     * will be used only if it has been enabled.
+     *
+     * Subclasses should call this method to start and stop using the drawing cache when
+     * they perform performance sensitive operations, like scrolling or animating.
+     *
+     * @param enabled true if children should be drawn with their cache, false otherwise
+     *
+     * @see #setAlwaysDrawnWithCacheEnabled(boolean)
+     * @see #isChildrenDrawnWithCacheEnabled()
+     */
+    protected void setChildrenDrawnWithCacheEnabled(boolean enabled) {
+        setBooleanFlag(FLAG_CHILDREN_DRAWN_WITH_CACHE, enabled);
+    }
+
+    private void setBooleanFlag(int flag, boolean value) {
+        if (value) {
+            mGroupFlags |= flag;
+        } else {
+            mGroupFlags &= ~flag;
+        }
+    }
+
+    /**
+     * Returns an integer indicating what types of drawing caches are kept in memory.
+     *
+     * @see #setPersistentDrawingCache(int)
+     * @see #setAnimationCacheEnabled(boolean)
+     *
+     * @return one or a combination of {@link #PERSISTENT_NO_CACHE},
+     *         {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
+     *         and {@link #PERSISTENT_ALL_CACHES}
+     */
+    @ViewDebug.ExportedProperty(mapping = {
+        @ViewDebug.IntToString(from = PERSISTENT_NO_CACHE,        to = "NONE"),
+        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ANIMATION"),
+        @ViewDebug.IntToString(from = PERSISTENT_SCROLLING_CACHE, to = "SCROLLING"),
+        @ViewDebug.IntToString(from = PERSISTENT_ALL_CACHES,      to = "ALL")
+    })
+    public int getPersistentDrawingCache() {
+        return mPersistentDrawingCache;
+    }
+
+    /**
+     * Indicates what types of drawing caches should be kept in memory after
+     * they have been created.
+     *
+     * @see #getPersistentDrawingCache()
+     * @see #setAnimationCacheEnabled(boolean)
+     *
+     * @param drawingCacheToKeep one or a combination of {@link #PERSISTENT_NO_CACHE},
+     *        {@link #PERSISTENT_ANIMATION_CACHE}, {@link #PERSISTENT_SCROLLING_CACHE}
+     *        and {@link #PERSISTENT_ALL_CACHES}
+     */
+    public void setPersistentDrawingCache(int drawingCacheToKeep) {
+        mPersistentDrawingCache = drawingCacheToKeep & PERSISTENT_ALL_CACHES;
+    }
+
+    /**
+     * Returns a new set of layout parameters based on the supplied attributes set.
+     *
+     * @param attrs the attributes to build the layout parameters from
+     *
+     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
+     *         of its descendants
+     */
+    public LayoutParams generateLayoutParams(AttributeSet attrs) {
+        return new LayoutParams(getContext(), attrs);
+    }
+
+    /**
+     * Returns a safe set of layout parameters based on the supplied layout params.
+     * When a ViewGroup is passed a View whose layout params do not pass the test of
+     * {@link #checkLayoutParams(android.view.ViewGroup.LayoutParams)}, this method
+     * is invoked. This method should return a new set of layout params suitable for
+     * this ViewGroup, possibly by copying the appropriate attributes from the
+     * specified set of layout params.
+     *
+     * @param p The layout parameters to convert into a suitable set of layout parameters
+     *          for this ViewGroup.
+     *
+     * @return an instance of {@link android.view.ViewGroup.LayoutParams} or one
+     *         of its descendants
+     */
+    protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) {
+        return p;
+    }
+
+    /**
+     * Returns a set of default layout parameters. These parameters are requested
+     * when the View passed to {@link #addView(View)} has no layout parameters
+     * already set. If null is returned, an exception is thrown from addView.
+     *
+     * @return a set of default layout parameters or null
+     */
+    protected LayoutParams generateDefaultLayoutParams() {
+        return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected void debug(int depth) {
+        super.debug(depth);
+        String output;
+
+        if (mFocused != null) {
+            output = debugIndent(depth);
+            output += "mFocused";
+            Log.d(VIEW_LOG_TAG, output);
+        }
+        if (mChildrenCount != 0) {
+            output = debugIndent(depth);
+            output += "{";
+            Log.d(VIEW_LOG_TAG, output);
+        }
+        int count = mChildrenCount;
+        for (int i = 0; i < count; i++) {
+            View child = mChildren[i];
+            child.debug(depth + 1);
+        }
+
+        if (mChildrenCount != 0) {
+            output = debugIndent(depth);
+            output += "}";
+            Log.d(VIEW_LOG_TAG, output);
+        }
+    }
+
+    /**
+     * Returns the position in the group of the specified child view.
+     *
+     * @param child the view for which to get the position
+     * @return a positive integer representing the position of the view in the
+     *         group, or -1 if the view does not exist in the group
+     */
+    public int indexOfChild(View child) {
+        final int count = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < count; i++) {
+            if (children[i] == child) {
+                return i;
+            }
+        }
+        return -1;
+    }
+
+    /**
+     * Returns the number of children in the group.
+     *
+     * @return a positive integer representing the number of children in
+     *         the group
+     */
+    public int getChildCount() {
+        return mChildrenCount;
+    }
+
+    /**
+     * Returns the view at the specified position in the group.
+     *
+     * @param index the position at which to get the view from
+     * @return the view at the specified position or null if the position
+     *         does not exist within the group
+     */
+    public View getChildAt(int index) {
+        try {
+            return mChildren[index];
+        } catch (IndexOutOfBoundsException ex) {
+            return null;
+        }
+    }
+
+    /**
+     * Ask all of the children of this view to measure themselves, taking into
+     * account both the MeasureSpec requirements for this view and its padding.
+     * We skip children that are in the GONE state The heavy lifting is done in
+     * getChildMeasureSpec.
+     *
+     * @param widthMeasureSpec The width requirements for this view
+     * @param heightMeasureSpec The height requirements for this view
+     */
+    protected void measureChildren(int widthMeasureSpec, int heightMeasureSpec) {
+        final int size = mChildrenCount;
+        final View[] children = mChildren;
+        for (int i = 0; i < size; ++i) {
+            final View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) != GONE) {
+                measureChild(child, widthMeasureSpec, heightMeasureSpec);
+            }
+        }
+    }
+
+    /**
+     * Ask one of the children of this view to measure itself, taking into
+     * account both the MeasureSpec requirements for this view and its padding.
+     * The heavy lifting is done in getChildMeasureSpec.
+     *
+     * @param child The child to measure
+     * @param parentWidthMeasureSpec The width requirements for this view
+     * @param parentHeightMeasureSpec The height requirements for this view
+     */
+    protected void measureChild(View child, int parentWidthMeasureSpec,
+            int parentHeightMeasureSpec) {
+        final LayoutParams lp = child.getLayoutParams();
+
+        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+                mPaddingLeft + mPaddingRight, lp.width);
+        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+                mPaddingTop + mPaddingBottom, lp.height);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+    }
+
+    /**
+     * Ask one of the children of this view to measure itself, taking into
+     * account both the MeasureSpec requirements for this view and its padding
+     * and margins. The child must have MarginLayoutParams The heavy lifting is
+     * done in getChildMeasureSpec.
+     *
+     * @param child The child to measure
+     * @param parentWidthMeasureSpec The width requirements for this view
+     * @param widthUsed Extra space that has been used up by the parent
+     *        horizontally (possibly by other children of the parent)
+     * @param parentHeightMeasureSpec The height requirements for this view
+     * @param heightUsed Extra space that has been used up by the parent
+     *        vertically (possibly by other children of the parent)
+     */
+    protected void measureChildWithMargins(View child,
+            int parentWidthMeasureSpec, int widthUsed,
+            int parentHeightMeasureSpec, int heightUsed) {
+        final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams();
+
+        final int childWidthMeasureSpec = getChildMeasureSpec(parentWidthMeasureSpec,
+                mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin
+                        + widthUsed, lp.width);
+        final int childHeightMeasureSpec = getChildMeasureSpec(parentHeightMeasureSpec,
+                mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin
+                        + heightUsed, lp.height);
+
+        child.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+    }
+
+    /**
+     * Does the hard part of measureChildren: figuring out the MeasureSpec to
+     * pass to a particular child. This method figures out the right MeasureSpec
+     * for one dimension (height or width) of one child view.
+     *
+     * The goal is to combine information from our MeasureSpec with the
+     * LayoutParams of the child to get the best possible results. For example,
+     * if the this view knows its size (because its MeasureSpec has a mode of
+     * EXACTLY), and the child has indicated in its LayoutParams that it wants
+     * to be the same size as the parent, the parent should ask the child to
+     * layout given an exact size.
+     *
+     * @param spec The requirements for this view
+     * @param padding The padding of this view for the current dimension and
+     *        margins, if applicable
+     * @param childDimension How big the child wants to be in the current
+     *        dimension
+     * @return a MeasureSpec integer for the child
+     */
+    public static int getChildMeasureSpec(int spec, int padding, int childDimension) {
+        int specMode = MeasureSpec.getMode(spec);
+        int specSize = MeasureSpec.getSize(spec);
+
+        int size = Math.max(0, specSize - padding);
+
+        int resultSize = 0;
+        int resultMode = 0;
+
+        switch (specMode) {
+        // Parent has imposed an exact size on us
+        case MeasureSpec.EXACTLY:
+            if (childDimension >= 0) {
+                resultSize = childDimension;
+                resultMode = MeasureSpec.EXACTLY;
+            } else if (childDimension == LayoutParams.FILL_PARENT) {
+                // Child wants to be our size. So be it.
+                resultSize = size;
+                resultMode = MeasureSpec.EXACTLY;
+            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                // Child wants to determine its own size. It can't be
+                // bigger than us.
+                resultSize = size;
+                resultMode = MeasureSpec.AT_MOST;
+            }
+            break;
+
+        // Parent has imposed a maximum size on us
+        case MeasureSpec.AT_MOST:
+            if (childDimension >= 0) {
+                // Child wants a specific size... so be it
+                resultSize = childDimension;
+                resultMode = MeasureSpec.EXACTLY;
+            } else if (childDimension == LayoutParams.FILL_PARENT) {
+                // Child wants to be our size, but our size is not fixed.
+                // Constrain child to not be bigger than us.
+                resultSize = size;
+                resultMode = MeasureSpec.AT_MOST;
+            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                // Child wants to determine its own size. It can't be
+                // bigger than us.
+                resultSize = size;
+                resultMode = MeasureSpec.AT_MOST;
+            }
+            break;
+
+        // Parent asked to see how big we want to be
+        case MeasureSpec.UNSPECIFIED:
+            if (childDimension >= 0) {
+                // Child wants a specific size... let him have it
+                resultSize = childDimension;
+                resultMode = MeasureSpec.EXACTLY;
+            } else if (childDimension == LayoutParams.FILL_PARENT) {
+                // Child wants to be our size... find out how big it should
+                // be
+                resultSize = 0;
+                resultMode = MeasureSpec.UNSPECIFIED;
+            } else if (childDimension == LayoutParams.WRAP_CONTENT) {
+                // Child wants to determine its own size.... find out how
+                // big it should be
+                resultSize = 0;
+                resultMode = MeasureSpec.UNSPECIFIED;
+            }
+            break;
+        }
+        return MeasureSpec.makeMeasureSpec(resultSize, resultMode);
+    }
+
+
+    /**
+     * Removes any pending animations for views that have been removed. Call
+     * this if you don't want animations for exiting views to stack up.
+     */
+    public void clearDisappearingChildren() {
+        if (mDisappearingChildren != null) {
+            mDisappearingChildren.clear();
+        }
+    }
+
+    /**
+     * Add a view which is removed from mChildren but still needs animation
+     *
+     * @param v View to add
+     */
+    private void addDisappearingView(View v) {
+        ArrayList<View> disappearingChildren = mDisappearingChildren;
+
+        if (disappearingChildren == null) {
+            disappearingChildren = mDisappearingChildren = new ArrayList<View>();
+        }
+
+        disappearingChildren.add(v);
+    }
+
+    /**
+     * Cleanup a view when its animation is done. This may mean removing it from
+     * the list of disappearing views.
+     *
+     * @param view The view whose animation has finished
+     * @param animation The animation, cannot be null
+     */
+    private void finishAnimatingView(final View view, Animation animation) {
+        final ArrayList<View> disappearingChildren = mDisappearingChildren;
+        if (disappearingChildren != null) {
+            if (disappearingChildren.contains(view)) {
+                disappearingChildren.remove(view);
+
+                if (view.mAttachInfo != null) {
+                    view.dispatchDetachedFromWindow();
+                }
+
+                view.clearAnimation();
+                mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+            }
+        }
+
+        if (animation != null && !animation.getFillAfter()) {
+            view.clearAnimation();
+        }
+
+        if ((view.mPrivateFlags & ANIMATION_STARTED) == ANIMATION_STARTED) {
+            view.onAnimationEnd();
+            // Should be performed by onAnimationEnd() but this avoid an infinite loop,
+            // so we'd rather be safe than sorry
+            view.mPrivateFlags &= ~ANIMATION_STARTED;
+            // Draw one more frame after the animation is done
+            mGroupFlags |= FLAG_INVALIDATE_REQUIRED;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean gatherTransparentRegion(Region region) {
+        // If no transparent regions requested, we are always opaque.
+        final boolean meOpaque = (mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) == 0;
+        if (meOpaque && region == null) {
+            // The caller doesn't care about the region, so stop now.
+            return true;
+        }
+        super.gatherTransparentRegion(region);
+        final View[] children = mChildren;
+        final int count = mChildrenCount;
+        boolean noneOfTheChildrenAreTransparent = true;
+        for (int i = 0; i < count; i++) {
+            final View child = children[i];
+            if ((child.mViewFlags & VISIBILITY_MASK) != GONE || child.getAnimation() != null) {
+                if (!child.gatherTransparentRegion(region)) {
+                    noneOfTheChildrenAreTransparent = false;
+                }
+            }
+        }
+        return meOpaque || noneOfTheChildrenAreTransparent;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void requestTransparentRegion(View child) {
+        if (child != null) {
+            child.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
+            if (mParent != null) {
+                mParent.requestTransparentRegion(this);
+            }
+        }
+    }
+    
+
+    @Override
+    protected boolean fitSystemWindows(Rect insets) {
+        boolean done = super.fitSystemWindows(insets);
+        if (!done) {
+            final int count = mChildrenCount;
+            final View[] children = mChildren;
+            for (int i = 0; i < count; i++) {
+                done = children[i].fitSystemWindows(insets);
+                if (done) {
+                    break;
+                }
+            }
+        }
+        return done;
+    }
+
+    /**
+     * Returns the animation listener to which layout animation events are
+     * sent.
+     *
+     * @return an {@link android.view.animation.Animation.AnimationListener}
+     */
+    public Animation.AnimationListener getLayoutAnimationListener() {
+        return mAnimationListener;
+    }
+
+    @Override
+    protected void drawableStateChanged() {
+        super.drawableStateChanged();
+
+        if ((mGroupFlags & FLAG_NOTIFY_CHILDREN_ON_DRAWABLE_STATE_CHANGE) != 0) {
+            if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
+                throw new IllegalStateException("addStateFromChildren cannot be enabled if a"
+                        + " child has duplicateParentState set to true");
+            }
+
+            final View[] children = mChildren;
+            final int count = mChildrenCount;
+
+            for (int i = 0; i < count; i++) {
+                final View child = children[i];
+                if ((child.mViewFlags & DUPLICATE_PARENT_STATE) != 0) {
+                    child.refreshDrawableState();
+                }
+            }
+        }
+    }
+
+    @Override
+    protected int[] onCreateDrawableState(int extraSpace) {
+        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) == 0) {
+            return super.onCreateDrawableState(extraSpace);
+        }
+
+        int need = 0;
+        int n = getChildCount();
+        for (int i = 0; i < n; i++) {
+            int[] childState = getChildAt(i).getDrawableState();
+
+            if (childState != null) {
+                need += childState.length;
+            }
+        }
+
+        int[] state = super.onCreateDrawableState(extraSpace + need);
+
+        for (int i = 0; i < n; i++) {
+            int[] childState = getChildAt(i).getDrawableState();
+
+            if (childState != null) {
+                state = mergeDrawableStates(state, childState);
+            }
+        }
+
+        return state;
+    }
+
+    /**
+     * Sets whether this ViewGroup's drawable states also include
+     * its children's drawable states.  This is used, for example, to
+     * make a group appear to be focused when its child EditText or button
+     * is focused.
+     */
+    public void setAddStatesFromChildren(boolean addsStates) {
+        if (addsStates) {
+            mGroupFlags |= FLAG_ADD_STATES_FROM_CHILDREN;
+        } else {
+            mGroupFlags &= ~FLAG_ADD_STATES_FROM_CHILDREN;
+        }
+
+        refreshDrawableState();
+    }
+
+    /**
+     * Returns whether this ViewGroup's drawable states also include
+     * its children's drawable states.  This is used, for example, to
+     * make a group appear to be focused when its child EditText or button
+     * is focused.
+     */
+    public boolean addStatesFromChildren() {
+        return (mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0;
+    }
+
+    /**
+     * If {link #addStatesFromChildren} is true, refreshes this group's
+     * drawable state (to include the states from its children).
+     */
+    public void childDrawableStateChanged(View child) {
+        if ((mGroupFlags & FLAG_ADD_STATES_FROM_CHILDREN) != 0) {
+            refreshDrawableState();
+        }
+    }
+
+    /**
+     * Specifies the animation listener to which layout animation events must
+     * be sent. Only
+     * {@link android.view.animation.Animation.AnimationListener#onAnimationStart(Animation)}
+     * and
+     * {@link android.view.animation.Animation.AnimationListener#onAnimationEnd(Animation)}
+     * are invoked.
+     *
+     * @param animationListener the layout animation listener
+     */
+    public void setLayoutAnimationListener(Animation.AnimationListener animationListener) {
+        mAnimationListener = animationListener;
+    }
+
+    /**
+     * LayoutParams are used by views to tell their parents how they want to be
+     * laid out. See
+     * {@link android.R.styleable#ViewGroup_Layout ViewGroup Layout Attributes}
+     * for a list of all child view attributes that this class supports.
+     * 
+     * <p>
+     * The base LayoutParams class just describes how big the view wants to be
+     * for both width and height. For each dimension, it can specify one of:
+     * <ul>
+     * <li> an exact number
+     * <li>FILL_PARENT, which means the view wants to be as big as its parent
+     * (minus padding)
+     * <li> WRAP_CONTENT, which means that the view wants to be just big enough
+     * to enclose its content (plus padding)
+     * </ul>
+     * There are subclasses of LayoutParams for different subclasses of
+     * ViewGroup. For example, AbsoluteLayout has its own subclass of
+     * LayoutParams which adds an X and Y value.
+     *
+     * @attr ref android.R.styleable#ViewGroup_Layout_layout_height
+     * @attr ref android.R.styleable#ViewGroup_Layout_layout_width
+     */
+    public static class LayoutParams {
+        /**
+         * Special value for the height or width requested by a View.
+         * FILL_PARENT means that the view wants to fill the available space
+         * within the parent, taking the parent's padding into account.
+         */
+        public static final int FILL_PARENT = -1;
+
+        /**
+         * Special value for the height or width requested by a View.
+         * WRAP_CONTENT means that the view wants to be just large enough to fit
+         * its own internal content, taking its own padding into account.
+         */
+        public static final int WRAP_CONTENT = -2;
+
+        /**
+         * Information about how wide the view wants to be. Can be an exact
+         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
+         */
+        @ViewDebug.ExportedProperty(mapping = {
+            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
+            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
+        })
+        public int width;
+
+        /**
+         * Information about how tall the view wants to be. Can be an exact
+         * size, or one of the constants FILL_PARENT or WRAP_CONTENT.
+         */
+        @ViewDebug.ExportedProperty(mapping = {
+            @ViewDebug.IntToString(from = FILL_PARENT, to = "FILL_PARENT"),
+            @ViewDebug.IntToString(from = WRAP_CONTENT, to = "WRAP_CONTENT")
+        })
+        public int height;
+
+        /**
+         * Used to animate layouts.
+         */
+        public LayoutAnimationController.AnimationParameters layoutAnimationParameters;
+
+        /**
+         * Creates a new set of layout parameters. The values are extracted from
+         * the supplied attributes set and context. The XML attributes mapped
+         * to this set of layout parameters are:
+         *
+         * <ul>
+         *   <li><code>layout_width</code>: the width, either an exact value,
+         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
+         *   <li><code>layout_height</code>: the height, either an exact value,
+         *   {@link #WRAP_CONTENT} or {@link #FILL_PARENT}</li>
+         * </ul>
+         *
+         * @param c the application environment
+         * @param attrs the set of attributes from which to extract the layout
+         *              parameters' values
+         */
+        public LayoutParams(Context c, AttributeSet attrs) {
+            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_Layout);
+            setBaseAttributes(a,
+                    R.styleable.ViewGroup_Layout_layout_width,
+                    R.styleable.ViewGroup_Layout_layout_height);
+            a.recycle();
+        }
+
+        /**
+         * Creates a new set of layout parameters with the specified width
+         * and height.
+         *
+         * @param width the width, either {@link #FILL_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         * @param height the height, either {@link #FILL_PARENT},
+         *        {@link #WRAP_CONTENT} or a fixed size in pixels
+         */
+        public LayoutParams(int width, int height) {
+            this.width = width;
+            this.height = height;
+        }
+
+        /**
+         * Copy constructor. Clones the width and height values of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public LayoutParams(LayoutParams source) {
+            this.width = source.width;
+            this.height = source.height;
+        }
+
+        /**
+         * Used internally by MarginLayoutParams.
+         * @hide
+         */
+        LayoutParams() {
+        }
+
+        /**
+         * Extracts the layout parameters from the supplied attributes.
+         *
+         * @param a the style attributes to extract the parameters from
+         * @param widthAttr the identifier of the width attribute
+         * @param heightAttr the identifier of the height attribute
+         */
+        protected void setBaseAttributes(TypedArray a, int widthAttr, int heightAttr) {
+            width = a.getLayoutDimension(widthAttr, "layout_width");
+            height = a.getLayoutDimension(heightAttr, "layout_height");
+        }
+
+        /**
+         * Returns a String representation of this set of layout parameters.
+         *
+         * @param output the String to prepend to the internal representation
+         * @return a String with the following format: output +
+         *         "ViewGroup.LayoutParams={ width=WIDTH, height=HEIGHT }"
+         * 
+         * @hide
+         */
+        public String debug(String output) {
+            return output + "ViewGroup.LayoutParams={ width="
+                    + sizeToString(width) + ", height=" + sizeToString(height) + " }";
+        }
+
+        /**
+         * Converts the specified size to a readable String.
+         *
+         * @param size the size to convert
+         * @return a String instance representing the supplied size
+         * 
+         * @hide
+         */
+        protected static String sizeToString(int size) {
+            if (size == WRAP_CONTENT) {
+                return "wrap-content";
+            }
+            if (size == FILL_PARENT) {
+                return "fill-parent";
+            }
+            return String.valueOf(size);
+        }
+    }
+
+    /**
+     * Per-child layout information for layouts that support margins.
+     * See
+     * {@link android.R.styleable#ViewGroup_MarginLayout ViewGroup Margin Layout Attributes}
+     * for a list of all child view attributes that this class supports.
+     */
+    public static class MarginLayoutParams extends ViewGroup.LayoutParams {
+        /**
+         * The left margin in pixels of the child.
+         */
+        @ViewDebug.ExportedProperty
+        public int leftMargin;
+
+        /**
+         * The top margin in pixels of the child.
+         */
+        @ViewDebug.ExportedProperty
+        public int topMargin;
+
+        /**
+         * The right margin in pixels of the child.
+         */
+        @ViewDebug.ExportedProperty
+        public int rightMargin;
+
+        /**
+         * The bottom margin in pixels of the child.
+         */
+        @ViewDebug.ExportedProperty
+        public int bottomMargin;
+
+        /**
+         * Creates a new set of layout parameters. The values are extracted from
+         * the supplied attributes set and context.
+         *
+         * @param c the application environment
+         * @param attrs the set of attributes from which to extract the layout
+         *              parameters' values
+         */
+        public MarginLayoutParams(Context c, AttributeSet attrs) {
+            super();
+
+            TypedArray a = c.obtainStyledAttributes(attrs, R.styleable.ViewGroup_MarginLayout);
+            setBaseAttributes(a,
+                    R.styleable.ViewGroup_MarginLayout_layout_width,
+                    R.styleable.ViewGroup_MarginLayout_layout_height);
+
+            int margin = a.getDimensionPixelSize(
+                    com.android.internal.R.styleable.ViewGroup_MarginLayout_layout_margin, -1);
+            if (margin >= 0) {
+                leftMargin = margin;
+                topMargin = margin;
+                rightMargin= margin;
+                bottomMargin = margin;
+            } else {
+                leftMargin = a.getDimensionPixelSize(
+                        R.styleable.ViewGroup_MarginLayout_layout_marginLeft, 0);
+                topMargin = a.getDimensionPixelSize(
+                        R.styleable.ViewGroup_MarginLayout_layout_marginTop, 0);
+                rightMargin = a.getDimensionPixelSize(
+                        R.styleable.ViewGroup_MarginLayout_layout_marginRight, 0);
+                bottomMargin = a.getDimensionPixelSize(
+                        R.styleable.ViewGroup_MarginLayout_layout_marginBottom, 0);
+            }
+
+            a.recycle();
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public MarginLayoutParams(int width, int height) {
+            super(width, height);
+        }
+
+        /**
+         * Copy constructor. Clones the width, height and margin values of the source.
+         *
+         * @param source The layout params to copy from.
+         */
+        public MarginLayoutParams(MarginLayoutParams source) {
+            this.width = source.width;
+            this.height = source.height;
+
+            this.leftMargin = source.leftMargin;
+            this.topMargin = source.topMargin;
+            this.rightMargin = source.rightMargin;
+            this.bottomMargin = source.bottomMargin;
+        }
+
+        /**
+         * {@inheritDoc}
+         */
+        public MarginLayoutParams(LayoutParams source) {
+            super(source);
+        }
+
+        /**
+         * Sets the margins, in pixels.
+         *
+         * @param left the left margin size
+         * @param top the top margin size
+         * @param right the right margin size
+         * @param bottom the bottom margin size
+         *
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginLeft
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginTop
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginRight
+         * @attr ref android.R.styleable#ViewGroup_MarginLayout_layout_marginBottom
+         */
+        public void setMargins(int left, int top, int right, int bottom) {
+            leftMargin = left;
+            topMargin = top;
+            rightMargin = right;
+            bottomMargin = bottom;
+        }
+    }
+}
diff --git a/core/java/android/view/ViewManager.java b/core/java/android/view/ViewManager.java
new file mode 100644
index 0000000..7f318c1
--- /dev/null
+++ b/core/java/android/view/ViewManager.java
@@ -0,0 +1,27 @@
+/*
+ * Copyright (C) 2006 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;
+
+/** Interface to let you add and remove child views to an Activity. To get an instance
+  * of this class, call {@link android.content.Context#getSystemService(java.lang.String) Context.getSystemService()}.
+  */
+public interface ViewManager
+{
+    public void addView(View view, ViewGroup.LayoutParams params);
+    public void updateViewLayout(View view, ViewGroup.LayoutParams params);
+    public void removeView(View view);
+}
diff --git a/core/java/android/view/ViewParent.java b/core/java/android/view/ViewParent.java
new file mode 100644
index 0000000..b456c5d
--- /dev/null
+++ b/core/java/android/view/ViewParent.java
@@ -0,0 +1,211 @@
+/*
+ * Copyright (C) 2006 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.graphics.Rect;
+
+/**
+ * Defines the responsibilities for a class that will be a parent of a View.
+ * This is the API that a view sees when it wants to interact with its parent.
+ * 
+ */
+public interface ViewParent {
+    /**
+     * Called when something has changed which has invalidated the layout of a
+     * child of this view parent. This will schedule a layout pass of the view
+     * tree.
+     */
+    public void requestLayout();
+
+    /**
+     * Indicates whether layout was requested on this view parent.
+     *
+     * @return true if layout was requested, false otherwise
+     */
+    public boolean isLayoutRequested();
+
+    /**
+     * Called when a child wants the view hierarchy to gather and report
+     * transparent regions to the window compositor. Views that "punch" holes in
+     * the view hierarchy, such as SurfaceView can use this API to improve
+     * performance of the system. When no such a view is present in the
+     * hierarchy, this optimization in unnecessary and might slightly reduce the
+     * view hierarchy performance.
+     * 
+     * @param child the view requesting the transparent region computation
+     * 
+     */
+    public void requestTransparentRegion(View child);
+
+    /**
+     * All or part of a child is dirty and needs to be redrawn.
+     * 
+     * @param child The child which is dirty
+     * @param r The area within the child that is invalid
+     */
+    public void invalidateChild(View child, Rect r);
+
+    /**
+     * All or part of a child is dirty and needs to be redrawn.
+     *
+     * The location array is an array of two int values which respectively
+     * define the left and the top position of the dirty child.
+     *
+     * This method must return the parent of this ViewParent if the specified
+     * rectangle must be invalidated in the parent. If the specified rectangle
+     * does not require invalidation in the parent or if the parent does not
+     * exist, this method must return null.
+     *
+     * When this method returns a non-null value, the location array must
+     * have been updated with the left and top coordinates of this ViewParent.
+     *
+     * @param location An array of 2 ints containing the left and top
+     *        coordinates of the child to invalidate
+     * @param r The area within the child that is invalid
+     *
+     * @return the parent of this ViewParent or null
+     */
+    public ViewParent invalidateChildInParent(int[] location, Rect r);
+
+    /**
+     * Returns the parent if it exists, or null.
+     *
+     * @return a ViewParent or null if this ViewParent does not have a parent
+     */
+    public ViewParent getParent();
+
+    /**
+     * Called when a child of this parent wants focus
+     * 
+     * @param child The child of this ViewParent that wants focus. This view
+     *        will contain the focused view. It is not necessarily the view that
+     *        actually has focus.
+     * @param focused The view that is a descendant of child that actually has
+     *        focus
+     */
+    public void requestChildFocus(View child, View focused);
+
+    /**
+     * Tell view hierarchy that the global view attributes need to be
+     * re-evaluated.
+     * 
+     * @param child View whose attributes have changed.
+     */
+    public void recomputeViewAttributes(View child);
+    
+    /**
+     * Called when a child of this parent is giving up focus
+     * 
+     * @param child The view that is giving up focus
+     */
+    public void clearChildFocus(View child);
+
+    public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset);
+
+    /**
+     * Find the nearest view in the specified direction that wants to take focus
+     * 
+     * @param v The view that currently has focus
+     * @param direction One of FOCUS_UP, FOCUS_DOWN, FOCUS_LEFT, and FOCUS_RIGHT
+     */
+    public View focusSearch(View v, int direction);
+
+    /**
+     * Change the z order of the child so it's on top of all other children
+     * 
+     * @param child
+     */
+    public void bringChildToFront(View child);
+
+    /**
+     * Tells the parent that a new focusable view has become available. This is
+     * to handle transitions from the case where there are no focusable views to
+     * the case where the first focusable view appears.
+     * 
+     * @param v The view that has become newly focusable
+     */
+    public void focusableViewAvailable(View v);
+
+    /**
+     * Bring up a context menu for the specified view or its ancestors.
+     * <p>
+     * In most cases, a subclass does not need to override this.  However, if
+     * the subclass is added directly to the window manager (for example,
+     * {@link ViewManager#addView(View, android.view.ViewGroup.LayoutParams)})
+     * then it should override this and show the context menu.
+     * 
+     * @param originalView The source view where the context menu was first invoked
+     * @return true if a context menu was displayed
+     */
+    public boolean showContextMenuForChild(View originalView);
+
+    /**
+     * Have the parent populate the specified context menu if it has anything to
+     * add (and then recurse on its parent).
+     * 
+     * @param menu The menu to populate
+     */
+    public void createContextMenu(ContextMenu menu);
+
+    /**
+     * This method is called on the parent when a child's drawable state
+     * has changed.
+     *
+     * @param child The child whose drawable state has changed.
+     */
+    public void childDrawableStateChanged(View child);
+    
+    /**
+     * Called when a child does not want this parent and its ancestors to
+     * intercept touch events with
+     * {@link ViewGroup#onInterceptTouchEvent(MotionEvent)}.
+     * <p>
+     * This parent should pass this call onto its parents. This parent must obey
+     * this request for the duration of the touch (that is, only clear the flag
+     * after this parent has received an up or a cancel.
+     * 
+     * @param disallowIntercept True if the child does not want the parent to
+     *            intercept touch events.
+     */
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept);
+    
+    /**
+     * Called when a child of this group wants a particular rectangle to be
+     * positioned onto the screen.  {@link ViewGroup}s overriding this can trust
+     * that:
+     * <ul>
+     *   <li>child will be a direct child of this group</li>
+     *   <li>rectangle will be in the child's coordinates</li>
+     * </ul>
+     *
+     * <p>{@link ViewGroup}s overriding this should uphold the contract:</p>
+     * <ul>
+     *   <li>nothing will change if the rectangle is already visible</li>
+     *   <li>the view port will be scrolled only just enough to make the
+     *       rectangle visible</li>
+     * <ul>
+     *
+     * @param child The direct child making the request.
+     * @param rectangle The rectangle in the child's coordinates the child
+     *        wishes to be on the screen.
+     * @param immediate True to forbid animated or delayed scrolling,
+     *        false otherwise
+     * @return Whether the group scrolled to handle the operation
+     */
+    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
+            boolean immediate);
+}
diff --git a/core/java/android/view/ViewRoot.java b/core/java/android/view/ViewRoot.java
new file mode 100644
index 0000000..9b13d38
--- /dev/null
+++ b/core/java/android/view/ViewRoot.java
@@ -0,0 +1,2872 @@
+/*
+ * Copyright (C) 2006 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 com.android.internal.view.IInputMethodCallback;
+import com.android.internal.view.IInputMethodSession;
+
+import android.graphics.Canvas;
+import android.graphics.PixelFormat;
+import android.graphics.Point;
+import android.graphics.PorterDuff;
+import android.graphics.Rect;
+import android.graphics.Region;
+import android.os.*;
+import android.os.Process;
+import android.os.SystemProperties;
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.Log;
+import android.util.EventLog;
+import android.util.SparseArray;
+import android.util.DisplayMetrics;
+import android.view.View.MeasureSpec;
+import android.view.inputmethod.InputConnection;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.Scroller;
+import android.content.pm.PackageManager;
+import android.content.Context;
+import android.app.ActivityManagerNative;
+import android.Manifest;
+import android.media.AudioManager;
+
+import java.lang.ref.WeakReference;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.util.ArrayList;
+
+import javax.microedition.khronos.egl.*;
+import javax.microedition.khronos.opengles.*;
+import static javax.microedition.khronos.opengles.GL10.*;
+
+/**
+ * The top of a view hierarchy, implementing the needed protocol between View
+ * and the WindowManager.  This is for the most part an internal implementation
+ * detail of {@link WindowManagerImpl}.
+ *
+ * {@hide}
+ */
+@SuppressWarnings({"EmptyCatchBlock"})
+public final class ViewRoot extends Handler implements ViewParent,
+        View.AttachInfo.Callbacks {
+    private static final String TAG = "ViewRoot";
+    private static final boolean DBG = false;
+    @SuppressWarnings({"ConstantConditionalExpression"})
+    private static final boolean LOCAL_LOGV = false ? Config.LOGD : Config.LOGV;
+    /** @noinspection PointlessBooleanExpression*/
+    private static final boolean DEBUG_DRAW = false || LOCAL_LOGV;
+    private static final boolean DEBUG_LAYOUT = false || LOCAL_LOGV;
+    private static final boolean DEBUG_INPUT_RESIZE = false || LOCAL_LOGV;
+    private static final boolean DEBUG_ORIENTATION = false || LOCAL_LOGV;
+    private static final boolean DEBUG_TRACKBALL = false || LOCAL_LOGV;
+    private static final boolean DEBUG_IMF = false || LOCAL_LOGV;
+    private static final boolean WATCH_POINTER = false;
+
+    static final boolean PROFILE_DRAWING = false;
+    private static final boolean PROFILE_LAYOUT = false;
+    // profiles real fps (times between draws) and displays the result
+    private static final boolean SHOW_FPS = false;
+    // used by SHOW_FPS
+    private static int sDrawTime;
+
+    /**
+     * Maximum time we allow the user to roll the trackball enough to generate
+     * a key event, before resetting the counters.
+     */
+    static final int MAX_TRACKBALL_DELAY = 250;
+
+    static long sInstanceCount = 0;
+
+    static IWindowSession sWindowSession;
+
+    static final Object mStaticInit = new Object();
+    static boolean mInitialized = false;
+
+    static final ThreadLocal<RunQueue> sRunQueues = new ThreadLocal<RunQueue>();
+
+    long mLastTrackballTime = 0;
+    final TrackballAxis mTrackballAxisX = new TrackballAxis();
+    final TrackballAxis mTrackballAxisY = new TrackballAxis();
+
+    final int[] mTmpLocation = new int[2];
+    
+    final InputMethodCallback mInputMethodCallback;
+    final SparseArray<Object> mPendingEvents = new SparseArray<Object>();
+    int mPendingEventSeq = 0;
+    
+    final Thread mThread;
+
+    final WindowLeaked mLocation;
+
+    final WindowManager.LayoutParams mWindowAttributes = new WindowManager.LayoutParams();
+
+    final W mWindow;
+
+    View mView;
+    View mFocusedView;
+    View mRealFocusedView;  // this is not set to null in touch mode
+    int mViewVisibility;
+    boolean mAppVisible = true;
+
+    final Region mTransparentRegion;
+    final Region mPreviousTransparentRegion;
+
+    int mWidth;
+    int mHeight;
+    Rect mDirty; // will be a graphics.Region soon
+
+    final View.AttachInfo mAttachInfo;
+
+    final Rect mTempRect; // used in the transaction to not thrash the heap.
+    final Rect mVisRect; // used to retrieve visible rect of focused view.
+    final Point mVisPoint; // used to retrieve global offset of focused view.
+
+    boolean mTraversalScheduled;
+    boolean mWillDrawSoon;
+    boolean mLayoutRequested;
+    boolean mFirst;
+    boolean mReportNextDraw;
+    boolean mFullRedrawNeeded;
+    boolean mNewSurfaceNeeded;
+    boolean mHasHadWindowFocus;
+    boolean mLastWasImTarget;
+
+    boolean mWindowAttributesChanged = false;
+
+    // These can be accessed by any thread, must be protected with a lock.
+    Surface mSurface;
+
+    boolean mAdded;
+    boolean mAddedTouchMode;
+
+    /*package*/ int mAddNesting;
+
+    // These are accessed by multiple threads.
+    final Rect mWinFrame; // frame given by window manager.
+
+    final Rect mPendingVisibleInsets = new Rect();
+    final Rect mPendingContentInsets = new Rect();
+    final ViewTreeObserver.InternalInsetsInfo mLastGivenInsets
+            = new ViewTreeObserver.InternalInsetsInfo();
+
+    boolean mScrollMayChange;
+    int mSoftInputMode;
+    View mLastScrolledFocus;
+    int mScrollY;
+    int mCurScrollY;
+    Scroller mScroller;
+    
+    EGL10 mEgl;
+    EGLDisplay mEglDisplay;
+    EGLContext mEglContext;
+    EGLSurface mEglSurface;
+    GL11 mGL;
+    Canvas mGlCanvas;
+    boolean mUseGL;
+    boolean mGlWanted;
+
+    final ViewConfiguration mViewConfiguration;    
+
+    /**
+     * see {@link #playSoundEffect(int)}
+     */
+    AudioManager mAudioManager;
+
+    private final float mDensity;
+
+    public ViewRoot(Context context) {
+        super();
+
+        ++sInstanceCount;
+
+        // Initialize the statics when this class is first instantiated. This is
+        // done here instead of in the static block because Zygote does not
+        // allow the spawning of threads.
+        synchronized (mStaticInit) {
+            if (!mInitialized) {
+                try {
+                    InputMethodManager imm = InputMethodManager.getInstance(context);
+                    sWindowSession = IWindowManager.Stub.asInterface(
+                            ServiceManager.getService("window"))
+                            .openSession(imm.getClient(), imm.getInputContext());
+                    mInitialized = true;
+                } catch (RemoteException e) {
+                }
+            }
+        }
+
+        mThread = Thread.currentThread();
+        mLocation = new WindowLeaked(null);
+        mLocation.fillInStackTrace();
+        mWidth = -1;
+        mHeight = -1;
+        mDirty = new Rect();
+        mTempRect = new Rect();
+        mVisRect = new Rect();
+        mVisPoint = new Point();
+        mWinFrame = new Rect();
+        mWindow = new W(this);
+        mInputMethodCallback = new InputMethodCallback(this);
+        mViewVisibility = View.GONE;
+        mTransparentRegion = new Region();
+        mPreviousTransparentRegion = new Region();
+        mFirst = true; // true for the first time the view is added
+        mSurface = new Surface();
+        mAdded = false;
+        mAttachInfo = new View.AttachInfo(sWindowSession, mWindow, this, this);
+        mViewConfiguration = ViewConfiguration.get(context);
+        mDensity = context.getResources().getDisplayMetrics().density;
+    }
+
+    @Override
+    protected void finalize() throws Throwable {
+        super.finalize();
+        --sInstanceCount;
+    }
+
+    public static long getInstanceCount() {
+        return sInstanceCount;
+    }
+
+    // FIXME for perf testing only
+    private boolean mProfile = false;
+
+    /**
+     * Call this to profile the next traversal call.
+     * FIXME for perf testing only. Remove eventually
+     */
+    public void profile() {
+        mProfile = true;
+    }
+
+    /**
+     * Indicates whether we are in touch mode. Calling this method triggers an IPC
+     * call and should be avoided whenever possible.
+     *
+     * @return True, if the device is in touch mode, false otherwise.
+     *
+     * @hide
+     */
+    static boolean isInTouchMode() {
+        if (mInitialized) {
+            try {
+                return sWindowSession.getInTouchMode();
+            } catch (RemoteException e) {
+            }
+        }
+        return false;
+    }
+
+    private void initializeGL() {
+        initializeGLInner();
+        int err = mEgl.eglGetError();
+        if (err != EGL10.EGL_SUCCESS) {
+            // give-up on using GL
+            destroyGL();
+            mGlWanted = false;
+        }
+    }
+
+    private void initializeGLInner() {
+        final EGL10 egl = (EGL10) EGLContext.getEGL();
+        mEgl = egl;
+
+        /*
+         * Get to the default display.
+         */
+        final EGLDisplay eglDisplay = egl.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY);
+        mEglDisplay = eglDisplay;
+
+        /*
+         * We can now initialize EGL for that display
+         */
+        int[] version = new int[2];
+        egl.eglInitialize(eglDisplay, version);
+
+        /*
+         * Specify a configuration for our opengl session
+         * and grab the first configuration that matches is
+         */
+        final int[] configSpec = {
+                EGL10.EGL_RED_SIZE,      5,
+                EGL10.EGL_GREEN_SIZE,    6,
+                EGL10.EGL_BLUE_SIZE,     5,
+                EGL10.EGL_DEPTH_SIZE,    0,
+                EGL10.EGL_NONE
+        };
+        final EGLConfig[] configs = new EGLConfig[1];
+        final int[] num_config = new int[1];
+        egl.eglChooseConfig(eglDisplay, configSpec, configs, 1, num_config);
+        final EGLConfig config = configs[0];
+
+        /*
+         * Create an OpenGL ES context. This must be done only once, an
+         * OpenGL context is a somewhat heavy object.
+         */
+        final EGLContext context = egl.eglCreateContext(eglDisplay, config,
+                EGL10.EGL_NO_CONTEXT, null);
+        mEglContext = context;
+
+        /*
+         * Create an EGL surface we can render into.
+         */
+        final EGLSurface surface = egl.eglCreateWindowSurface(eglDisplay, config, mHolder, null);
+        mEglSurface = surface;
+
+        /*
+         * Before we can issue GL commands, we need to make sure
+         * the context is current and bound to a surface.
+         */
+        egl.eglMakeCurrent(eglDisplay, surface, surface, context);
+
+        /*
+         * Get to the appropriate GL interface.
+         * This is simply done by casting the GL context to either
+         * GL10 or GL11.
+         */
+        final GL11 gl = (GL11) context.getGL();
+        mGL = gl;
+        mGlCanvas = new Canvas(gl);
+        mUseGL = true;
+    }
+
+    private void destroyGL() {
+        // inform skia that the context is gone
+        nativeAbandonGlCaches();
+
+        mEgl.eglMakeCurrent(mEglDisplay, EGL10.EGL_NO_SURFACE,
+                EGL10.EGL_NO_SURFACE, EGL10.EGL_NO_CONTEXT);
+        mEgl.eglDestroyContext(mEglDisplay, mEglContext);
+        mEgl.eglDestroySurface(mEglDisplay, mEglSurface);
+        mEgl.eglTerminate(mEglDisplay);
+        mEglContext = null;
+        mEglSurface = null;
+        mEglDisplay = null;
+        mEgl = null;
+        mGlCanvas = null;
+        mGL = null;
+        mUseGL = false;
+    }
+
+    private void checkEglErrors() {
+        if (mUseGL) {
+            int err = mEgl.eglGetError();
+            if (err != EGL10.EGL_SUCCESS) {
+                // something bad has happened revert to
+                // normal rendering.
+                destroyGL();
+                if (err != EGL11.EGL_CONTEXT_LOST) {
+                    // we'll try again if it was context lost
+                    mGlWanted = false;
+                }
+            }
+        }
+    }
+
+    /**
+     * We have one child
+     */
+    public void setView(View view, WindowManager.LayoutParams attrs,
+            View panelParentView) {
+        synchronized (this) {
+            if (mView == null) {
+                mWindowAttributes.copyFrom(attrs);
+                mSoftInputMode = attrs.softInputMode;
+                mWindowAttributesChanged = true;
+                mView = view;
+                mAttachInfo.mRootView = view;
+                if (panelParentView != null) {
+                    mAttachInfo.mPanelParentWindowToken
+                            = panelParentView.getApplicationWindowToken();
+                }
+                mAdded = true;
+                int res; /* = WindowManagerImpl.ADD_OKAY; */
+                
+                // Schedule the first layout -before- adding to the window
+                // manager, to make sure we do the relayout before receiving
+                // any other events from the system.
+                requestLayout();
+                
+                try {
+                    res = sWindowSession.add(mWindow, attrs,
+                            getHostVisibility(), mAttachInfo.mContentInsets);
+                } catch (RemoteException e) {
+                    mAdded = false;
+                    mView = null;
+                    mAttachInfo.mRootView = null;
+                    unscheduleTraversals();
+                    throw new RuntimeException("Adding window failed", e);
+                }
+                mPendingContentInsets.set(mAttachInfo.mContentInsets);
+                mPendingVisibleInsets.set(0, 0, 0, 0);
+                if (Config.LOGV) Log.v("ViewRoot", "Added window " + mWindow);
+                if (res < WindowManagerImpl.ADD_OKAY) {
+                    mView = null;
+                    mAttachInfo.mRootView = null;
+                    mAdded = false;
+                    unscheduleTraversals();
+                    switch (res) {
+                        case WindowManagerImpl.ADD_BAD_APP_TOKEN:
+                        case WindowManagerImpl.ADD_BAD_SUBWINDOW_TOKEN:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window -- token " + attrs.token
+                                + " is not valid; is your activity running?");
+                        case WindowManagerImpl.ADD_NOT_APP_TOKEN:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window -- token " + attrs.token
+                                + " is not for an application");
+                        case WindowManagerImpl.ADD_APP_EXITING:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window -- app for token " + attrs.token
+                                + " is exiting");
+                        case WindowManagerImpl.ADD_DUPLICATE_ADD:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window -- window " + mWindow
+                                + " has already been added");
+                        case WindowManagerImpl.ADD_STARTING_NOT_NEEDED:
+                            // Silently ignore -- we would have just removed it
+                            // right away, anyway.
+                            return;
+                        case WindowManagerImpl.ADD_MULTIPLE_SINGLETON:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window " + mWindow +
+                                " -- another window of this type already exists");
+                        case WindowManagerImpl.ADD_PERMISSION_DENIED:
+                            throw new WindowManagerImpl.BadTokenException(
+                                "Unable to add window " + mWindow +
+                                " -- permission denied for this window type");
+                    }
+                    throw new RuntimeException(
+                        "Unable to add window -- unknown error code " + res);
+                }
+                view.assignParent(this);
+                mAddedTouchMode = (res&WindowManagerImpl.ADD_FLAG_IN_TOUCH_MODE) != 0;
+                mAppVisible = (res&WindowManagerImpl.ADD_FLAG_APP_VISIBLE) != 0;
+            }
+        }
+    }
+
+    public View getView() {
+        return mView;
+    }
+
+    final WindowLeaked getLocation() {
+        return mLocation;
+    }
+
+    void setLayoutParams(WindowManager.LayoutParams attrs, boolean newView) {
+        synchronized (this) {
+            mWindowAttributes.copyFrom(attrs);
+            if (newView) {
+                mSoftInputMode = attrs.softInputMode;
+                requestLayout();
+            }
+            mWindowAttributesChanged = true;
+            scheduleTraversals();
+        }
+    }
+
+    void handleAppVisibility(boolean visible) {
+        if (mAppVisible != visible) {
+            mAppVisible = visible;
+            scheduleTraversals();
+        }
+    }
+
+    void handleGetNewSurface() {
+        mNewSurfaceNeeded = true;
+        mFullRedrawNeeded = true;
+        scheduleTraversals();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void requestLayout() {
+        checkThread();
+        mLayoutRequested = true;
+        scheduleTraversals();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isLayoutRequested() {
+        return mLayoutRequested;
+    }
+
+    public void invalidateChild(View child, Rect dirty) {
+        checkThread();
+        if (LOCAL_LOGV) Log.v(TAG, "Invalidate child: " + dirty);
+        if (mCurScrollY != 0) {
+            mTempRect.set(dirty);
+            mTempRect.offset(0, -mCurScrollY);
+            dirty = mTempRect;
+        }
+        mDirty.union(dirty);
+        if (!mWillDrawSoon) {
+            scheduleTraversals();
+        }
+    }
+
+    public ViewParent getParent() {
+        return null;
+    }
+
+    public ViewParent invalidateChildInParent(final int[] location, final Rect dirty) {
+        invalidateChild(null, dirty);
+        return null;
+    }
+
+     public boolean getChildVisibleRect(View child, Rect r, android.graphics.Point offset) {
+        if (child != mView) {
+            throw new RuntimeException("child is not mine, honest!");
+        }
+        // Note: don't apply scroll offset, because we want to know its
+        // visibility in the virtual canvas being given to the view hierarchy.
+        return r.intersect(0, 0, mWidth, mHeight);
+    }
+
+    public void bringChildToFront(View child) {
+    }
+
+    public void scheduleTraversals() {
+        if (!mTraversalScheduled) {
+            mTraversalScheduled = true;
+            sendEmptyMessage(DO_TRAVERSAL);
+        }
+    }
+
+    public void unscheduleTraversals() {
+        if (mTraversalScheduled) {
+            mTraversalScheduled = false;
+            removeMessages(DO_TRAVERSAL);
+        }
+    }
+
+    int getHostVisibility() {
+        return mAppVisible ? mView.getVisibility() : View.GONE;
+    }
+    
+    private void performTraversals() {
+        // cache mView since it is used so much below...
+        final View host = mView;
+
+        if (DBG) {
+            System.out.println("======================================");
+            System.out.println("performTraversals");
+            host.debug();
+        }
+
+        if (host == null || !mAdded)
+            return;
+
+        mTraversalScheduled = false;
+        mWillDrawSoon = true;
+        boolean windowResizesToFitContent = false;
+        boolean fullRedrawNeeded = mFullRedrawNeeded;
+        boolean newSurface = false;
+        WindowManager.LayoutParams lp = mWindowAttributes;
+
+        int desiredWindowWidth;
+        int desiredWindowHeight;
+        int childWidthMeasureSpec;
+        int childHeightMeasureSpec;
+
+        final View.AttachInfo attachInfo = mAttachInfo;
+
+        final int viewVisibility = getHostVisibility();
+        boolean viewVisibilityChanged = mViewVisibility != viewVisibility
+                || mNewSurfaceNeeded;
+
+        WindowManager.LayoutParams params = null;
+        if (mWindowAttributesChanged) {
+            mWindowAttributesChanged = false;
+            params = lp;
+        }
+
+        if (mFirst) {
+            fullRedrawNeeded = true;
+            mLayoutRequested = true;
+
+            Display d = new Display(0);
+            desiredWindowWidth = d.getWidth();
+            desiredWindowHeight = d.getHeight();
+
+            // For the very first time, tell the view hierarchy that it
+            // is attached to the window.  Note that at this point the surface
+            // object is not initialized to its backing store, but soon it
+            // will be (assuming the window is visible).
+            attachInfo.mSurface = mSurface;
+            attachInfo.mHasWindowFocus = false;
+            attachInfo.mWindowVisibility = viewVisibility;
+            attachInfo.mRecomputeGlobalAttributes = false;
+            attachInfo.mKeepScreenOn = false;
+            viewVisibilityChanged = false;
+            host.dispatchAttachedToWindow(attachInfo, 0);
+            getRunQueue().executeActions(attachInfo.mHandler);
+            //Log.i(TAG, "Screen on initialized: " + attachInfo.mKeepScreenOn);
+        } else {
+            desiredWindowWidth = mWinFrame.width();
+            desiredWindowHeight = mWinFrame.height();
+            if (desiredWindowWidth != mWidth || desiredWindowHeight != mHeight) {
+                if (DEBUG_ORIENTATION) Log.v("ViewRoot",
+                        "View " + host + " resized to: " + mWinFrame);
+                fullRedrawNeeded = true;
+                mLayoutRequested = true;
+                windowResizesToFitContent = true;
+            }
+        }
+
+        if (viewVisibilityChanged) {
+            attachInfo.mWindowVisibility = viewVisibility;
+            host.dispatchWindowVisibilityChanged(viewVisibility);
+            if (viewVisibility != View.VISIBLE || mNewSurfaceNeeded) {
+                if (mUseGL) {
+                    destroyGL();
+                }
+            }
+            if (viewVisibility == View.GONE) {
+                // After making a window gone, we will count it as being
+                // shown for the first time the next time it gets focus.
+                mHasHadWindowFocus = false;
+            }
+        }
+
+        boolean insetsChanged = false;
+        
+        if (mLayoutRequested) {
+            if (mFirst) {
+                host.fitSystemWindows(mAttachInfo.mContentInsets);
+                // make sure touch mode code executes by setting cached value
+                // to opposite of the added touch mode.
+                mAttachInfo.mInTouchMode = !mAddedTouchMode;
+                ensureTouchModeLocally(mAddedTouchMode);
+            } else {
+                if (!mAttachInfo.mContentInsets.equals(mPendingContentInsets)) {
+                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
+                    host.fitSystemWindows(mAttachInfo.mContentInsets);
+                    insetsChanged = true;
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+                            + mAttachInfo.mContentInsets);
+                }
+                if (!mAttachInfo.mVisibleInsets.equals(mPendingVisibleInsets)) {
+                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+                            + mAttachInfo.mVisibleInsets);
+                }
+                if (lp.width == ViewGroup.LayoutParams.WRAP_CONTENT
+                        || lp.height == ViewGroup.LayoutParams.WRAP_CONTENT) {
+                    windowResizesToFitContent = true;
+
+                    Display d = new Display(0);
+                    desiredWindowWidth = d.getWidth();
+                    desiredWindowHeight = d.getHeight();
+                }
+            }
+
+            childWidthMeasureSpec = getRootMeasureSpec(desiredWindowWidth, lp.width);
+            childHeightMeasureSpec = getRootMeasureSpec(desiredWindowHeight, lp.height);
+
+            // Ask host how big it wants to be
+            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v("ViewRoot",
+                    "Measuring " + host + " in display " + desiredWindowWidth
+                    + "x" + desiredWindowHeight + "...");
+            host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+
+            if (DBG) {
+                System.out.println("======================================");
+                System.out.println("performTraversals -- after measure");
+                host.debug();
+            }
+        }
+
+        if (attachInfo.mRecomputeGlobalAttributes) {
+            //Log.i(TAG, "Computing screen on!");
+            attachInfo.mRecomputeGlobalAttributes = false;
+            boolean oldVal = attachInfo.mKeepScreenOn;
+            attachInfo.mKeepScreenOn = false;
+            host.dispatchCollectViewAttributes(0);
+            if (attachInfo.mKeepScreenOn != oldVal) {
+                params = lp;
+                //Log.i(TAG, "Keep screen on changed: " + attachInfo.mKeepScreenOn);
+            }
+        }
+
+        if (mFirst || attachInfo.mViewVisibilityChanged) {
+            attachInfo.mViewVisibilityChanged = false;
+            int resizeMode = mSoftInputMode &
+                    WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST;
+            // If we are in auto resize mode, then we need to determine
+            // what mode to use now.
+            if (resizeMode == WindowManager.LayoutParams.SOFT_INPUT_ADJUST_UNSPECIFIED) {
+                final int N = attachInfo.mScrollContainers.size();
+                for (int i=0; i<N; i++) {
+                    if (attachInfo.mScrollContainers.get(i).isShown()) {
+                        resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_RESIZE;
+                    }
+                }
+                if (resizeMode == 0) {
+                    resizeMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_PAN;
+                }
+                if ((lp.softInputMode &
+                        WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) != resizeMode) {
+                    lp.softInputMode = (lp.softInputMode &
+                            ~WindowManager.LayoutParams.SOFT_INPUT_MASK_ADJUST) |
+                            resizeMode;
+                    params = lp;
+                }
+            }
+        }
+        
+        if (params != null && (host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
+            if (!PixelFormat.formatHasAlpha(params.format)) {
+                params.format = PixelFormat.TRANSLUCENT;
+            }
+        }
+
+        boolean windowShouldResize = mLayoutRequested && windowResizesToFitContent
+            && (mWidth != host.mMeasuredWidth || mHeight != host.mMeasuredHeight);
+
+        final boolean computesInternalInsets =
+                attachInfo.mTreeObserver.hasComputeInternalInsetsListeners();
+        boolean insetsPending = false;
+        int relayoutResult = 0;
+        if (mFirst || windowShouldResize || insetsChanged
+                || viewVisibilityChanged || params != null) {
+
+            if (viewVisibility == View.VISIBLE) {
+                // If this window is giving internal insets to the window
+                // manager, and it is being added or changing its visibility,
+                // then we want to first give the window manager "fake"
+                // insets to cause it to effectively ignore the content of
+                // the window during layout.  This avoids it briefly causing
+                // other windows to resize/move based on the raw frame of the
+                // window, waiting until we can finish laying out this window
+                // and get back to the window manager with the ultimately
+                // computed insets.
+                insetsPending = computesInternalInsets
+                        && (mFirst || viewVisibilityChanged);
+                
+                if (mWindowAttributes.memoryType == WindowManager.LayoutParams.MEMORY_TYPE_GPU) {
+                    if (params == null) {
+                        params = mWindowAttributes;
+                    }
+                    mGlWanted = true;
+                }
+            }
+
+            final Rect frame = mWinFrame;
+            boolean initialized = false;
+            boolean contentInsetsChanged = false;
+            boolean visibleInsetsChanged = false;
+            try {
+                boolean hadSurface = mSurface.isValid();
+                int fl = 0;
+                if (params != null) {
+                    fl = params.flags;
+                    if (attachInfo.mKeepScreenOn) {
+                        params.flags |= WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON;
+                    }
+                }
+                relayoutResult = sWindowSession.relayout(
+                    mWindow, params, host.mMeasuredWidth, host.mMeasuredHeight,
+                    viewVisibility, insetsPending, frame,
+                    mPendingContentInsets, mPendingVisibleInsets, mSurface);
+                if (params != null) {
+                    params.flags = fl;
+                }
+
+                if (DEBUG_LAYOUT) Log.v(TAG, "relayout: frame=" + frame.toShortString()
+                        + " content=" + mPendingContentInsets.toShortString()
+                        + " visible=" + mPendingVisibleInsets.toShortString()
+                        + " surface=" + mSurface);
+                
+                contentInsetsChanged = !mPendingContentInsets.equals(
+                        mAttachInfo.mContentInsets);
+                visibleInsetsChanged = !mPendingVisibleInsets.equals(
+                        mAttachInfo.mVisibleInsets);
+                if (contentInsetsChanged) {
+                    mAttachInfo.mContentInsets.set(mPendingContentInsets);
+                    host.fitSystemWindows(mAttachInfo.mContentInsets);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Content insets changing to: "
+                            + mAttachInfo.mContentInsets);
+                }
+                if (visibleInsetsChanged) {
+                    mAttachInfo.mVisibleInsets.set(mPendingVisibleInsets);
+                    if (DEBUG_LAYOUT) Log.v(TAG, "Visible insets changing to: "
+                            + mAttachInfo.mVisibleInsets);
+                }
+
+                if (!hadSurface) {
+                    if (mSurface.isValid()) {
+                        // If we are creating a new surface, then we need to
+                        // completely redraw it.  Also, when we get to the
+                        // point of drawing it we will hold off and schedule
+                        // a new traversal instead.  This is so we can tell the
+                        // window manager about all of the windows being displayed
+                        // before actually drawing them, so it can display then
+                        // all at once.
+                        newSurface = true;
+                        fullRedrawNeeded = true;
+    
+                        if (mGlWanted && !mUseGL) {
+                            initializeGL();
+                            initialized = mGlCanvas != null;
+                        }
+                    }
+                } else if (!mSurface.isValid()) {
+                    // If the surface has been removed, then reset the scroll
+                    // positions.
+                    mLastScrolledFocus = null;
+                    mScrollY = mCurScrollY = 0;
+                    if (mScroller != null) {
+                        mScroller.abortAnimation();
+                    }
+                }
+            } catch (RemoteException e) {
+            }
+            if (DEBUG_ORIENTATION) Log.v(
+                    "ViewRoot", "Relayout returned: frame=" + mWinFrame + ", surface=" + mSurface);
+
+            attachInfo.mWindowLeft = frame.left;
+            attachInfo.mWindowTop = frame.top;
+
+            // !!FIXME!! This next section handles the case where we did not get the
+            // window size we asked for. We should avoid this by getting a maximum size from
+            // the window session beforehand.
+            mWidth = frame.width();
+            mHeight = frame.height();
+
+            if (initialized) {
+                mGlCanvas.setViewport(mWidth, mHeight);
+            }
+
+            boolean focusChangedDueToTouchMode = ensureTouchModeLocally(
+                    (relayoutResult&WindowManagerImpl.RELAYOUT_IN_TOUCH_MODE) != 0);
+            if (focusChangedDueToTouchMode || mWidth != host.mMeasuredWidth
+                    || mHeight != host.mMeasuredHeight || contentInsetsChanged) {
+                childWidthMeasureSpec = getRootMeasureSpec(mWidth, lp.width);
+                childHeightMeasureSpec = getRootMeasureSpec(mHeight, lp.height);
+
+                if (DEBUG_LAYOUT) Log.v(TAG, "Ooops, something changed!  mWidth="
+                        + mWidth + " measuredWidth=" + host.mMeasuredWidth
+                        + " mHeight=" + mHeight
+                        + " measuredHeight" + host.mMeasuredHeight
+                        + " coveredInsetsChanged=" + contentInsetsChanged);
+                
+                 // Ask host how big it wants to be
+                host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+
+                // Implementation of weights from WindowManager.LayoutParams
+                // We just grow the dimensions as needed and re-measure if
+                // needs be
+                int width = host.mMeasuredWidth;
+                int height = host.mMeasuredHeight;
+                boolean measureAgain = false;
+
+                if (lp.horizontalWeight > 0.0f) {
+                    width += (int) ((mWidth - width) * lp.horizontalWeight);
+                    childWidthMeasureSpec = MeasureSpec.makeMeasureSpec(width,
+                            MeasureSpec.EXACTLY);
+                    measureAgain = true;
+                }
+                if (lp.verticalWeight > 0.0f) {
+                    height += (int) ((mHeight - height) * lp.verticalWeight);
+                    childHeightMeasureSpec = MeasureSpec.makeMeasureSpec(height,
+                            MeasureSpec.EXACTLY);
+                    measureAgain = true;
+                }
+
+                if (measureAgain) {
+                    if (DEBUG_LAYOUT) Log.v(TAG,
+                            "And hey let's measure once more: width=" + width
+                            + " height=" + height);
+                    host.measure(childWidthMeasureSpec, childHeightMeasureSpec);
+                }
+
+                mLayoutRequested = true;
+            }
+        }
+
+        final boolean didLayout = mLayoutRequested;
+        boolean triggerGlobalLayoutListener = didLayout
+                || attachInfo.mRecomputeGlobalAttributes;
+        if (didLayout) {
+            mLayoutRequested = false;
+            mScrollMayChange = true;
+            if (DEBUG_ORIENTATION || DEBUG_LAYOUT) Log.v(
+                "ViewRoot", "Laying out " + host + " to (" +
+                host.mMeasuredWidth + ", " + host.mMeasuredHeight + ")");
+            long startTime;
+            if (PROFILE_LAYOUT) {
+                startTime = SystemClock.elapsedRealtime();
+            }
+
+            host.layout(0, 0, host.mMeasuredWidth, host.mMeasuredHeight);
+
+            if (PROFILE_LAYOUT) {
+                EventLog.writeEvent(60001, SystemClock.elapsedRealtime() - startTime);
+            }
+
+            // By this point all views have been sized and positionned
+            // We can compute the transparent area
+
+            if ((host.mPrivateFlags & View.REQUEST_TRANSPARENT_REGIONS) != 0) {
+                // start out transparent
+                // TODO: AVOID THAT CALL BY CACHING THE RESULT?
+                host.getLocationInWindow(mTmpLocation);
+                mTransparentRegion.set(mTmpLocation[0], mTmpLocation[1],
+                        mTmpLocation[0] + host.mRight - host.mLeft,
+                        mTmpLocation[1] + host.mBottom - host.mTop);
+
+                host.gatherTransparentRegion(mTransparentRegion);
+                if (!mTransparentRegion.equals(mPreviousTransparentRegion)) {
+                    mPreviousTransparentRegion.set(mTransparentRegion);
+                    // reconfigure window manager
+                    try {
+                        sWindowSession.setTransparentRegion(mWindow, mTransparentRegion);
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+
+
+            if (DBG) {
+                System.out.println("======================================");
+                System.out.println("performTraversals -- after setFrame");
+                host.debug();
+            }
+        }
+
+        if (triggerGlobalLayoutListener) {
+            attachInfo.mRecomputeGlobalAttributes = false;
+            attachInfo.mTreeObserver.dispatchOnGlobalLayout();
+        }
+
+        if (computesInternalInsets) {
+            ViewTreeObserver.InternalInsetsInfo insets = attachInfo.mGivenInternalInsets;
+            final Rect givenContent = attachInfo.mGivenInternalInsets.contentInsets;
+            final Rect givenVisible = attachInfo.mGivenInternalInsets.visibleInsets;
+            givenContent.left = givenContent.top = givenContent.right
+                    = givenContent.bottom = givenVisible.left = givenVisible.top
+                    = givenVisible.right = givenVisible.bottom = 0;
+            attachInfo.mTreeObserver.dispatchOnComputeInternalInsets(insets);
+            if (insetsPending || !mLastGivenInsets.equals(insets)) {
+                mLastGivenInsets.set(insets);
+                try {
+                    sWindowSession.setInsets(mWindow, insets.mTouchableInsets,
+                            insets.contentInsets, insets.visibleInsets);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+            
+        if (mFirst) {
+            // handle first focus request
+            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: mView.hasFocus()="
+                    + mView.hasFocus());
+            if (mView != null) {
+                if (!mView.hasFocus()) {
+                    mView.requestFocus(View.FOCUS_FORWARD);
+                    mFocusedView = mRealFocusedView = mView.findFocus();
+                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: requested focused view="
+                            + mFocusedView);
+                } else {
+                    mRealFocusedView = mView.findFocus();
+                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "First: existing focused view="
+                            + mRealFocusedView);
+                }
+            }
+        }
+
+        mFirst = false;
+        mWillDrawSoon = false;
+        mNewSurfaceNeeded = false;
+        mViewVisibility = viewVisibility;
+
+        if (mAttachInfo.mHasWindowFocus) {
+            final boolean imTarget = WindowManager.LayoutParams
+                    .mayUseInputMethod(mWindowAttributes.flags);
+            if (imTarget != mLastWasImTarget) {
+                mLastWasImTarget = imTarget;
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (imm != null && imTarget) {
+                    imm.startGettingWindowFocus(mView);
+                    imm.onWindowFocus(mView, mView.findFocus(),
+                            mWindowAttributes.softInputMode,
+                            !mHasHadWindowFocus, mWindowAttributes.flags);
+                }
+            }
+        }
+        
+        boolean cancelDraw = attachInfo.mTreeObserver.dispatchOnPreDraw();
+
+        if (!cancelDraw && !newSurface) {
+            mFullRedrawNeeded = false;
+            draw(fullRedrawNeeded);
+
+            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0
+                    || mReportNextDraw) {
+                if (LOCAL_LOGV) {
+                    Log.v("ViewRoot", "FINISHED DRAWING: " + mWindowAttributes.getTitle());
+                }
+                mReportNextDraw = false;
+                try {
+                    sWindowSession.finishDrawing(mWindow);
+                } catch (RemoteException e) {
+                }
+            }
+        } else {
+            // We were supposed to report when we are done drawing. Since we canceled the
+            // draw, remember it here.
+            if ((relayoutResult&WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                mReportNextDraw = true;
+            }
+            if (fullRedrawNeeded) {
+                mFullRedrawNeeded = true;
+            }
+            // Try again
+            scheduleTraversals();
+        }
+    }
+
+    public void requestTransparentRegion(View child) {
+        // the test below should not fail unless someone is messing with us
+        checkThread();
+        if (mView == child) {
+            mView.mPrivateFlags |= View.REQUEST_TRANSPARENT_REGIONS;
+            // Need to make sure we re-evaluate the window attributes next
+            // time around, to ensure the window has the correct format.
+            mWindowAttributesChanged = true;
+        }
+    }
+
+    /**
+     * Figures out the measure spec for the root view in a window based on it's
+     * layout params.
+     *
+     * @param windowSize
+     *            The available width or height of the window
+     *
+     * @param rootDimension
+     *            The layout params for one dimension (width or height) of the
+     *            window.
+     *
+     * @return The measure spec to use to measure the root view.
+     */
+    private int getRootMeasureSpec(int windowSize, int rootDimension) {
+        int measureSpec;
+        switch (rootDimension) {
+
+        case ViewGroup.LayoutParams.FILL_PARENT:
+            // Window can't resize. Force root view to be windowSize.
+            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.EXACTLY);
+            break;
+        case ViewGroup.LayoutParams.WRAP_CONTENT:
+            // Window can resize. Set max size for root view.
+            measureSpec = MeasureSpec.makeMeasureSpec(windowSize, MeasureSpec.AT_MOST);
+            break;
+        default:
+            // Window wants to be an exact size. Force root view to be that size.
+            measureSpec = MeasureSpec.makeMeasureSpec(rootDimension, MeasureSpec.EXACTLY);
+            break;
+        }
+        return measureSpec;
+    }
+
+    private void draw(boolean fullRedrawNeeded) {
+        Surface surface = mSurface;
+        if (surface == null || !surface.isValid()) {
+            return;
+        }
+
+        scrollToRectOrFocus(null, false);
+
+        if (mAttachInfo.mViewScrollChanged) {
+            mAttachInfo.mViewScrollChanged = false;
+            mAttachInfo.mTreeObserver.dispatchOnScrollChanged();
+        }
+        
+        int yoff;
+        final boolean scrolling = mScroller != null
+                && mScroller.computeScrollOffset(); 
+        if (scrolling) {
+            yoff = mScroller.getCurrY();
+        } else {
+            yoff = mScrollY;
+        }
+        if (mCurScrollY != yoff) {
+            mCurScrollY = yoff;
+            fullRedrawNeeded = true;
+        }
+
+        Rect dirty = mDirty;
+        if (mUseGL) {
+            if (!dirty.isEmpty()) {
+                Canvas canvas = mGlCanvas;
+                if (mGL!=null && canvas != null) {
+                    mGL.glDisable(GL_SCISSOR_TEST);
+                    mGL.glClearColor(0, 0, 0, 0);
+                    mGL.glClear(GL_COLOR_BUFFER_BIT);
+                    mGL.glEnable(GL_SCISSOR_TEST);
+
+                    mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
+                    canvas.translate(0, -yoff);
+                    mView.mPrivateFlags |= View.DRAWN;
+                    mView.draw(canvas);
+                    canvas.translate(0, yoff);
+
+                    mEgl.eglSwapBuffers(mEglDisplay, mEglSurface);
+                    checkEglErrors();
+
+                    if (SHOW_FPS) {
+                        int now = (int)SystemClock.elapsedRealtime();
+                        if (sDrawTime != 0) {
+                            nativeShowFPS(canvas, now - sDrawTime);
+                        }
+                        sDrawTime = now;
+                    }
+                }
+            }
+            if (scrolling) {
+                mFullRedrawNeeded = true;
+                scheduleTraversals();
+            }
+            return;
+        }
+
+        if (fullRedrawNeeded)
+            dirty.union(0, 0, mWidth, mHeight);
+
+        if (DEBUG_ORIENTATION || DEBUG_DRAW) {
+            Log.v("ViewRoot", "Draw " + mView + "/"
+                    + mWindowAttributes.getTitle()
+                    + ": dirty={" + dirty.left + "," + dirty.top
+                    + "," + dirty.right + "," + dirty.bottom + "} surface="
+                    + surface + " surface.isValid()=" + surface.isValid());
+        }
+
+        Canvas canvas;
+        try {
+            canvas = surface.lockCanvas(dirty);
+            // TODO: Do this in native
+            canvas.setDensityScale(mDensity);
+        } catch (Surface.OutOfResourcesException e) {
+            Log.e("ViewRoot", "OutOfResourcesException locking surface", e);
+            // TODO: we should ask the window manager to do something!
+            // for now we just do nothing
+            return;
+        }
+
+        try {
+            if (!dirty.isEmpty()) {
+                long startTime;
+
+                if (DEBUG_ORIENTATION || DEBUG_DRAW) {
+                    Log.v("ViewRoot", "Surface " + surface + " drawing to bitmap w="
+                            + canvas.getWidth() + ", h=" + canvas.getHeight());
+                    //canvas.drawARGB(255, 255, 0, 0);
+                }
+
+                if (PROFILE_DRAWING) {
+                    startTime = SystemClock.elapsedRealtime();
+                }
+
+                // If this bitmap's format includes an alpha channel, we
+                // need to clear it before drawing so that the child will
+                // properly re-composite its drawing on a transparent
+                // background. This automatically respects the clip/dirty region
+                if (!canvas.isOpaque()) {
+                    canvas.drawColor(0x00000000, PorterDuff.Mode.CLEAR);
+                } else if (yoff != 0) {
+                    // If we are applying an offset, we need to clear the area
+                    // where the offset doesn't appear to avoid having garbage
+                    // left in the blank areas.
+                    canvas.drawColor(0, PorterDuff.Mode.CLEAR);
+                }
+
+                dirty.setEmpty();
+                mAttachInfo.mDrawingTime = SystemClock.uptimeMillis();
+                canvas.translate(0, -yoff);
+                mView.mPrivateFlags |= View.DRAWN;                    
+                mView.draw(canvas);
+                canvas.translate(0, yoff);
+
+                if (SHOW_FPS) {
+                    int now = (int)SystemClock.elapsedRealtime();
+                    if (sDrawTime != 0) {
+                        nativeShowFPS(canvas, now - sDrawTime);
+                    }
+                    sDrawTime = now;
+                }
+
+                if (PROFILE_DRAWING) {
+                    EventLog.writeEvent(60000, SystemClock.elapsedRealtime() - startTime);
+                }
+            }
+            
+        } finally {
+            surface.unlockCanvasAndPost(canvas);
+        }
+
+        if (LOCAL_LOGV) {
+            Log.v("ViewRoot", "Surface " + surface + " unlockCanvasAndPost");
+        }
+        
+        if (scrolling) {
+            mFullRedrawNeeded = true;
+            scheduleTraversals();
+        }
+    }
+
+    boolean scrollToRectOrFocus(Rect rectangle, boolean immediate) {
+        final View.AttachInfo attachInfo = mAttachInfo;
+        final Rect ci = attachInfo.mContentInsets;
+        final Rect vi = attachInfo.mVisibleInsets;
+        int scrollY = 0;
+        boolean handled = false;
+        
+        if (vi.left > ci.left || vi.top > ci.top
+                || vi.right > ci.right || vi.bottom > ci.bottom) {
+            // We'll assume that we aren't going to change the scroll
+            // offset, since we want to avoid that unless it is actually
+            // going to make the focus visible...  otherwise we scroll
+            // all over the place.
+            scrollY = mScrollY;
+            // We can be called for two different situations: during a draw,
+            // to update the scroll position if the focus has changed (in which
+            // case 'rectangle' is null), or in response to a
+            // requestChildRectangleOnScreen() call (in which case 'rectangle'
+            // is non-null and we just want to scroll to whatever that
+            // rectangle is).
+            View focus = mRealFocusedView;
+            if (focus != mLastScrolledFocus) {
+                // If the focus has changed, then ignore any requests to scroll
+                // to a rectangle; first we want to make sure the entire focus
+                // view is visible.
+                rectangle = null;
+            }
+            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Eval scroll: focus=" + focus
+                    + " rectangle=" + rectangle + " ci=" + ci
+                    + " vi=" + vi);
+            if (focus == mLastScrolledFocus && !mScrollMayChange
+                    && rectangle == null) {
+                // Optimization: if the focus hasn't changed since last
+                // time, and no layout has happened, then just leave things
+                // as they are.
+                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Keeping scroll y="
+                        + mScrollY + " vi=" + vi.toShortString());
+            } else if (focus != null) {
+                // We need to determine if the currently focused view is
+                // within the visible part of the window and, if not, apply
+                // a pan so it can be seen.
+                mLastScrolledFocus = focus;
+                mScrollMayChange = false;
+                if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Need to scroll?");
+                // Try to find the rectangle from the focus view.
+                if (focus.getGlobalVisibleRect(mVisRect, null)) {
+                    if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Root w="
+                            + mView.getWidth() + " h=" + mView.getHeight()
+                            + " ci=" + ci.toShortString()
+                            + " vi=" + vi.toShortString());
+                    if (rectangle == null) {
+                        focus.getFocusedRect(mTempRect);
+                        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Focus " + focus
+                                + ": focusRect=" + mTempRect.toShortString());
+                        ((ViewGroup) mView).offsetDescendantRectToMyCoords(
+                                focus, mTempRect);
+                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                "Focus in window: focusRect="
+                                + mTempRect.toShortString()
+                                + " visRect=" + mVisRect.toShortString());
+                    } else {
+                        mTempRect.set(rectangle);
+                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                "Request scroll to rect: "
+                                + mTempRect.toShortString()
+                                + " visRect=" + mVisRect.toShortString());
+                    }
+                    if (mTempRect.intersect(mVisRect)) {
+                        if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                "Focus window visible rect: "
+                                + mTempRect.toShortString());
+                        if (mTempRect.height() >
+                                (mView.getHeight()-vi.top-vi.bottom)) {
+                            // If the focus simply is not going to fit, then
+                            // best is probably just to leave things as-is.
+                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                    "Too tall; leaving scrollY=" + scrollY);
+                        } else if ((mTempRect.top-scrollY) < vi.top) {
+                            scrollY -= vi.top - (mTempRect.top-scrollY);
+                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                    "Top covered; scrollY=" + scrollY);
+                        } else if ((mTempRect.bottom-scrollY)
+                                > (mView.getHeight()-vi.bottom)) {
+                            scrollY += (mTempRect.bottom-scrollY)
+                                    - (mView.getHeight()-vi.bottom);
+                            if (DEBUG_INPUT_RESIZE) Log.v(TAG,
+                                    "Bottom covered; scrollY=" + scrollY);
+                        }
+                        handled = true;
+                    }
+                }
+            }
+        }
+        
+        if (scrollY != mScrollY) {
+            if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Pan scroll changed: old="
+                    + mScrollY + " , new=" + scrollY);
+            if (!immediate) {
+                if (mScroller == null) {
+                    mScroller = new Scroller(mView.getContext());
+                }
+                mScroller.startScroll(0, mScrollY, 0, scrollY-mScrollY);
+            } else if (mScroller != null) {
+                mScroller.abortAnimation();
+            }
+            mScrollY = scrollY;
+        }
+        
+        return handled;
+    }
+    
+    public void requestChildFocus(View child, View focused) {
+        checkThread();
+        if (mFocusedView != focused) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(mFocusedView, focused);
+            scheduleTraversals();
+        }
+        mFocusedView = mRealFocusedView = focused;
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Request child focus: focus now "
+                + mFocusedView);
+    }
+
+    public void clearChildFocus(View child) {
+        checkThread();
+
+        View oldFocus = mFocusedView;
+
+        if (DEBUG_INPUT_RESIZE) Log.v(TAG, "Clearing child focus");
+        mFocusedView = mRealFocusedView = null;
+        if (mView != null && !mView.hasFocus()) {
+            // If a view gets the focus, the listener will be invoked from requestChildFocus()
+            if (!mView.requestFocus(View.FOCUS_FORWARD)) {
+                mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+            }
+        } else if (oldFocus != null) {
+            mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(oldFocus, null);
+        }
+    }
+
+
+    public void focusableViewAvailable(View v) {
+        checkThread();
+
+        if (mView != null && !mView.hasFocus()) {
+            v.requestFocus();
+        } else {
+            // the one case where will transfer focus away from the current one
+            // is if the current view is a view group that prefers to give focus
+            // to its children first AND the view is a descendant of it.
+            mFocusedView = mView.findFocus();
+            boolean descendantsHaveDibsOnFocus =
+                    (mFocusedView instanceof ViewGroup) &&
+                        (((ViewGroup) mFocusedView).getDescendantFocusability() ==
+                                ViewGroup.FOCUS_AFTER_DESCENDANTS);
+            if (descendantsHaveDibsOnFocus && isViewDescendantOf(v, mFocusedView)) {
+                // If a view gets the focus, the listener will be invoked from requestChildFocus()
+                v.requestFocus();
+            }
+        }
+    }
+
+    public void recomputeViewAttributes(View child) {
+        checkThread();
+        if (mView == child) {
+            mAttachInfo.mRecomputeGlobalAttributes = true;
+            if (!mWillDrawSoon) {
+                scheduleTraversals();
+            }
+        }
+    }
+
+    void dispatchDetachedFromWindow() {
+        if (Config.LOGV) Log.v("ViewRoot", "Detaching in " + this + " of " + mSurface);
+
+        if (mView != null) {
+            mView.dispatchDetachedFromWindow();
+        }
+
+        mView = null;
+        mAttachInfo.mRootView = null;
+
+        if (mUseGL) {
+            destroyGL();
+        }
+
+        try {
+            sWindowSession.remove(mWindow);
+        } catch (RemoteException e) {
+        }
+    }
+    
+    /**
+     * Return true if child is an ancestor of parent, (or equal to the parent).
+     */
+    private static boolean isViewDescendantOf(View child, View parent) {
+        if (child == parent) {
+            return true;
+        }
+
+        final ViewParent theParent = child.getParent();
+        return (theParent instanceof ViewGroup) && isViewDescendantOf((View) theParent, parent);
+    }
+
+
+    public final static int DO_TRAVERSAL = 1000;
+    public final static int DIE = 1001;
+    public final static int RESIZED = 1002;
+    public final static int RESIZED_REPORT = 1003;
+    public final static int WINDOW_FOCUS_CHANGED = 1004;
+    public final static int DISPATCH_KEY = 1005;
+    public final static int DISPATCH_POINTER = 1006;
+    public final static int DISPATCH_TRACKBALL = 1007;
+    public final static int DISPATCH_APP_VISIBILITY = 1008;
+    public final static int DISPATCH_GET_NEW_SURFACE = 1009;
+    public final static int FINISHED_EVENT = 1010;
+    public final static int DISPATCH_KEY_FROM_IME = 1011;
+    public final static int FINISH_INPUT_CONNECTION = 1012;
+    public final static int CHECK_FOCUS = 1013;
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+        case View.AttachInfo.INVALIDATE_MSG:
+            ((View) msg.obj).invalidate();
+            break;
+        case View.AttachInfo.INVALIDATE_RECT_MSG:
+            final View.AttachInfo.InvalidateInfo info = (View.AttachInfo.InvalidateInfo) msg.obj;
+            info.target.invalidate(info.left, info.top, info.right, info.bottom);
+            info.release();
+            break;
+        case DO_TRAVERSAL:
+            if (mProfile) {
+                Debug.startMethodTracing("ViewRoot");
+            }
+
+            performTraversals();
+
+            if (mProfile) {
+                Debug.stopMethodTracing();
+                mProfile = false;
+            }
+            break;
+        case FINISHED_EVENT:
+            handleFinishedEvent(msg.arg1, msg.arg2 != 0);
+            break;
+        case DISPATCH_KEY:
+            if (LOCAL_LOGV) Log.v(
+                "ViewRoot", "Dispatching key "
+                + msg.obj + " to " + mView);
+            deliverKeyEvent((KeyEvent)msg.obj, true);
+            break;
+        case DISPATCH_POINTER:
+            MotionEvent event = (MotionEvent)msg.obj;
+
+            boolean didFinish;
+            if (event == null) {
+                try {
+                    event = sWindowSession.getPendingPointerMove(mWindow);
+                } catch (RemoteException e) {
+                }
+                didFinish = true;
+            } else {
+                didFinish = event.getAction() == MotionEvent.ACTION_OUTSIDE;
+            }
+
+            try {
+                boolean handled;
+                if (mView != null && mAdded && event != null) {
+
+                    // enter touch mode on the down
+                    boolean isDown = event.getAction() == MotionEvent.ACTION_DOWN;
+                    if (isDown) {
+                        ensureTouchMode(true);
+                    }
+                    if(Config.LOGV) {
+                        captureMotionLog("captureDispatchPointer", event);
+                    }
+                    event.offsetLocation(0, mCurScrollY);
+                    handled = mView.dispatchTouchEvent(event);
+                    if (!handled && isDown) {
+                        int edgeSlop = mViewConfiguration.getScaledEdgeSlop();
+
+                        final int edgeFlags = event.getEdgeFlags();
+                        int direction = View.FOCUS_UP;
+                        int x = (int)event.getX();
+                        int y = (int)event.getY();
+                        final int[] deltas = new int[2];
+
+                        if ((edgeFlags & MotionEvent.EDGE_TOP) != 0) {
+                            direction = View.FOCUS_DOWN;
+                            if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+                                deltas[0] = edgeSlop;
+                                x += edgeSlop;
+                            } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+                                deltas[0] = -edgeSlop;
+                                x -= edgeSlop;
+                            }
+                        } else if ((edgeFlags & MotionEvent.EDGE_BOTTOM) != 0) {
+                            direction = View.FOCUS_UP;
+                            if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+                                deltas[0] = edgeSlop;
+                                x += edgeSlop;
+                            } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+                                deltas[0] = -edgeSlop;
+                                x -= edgeSlop;
+                            }
+                        } else if ((edgeFlags & MotionEvent.EDGE_LEFT) != 0) {
+                            direction = View.FOCUS_RIGHT;
+                        } else if ((edgeFlags & MotionEvent.EDGE_RIGHT) != 0) {
+                            direction = View.FOCUS_LEFT;
+                        }
+
+                        if (edgeFlags != 0 && mView instanceof ViewGroup) {
+                            View nearest = FocusFinder.getInstance().findNearestTouchable(
+                                    ((ViewGroup) mView), x, y, direction, deltas);
+                            if (nearest != null) {
+                                event.offsetLocation(deltas[0], deltas[1]);
+                                event.setEdgeFlags(0);
+                                mView.dispatchTouchEvent(event);
+                            }
+                        }
+                    }
+                }
+            } finally {
+                if (!didFinish) {
+                    try {
+                        sWindowSession.finishKey(mWindow);
+                    } catch (RemoteException e) {
+                    }
+                }
+                if (event != null) {
+                    event.recycle();
+                }
+                if (LOCAL_LOGV || WATCH_POINTER) Log.i(TAG, "Done dispatching!");
+                // Let the exception fall through -- the looper will catch
+                // it and take care of the bad app for us.
+            }
+            break;
+        case DISPATCH_TRACKBALL:
+            deliverTrackballEvent((MotionEvent)msg.obj);
+            break;
+        case DISPATCH_APP_VISIBILITY:
+            handleAppVisibility(msg.arg1 != 0);
+            break;
+        case DISPATCH_GET_NEW_SURFACE:
+            handleGetNewSurface();
+            break;
+        case RESIZED:
+            Rect coveredInsets = ((Rect[])msg.obj)[0];
+            Rect visibleInsets = ((Rect[])msg.obj)[1];
+            if (mWinFrame.width() == msg.arg1 && mWinFrame.height() == msg.arg2
+                    && mPendingContentInsets.equals(coveredInsets)
+                    && mPendingVisibleInsets.equals(visibleInsets)) {
+                break;
+            }
+            // fall through...
+        case RESIZED_REPORT:
+            if (mAdded) {
+                mWinFrame.left = 0;
+                mWinFrame.right = msg.arg1;
+                mWinFrame.top = 0;
+                mWinFrame.bottom = msg.arg2;
+                mPendingContentInsets.set(((Rect[])msg.obj)[0]);
+                mPendingVisibleInsets.set(((Rect[])msg.obj)[1]);
+                if (msg.what == RESIZED_REPORT) {
+                    mReportNextDraw = true;
+                }
+                requestLayout();
+            }
+            break;
+        case WINDOW_FOCUS_CHANGED: {
+            if (mAdded) {
+                boolean hasWindowFocus = msg.arg1 != 0;
+                mAttachInfo.mHasWindowFocus = hasWindowFocus;
+                if (hasWindowFocus) {
+                    boolean inTouchMode = msg.arg2 != 0;
+                    ensureTouchModeLocally(inTouchMode);
+
+                    if (mGlWanted) {
+                        checkEglErrors();
+                        // we lost the gl context, so recreate it.
+                        if (mGlWanted && !mUseGL) {
+                            initializeGL();
+                            if (mGlCanvas != null) {
+                                mGlCanvas.setViewport(mWidth, mHeight);
+                            }
+                        }
+                    }
+                }
+                
+                mLastWasImTarget = WindowManager.LayoutParams
+                        .mayUseInputMethod(mWindowAttributes.flags);
+                
+                InputMethodManager imm = InputMethodManager.peekInstance();
+                if (mView != null) {
+                    if (hasWindowFocus && imm != null && mLastWasImTarget) {
+                        imm.startGettingWindowFocus(mView);
+                    }
+                    mView.dispatchWindowFocusChanged(hasWindowFocus);
+                }
+                
+                // Note: must be done after the focus change callbacks,
+                // so all of the view state is set up correctly.
+                if (hasWindowFocus) {
+                    if (imm != null && mLastWasImTarget) {
+                        imm.onWindowFocus(mView, mView.findFocus(),
+                                mWindowAttributes.softInputMode,
+                                !mHasHadWindowFocus, mWindowAttributes.flags);
+                    }
+                    // Clear the forward bit.  We can just do this directly, since
+                    // the window manager doesn't care about it.
+                    mWindowAttributes.softInputMode &=
+                            ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+                    ((WindowManager.LayoutParams)mView.getLayoutParams())
+                            .softInputMode &=
+                                ~WindowManager.LayoutParams.SOFT_INPUT_IS_FORWARD_NAVIGATION;
+                    mHasHadWindowFocus = true;
+                }
+            }
+        } break;
+        case DIE:
+            dispatchDetachedFromWindow();
+            break;
+        case DISPATCH_KEY_FROM_IME:
+            if (LOCAL_LOGV) Log.v(
+                "ViewRoot", "Dispatching key "
+                + msg.obj + " from IME to " + mView);
+            deliverKeyEventToViewHierarchy((KeyEvent)msg.obj, false);
+            break;
+        case FINISH_INPUT_CONNECTION: {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                imm.reportFinishInputConnection((InputConnection)msg.obj);
+            }
+        } break;
+        case CHECK_FOCUS: {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null) {
+                imm.checkFocus();
+            }
+        } break;
+        }
+    }
+
+    /**
+     * Something in the current window tells us we need to change the touch mode.  For
+     * example, we are not in touch mode, and the user touches the screen.
+     *
+     * If the touch mode has changed, tell the window manager, and handle it locally.
+     *
+     * @param inTouchMode Whether we want to be in touch mode.
+     * @return True if the touch mode changed and focus changed was changed as a result
+     */
+    boolean ensureTouchMode(boolean inTouchMode) {
+        if (DBG) Log.d("touchmode", "ensureTouchMode(" + inTouchMode + "), current "
+                + "touch mode is " + mAttachInfo.mInTouchMode);
+        if (mAttachInfo.mInTouchMode == inTouchMode) return false;
+
+        // tell the window manager
+        try {
+            sWindowSession.setInTouchMode(inTouchMode);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+
+        // handle the change
+        return ensureTouchModeLocally(inTouchMode);
+    }
+
+    /**
+     * Ensure that the touch mode for this window is set, and if it is changing,
+     * take the appropriate action.
+     * @param inTouchMode Whether we want to be in touch mode.
+     * @return True if the touch mode changed and focus changed was changed as a result
+     */
+    private boolean ensureTouchModeLocally(boolean inTouchMode) {
+        if (DBG) Log.d("touchmode", "ensureTouchModeLocally(" + inTouchMode + "), current "
+                + "touch mode is " + mAttachInfo.mInTouchMode);
+
+        if (mAttachInfo.mInTouchMode == inTouchMode) return false;
+
+        mAttachInfo.mInTouchMode = inTouchMode;
+        mAttachInfo.mTreeObserver.dispatchOnTouchModeChanged(inTouchMode);
+
+        return (inTouchMode) ? enterTouchMode() : leaveTouchMode();
+    }
+
+    private boolean enterTouchMode() {
+        if (mView != null) {
+            if (mView.hasFocus()) {
+                // note: not relying on mFocusedView here because this could
+                // be when the window is first being added, and mFocused isn't
+                // set yet.
+                final View focused = mView.findFocus();
+                if (focused != null && !focused.isFocusableInTouchMode()) {
+
+                    final ViewGroup ancestorToTakeFocus =
+                            findAncestorToTakeFocusInTouchMode(focused);
+                    if (ancestorToTakeFocus != null) {
+                        // there is an ancestor that wants focus after its descendants that
+                        // is focusable in touch mode.. give it focus
+                        return ancestorToTakeFocus.requestFocus();
+                    } else {
+                        // nothing appropriate to have focus in touch mode, clear it out
+                        mView.unFocus();
+                        mAttachInfo.mTreeObserver.dispatchOnGlobalFocusChange(focused, null);
+                        mFocusedView = null;
+                        return true;
+                    }
+                }
+            }
+        }
+        return false;
+    }
+
+
+    /**
+     * Find an ancestor of focused that wants focus after its descendants and is
+     * focusable in touch mode.
+     * @param focused The currently focused view.
+     * @return An appropriate view, or null if no such view exists.
+     */
+    private ViewGroup findAncestorToTakeFocusInTouchMode(View focused) {
+        ViewParent parent = focused.getParent();
+        while (parent instanceof ViewGroup) {
+            final ViewGroup vgParent = (ViewGroup) parent;
+            if (vgParent.getDescendantFocusability() == ViewGroup.FOCUS_AFTER_DESCENDANTS
+                    && vgParent.isFocusableInTouchMode()) {
+                return vgParent;
+            }
+            if (vgParent.isRootNamespace()) {
+                return null;
+            } else {
+                parent = vgParent.getParent();
+            }
+        }
+        return null;
+    }
+
+    private boolean leaveTouchMode() {
+        if (mView != null) {
+            if (mView.hasFocus()) {
+                // i learned the hard way to not trust mFocusedView :)
+                mFocusedView = mView.findFocus();
+                if (!(mFocusedView instanceof ViewGroup)) {
+                    // some view has focus, let it keep it
+                    return false;
+                } else if (((ViewGroup)mFocusedView).getDescendantFocusability() !=
+                        ViewGroup.FOCUS_AFTER_DESCENDANTS) {
+                    // some view group has focus, and doesn't prefer its children
+                    // over itself for focus, so let them keep it.
+                    return false;
+                }
+            }
+
+            // find the best view to give focus to in this brave new non-touch-mode
+            // world
+            final View focused = focusSearch(null, View.FOCUS_DOWN);
+            if (focused != null) {
+                return focused.requestFocus(View.FOCUS_DOWN);
+            }
+        }
+        return false;
+    }
+
+
+    private void deliverTrackballEvent(MotionEvent event) {
+        boolean didFinish;
+        if (event == null) {
+            try {
+                event = sWindowSession.getPendingTrackballMove(mWindow);
+            } catch (RemoteException e) {
+            }
+            didFinish = true;
+        } else {
+            didFinish = false;
+        }
+
+        if (DEBUG_TRACKBALL) Log.v(TAG, "Motion event:" + event);
+
+        boolean handled = false;
+        try {
+            if (event == null) {
+                handled = true;
+            } else if (mView != null && mAdded) {
+                handled = mView.dispatchTrackballEvent(event);
+                if (!handled) {
+                    // we could do something here, like changing the focus
+                    // or something?
+                }
+            }
+        } finally {
+            if (handled) {
+                if (!didFinish) {
+                    try {
+                        sWindowSession.finishKey(mWindow);
+                    } catch (RemoteException e) {
+                    }
+                }
+                if (event != null) {
+                    event.recycle();
+                }
+                // If we reach this, we delivered a trackball event to mView and
+                // mView consumed it. Because we will not translate the trackball
+                // event into a key event, touch mode will not exit, so we exit
+                // touch mode here.
+                ensureTouchMode(false);
+                //noinspection ReturnInsideFinallyBlock
+                return;
+            }
+            // Let the exception fall through -- the looper will catch
+            // it and take care of the bad app for us.
+        }
+
+        final TrackballAxis x = mTrackballAxisX;
+        final TrackballAxis y = mTrackballAxisY;
+
+        long curTime = SystemClock.uptimeMillis();
+        if ((mLastTrackballTime+MAX_TRACKBALL_DELAY) < curTime) {
+            // It has been too long since the last movement,
+            // so restart at the beginning.
+            x.reset(0);
+            y.reset(0);
+            mLastTrackballTime = curTime;
+        }
+
+        try {
+            final int action = event.getAction();
+            final int metastate = event.getMetaState();
+            switch (action) {
+                case MotionEvent.ACTION_DOWN:
+                    x.reset(2);
+                    y.reset(2);
+                    deliverKeyEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DPAD_CENTER,
+                            0, metastate), false);
+                    break;
+                case MotionEvent.ACTION_UP:
+                    x.reset(2);
+                    y.reset(2);
+                    deliverKeyEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_UP, KeyEvent.KEYCODE_DPAD_CENTER,
+                            0, metastate), false);
+                    break;
+            }
+
+            if (DEBUG_TRACKBALL) Log.v(TAG, "TB X=" + x.position + " step="
+                    + x.step + " dir=" + x.dir + " acc=" + x.acceleration
+                    + " move=" + event.getX()
+                    + " / Y=" + y.position + " step="
+                    + y.step + " dir=" + y.dir + " acc=" + y.acceleration
+                    + " move=" + event.getY());
+            final float xOff = x.collect(event.getX(), event.getEventTime(), "X");
+            final float yOff = y.collect(event.getY(), event.getEventTime(), "Y");
+
+            // Generate DPAD events based on the trackball movement.
+            // We pick the axis that has moved the most as the direction of
+            // the DPAD.  When we generate DPAD events for one axis, then the
+            // other axis is reset -- we don't want to perform DPAD jumps due
+            // to slight movements in the trackball when making major movements
+            // along the other axis.
+            int keycode = 0;
+            int movement = 0;
+            float accel = 1;
+            if (xOff > yOff) {
+                movement = x.generate((2/event.getXPrecision()));
+                if (movement != 0) {
+                    keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_RIGHT
+                            : KeyEvent.KEYCODE_DPAD_LEFT;
+                    accel = x.acceleration;
+                    y.reset(2);
+                }
+            } else if (yOff > 0) {
+                movement = y.generate((2/event.getYPrecision()));
+                if (movement != 0) {
+                    keycode = movement > 0 ? KeyEvent.KEYCODE_DPAD_DOWN
+                            : KeyEvent.KEYCODE_DPAD_UP;
+                    accel = y.acceleration;
+                    x.reset(2);
+                }
+            }
+
+            if (keycode != 0) {
+                if (movement < 0) movement = -movement;
+                int accelMovement = (int)(movement * accel);
+                if (DEBUG_TRACKBALL) Log.v(TAG, "Move: movement=" + movement
+                        + " accelMovement=" + accelMovement
+                        + " accel=" + accel);
+                if (accelMovement > movement) {
+                    if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+                            + keycode);
+                    movement--;
+                    deliverKeyEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_MULTIPLE, keycode,
+                            accelMovement-movement, metastate), false);
+                }
+                while (movement > 0) {
+                    if (DEBUG_TRACKBALL) Log.v("foo", "Delivering fake DPAD: "
+                            + keycode);
+                    movement--;
+                    curTime = SystemClock.uptimeMillis();
+                    deliverKeyEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_DOWN, keycode, 0, event.getMetaState()), false);
+                    deliverKeyEvent(new KeyEvent(curTime, curTime,
+                            KeyEvent.ACTION_UP, keycode, 0, metastate), false);
+                }
+                mLastTrackballTime = curTime;
+            }
+        } finally {
+            if (!didFinish) {
+                try {
+                    sWindowSession.finishKey(mWindow);
+                } catch (RemoteException e) {
+                }
+                if (event != null) {
+                    event.recycle();
+                }
+            }
+            // Let the exception fall through -- the looper will catch
+            // it and take care of the bad app for us.
+        }
+    }
+
+    /**
+     * @param keyCode The key code
+     * @return True if the key is directional.
+     */
+    static boolean isDirectional(int keyCode) {
+        switch (keyCode) {
+        case KeyEvent.KEYCODE_DPAD_LEFT:
+        case KeyEvent.KEYCODE_DPAD_RIGHT:
+        case KeyEvent.KEYCODE_DPAD_UP:
+        case KeyEvent.KEYCODE_DPAD_DOWN:
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Returns true if this key is a keyboard key.
+     * @param keyEvent The key event.
+     * @return whether this key is a keyboard key.
+     */
+    private static boolean isKeyboardKey(KeyEvent keyEvent) {
+      final int convertedKey = keyEvent.getUnicodeChar();
+        return convertedKey > 0;
+    }
+
+
+
+    /**
+     * See if the key event means we should leave touch mode (and leave touch
+     * mode if so).
+     * @param event The key event.
+     * @return Whether this key event should be consumed (meaning the act of
+     *   leaving touch mode alone is considered the event).
+     */
+    private boolean checkForLeavingTouchModeAndConsume(KeyEvent event) {
+        if (event.getAction() != KeyEvent.ACTION_DOWN) {
+            return false;
+        }
+        if ((event.getFlags()&KeyEvent.FLAG_KEEP_TOUCH_MODE) != 0) {
+            return false;
+        }
+
+        // only relevant if we are in touch mode
+        if (!mAttachInfo.mInTouchMode) {
+            return false;
+        }
+
+        // if something like an edit text has focus and the user is typing,
+        // leave touch mode
+        //
+        // note: the condition of not being a keyboard key is kind of a hacky
+        // approximation of whether we think the focused view will want the
+        // key; if we knew for sure whether the focused view would consume
+        // the event, that would be better.
+        if (isKeyboardKey(event) && mView != null && mView.hasFocus()) {
+            mFocusedView = mView.findFocus();
+            if ((mFocusedView instanceof ViewGroup)
+                    && ((ViewGroup) mFocusedView).getDescendantFocusability() ==
+                    ViewGroup.FOCUS_AFTER_DESCENDANTS) {
+                // something has focus, but is holding it weakly as a container
+                return false;
+            }
+            if (ensureTouchMode(false)) {
+                throw new IllegalStateException("should not have changed focus "
+                        + "when leaving touch mode while a view has focus.");
+            }
+            return false;
+        }
+
+        if (isDirectional(event.getKeyCode())) {
+            // no view has focus, so we leave touch mode (and find something
+            // to give focus to).  the event is consumed if we were able to
+            // find something to give focus to.
+            return ensureTouchMode(false);
+        }
+        return false;
+    }
+
+    /**
+     * log motion events 
+     */
+    private static void captureMotionLog(String subTag, MotionEvent ev) {
+        //check dynamic switch        
+        if (ev == null ||
+                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
+            return;
+        } 
+        
+        StringBuilder sb = new StringBuilder(subTag + ": ");        
+        sb.append(ev.getDownTime()).append(',');        
+        sb.append(ev.getEventTime()).append(',');        
+        sb.append(ev.getAction()).append(',');        
+        sb.append(ev.getX()).append(',');        
+        sb.append(ev.getY()).append(',');        
+        sb.append(ev.getPressure()).append(',');        
+        sb.append(ev.getSize()).append(',');        
+        sb.append(ev.getMetaState()).append(',');        
+        sb.append(ev.getXPrecision()).append(',');        
+        sb.append(ev.getYPrecision()).append(',');        
+        sb.append(ev.getDeviceId()).append(',');        
+        sb.append(ev.getEdgeFlags());
+        Log.d(TAG, sb.toString());        
+    }
+    /**
+     * log motion events 
+     */
+    private static void captureKeyLog(String subTag, KeyEvent ev) {
+        //check dynamic switch                
+        if (ev == null || 
+                SystemProperties.getInt(ViewDebug.SYSTEM_PROPERTY_CAPTURE_EVENT, 0) == 0) {
+            return;
+        }
+        StringBuilder sb = new StringBuilder(subTag + ": ");        
+        sb.append(ev.getDownTime()).append(',');
+        sb.append(ev.getEventTime()).append(',');
+        sb.append(ev.getAction()).append(',');
+        sb.append(ev.getKeyCode()).append(',');        
+        sb.append(ev.getRepeatCount()).append(',');
+        sb.append(ev.getMetaState()).append(',');
+        sb.append(ev.getDeviceId()).append(',');
+        sb.append(ev.getScanCode());
+        Log.d(TAG, sb.toString());        
+    }    
+
+    int enqueuePendingEvent(Object event, boolean sendDone) {
+        int seq = mPendingEventSeq+1;
+        if (seq < 0) seq = 0;
+        mPendingEventSeq = seq;
+        mPendingEvents.put(seq, event);
+        return sendDone ? seq : -seq;
+    }
+
+    Object retrievePendingEvent(int seq) {
+        if (seq < 0) seq = -seq;
+        Object event = mPendingEvents.get(seq);
+        if (event != null) {
+            mPendingEvents.remove(seq);
+        }
+        return event;
+    }
+    
+    private void deliverKeyEvent(KeyEvent event, boolean sendDone) {
+        // If mView is null, we just consume the key event because it doesn't
+        // make sense to do anything else with it.
+        boolean handled = mView != null
+                ? mView.dispatchKeyEventPreIme(event) : true;
+        if (handled) {
+            if (sendDone) {
+                if (LOCAL_LOGV) Log.v(
+                    "ViewRoot", "Telling window manager key is finished");
+                try {
+                    sWindowSession.finishKey(mWindow);
+                } catch (RemoteException e) {
+                }
+            }
+            return;
+        }
+        // If it is possible for this window to interact with the input
+        // method window, then we want to first dispatch our key events
+        // to the input method.
+        if (mLastWasImTarget) {
+            InputMethodManager imm = InputMethodManager.peekInstance();
+            if (imm != null && mView != null) {
+                int seq = enqueuePendingEvent(event, sendDone);
+                if (DEBUG_IMF) Log.v(TAG, "Sending key event to IME: seq="
+                        + seq + " event=" + event);
+                imm.dispatchKeyEvent(mView.getContext(), seq, event,
+                        mInputMethodCallback);
+                return;
+            }
+        }
+        deliverKeyEventToViewHierarchy(event, sendDone);
+    }
+
+    void handleFinishedEvent(int seq, boolean handled) {
+        final KeyEvent event = (KeyEvent)retrievePendingEvent(seq);
+        if (DEBUG_IMF) Log.v(TAG, "IME finished event: seq=" + seq
+                + " handled=" + handled + " event=" + event);
+        if (event != null) {
+            final boolean sendDone = seq >= 0;
+            if (!handled) {
+                deliverKeyEventToViewHierarchy(event, sendDone);
+                return;
+            } else if (sendDone) {
+                if (LOCAL_LOGV) Log.v(
+                        "ViewRoot", "Telling window manager key is finished");
+                try {
+                    sWindowSession.finishKey(mWindow);
+                } catch (RemoteException e) {
+                }
+            } else {
+                Log.w("ViewRoot", "handleFinishedEvent(seq=" + seq
+                        + " handled=" + handled + " ev=" + event
+                        + ") neither delivering nor finishing key");
+            }
+        }
+    }
+    
+    private void deliverKeyEventToViewHierarchy(KeyEvent event, boolean sendDone) {
+        try {
+            if (mView != null && mAdded) {
+                final int action = event.getAction();
+                boolean isDown = (action == KeyEvent.ACTION_DOWN);
+
+                if (checkForLeavingTouchModeAndConsume(event)) {
+                    return;
+                }                
+                
+                if (Config.LOGV) {
+                    captureKeyLog("captureDispatchKeyEvent", event);
+                }
+                boolean keyHandled = mView.dispatchKeyEvent(event);
+
+                if (!keyHandled && isDown) {
+                    int direction = 0;
+                    switch (event.getKeyCode()) {
+                    case KeyEvent.KEYCODE_DPAD_LEFT:
+                        direction = View.FOCUS_LEFT;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_RIGHT:
+                        direction = View.FOCUS_RIGHT;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_UP:
+                        direction = View.FOCUS_UP;
+                        break;
+                    case KeyEvent.KEYCODE_DPAD_DOWN:
+                        direction = View.FOCUS_DOWN;
+                        break;
+                    }
+
+                    if (direction != 0) {
+
+                        View focused = mView != null ? mView.findFocus() : null;
+                        if (focused != null) {
+                            View v = focused.focusSearch(direction);
+                            boolean focusPassed = false;
+                            if (v != null && v != focused) {
+                                // do the math the get the interesting rect
+                                // of previous focused into the coord system of
+                                // newly focused view
+                                focused.getFocusedRect(mTempRect);
+                                ((ViewGroup) mView).offsetDescendantRectToMyCoords(focused, mTempRect);
+                                ((ViewGroup) mView).offsetRectIntoDescendantCoords(v, mTempRect);
+                                focusPassed = v.requestFocus(direction, mTempRect);
+                            }
+
+                            if (!focusPassed) {
+                                mView.dispatchUnhandledMove(focused, direction);
+                            } else {
+                                playSoundEffect(SoundEffectConstants.getContantForFocusDirection(direction));
+                            }
+                        }
+                    }
+                }
+            }
+
+        } finally {
+            if (sendDone) {
+                if (LOCAL_LOGV) Log.v(
+                    "ViewRoot", "Telling window manager key is finished");
+                try {
+                    sWindowSession.finishKey(mWindow);
+                } catch (RemoteException e) {
+                }
+            }
+            // Let the exception fall through -- the looper will catch
+            // it and take care of the bad app for us.
+        }
+    }
+
+    private AudioManager getAudioManager() {
+        if (mView == null) {
+            throw new IllegalStateException("getAudioManager called when there is no mView");
+        }
+        if (mAudioManager == null) {
+            mAudioManager = (AudioManager) mView.getContext().getSystemService(Context.AUDIO_SERVICE);
+        }
+        return mAudioManager;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void playSoundEffect(int effectId) {
+        checkThread();
+
+        final AudioManager audioManager = getAudioManager();
+
+        switch (effectId) {
+            case SoundEffectConstants.CLICK:
+                audioManager.playSoundEffect(AudioManager.FX_KEY_CLICK);
+                return;
+            case SoundEffectConstants.NAVIGATION_DOWN:
+                audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_DOWN);
+                return;
+            case SoundEffectConstants.NAVIGATION_LEFT:
+                audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_LEFT);
+                return;
+            case SoundEffectConstants.NAVIGATION_RIGHT:
+                audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_RIGHT);
+                return;
+            case SoundEffectConstants.NAVIGATION_UP:
+                audioManager.playSoundEffect(AudioManager.FX_FOCUS_NAVIGATION_UP);
+                return;
+            default:
+                throw new IllegalArgumentException("unknown effect id " + effectId +
+                        " not defined in " + SoundEffectConstants.class.getCanonicalName());
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean performHapticFeedback(int effectId, boolean always) {
+        try {
+            return sWindowSession.performHapticFeedback(mWindow, effectId, always);
+        } catch (RemoteException e) {
+            return false;
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public View focusSearch(View focused, int direction) {
+        checkThread();
+        if (!(mView instanceof ViewGroup)) {
+            return null;
+        }
+        return FocusFinder.getInstance().findNextFocus((ViewGroup) mView, focused, direction);
+    }
+
+    public void debug() {
+        mView.debug();
+    }
+
+    public void die(boolean immediate) {
+        checkThread();
+        if (Config.LOGV) Log.v("ViewRoot", "DIE in " + this + " of " + mSurface);
+        synchronized (this) {
+            if (mAdded && !mFirst) {
+                int viewVisibility = mView.getVisibility();
+                boolean viewVisibilityChanged = mViewVisibility != viewVisibility;
+                if (mWindowAttributesChanged || viewVisibilityChanged) {
+                    // If layout params have been changed, first give them
+                    // to the window manager to make sure it has the correct
+                    // animation info.
+                    try {
+                        if ((sWindowSession.relayout(
+                                    mWindow, mWindowAttributes,
+                                    mView.mMeasuredWidth, mView.mMeasuredHeight,
+                                    viewVisibility, false, mWinFrame, mPendingContentInsets,
+                                    mPendingVisibleInsets, mSurface)
+                                &WindowManagerImpl.RELAYOUT_FIRST_TIME) != 0) {
+                            sWindowSession.finishDrawing(mWindow);
+                        }
+                    } catch (RemoteException e) {
+                    }
+                }
+
+                mSurface = null;
+            }
+            if (mAdded) {
+                mAdded = false;
+                if (immediate) {
+                    dispatchDetachedFromWindow();
+                } else if (mView != null) {
+                    sendEmptyMessage(DIE);
+                }
+            }
+        }
+    }
+
+    public void dispatchFinishedEvent(int seq, boolean handled) {
+        Message msg = obtainMessage(FINISHED_EVENT);
+        msg.arg1 = seq;
+        msg.arg2 = handled ? 1 : 0;
+        sendMessage(msg);
+    }
+    
+    public void dispatchResized(int w, int h, Rect coveredInsets,
+            Rect visibleInsets, boolean reportDraw) {
+        if (DEBUG_LAYOUT) Log.v(TAG, "Resizing " + this + ": w=" + w
+                + " h=" + h + " coveredInsets=" + coveredInsets.toShortString()
+                + " visibleInsets=" + visibleInsets.toShortString()
+                + " reportDraw=" + reportDraw);
+        Message msg = obtainMessage(reportDraw ? RESIZED_REPORT :RESIZED);
+        msg.arg1 = w;
+        msg.arg2 = h;
+        msg.obj = new Rect[] { new Rect(coveredInsets), new Rect(visibleInsets) };
+        sendMessage(msg);
+    }
+
+    public void dispatchKey(KeyEvent event) {
+        if (event.getAction() == KeyEvent.ACTION_DOWN) {
+            //noinspection ConstantConditions
+            if (false && event.getKeyCode() == KeyEvent.KEYCODE_CAMERA) {
+                if (Config.LOGD) Log.d("keydisp",
+                        "===================================================");
+                if (Config.LOGD) Log.d("keydisp", "Focused view Hierarchy is:");
+                debug();
+
+                if (Config.LOGD) Log.d("keydisp",
+                        "===================================================");
+            }
+        }
+
+        Message msg = obtainMessage(DISPATCH_KEY);
+        msg.obj = event;
+
+        if (LOCAL_LOGV) Log.v(
+            "ViewRoot", "sending key " + event + " to " + mView);
+
+        sendMessageAtTime(msg, event.getEventTime());
+    }
+
+    public void dispatchPointer(MotionEvent event, long eventTime) {
+        Message msg = obtainMessage(DISPATCH_POINTER);
+        msg.obj = event;
+        sendMessageAtTime(msg, eventTime);
+    }
+
+    public void dispatchTrackball(MotionEvent event, long eventTime) {
+        Message msg = obtainMessage(DISPATCH_TRACKBALL);
+        msg.obj = event;
+        sendMessageAtTime(msg, eventTime);
+    }
+
+    public void dispatchAppVisibility(boolean visible) {
+        Message msg = obtainMessage(DISPATCH_APP_VISIBILITY);
+        msg.arg1 = visible ? 1 : 0;
+        sendMessage(msg);
+    }
+
+    public void dispatchGetNewSurface() {
+        Message msg = obtainMessage(DISPATCH_GET_NEW_SURFACE);
+        sendMessage(msg);
+    }
+
+    public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+        Message msg = Message.obtain();
+        msg.what = WINDOW_FOCUS_CHANGED;
+        msg.arg1 = hasFocus ? 1 : 0;
+        msg.arg2 = inTouchMode ? 1 : 0;
+        sendMessage(msg);
+    }
+
+    public boolean showContextMenuForChild(View originalView) {
+        return false;
+    }
+
+    public void createContextMenu(ContextMenu menu) {
+    }
+
+    public void childDrawableStateChanged(View child) {
+    }
+
+    protected Rect getWindowFrame() {
+        return mWinFrame;
+    }
+
+    void checkThread() {
+        if (mThread != Thread.currentThread()) {
+            throw new CalledFromWrongThreadException(
+                    "Only the original thread that created a view hierarchy can touch its views.");
+        }
+    }
+
+    public void requestDisallowInterceptTouchEvent(boolean disallowIntercept) {
+        // ViewRoot never intercepts touch event, so this can be a no-op
+    }
+
+    public boolean requestChildRectangleOnScreen(View child, Rect rectangle,
+            boolean immediate) {
+        return scrollToRectOrFocus(rectangle, immediate);
+    }
+    
+    static class InputMethodCallback extends IInputMethodCallback.Stub {
+        private WeakReference<ViewRoot> mViewRoot;
+
+        public InputMethodCallback(ViewRoot viewRoot) {
+            mViewRoot = new WeakReference<ViewRoot>(viewRoot);
+        }
+        
+        public void finishedEvent(int seq, boolean handled) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchFinishedEvent(seq, handled);
+            }
+        }
+
+        public void sessionCreated(IInputMethodSession session) throws RemoteException {
+            // Stub -- not for use in the client.
+        }
+    }
+    
+    static class W extends IWindow.Stub {
+        private WeakReference<ViewRoot> mViewRoot;
+
+        public W(ViewRoot viewRoot) {
+            mViewRoot = new WeakReference<ViewRoot>(viewRoot);
+        }
+
+        public void resized(int w, int h, Rect coveredInsets,
+                Rect visibleInsets, boolean reportDraw) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchResized(w, h, coveredInsets,
+                        visibleInsets, reportDraw);
+            }
+        }
+
+        public void dispatchKey(KeyEvent event) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchKey(event);
+            } else {
+                Log.w("ViewRoot.W", "Key event " + event + " but no ViewRoot available!");
+            }
+        }
+
+        public void dispatchPointer(MotionEvent event, long eventTime) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchPointer(event, eventTime);
+            }
+        }
+
+        public void dispatchTrackball(MotionEvent event, long eventTime) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchTrackball(event, eventTime);
+            }
+        }
+
+        public void dispatchAppVisibility(boolean visible) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchAppVisibility(visible);
+            }
+        }
+
+        public void dispatchGetNewSurface() {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.dispatchGetNewSurface();
+            }
+        }
+
+        public void windowFocusChanged(boolean hasFocus, boolean inTouchMode) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                viewRoot.windowFocusChanged(hasFocus, inTouchMode);
+            }
+        }
+
+        private static int checkCallingPermission(String permission) {
+            if (!Process.supportsProcesses()) {
+                return PackageManager.PERMISSION_GRANTED;
+            }
+
+            try {
+                return ActivityManagerNative.getDefault().checkPermission(
+                        permission, Binder.getCallingPid(), Binder.getCallingUid());
+            } catch (RemoteException e) {
+                return PackageManager.PERMISSION_DENIED;
+            }
+        }
+
+        public void executeCommand(String command, String parameters, ParcelFileDescriptor out) {
+            final ViewRoot viewRoot = mViewRoot.get();
+            if (viewRoot != null) {
+                final View view = viewRoot.mView;
+                if (view != null) {
+                    if (checkCallingPermission(Manifest.permission.DUMP) !=
+                            PackageManager.PERMISSION_GRANTED) {
+                        throw new SecurityException("Insufficient permissions to invoke"
+                                + " executeCommand() from pid=" + Binder.getCallingPid()
+                                + ", uid=" + Binder.getCallingUid());
+                    }
+
+                    OutputStream clientStream = null;
+                    try {
+                        clientStream = new ParcelFileDescriptor.AutoCloseOutputStream(out);
+                        ViewDebug.dispatchCommand(view, command, parameters, clientStream);
+                    } catch (IOException e) {
+                        e.printStackTrace();
+                    } finally {
+                        if (clientStream != null) {
+                            try {
+                                clientStream.close();
+                            } catch (IOException e) {
+                                e.printStackTrace();
+                            }
+                        }
+                    }
+                }
+            }
+        }
+    }
+
+    /**
+     * Maintains state information for a single trackball axis, generating
+     * discrete (DPAD) movements based on raw trackball motion.
+     */
+    static final class TrackballAxis {
+        /**
+         * The maximum amount of acceleration we will apply.
+         */
+        static final float MAX_ACCELERATION = 20;
+        
+        /**
+         * The maximum amount of time (in milliseconds) between events in order
+         * for us to consider the user to be doing fast trackball movements,
+         * and thus apply an acceleration.
+         */
+        static final long FAST_MOVE_TIME = 150;
+        
+        /**
+         * Scaling factor to the time (in milliseconds) between events to how
+         * much to multiple/divide the current acceleration.  When movement
+         * is < FAST_MOVE_TIME this multiplies the acceleration; when >
+         * FAST_MOVE_TIME it divides it.
+         */
+        static final float ACCEL_MOVE_SCALING_FACTOR = (1.0f/40);
+        
+        float position;
+        float absPosition;
+        float acceleration = 1;
+        long lastMoveTime = 0;
+        int step;
+        int dir;
+        int nonAccelMovement;
+
+        void reset(int _step) {
+            position = 0;
+            acceleration = 1;
+            lastMoveTime = 0;
+            step = _step;
+            dir = 0;
+        }
+
+        /**
+         * Add trackball movement into the state.  If the direction of movement
+         * has been reversed, the state is reset before adding the
+         * movement (so that you don't have to compensate for any previously
+         * collected movement before see the result of the movement in the
+         * new direction).
+         *
+         * @return Returns the absolute value of the amount of movement
+         * collected so far.
+         */
+        float collect(float off, long time, String axis) {
+            long normTime;
+            if (off > 0) {
+                normTime = (long)(off * FAST_MOVE_TIME);
+                if (dir < 0) {
+                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to positive!");
+                    position = 0;
+                    step = 0;
+                    acceleration = 1;
+                    lastMoveTime = 0;
+                }
+                dir = 1;
+            } else if (off < 0) {
+                normTime = (long)((-off) * FAST_MOVE_TIME);
+                if (dir > 0) {
+                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " reversed to negative!");
+                    position = 0;
+                    step = 0;
+                    acceleration = 1;
+                    lastMoveTime = 0;
+                }
+                dir = -1;
+            } else {
+                normTime = 0;
+            }
+            
+            // The number of milliseconds between each movement that is
+            // considered "normal" and will not result in any acceleration
+            // or deceleration, scaled by the offset we have here.
+            if (normTime > 0) {
+                long delta = time - lastMoveTime;
+                lastMoveTime = time;
+                float acc = acceleration;
+                if (delta < normTime) {
+                    // The user is scrolling rapidly, so increase acceleration.
+                    float scale = (normTime-delta) * ACCEL_MOVE_SCALING_FACTOR;
+                    if (scale > 1) acc *= scale;
+                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " accelerate: off="
+                            + off + " normTime=" + normTime + " delta=" + delta
+                            + " scale=" + scale + " acc=" + acc);
+                    acceleration = acc < MAX_ACCELERATION ? acc : MAX_ACCELERATION;
+                } else {
+                    // The user is scrolling slowly, so decrease acceleration.
+                    float scale = (delta-normTime) * ACCEL_MOVE_SCALING_FACTOR;
+                    if (scale > 1) acc /= scale;
+                    if (DEBUG_TRACKBALL) Log.v(TAG, axis + " deccelerate: off="
+                            + off + " normTime=" + normTime + " delta=" + delta
+                            + " scale=" + scale + " acc=" + acc);
+                    acceleration = acc > 1 ? acc : 1;
+                }
+            }
+            position += off;
+            return (absPosition = Math.abs(position));
+        }
+
+        /**
+         * Generate the number of discrete movement events appropriate for
+         * the currently collected trackball movement.
+         *
+         * @param precision The minimum movement required to generate the
+         * first discrete movement.
+         *
+         * @return Returns the number of discrete movements, either positive
+         * or negative, or 0 if there is not enough trackball movement yet
+         * for a discrete movement.
+         */
+        int generate(float precision) {
+            int movement = 0;
+            nonAccelMovement = 0;
+            do {
+                final int dir = position >= 0 ? 1 : -1;
+                switch (step) {
+                    // If we are going to execute the first step, then we want
+                    // to do this as soon as possible instead of waiting for
+                    // a full movement, in order to make things look responsive.
+                    case 0:
+                        if (absPosition < precision) {
+                            return movement;
+                        }
+                        movement += dir;
+                        nonAccelMovement += dir;
+                        step = 1;
+                        break;
+                    // If we have generated the first movement, then we need
+                    // to wait for the second complete trackball motion before
+                    // generating the second discrete movement.
+                    case 1:
+                        if (absPosition < 2) {
+                            return movement;
+                        }
+                        movement += dir;
+                        nonAccelMovement += dir;
+                        position += dir > 0 ? -2 : 2;
+                        absPosition = Math.abs(position);
+                        step = 2;
+                        break;
+                    // After the first two, we generate discrete movements
+                    // consistently with the trackball, applying an acceleration
+                    // if the trackball is moving quickly.  This is a simple
+                    // acceleration on top of what we already compute based
+                    // on how quickly the wheel is being turned, to apply
+                    // a longer increasing acceleration to continuous movement
+                    // in one direction.
+                    default:
+                        if (absPosition < 1) {
+                            return movement;
+                        }
+                        movement += dir;
+                        position += dir >= 0 ? -1 : 1;
+                        absPosition = Math.abs(position);
+                        float acc = acceleration;
+                        acc *= 1.1f;
+                        acceleration = acc < MAX_ACCELERATION ? acc : acceleration;
+                        break;
+                }
+            } while (true);
+        }
+    }
+
+    public static final class CalledFromWrongThreadException extends AndroidRuntimeException {
+        public CalledFromWrongThreadException(String msg) {
+            super(msg);
+        }
+    }
+
+    private SurfaceHolder mHolder = new SurfaceHolder() {
+        // we only need a SurfaceHolder for opengl. it would be nice
+        // to implement everything else though, especially the callback
+        // support (opengl doesn't make use of it right now, but eventually
+        // will).
+        public Surface getSurface() {
+            return mSurface;
+        }
+
+        public boolean isCreating() {
+            return false;
+        }
+
+        public void addCallback(Callback callback) {
+        }
+
+        public void removeCallback(Callback callback) {
+        }
+
+        public void setFixedSize(int width, int height) {
+        }
+
+        public void setSizeFromLayout() {
+        }
+
+        public void setFormat(int format) {
+        }
+
+        public void setType(int type) {
+        }
+
+        public void setKeepScreenOn(boolean screenOn) {
+        }
+
+        public Canvas lockCanvas() {
+            return null;
+        }
+
+        public Canvas lockCanvas(Rect dirty) {
+            return null;
+        }
+
+        public void unlockCanvasAndPost(Canvas canvas) {
+        }
+        public Rect getSurfaceFrame() {
+            return null;
+        }
+    };
+
+    static RunQueue getRunQueue() {
+        RunQueue rq = sRunQueues.get();
+        if (rq != null) {
+            return rq;
+        }
+        rq = new RunQueue();
+        sRunQueues.set(rq);
+        return rq;
+    }
+    
+    /**
+     * @hide
+     */
+    static final class RunQueue {
+        private final ArrayList<HandlerAction> mActions = new ArrayList<HandlerAction>();
+
+        void post(Runnable action) {
+            postDelayed(action, 0);
+        }
+
+        void postDelayed(Runnable action, long delayMillis) {
+            HandlerAction handlerAction = new HandlerAction();
+            handlerAction.action = action;
+            handlerAction.delay = delayMillis;
+
+            synchronized (mActions) {
+                mActions.add(handlerAction);
+            }
+        }
+
+        void removeCallbacks(Runnable action) {
+            final HandlerAction handlerAction = new HandlerAction();
+            handlerAction.action = action;
+
+            synchronized (mActions) {
+                final ArrayList<HandlerAction> actions = mActions;
+
+                while (actions.remove(handlerAction)) {
+                    // Keep going
+                }
+            }
+        }
+
+        void executeActions(Handler handler) {
+            synchronized (mActions) {
+                final ArrayList<HandlerAction> actions = mActions;
+                final int count = actions.size();
+
+                for (int i = 0; i < count; i++) {
+                    final HandlerAction handlerAction = actions.get(i);
+                    handler.postDelayed(handlerAction.action, handlerAction.delay);
+                }
+
+                mActions.clear();
+            }
+        }
+
+        private static class HandlerAction {
+            Runnable action;
+            long delay;
+
+            @Override
+            public boolean equals(Object o) {
+                if (this == o) return true;
+                if (o == null || getClass() != o.getClass()) return false;
+
+                HandlerAction that = (HandlerAction) o;
+
+                return !(action != null ? !action.equals(that.action) : that.action != null);
+
+            }
+
+            @Override
+            public int hashCode() {
+                int result = action != null ? action.hashCode() : 0;
+                result = 31 * result + (int) (delay ^ (delay >>> 32));
+                return result;
+            }
+        }
+    }
+
+    private static native void nativeShowFPS(Canvas canvas, int durationMillis);
+
+    // inform skia to just abandon its texture cache IDs
+    // doesn't call glDeleteTextures
+    private static native void nativeAbandonGlCaches();
+}
diff --git a/core/java/android/view/ViewStub.java b/core/java/android/view/ViewStub.java
new file mode 100644
index 0000000..e159de4
--- /dev/null
+++ b/core/java/android/view/ViewStub.java
@@ -0,0 +1,279 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.graphics.Canvas;
+import android.util.AttributeSet;
+
+import com.android.internal.R;
+
+/**
+ * A ViewStub is an invisible, zero-sized View that can be used to lazily inflate
+ * layout resources at runtime.
+ *
+ * When a ViewStub is made visible, or when {@link #inflate()}  is invoked, the layout resource 
+ * is inflated. The ViewStub then replaces itself in its parent with the inflated View or Views.
+ * Therefore, the ViewStub exists in the view hierarchy until {@link #setVisibility(int)} or
+ * {@link #inflate()} is invoked.
+ *
+ * The inflated View is added to the ViewStub's parent with the ViewStub's layout
+ * parameters. Similarly, you can define/override the inflate View's id by using the
+ * ViewStub's inflatedId property. For instance:
+ *
+ * <pre>
+ *     &lt;ViewStub android:id="@+id/stub"
+ *               android:inflatedId="@+id/subTree"
+ *               android:layout="@layout/mySubTree"
+ *               android:layout_width="120dip"
+ *               android:layout_height="40dip" /&gt;
+ * </pre>
+ *
+ * The ViewStub thus defined can be found using the id "stub." After inflation of
+ * the layout resource "mySubTree," the ViewStub is removed from its parent. The
+ * View created by inflating the layout resource "mySubTree" can be found using the
+ * id "subTree," specified by the inflatedId property. The inflated View is finally
+ * assigned a width of 120dip and a height of 40dip.
+ *
+ * The preferred way to perform the inflation of the layout resource is the following:
+ *
+ * <pre>
+ *     ViewStub stub = (ViewStub) findViewById(R.id.stub);
+ *     View inflated = stub.inflate();
+ * </pre>
+ *
+ * When {@link #inflate()} is invoked, the ViewStub is replaced by the inflated View
+ * and the inflated View is returned. This lets applications get a reference to the
+ * inflated View without executing an extra findViewById().
+ *
+ * @attr ref android.R.styleable#ViewStub_inflatedId
+ * @attr ref android.R.styleable#ViewStub_layout
+ */
+public final class ViewStub extends View {
+    private int mLayoutResource = 0;
+    private int mInflatedId;
+
+    private OnInflateListener mInflateListener;
+
+    public ViewStub(Context context) {
+        initialize(context);
+    }
+
+    /**
+     * Creates a new ViewStub with the specified layout resource.
+     *
+     * @param context The application's environment.
+     * @param layoutResource The reference to a layout resource that will be inflated.
+     */
+    public ViewStub(Context context, int layoutResource) {
+        mLayoutResource = layoutResource;
+        initialize(context);
+    }
+
+    public ViewStub(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    @SuppressWarnings({"UnusedDeclaration"})
+    public ViewStub(Context context, AttributeSet attrs, int defStyle) {
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.ViewStub,
+                defStyle, 0);
+
+        mInflatedId = a.getResourceId(R.styleable.ViewStub_inflatedId, NO_ID);
+        mLayoutResource = a.getResourceId(R.styleable.ViewStub_layout, 0);
+
+        a.recycle();
+
+        a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.View, defStyle, 0);
+        mID = a.getResourceId(R.styleable.View_id, NO_ID);
+        a.recycle();
+
+        initialize(context);
+    }
+
+    private void initialize(Context context) {
+        mContext = context;
+        setVisibility(GONE);
+        setWillNotDraw(true);
+    }
+
+    /**
+     * Returns the id taken by the inflated view. If the inflated id is
+     * {@link View#NO_ID}, the inflated view keeps its original id.
+     *
+     * @return A positive integer used to identify the inflated view or
+     *         {@link #NO_ID} if the inflated view should keep its id.
+     *
+     * @see #setInflatedId(int)
+     * @attr ref android.R.styleable#ViewStub_inflatedId
+     */
+    public int getInflatedId() {
+        return mInflatedId;
+    }
+
+    /**
+     * Defines the id taken by the inflated view. If the inflated id is
+     * {@link View#NO_ID}, the inflated view keeps its original id.
+     *
+     * @param inflatedId A positive integer used to identify the inflated view or
+     *                   {@link #NO_ID} if the inflated view should keep its id.
+     *
+     * @see #getInflatedId()
+     * @attr ref android.R.styleable#ViewStub_inflatedId
+     */
+    public void setInflatedId(int inflatedId) {
+        mInflatedId = inflatedId;
+    }
+
+    /**
+     * Returns the layout resource that will be used by {@link #setVisibility(int)} or
+     * {@link #inflate()} to replace this StubbedView
+     * in its parent by another view.
+     *
+     * @return The layout resource identifier used to inflate the new View.
+     *
+     * @see #setLayoutResource(int)
+     * @see #setVisibility(int)
+     * @see #inflate()
+     * @attr ref android.R.styleable#ViewStub_layout
+     */
+    public int getLayoutResource() {
+        return mLayoutResource;
+    }
+
+    /**
+     * Specifies the layout resource to inflate when this StubbedView becomes visible or invisible
+     * or when {@link #inflate()} is invoked. The View created by inflating the layout resource is
+     * used to replace this StubbedView in its parent.
+     * 
+     * @param layoutResource A valid layout resource identifier (different from 0.)
+     * 
+     * @see #getLayoutResource()
+     * @see #setVisibility(int)
+     * @see #inflate()
+     * @attr ref android.R.styleable#ViewStub_layout
+     */
+    public void setLayoutResource(int layoutResource) {
+        mLayoutResource = layoutResource;
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        setMeasuredDimension(0, 0);
+    }
+
+    @Override
+    public void draw(Canvas canvas) {
+    }
+
+    @Override
+    protected void dispatchDraw(Canvas canvas) {
+    }
+
+    /**
+     * When visibility is set to {@link #VISIBLE} or {@link #INVISIBLE},
+     * {@link #inflate()} is invoked and this StubbedView is replaced in its parent
+     * by the inflated layout resource.
+     *
+     * @param visibility One of {@link #VISIBLE}, {@link #INVISIBLE}, or {@link #GONE}.
+     *
+     * @see #inflate() 
+     */
+    @Override
+    public void setVisibility(int visibility) {
+        super.setVisibility(visibility);
+
+        if (visibility == VISIBLE || visibility == INVISIBLE) {
+            inflate();
+        }
+    }
+
+    /**
+     * Inflates the layout resource identified by {@link #getLayoutResource()}
+     * and replaces this StubbedView in its parent by the inflated layout resource.
+     *
+     * @return The inflated layout resource.
+     *
+     */
+    public View inflate() {
+        final ViewParent viewParent = getParent();
+
+        if (viewParent != null && viewParent instanceof ViewGroup) {
+            if (mLayoutResource != 0) {
+                final ViewGroup parent = (ViewGroup) viewParent;
+                final LayoutInflater factory = LayoutInflater.from(mContext);
+                final View view = factory.inflate(mLayoutResource, parent,
+                        false);
+
+                if (mInflatedId != NO_ID) {
+                    view.setId(mInflatedId);
+                }
+
+                final int index = parent.indexOfChild(this);
+                parent.removeViewInLayout(this);
+
+                final ViewGroup.LayoutParams layoutParams = getLayoutParams();
+                if (layoutParams != null) {
+                    parent.addView(view, index, layoutParams);
+                } else {
+                    parent.addView(view, index);
+                }
+
+                if (mInflateListener != null) {
+                    mInflateListener.onInflate(this, view);
+                }
+
+                return view;
+            } else {
+                throw new IllegalArgumentException("ViewStub must have a valid layoutResource");
+            }
+        } else {
+            throw new IllegalStateException("ViewStub must have a non-null ViewGroup viewParent");
+        }
+    }
+
+    /**
+     * Specifies the inflate listener to be notified after this ViewStub successfully
+     * inflated its layout resource.
+     *
+     * @param inflateListener The OnInflateListener to notify of successful inflation.
+     *
+     * @see android.view.ViewStub.OnInflateListener
+     */
+    public void setOnInflateListener(OnInflateListener inflateListener) {
+        mInflateListener = inflateListener;
+    }
+
+    /**
+     * Listener used to receive a notification after a ViewStub has successfully
+     * inflated its layout resource.
+     *
+     * @see android.view.ViewStub#setOnInflateListener(android.view.ViewStub.OnInflateListener) 
+     */
+    public static interface OnInflateListener {
+        /**
+         * Invoked after a ViewStub successfully inflated its layout resource.
+         * This method is invoked after the inflated view was added to the
+         * hierarchy but before the layout pass.
+         *
+         * @param stub The ViewStub that initiated the inflation.
+         * @param inflated The inflated View.
+         */
+        void onInflate(ViewStub stub, View inflated);
+    }
+}
diff --git a/core/java/android/view/ViewTreeObserver.java b/core/java/android/view/ViewTreeObserver.java
new file mode 100644
index 0000000..47b52e4
--- /dev/null
+++ b/core/java/android/view/ViewTreeObserver.java
@@ -0,0 +1,613 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.graphics.Rect;
+
+import java.util.ArrayList;
+
+/**
+ * A view tree observer is used to register listeners that can be notified of global
+ * changes in the view tree. Such global events include, but are not limited to,
+ * layout of the whole tree, beginning of the drawing pass, touch mode change....
+ *
+ * A ViewTreeObserver should never be instantiated by applications as it is provided
+ * by the views hierarchy. Refer to {@link android.view.View#getViewTreeObserver()}
+ * for more information.
+ */
+public final class ViewTreeObserver {
+    private ArrayList<OnGlobalFocusChangeListener> mOnGlobalFocusListeners;
+    private ArrayList<OnGlobalLayoutListener> mOnGlobalLayoutListeners;
+    private ArrayList<OnPreDrawListener> mOnPreDrawListeners;
+    private ArrayList<OnTouchModeChangeListener> mOnTouchModeChangeListeners;
+    private ArrayList<OnComputeInternalInsetsListener> mOnComputeInternalInsetsListeners;
+    private ArrayList<OnScrollChangedListener> mOnScrollChangedListeners;
+
+    private boolean mAlive = true;
+
+    /**
+     * Interface definition for a callback to be invoked when the focus state within
+     * the view tree changes.
+     */
+    public interface OnGlobalFocusChangeListener {
+        /**
+         * Callback method to be invoked when the focus changes in the view tree. When
+         * the view tree transitions from touch mode to non-touch mode, oldFocus is null.
+         * When the view tree transitions from non-touch mode to touch mode, newFocus is
+         * null. When focus changes in non-touch mode (without transition from or to
+         * touch mode) either oldFocus or newFocus can be null.
+         *
+         * @param oldFocus The previously focused view, if any.
+         * @param newFocus The newly focused View, if any.
+         */
+        public void onGlobalFocusChanged(View oldFocus, View newFocus);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the global layout state
+     * or the visibility of views within the view tree changes.
+     */
+    public interface OnGlobalLayoutListener {
+        /**
+         * Callback method to be invoked when the global layout state or the visibility of views
+         * within the view tree changes
+         */
+        public void onGlobalLayout();
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the view tree is about to be drawn.
+     */
+    public interface OnPreDrawListener {
+        /**
+         * Callback method to be invoked when the view tree is about to be drawn. At this point, all
+         * views in the tree have been measured and given a frame. Clients can use this to adjust
+         * their scroll bounds or even to request a new layout before drawing occurs.
+         *
+         * @return Return true to proceed with the current drawing pass, or false to cancel.
+         *
+         * @see android.view.View#onMeasure
+         * @see android.view.View#onLayout
+         * @see android.view.View#onDraw
+         */
+        public boolean onPreDraw();
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the touch mode changes.
+     */
+    public interface OnTouchModeChangeListener {
+        /**
+         * Callback method to be invoked when the touch mode changes.
+         *
+         * @param isInTouchMode True if the view hierarchy is now in touch mode, false  otherwise.
+         */
+        public void onTouchModeChanged(boolean isInTouchMode);
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when
+     * something in the view tree has been scrolled.
+     * 
+     * @hide pending API council approval
+     */
+    public interface OnScrollChangedListener {
+        /**
+         * Callback method to be invoked when something in the view tree
+         * has been scrolled.
+         */
+        public void onScrollChanged();
+    }
+
+    /**
+     * Parameters used with OnComputeInternalInsetsListener.
+     * {@hide pending API Council approval}
+     */
+    public final static class InternalInsetsInfo {
+        /**
+         * Offsets from the frame of the window at which the content of
+         * windows behind it should be placed.
+         */
+        public final Rect contentInsets = new Rect();
+        
+        /**
+         * Offsets from the fram of the window at which windows behind it
+         * are visible.
+         */
+        public final Rect visibleInsets = new Rect();
+        
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the entire window frame
+         * can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_FRAME = 0;
+        
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the content insets can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_CONTENT = 1;
+        
+        /**
+         * Option for {@link #setTouchableInsets(int)}: the area inside of
+         * the visible insets can be touched.
+         */
+        public static final int TOUCHABLE_INSETS_VISIBLE = 2;
+        
+        /**
+         * Set which parts of the window can be touched: either
+         * {@link #TOUCHABLE_INSETS_FRAME}, {@link #TOUCHABLE_INSETS_CONTENT},
+         * or {@link #TOUCHABLE_INSETS_VISIBLE}. 
+         */
+        public void setTouchableInsets(int val) {
+            mTouchableInsets = val;
+        }
+        
+        public int getTouchableInsets() {
+            return mTouchableInsets;
+        }
+        
+        int mTouchableInsets;
+        
+        void reset() {
+            final Rect givenContent = contentInsets;
+            final Rect givenVisible = visibleInsets;
+            givenContent.left = givenContent.top = givenContent.right
+                    = givenContent.bottom = givenVisible.left = givenVisible.top
+                    = givenVisible.right = givenVisible.bottom = 0;
+            mTouchableInsets = TOUCHABLE_INSETS_FRAME;
+        }
+        
+        @Override public boolean equals(Object o) {
+            try {
+                if (o == null) {
+                    return false;
+                }
+                InternalInsetsInfo other = (InternalInsetsInfo)o;
+                if (!contentInsets.equals(other.contentInsets)) {
+                    return false;
+                }
+                if (!visibleInsets.equals(other.visibleInsets)) {
+                    return false;
+                }
+                return mTouchableInsets == other.mTouchableInsets;
+            } catch (ClassCastException e) {
+                return false;
+            }
+        }
+        
+        void set(InternalInsetsInfo other) {
+            contentInsets.set(other.contentInsets);
+            visibleInsets.set(other.visibleInsets);
+            mTouchableInsets = other.mTouchableInsets;
+        }
+    }
+    
+    /**
+     * Interface definition for a callback to be invoked when layout has
+     * completed and the client can compute its interior insets.
+     * {@hide pending API Council approval}
+     */
+    public interface OnComputeInternalInsetsListener {
+        /**
+         * Callback method to be invoked when layout has completed and the
+         * client can compute its interior insets.
+         *
+         * @param inoutInfo Should be filled in by the implementation with
+         * the information about the insets of the window.  This is called
+         * with whatever values the previous OnComputeInternalInsetsListener
+         * returned, if there are multiple such listeners in the window.
+         */
+        public void onComputeInternalInsets(InternalInsetsInfo inoutInfo);
+    }
+
+    /**
+     * Creates a new ViewTreeObserver. This constructor should not be called
+     */
+    ViewTreeObserver() {
+    }
+
+    /**
+     * Merges all the listeners registered on the specified observer with the listeners
+     * registered on this object. After this method is invoked, the specified observer
+     * will return false in {@link #isAlive()} and should not be used anymore.
+     *
+     * @param observer The ViewTreeObserver whose listeners must be added to this observer
+     */
+    void merge(ViewTreeObserver observer) {
+        if (observer.mOnGlobalFocusListeners != null) {
+            if (mOnGlobalFocusListeners != null) {
+                mOnGlobalFocusListeners.addAll(observer.mOnGlobalFocusListeners);
+            } else {
+                mOnGlobalFocusListeners = observer.mOnGlobalFocusListeners;
+            }
+        }
+
+        if (observer.mOnGlobalLayoutListeners != null) {
+            if (mOnGlobalLayoutListeners != null) {
+                mOnGlobalLayoutListeners.addAll(observer.mOnGlobalLayoutListeners);
+            } else {
+                mOnGlobalLayoutListeners = observer.mOnGlobalLayoutListeners;
+            }
+        }
+
+        if (observer.mOnPreDrawListeners != null) {
+            if (mOnPreDrawListeners != null) {
+                mOnPreDrawListeners.addAll(observer.mOnPreDrawListeners);
+            } else {
+                mOnPreDrawListeners = observer.mOnPreDrawListeners;
+            }
+        }
+
+        if (observer.mOnTouchModeChangeListeners != null) {
+            if (mOnTouchModeChangeListeners != null) {
+                mOnTouchModeChangeListeners.addAll(observer.mOnTouchModeChangeListeners);
+            } else {
+                mOnTouchModeChangeListeners = observer.mOnTouchModeChangeListeners;
+            }
+        }
+
+        if (observer.mOnComputeInternalInsetsListeners != null) {
+            if (mOnComputeInternalInsetsListeners != null) {
+                mOnComputeInternalInsetsListeners.addAll(observer.mOnComputeInternalInsetsListeners);
+            } else {
+                mOnComputeInternalInsetsListeners = observer.mOnComputeInternalInsetsListeners;
+            }
+        }
+
+        observer.kill();
+    }
+
+    /**
+     * Register a callback to be invoked when the focus state within the view tree changes.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener listener) {
+        checkIsAlive();
+
+        if (mOnGlobalFocusListeners == null) {
+            mOnGlobalFocusListeners = new ArrayList<OnGlobalFocusChangeListener>();
+        }
+
+        mOnGlobalFocusListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed focus change callback.
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnGlobalFocusChangeListener(OnGlobalFocusChangeListener)
+     */
+    public void removeOnGlobalFocusChangeListener(OnGlobalFocusChangeListener victim) {
+        checkIsAlive();
+        if (mOnGlobalFocusListeners == null) {
+            return;
+        }
+        mOnGlobalFocusListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when the global layout state or the visibility of views
+     * within the view tree changes
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnGlobalLayoutListener(OnGlobalLayoutListener listener) {
+        checkIsAlive();
+
+        if (mOnGlobalLayoutListeners == null) {
+            mOnGlobalLayoutListeners = new ArrayList<OnGlobalLayoutListener>();
+        }
+
+        mOnGlobalLayoutListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed global layout callback
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnGlobalLayoutListener(OnGlobalLayoutListener)
+     */
+    public void removeGlobalOnLayoutListener(OnGlobalLayoutListener victim) {
+        checkIsAlive();
+        if (mOnGlobalLayoutListeners == null) {
+            return;
+        }
+        mOnGlobalLayoutListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when the view tree is about to be drawn
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnPreDrawListener(OnPreDrawListener listener) {
+        checkIsAlive();
+
+        if (mOnPreDrawListeners == null) {
+            mOnPreDrawListeners = new ArrayList<OnPreDrawListener>();
+        }
+
+        mOnPreDrawListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed pre-draw callback
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnPreDrawListener(OnPreDrawListener)
+     */
+    public void removeOnPreDrawListener(OnPreDrawListener victim) {
+        checkIsAlive();
+        if (mOnPreDrawListeners == null) {
+            return;
+        }
+        mOnPreDrawListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when a view has been scrolled.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @hide pending API council approval
+     */
+    public void addOnScrollChangedListener(OnScrollChangedListener listener) {
+        checkIsAlive();
+
+        if (mOnScrollChangedListeners == null) {
+            mOnScrollChangedListeners = new ArrayList<OnScrollChangedListener>();
+        }
+
+        mOnScrollChangedListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed scroll-changed callback
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnScrollChangedListener(OnScrollChangedListener)
+     *
+     * @hide pending API council approval
+     */
+    public void removeOnScrollChangedListener(OnScrollChangedListener victim) {
+        checkIsAlive();
+        if (mOnScrollChangedListeners == null) {
+            return;
+        }
+        mOnScrollChangedListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when the invoked when the touch mode changes.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     */
+    public void addOnTouchModeChangeListener(OnTouchModeChangeListener listener) {
+        checkIsAlive();
+
+        if (mOnTouchModeChangeListeners == null) {
+            mOnTouchModeChangeListeners = new ArrayList<OnTouchModeChangeListener>();
+        }
+
+        mOnTouchModeChangeListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed touch mode change callback
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnTouchModeChangeListener(OnTouchModeChangeListener)
+     */
+    public void removeOnTouchModeChangeListener(OnTouchModeChangeListener victim) {
+        checkIsAlive();
+        if (mOnTouchModeChangeListeners == null) {
+            return;
+        }
+        mOnTouchModeChangeListeners.remove(victim);
+    }
+
+    /**
+     * Register a callback to be invoked when the invoked when it is time to
+     * compute the window's internal insets.
+     *
+     * @param listener The callback to add
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     * {@hide pending API Council approval}
+     */
+    public void addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener listener) {
+        checkIsAlive();
+
+        if (mOnComputeInternalInsetsListeners == null) {
+            mOnComputeInternalInsetsListeners = new ArrayList<OnComputeInternalInsetsListener>();
+        }
+
+        mOnComputeInternalInsetsListeners.add(listener);
+    }
+
+    /**
+     * Remove a previously installed internal insets computation callback
+     *
+     * @param victim The callback to remove
+     *
+     * @throws IllegalStateException If {@link #isAlive()} returns false
+     *
+     * @see #addOnComputeInternalInsetsListener(OnComputeInternalInsetsListener)
+     * {@hide pending API Council approval}
+     */
+    public void removeOnComputeInternalInsetsListener(OnComputeInternalInsetsListener victim) {
+        checkIsAlive();
+        if (mOnComputeInternalInsetsListeners == null) {
+            return;
+        }
+        mOnComputeInternalInsetsListeners.remove(victim);
+    }
+
+    private void checkIsAlive() {
+        if (!mAlive) {
+            throw new IllegalStateException("This ViewTreeObserver is not alive, call "
+                    + "getViewTreeObserver() again");
+        }
+    }
+
+    /**
+     * Indicates whether this ViewTreeObserver is alive. When an observer is not alive,
+     * any call to a method (except this one) will throw an exception.
+     *
+     * If an application keeps a long-lived reference to this ViewTreeObserver, it should
+     * always check for the result of this method before calling any other method.
+     *
+     * @return True if this object is alive and be used, false otherwise.
+     */
+    public boolean isAlive() {
+        return mAlive;
+    }
+
+    /**
+     * Marks this ViewTreeObserver as not alive. After invoking this method, invoking
+     * any other method but {@link #isAlive()} and {@link #kill()} will throw an Exception.
+     *
+     * @hide
+     */
+    private void kill() {
+        mAlive = false;
+    }
+
+    /**
+     * Notifies registered listeners that focus has changed.
+     */
+    final void dispatchOnGlobalFocusChange(View oldFocus, View newFocus) {
+        final ArrayList<OnGlobalFocusChangeListener> globaFocusListeners = mOnGlobalFocusListeners;
+        if (globaFocusListeners != null) {
+            final int count = globaFocusListeners.size();
+            for (int i = count - 1; i >= 0; i--) {
+                globaFocusListeners.get(i).onGlobalFocusChanged(oldFocus, newFocus);
+            }
+        }
+    }
+
+    /**
+     * Notifies registered listeners that a global layout happened. This can be called
+     * manually if you are forcing a layout on a View or a hierarchy of Views that are
+     * not attached to a Window or in the GONE state.
+     */
+    public final void dispatchOnGlobalLayout() {
+        final ArrayList<OnGlobalLayoutListener> globaLayoutListeners = mOnGlobalLayoutListeners;
+        if (globaLayoutListeners != null) {
+            final int count = globaLayoutListeners.size();
+            for (int i = count - 1; i >= 0; i--) {
+                globaLayoutListeners.get(i).onGlobalLayout();
+            }
+        }
+    }
+
+    /**
+     * Notifies registered listeners that the drawing pass is about to start. If a
+     * listener returns true, then the drawing pass is canceled and rescheduled. This can
+     * be called manually if you are forcing the drawing on a View or a hierarchy of Views
+     * that are not attached to a Window or in the GONE state.
+     *
+     * @return True if the current draw should be canceled and resceduled, false otherwise.
+     */
+    public final boolean dispatchOnPreDraw() {
+        boolean cancelDraw = false;
+        final ArrayList<OnPreDrawListener> preDrawListeners = mOnPreDrawListeners;
+        if (preDrawListeners != null) {
+            final int count = preDrawListeners.size();
+            for (int i = count - 1; i >= 0; i--) {
+                cancelDraw |= !preDrawListeners.get(i).onPreDraw();
+            }
+        }
+        return cancelDraw;
+    }
+
+    /**
+     * Notifies registered listeners that the touch mode has changed.
+     *
+     * @param inTouchMode True if the touch mode is now enabled, false otherwise.
+     */
+    final void dispatchOnTouchModeChanged(boolean inTouchMode) {
+        final ArrayList<OnTouchModeChangeListener> touchModeListeners = mOnTouchModeChangeListeners;
+        if (touchModeListeners != null) {
+            final int count = touchModeListeners.size();
+            for (int i = count - 1; i >= 0; i--) {
+                touchModeListeners.get(i).onTouchModeChanged(inTouchMode);
+            }
+        }
+    }
+
+    /**
+     * Notifies registered listeners that something has scrolled.
+     */
+    final void dispatchOnScrollChanged() {
+        final ArrayList<OnScrollChangedListener> listeners = mOnScrollChangedListeners;
+
+        if (listeners != null) {
+            for (OnScrollChangedListener scl : mOnScrollChangedListeners) {
+                scl.onScrollChanged();
+            }
+        }
+    }
+
+    /**
+     * Returns whether there are listeners for computing internal insets.
+     */
+    final boolean hasComputeInternalInsetsListeners() {
+        final ArrayList<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners;
+        return (listeners != null && listeners.size() > 0);
+    }
+    
+    /**
+     * Calls all listeners to compute the current insets.
+     */
+    final void dispatchOnComputeInternalInsets(InternalInsetsInfo inoutInfo) {
+        final ArrayList<OnComputeInternalInsetsListener> listeners = mOnComputeInternalInsetsListeners;
+        if (listeners != null) {
+            final int count = listeners.size();
+            for (int i = count - 1; i >= 0; i--) {
+                listeners.get(i).onComputeInternalInsets(inoutInfo);
+            }
+        }
+    }
+}
diff --git a/core/java/android/view/VolumePanel.java b/core/java/android/view/VolumePanel.java
new file mode 100644
index 0000000..a573983
--- /dev/null
+++ b/core/java/android/view/VolumePanel.java
@@ -0,0 +1,410 @@
+/*
+ * Copyright (C) 2007 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.bluetooth.HeadsetBase;
+import android.content.Context;
+import android.content.Intent;
+import android.content.res.Resources;
+import android.media.AudioManager;
+import android.media.AudioService;
+import android.media.AudioSystem;
+import android.media.ToneGenerator;
+import android.os.Handler;
+import android.os.Message;
+import android.os.Vibrator;
+import android.util.Config;
+import android.util.Log;
+import android.widget.ImageView;
+import android.widget.ProgressBar;
+import android.widget.TextView;
+import android.widget.Toast;
+
+/**
+ * Handle the volume up and down keys.
+ *
+ * This code really should be moved elsewhere.
+ *
+ * @hide
+ */
+public class VolumePanel extends Handler
+{
+    private static final String TAG = "VolumePanel";
+    private static boolean LOGD = false || Config.LOGD;
+
+    /**
+     * The delay before playing a sound. This small period exists so the user
+     * can press another key (non-volume keys, too) to have it NOT be audible.
+     * <p>
+     * PhoneWindow will implement this part.
+     */
+    public static final int PLAY_SOUND_DELAY = 300;
+
+    /**
+     * The delay before vibrating. This small period exists so if the user is
+     * moving to silent mode, it will not emit a short vibrate (it normally
+     * would since vibrate is between normal mode and silent mode using hardware
+     * keys).
+     */
+    public static final int VIBRATE_DELAY = 300;
+
+    private static final int VIBRATE_DURATION = 300;
+    private static final int BEEP_DURATION = 150;
+    private static final int MAX_VOLUME = 100;
+    private static final int FREE_DELAY = 10000;
+
+    private static final int MSG_VOLUME_CHANGED = 0;
+    private static final int MSG_FREE_RESOURCES = 1;
+    private static final int MSG_PLAY_SOUND = 2;
+    private static final int MSG_STOP_SOUNDS = 3;
+    private static final int MSG_VIBRATE = 4;
+
+    private static final int RINGTONE_VOLUME_TEXT = com.android.internal.R.string.volume_ringtone;
+    private static final int MUSIC_VOLUME_TEXT = com.android.internal.R.string.volume_music;
+    private static final int INCALL_VOLUME_TEXT = com.android.internal.R.string.volume_call;
+    private static final int ALARM_VOLUME_TEXT = com.android.internal.R.string.volume_alarm;
+    private static final int UNKNOWN_VOLUME_TEXT = com.android.internal.R.string.volume_unknown;
+    private static final int NOTIFICATION_VOLUME_TEXT =
+            com.android.internal.R.string.volume_notification;
+    private static final int BLUETOOTH_INCALL_VOLUME_TEXT =
+            com.android.internal.R.string.volume_bluetooth_call;
+
+    protected Context mContext;
+    private AudioManager mAudioManager;
+    protected AudioService mAudioService;
+
+    private final Toast mToast;
+    private final View mView;
+    private final TextView mMessage;
+    private final TextView mAdditionalMessage;
+    private final ImageView mSmallStreamIcon;
+    private final ImageView mLargeStreamIcon;
+    private final ProgressBar mLevel;
+
+    // Synchronize when accessing this
+    private ToneGenerator mToneGenerators[];
+    private Vibrator mVibrator;
+
+    public VolumePanel(Context context, AudioService volumeService) {
+        mContext = context;
+        mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+        mAudioService = volumeService;
+        mToast = new Toast(context);
+
+        LayoutInflater inflater = (LayoutInflater) context
+                .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
+        View view = mView = inflater.inflate(com.android.internal.R.layout.volume_adjust, null);
+        mMessage = (TextView) view.findViewById(com.android.internal.R.id.message);
+        mAdditionalMessage =
+                (TextView) view.findViewById(com.android.internal.R.id.additional_message);
+        mSmallStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.other_stream_icon);
+        mLargeStreamIcon = (ImageView) view.findViewById(com.android.internal.R.id.ringer_stream_icon);
+        mLevel = (ProgressBar) view.findViewById(com.android.internal.R.id.level);
+
+        mToneGenerators = new ToneGenerator[AudioSystem.getNumStreamTypes()];
+        mVibrator = new Vibrator();
+    }
+
+    public void postVolumeChanged(int streamType, int flags) {
+        if (hasMessages(MSG_VOLUME_CHANGED)) return;
+        removeMessages(MSG_FREE_RESOURCES);
+        obtainMessage(MSG_VOLUME_CHANGED, streamType, flags).sendToTarget();
+    }
+
+    /**
+     * Override this if you have other work to do when the volume changes (for
+     * example, vibrating, playing a sound, etc.). Make sure to call through to
+     * the superclass implementation.
+     */
+    protected void onVolumeChanged(int streamType, int flags) {
+
+        if (LOGD) Log.d(TAG, "onVolumeChanged(streamType: " + streamType + ", flags: " + flags + ")");
+
+        if ((flags & AudioManager.FLAG_SHOW_UI) != 0) {
+            onShowVolumeChanged(streamType, flags);
+        }
+
+        if ((flags & AudioManager.FLAG_PLAY_SOUND) != 0) {
+            removeMessages(MSG_PLAY_SOUND);
+            sendMessageDelayed(obtainMessage(MSG_PLAY_SOUND, streamType, flags), PLAY_SOUND_DELAY);
+        }
+
+        if ((flags & AudioManager.FLAG_REMOVE_SOUND_AND_VIBRATE) != 0) {
+            removeMessages(MSG_PLAY_SOUND);
+            removeMessages(MSG_VIBRATE);
+            onStopSounds();
+        }
+
+        removeMessages(MSG_FREE_RESOURCES);
+        sendMessageDelayed(obtainMessage(MSG_FREE_RESOURCES), FREE_DELAY);
+    }
+
+    protected void onShowVolumeChanged(int streamType, int flags) {
+        int index = mAudioService.getStreamVolume(streamType);
+        int message = UNKNOWN_VOLUME_TEXT;
+        int additionalMessage = 0;
+
+        if (LOGD) {
+            Log.d(TAG, "onShowVolumeChanged(streamType: " + streamType
+                    + ", flags: " + flags + "), index: " + index);
+        }
+
+        // get max volume for progress bar
+        int max = mAudioService.getStreamMaxVolume(streamType);
+
+        switch (streamType) {
+
+            case AudioManager.STREAM_RING: {
+                message = RINGTONE_VOLUME_TEXT;
+                setRingerIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_MUSIC: {
+                message = MUSIC_VOLUME_TEXT;
+                if (mAudioManager.isBluetoothA2dpOn()) {
+                    additionalMessage =
+                        com.android.internal.R.string.volume_music_hint_playing_through_bluetooth;
+                    setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_ad2p);
+                } else {
+                    setSmallIcon(index);
+                }
+                break;
+            }
+
+            case AudioManager.STREAM_VOICE_CALL: {
+                /*
+                 * For in-call voice call volume, there is no inaudible volume.
+                 * Rescale the UI control so the progress bar doesn't go all
+                 * the way to zero and don't show the mute icon.
+                 */
+                index++;
+                max++;
+                message = INCALL_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_ALARM: {
+                message = ALARM_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_NOTIFICATION: {
+                message = NOTIFICATION_VOLUME_TEXT;
+                setSmallIcon(index);
+                break;
+            }
+
+            case AudioManager.STREAM_BLUETOOTH_SCO: {
+                /*
+                 * For in-call voice call volume, there is no inaudible volume.
+                 * Rescale the UI control so the progress bar doesn't go all
+                 * the way to zero and don't show the mute icon.
+                 */
+                index++;
+                max++;
+                message = BLUETOOTH_INCALL_VOLUME_TEXT;
+                setLargeIcon(com.android.internal.R.drawable.ic_volume_bluetooth_in_call);
+                break;
+            }
+        }
+
+        String messageString = Resources.getSystem().getString(message);
+        if (!mMessage.getText().equals(messageString)) {
+            mMessage.setText(messageString);
+        }
+
+        if (additionalMessage == 0) {
+            mAdditionalMessage.setVisibility(View.GONE);
+        } else {
+            mAdditionalMessage.setVisibility(View.VISIBLE);
+            mAdditionalMessage.setText(Resources.getSystem().getString(additionalMessage));
+        }
+
+        if (max != mLevel.getMax()) {
+            mLevel.setMax(max);
+        }
+        mLevel.setProgress(index);
+
+        mToast.setView(mView);
+        mToast.setDuration(Toast.LENGTH_SHORT);
+        mToast.setGravity(Gravity.TOP, 0, 0);
+        mToast.show();
+
+        // Do a little vibrate if applicable (only when going into vibrate mode)
+        if ((flags & AudioManager.FLAG_VIBRATE) != 0 &&
+                mAudioService.isStreamAffectedByRingerMode(streamType) &&
+                mAudioService.getRingerMode() == AudioManager.RINGER_MODE_VIBRATE &&
+                mAudioService.shouldVibrate(AudioManager.VIBRATE_TYPE_RINGER)) {
+            sendMessageDelayed(obtainMessage(MSG_VIBRATE), VIBRATE_DELAY);
+        }
+
+    }
+
+    protected void onPlaySound(int streamType, int flags) {
+
+        if (hasMessages(MSG_STOP_SOUNDS)) {
+            removeMessages(MSG_STOP_SOUNDS);
+            // Force stop right now
+            onStopSounds();
+        }
+
+        synchronized (this) {
+            ToneGenerator toneGen = getOrCreateToneGenerator(streamType);
+            toneGen.startTone(ToneGenerator.TONE_PROP_BEEP);
+        }
+
+        sendMessageDelayed(obtainMessage(MSG_STOP_SOUNDS), BEEP_DURATION);
+    }
+
+    protected void onStopSounds() {
+
+        synchronized (this) {
+            int numStreamTypes = AudioSystem.getNumStreamTypes();
+            for (int i = numStreamTypes - 1; i >= 0; i--) {
+                ToneGenerator toneGen = mToneGenerators[i];
+                if (toneGen != null) {
+                    toneGen.stopTone();
+                }
+            }
+        }
+    }
+
+    protected void onVibrate() {
+
+        // Make sure we ended up in vibrate ringer mode
+        if (mAudioService.getRingerMode() != AudioManager.RINGER_MODE_VIBRATE) {
+            return;
+        }
+
+        mVibrator.vibrate(VIBRATE_DURATION);
+    }
+
+    /**
+     * Lock on this VolumePanel instance as long as you use the returned ToneGenerator.
+     */
+    private ToneGenerator getOrCreateToneGenerator(int streamType) {
+        synchronized (this) {
+            if (mToneGenerators[streamType] == null) {
+                return mToneGenerators[streamType] = new ToneGenerator(streamType, MAX_VOLUME);
+            } else {
+                return mToneGenerators[streamType];
+            }
+        }
+    }
+
+    /**
+     * Makes the small icon visible, and hides the large icon.
+     *
+     * @param index The volume index, where 0 means muted.
+     */
+    private void setSmallIcon(int index) {
+        mLargeStreamIcon.setVisibility(View.GONE);
+        mSmallStreamIcon.setVisibility(View.VISIBLE);
+
+        mSmallStreamIcon.setImageResource(index == 0
+                ? com.android.internal.R.drawable.ic_volume_off_small
+                : com.android.internal.R.drawable.ic_volume_small);
+    }
+
+    /**
+     * Makes the large image view visible with the given icon.
+     *
+     * @param resId The icon to display.
+     */
+    private void setLargeIcon(int resId) {
+        mSmallStreamIcon.setVisibility(View.GONE);
+        mLargeStreamIcon.setVisibility(View.VISIBLE);
+        mLargeStreamIcon.setImageResource(resId);
+    }
+
+    /**
+     * Makes the ringer icon visible with an icon that is chosen
+     * based on the current ringer mode.
+     *
+     * @param index
+     */
+    private void setRingerIcon(int index) {
+        mSmallStreamIcon.setVisibility(View.GONE);
+        mLargeStreamIcon.setVisibility(View.VISIBLE);
+
+        int ringerMode = mAudioService.getRingerMode();
+        int icon;
+
+        if (LOGD) Log.d(TAG, "setRingerIcon(index: " + index+ "), ringerMode: " + ringerMode);
+
+        if (ringerMode == AudioManager.RINGER_MODE_SILENT) {
+            icon = com.android.internal.R.drawable.ic_volume_off;
+        } else if (ringerMode == AudioManager.RINGER_MODE_VIBRATE) {
+            icon = com.android.internal.R.drawable.ic_vibrate;
+        } else {
+            icon = com.android.internal.R.drawable.ic_volume;
+        }
+        mLargeStreamIcon.setImageResource(icon);
+    }
+
+    protected void onFreeResources() {
+        // We'll keep the views, just ditch the cached drawable and hence
+        // bitmaps
+        mSmallStreamIcon.setImageDrawable(null);
+        mLargeStreamIcon.setImageDrawable(null);
+
+        synchronized (this) {
+            for (int i = mToneGenerators.length - 1; i >= 0; i--) {
+                if (mToneGenerators[i] != null) {
+                    mToneGenerators[i].release();
+                }
+                mToneGenerators[i] = null;
+            }
+        }
+    }
+
+    @Override
+    public void handleMessage(Message msg) {
+        switch (msg.what) {
+
+            case MSG_VOLUME_CHANGED: {
+                onVolumeChanged(msg.arg1, msg.arg2);
+                break;
+            }
+
+            case MSG_FREE_RESOURCES: {
+                onFreeResources();
+                break;
+            }
+
+            case MSG_STOP_SOUNDS: {
+                onStopSounds();
+                break;
+            }
+
+            case MSG_PLAY_SOUND: {
+                onPlaySound(msg.arg1, msg.arg2);
+                break;
+            }
+
+            case MSG_VIBRATE: {
+                onVibrate();
+                break;
+            }
+
+        }
+    }
+
+}
diff --git a/core/java/android/view/Window.java b/core/java/android/view/Window.java
new file mode 100644
index 0000000..428de67
--- /dev/null
+++ b/core/java/android/view/Window.java
@@ -0,0 +1,1005 @@
+/*
+ * Copyright (C) 2006 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.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.PixelFormat;
+import android.graphics.drawable.Drawable;
+import android.net.Uri;
+import android.os.Bundle;
+import android.os.IBinder;
+import android.util.Log;
+
+/**
+ * Abstract base class for a top-level window look and behavior policy.  An
+ * instance of this class should be used as the top-level view added to the
+ * window manager. It provides standard UI policies such as a background, title
+ * area, default key processing, etc.
+ *
+ * <p>The only existing implementation of this abstract class is
+ * android.policy.PhoneWindow, which you should instantiate when needing a
+ * Window.  Eventually that class will be refactored and a factory method
+ * added for creating Window instances without knowing about a particular
+ * implementation.
+ */
+public abstract class Window {
+    /** Flag for the "options panel" feature.  This is enabled by default. */
+    public static final int FEATURE_OPTIONS_PANEL = 0;
+    /** Flag for the "no title" feature, turning off the title at the top
+     *  of the screen. */
+    public static final int FEATURE_NO_TITLE = 1;
+    /** Flag for the progress indicator feature */
+    public static final int FEATURE_PROGRESS = 2;
+    /** Flag for having an icon on the left side of the title bar */
+    public static final int FEATURE_LEFT_ICON = 3;
+    /** Flag for having an icon on the right side of the title bar */
+    public static final int FEATURE_RIGHT_ICON = 4;
+    /** Flag for indeterminate progress */
+    public static final int FEATURE_INDETERMINATE_PROGRESS = 5;
+    /** Flag for the context menu.  This is enabled by default. */
+    public static final int FEATURE_CONTEXT_MENU = 6;
+    /** Flag for custom title. You cannot combine this feature with other title features. */
+    public static final int FEATURE_CUSTOM_TITLE = 7;
+    /*  Flag for asking for an OpenGL enabled window.
+        All 2D graphics will be handled by OpenGL ES.
+        Private for now, until it is better tested (not shipping in 1.0)
+    */
+    private static final int FEATURE_OPENGL = 8;
+    /** Flag for setting the progress bar's visibility to VISIBLE */
+    public static final int PROGRESS_VISIBILITY_ON = -1;
+    /** Flag for setting the progress bar's visibility to GONE */
+    public static final int PROGRESS_VISIBILITY_OFF = -2;
+    /** Flag for setting the progress bar's indeterminate mode on */
+    public static final int PROGRESS_INDETERMINATE_ON = -3;
+    /** Flag for setting the progress bar's indeterminate mode off */
+    public static final int PROGRESS_INDETERMINATE_OFF = -4;
+    /** Starting value for the (primary) progress */
+    public static final int PROGRESS_START = 0;
+    /** Ending value for the (primary) progress */
+    public static final int PROGRESS_END = 10000;
+    /** Lowest possible value for the secondary progress */
+    public static final int PROGRESS_SECONDARY_START = 20000;
+    /** Highest possible value for the secondary progress */
+    public static final int PROGRESS_SECONDARY_END = 30000;
+    
+    /** The default features enabled */
+    @SuppressWarnings({"PointlessBitwiseExpression"})
+    protected static final int DEFAULT_FEATURES = (1 << FEATURE_OPTIONS_PANEL) |
+            (1 << FEATURE_CONTEXT_MENU);
+
+    /**
+     * The ID that the main layout in the XML layout file should have.
+     */
+    public static final int ID_ANDROID_CONTENT = com.android.internal.R.id.content;
+
+    private final Context mContext;
+    
+    private TypedArray mWindowStyle;
+    private Callback mCallback;
+    private WindowManager mWindowManager;
+    private IBinder mAppToken;
+    private String mAppName;
+    private Window mContainer;
+    private Window mActiveChild;
+    private boolean mIsActive = false;
+    private boolean mHasChildren = false;
+    private int mForcedWindowFlags = 0;
+
+    private int mFeatures = DEFAULT_FEATURES;
+    private int mLocalFeatures = DEFAULT_FEATURES;
+
+    private boolean mHaveWindowFormat = false;
+    private int mDefaultWindowFormat = PixelFormat.OPAQUE;
+
+    private boolean mHasSoftInputMode = false;
+    
+    // The current window attributes.
+    private final WindowManager.LayoutParams mWindowAttributes =
+        new WindowManager.LayoutParams();
+
+    /**
+     * API from a Window back to its caller.  This allows the client to
+     * intercept key dispatching, panels and menus, etc.
+     */
+    public interface Callback {
+        /**
+         * Called to process key events.  At the very least your
+         * implementation must call
+         * {@link android.view.Window#superDispatchKeyEvent} to do the
+         * standard key processing.
+         *
+         * @param event The key event.
+         *
+         * @return boolean Return true if this event was consumed.
+         */
+        public boolean dispatchKeyEvent(KeyEvent event);
+
+        /**
+         * Called to process touch screen events.  At the very least your
+         * implementation must call
+         * {@link android.view.Window#superDispatchTouchEvent} to do the
+         * standard touch screen processing.
+         *
+         * @param event The touch screen event.
+         *
+         * @return boolean Return true if this event was consumed.
+         */
+        public boolean dispatchTouchEvent(MotionEvent event);
+        
+        /**
+         * Called to process trackball events.  At the very least your
+         * implementation must call
+         * {@link android.view.Window#superDispatchTrackballEvent} to do the
+         * standard trackball processing.
+         *
+         * @param event The trackball event.
+         *
+         * @return boolean Return true if this event was consumed.
+         */
+        public boolean dispatchTrackballEvent(MotionEvent event);
+        
+        /**
+         * Instantiate the view to display in the panel for 'featureId'.
+         * You can return null, in which case the default content (typically
+         * a menu) will be created for you.
+         *
+         * @param featureId Which panel is being created.
+         *
+         * @return view The top-level view to place in the panel.
+         *
+         * @see #onPreparePanel
+         */
+        public View onCreatePanelView(int featureId);
+
+        /**
+         * Initialize the contents of the menu for panel 'featureId'.  This is
+         * called if onCreatePanelView() returns null, giving you a standard
+         * menu in which you can place your items.  It is only called once for
+         * the panel, the first time it is shown.
+         *
+         * <p>You can safely hold on to <var>menu</var> (and any items created
+         * from it), making modifications to it as desired, until the next
+         * time onCreatePanelMenu() is called for this feature.
+         *
+         * @param featureId The panel being created.
+         * @param menu The menu inside the panel.
+         *
+         * @return boolean You must return true for the panel to be displayed;
+         *         if you return false it will not be shown.
+         */
+        public boolean onCreatePanelMenu(int featureId, Menu menu);
+
+        /**
+         * Prepare a panel to be displayed.  This is called right before the
+         * panel window is shown, every time it is shown.
+         *
+         * @param featureId The panel that is being displayed.
+         * @param view The View that was returned by onCreatePanelView().
+         * @param menu If onCreatePanelView() returned null, this is the Menu
+         *             being displayed in the panel.
+         *
+         * @return boolean You must return true for the panel to be displayed;
+         *         if you return false it will not be shown.
+         *
+         * @see #onCreatePanelView
+         */
+        public boolean onPreparePanel(int featureId, View view, Menu menu);
+
+        /**
+         * Called when a panel's menu is opened by the user. This may also be
+         * called when the menu is changing from one type to another (for
+         * example, from the icon menu to the expanded menu).
+         * 
+         * @param featureId The panel that the menu is in.
+         * @param menu The menu that is opened.
+         * @return Return true to allow the menu to open, or false to prevent
+         *         the menu from opening.
+         */
+        public boolean onMenuOpened(int featureId, Menu menu);
+        
+        /**
+         * Called when a panel's menu item has been selected by the user.
+         *
+         * @param featureId The panel that the menu is in.
+         * @param item The menu item that was selected.
+         *
+         * @return boolean Return true to finish processing of selection, or
+         *         false to perform the normal menu handling (calling its
+         *         Runnable or sending a Message to its target Handler).
+         */
+        public boolean onMenuItemSelected(int featureId, MenuItem item);
+
+        /**
+         * This is called whenever the current window attributes change.
+         *
+
+         */
+        public void onWindowAttributesChanged(WindowManager.LayoutParams attrs);
+
+        /**
+         * This hook is called whenever the content view of the screen changes
+         * (due to a call to
+         * {@link Window#setContentView(View, android.view.ViewGroup.LayoutParams)
+         * Window.setContentView} or
+         * {@link Window#addContentView(View, android.view.ViewGroup.LayoutParams)
+         * Window.addContentView}).
+         */
+        public void onContentChanged();
+
+        /**
+         * This hook is called whenever the window focus changes.
+         *
+         * @param hasFocus Whether the window now has focus.
+         */
+        public void onWindowFocusChanged(boolean hasFocus);
+
+        /**
+         * Called when a panel is being closed.  If another logical subsequent
+         * panel is being opened (and this panel is being closed to make room for the subsequent
+         * panel), this method will NOT be called.
+         * 
+         * @param featureId The panel that is being displayed.
+         * @param menu If onCreatePanelView() returned null, this is the Menu
+         *            being displayed in the panel.
+         */
+        public void onPanelClosed(int featureId, Menu menu);
+        
+        /**
+         * Called when the user signals the desire to start a search.
+         * 
+         * @return true if search launched, false if activity refuses (blocks)
+         * 
+         * @see android.app.Activity#onSearchRequested() 
+         */
+        public boolean onSearchRequested();
+    }
+
+    public Window(Context context) {
+        mContext = context;
+    }
+
+    /**
+     * Return the Context this window policy is running in, for retrieving
+     * resources and other information.
+     *
+     * @return Context The Context that was supplied to the constructor.
+     */
+    public final Context getContext() {
+        return mContext;
+    }
+
+    /**
+     * Return the {@link android.R.styleable#Window} attributes from this
+     * window's theme.
+     */
+    public final TypedArray getWindowStyle() {
+        synchronized (this) {
+            if (mWindowStyle == null) {
+                mWindowStyle = mContext.obtainStyledAttributes(
+                        com.android.internal.R.styleable.Window);
+            }
+            return mWindowStyle;
+        }
+    }
+    
+    /**
+     * Set the container for this window.  If not set, the DecorWindow
+     * operates as a top-level window; otherwise, it negotiates with the
+     * container to display itself appropriately.
+     *
+     * @param container The desired containing Window.
+     */
+    public void setContainer(Window container) {
+        mContainer = container;
+        if (container != null) {
+            // Embedded screens never have a title.
+            mFeatures |= 1<<FEATURE_NO_TITLE;
+            mLocalFeatures |= 1<<FEATURE_NO_TITLE;
+            container.mHasChildren = true;
+        }
+    }
+
+    /**
+     * Return the container for this Window.
+     *
+     * @return Window The containing window, or null if this is a
+     *         top-level window.
+     */
+    public final Window getContainer() {
+        return mContainer;
+    }
+
+    public final boolean hasChildren() {
+        return mHasChildren;
+    }
+    
+    /**
+     * Set the window manager for use by this Window to, for example,
+     * display panels.  This is <em>not</em> used for displaying the
+     * Window itself -- that must be done by the client.
+     *
+     * @param wm The ViewManager for adding new windows.
+     */
+    public void setWindowManager(WindowManager wm,
+            IBinder appToken, String appName) {
+        mAppToken = appToken;
+        mAppName = appName;
+        if (wm == null) {
+            wm = WindowManagerImpl.getDefault();
+        }
+        mWindowManager = new LocalWindowManager(wm);
+    }
+
+    private class LocalWindowManager implements WindowManager {
+        LocalWindowManager(WindowManager wm) {
+            mWindowManager = wm;
+        }
+
+        public final void addView(View view, ViewGroup.LayoutParams params) {
+            // Let this throw an exception on a bad params.
+            WindowManager.LayoutParams wp = (WindowManager.LayoutParams)params;
+            CharSequence curTitle = wp.getTitle();
+            if (wp.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
+                wp.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                if (wp.token == null) {
+                    View decor = peekDecorView();
+                    if (decor != null) {
+                        wp.token = decor.getWindowToken();
+                    }
+                }
+                if (curTitle == null || curTitle.length() == 0) {
+                    String title;
+                    if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_MEDIA) {
+                        title="Media";
+                    } else if (wp.type == WindowManager.LayoutParams.TYPE_APPLICATION_PANEL) {
+                        title="Panel";
+                    } else {
+                        title=Integer.toString(wp.type);
+                    }
+                    if (mAppName != null) {
+                        title += ":" + mAppName;
+                    }
+                    wp.setTitle(title);
+                }
+            } else {
+                if (wp.token == null) {
+                    wp.token = mContainer == null ? mAppToken : mContainer.mAppToken;
+                }
+                if ((curTitle == null || curTitle.length() == 0)
+                        && mAppName != null) {
+                    wp.setTitle(mAppName);
+                }
+           }
+            if (wp.packageName == null) {
+                wp.packageName = mContext.getPackageName();
+            }
+            mWindowManager.addView(view, params);
+        }
+
+        public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+            mWindowManager.updateViewLayout(view, params);
+        }
+
+        public final void removeView(View view) {
+            mWindowManager.removeView(view);
+        }
+
+        public final void removeViewImmediate(View view) {
+            mWindowManager.removeViewImmediate(view);
+        }
+
+        public Display getDefaultDisplay() {
+            return mWindowManager.getDefaultDisplay();
+        }
+        
+        WindowManager mWindowManager;
+    }
+
+    /**
+     * Return the window manager allowing this Window to display its own
+     * windows.
+     *
+     * @return WindowManager The ViewManager.
+     */
+    public WindowManager getWindowManager() {
+        return mWindowManager;
+    }
+
+    /**
+     * Set the Callback interface for this window, used to intercept key
+     * events and other dynamic operations in the window.
+     *
+     * @param callback The desired Callback interface.
+     */
+    public void setCallback(Callback callback) {
+        mCallback = callback;
+    }
+
+    /**
+     * Return the current Callback interface for this window.
+     */
+    public final Callback getCallback() {
+        return mCallback;
+    }
+
+    /**
+     * Return whether this window is being displayed with a floating style
+     * (based on the {@link android.R.attr#windowIsFloating} attribute in
+     * the style/theme).
+     *
+     * @return Returns true if the window is configured to be displayed floating
+     * on top of whatever is behind it.
+     */
+    public abstract boolean isFloating();
+
+    /**
+     * Set the width and height layout parameters of the window.  The default
+     * for both of these is FILL_PARENT; you can change them to WRAP_CONTENT to
+     * make a window that is not full-screen.
+     *
+     * @param width The desired layout width of the window.
+     * @param height The desired layout height of the window.
+     */
+    public void setLayout(int width, int height)
+    {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.width = width;
+        attrs.height = height;
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Set the gravity of the window, as per the Gravity constants.  This
+     * controls how the window manager is positioned in the overall window; it
+     * is only useful when using WRAP_CONTENT for the layout width or height.
+     *
+     * @param gravity The desired gravity constant.
+     *
+     * @see Gravity
+     * @see #setLayout
+     */
+    public void setGravity(int gravity)
+    {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.gravity = gravity;
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Set the type of the window, as per the WindowManager.LayoutParams
+     * types.
+     *
+     * @param type The new window type (see WindowManager.LayoutParams).
+     */
+    public void setType(int type) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.type = type;
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Set the format of window, as per the PixelFormat types.  This overrides
+     * the default format that is selected by the Window based on its
+     * window decorations.
+     *
+     * @param format The new window format (see PixelFormat).  Use
+     *               PixelFormat.UNKNOWN to allow the Window to select
+     *               the format.
+     *
+     * @see PixelFormat
+     */
+    public void setFormat(int format) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        if (format != PixelFormat.UNKNOWN) {
+            attrs.format = format;
+            mHaveWindowFormat = true;
+        } else {
+            attrs.format = mDefaultWindowFormat;
+            mHaveWindowFormat = false;
+        }
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Specify custom animations to use for the window, as per
+     * {@link WindowManager.LayoutParams#windowAnimations
+     * WindowManager.LayoutParams.windowAnimations}.  Providing anything besides
+     * 0 here will override the animations the window would
+     * normally retrieve from its theme.
+     */
+    public void setWindowAnimations(int resId) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.windowAnimations = resId;
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Specify an explicit soft input mode to use for the window, as per
+     * {@link WindowManager.LayoutParams#softInputMode
+     * WindowManager.LayoutParams.softInputMode}.  Providing anything besides
+     * "unspecified" here will override the input mode the window would
+     * normally retrieve from its theme.
+     */
+    public void setSoftInputMode(int mode) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        if (mode != WindowManager.LayoutParams.SOFT_INPUT_STATE_UNSPECIFIED) {
+            attrs.softInputMode = mode;
+            mHasSoftInputMode = true;
+        } else {
+            mHasSoftInputMode = false;
+        }
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+    
+    /**
+     * Convenience function to set the flag bits as specified in flags, as
+     * per {@link #setFlags}.
+     * @param flags The flag bits to be set.
+     * @see #setFlags
+     */
+    public void addFlags(int flags) {
+        setFlags(flags, flags);
+    }
+    
+    /**
+     * Convenience function to clear the flag bits as specified in flags, as
+     * per {@link #setFlags}.
+     * @param flags The flag bits to be cleared.
+     * @see #setFlags
+     */
+    public void clearFlags(int flags) {
+        setFlags(0, flags);
+    }
+
+    /**
+     * Set the flags of the window, as per the
+     * {@link WindowManager.LayoutParams WindowManager.LayoutParams}
+     * flags.
+     * 
+     * <p>Note that some flags must be set before the window decoration is
+     * created (by the first call to
+     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)} or
+     * {@link #getDecorView()}:
+     * {@link WindowManager.LayoutParams#FLAG_LAYOUT_IN_SCREEN} and
+     * {@link WindowManager.LayoutParams#FLAG_LAYOUT_INSET_DECOR}.  These
+     * will be set for you based on the {@link android.R.attr#windowIsFloating}
+     * attribute.
+     *
+     * @param flags The new window flags (see WindowManager.LayoutParams).
+     * @param mask Which of the window flag bits to modify.
+     */
+    public void setFlags(int flags, int mask) {
+        final WindowManager.LayoutParams attrs = getAttributes();
+        attrs.flags = (attrs.flags&~mask) | (flags&mask);
+        mForcedWindowFlags |= mask;
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(attrs);
+        }
+    }
+
+    /**
+     * Specify custom window attributes.  <strong>PLEASE NOTE:</strong> the
+     * layout params you give here should generally be from values previously
+     * retrieved with {@link #getAttributes()}; you probably do not want to
+     * blindly create and apply your own, since this will blow away any values
+     * set by the framework that you are not interested in.
+     *
+     * @param a The new window attributes, which will completely override any
+     *          current values.
+     */
+    public void setAttributes(WindowManager.LayoutParams a) {
+        mWindowAttributes.copyFrom(a);
+        if (mCallback != null) {
+            mCallback.onWindowAttributesChanged(mWindowAttributes);
+        }
+    }
+
+    /**
+     * Retrieve the current window attributes associated with this panel.
+     *
+     * @return WindowManager.LayoutParams Either the existing window
+     *         attributes object, or a freshly created one if there is none.
+     */
+    public final WindowManager.LayoutParams getAttributes() {
+        return mWindowAttributes;
+    }
+
+    /**
+     * Return the window flags that have been explicitly set by the client,
+     * so will not be modified by {@link #getDecorView}.
+     */
+    protected final int getForcedWindowFlags() {
+        return mForcedWindowFlags;
+    }
+    
+    /**
+     * Has the app specified their own soft input mode?
+     */
+    protected final boolean hasSoftInputMode() {
+        return mHasSoftInputMode;
+    }
+    
+    /**
+     * Enable extended screen features.  This must be called before
+     * setContentView().  May be called as many times as desired as long as it
+     * is before setContentView().  If not called, no extended features
+     * will be available.  You can not turn off a feature once it is requested.
+     * You canot use other title features with {@link #FEATURE_CUSTOM_TITLE}.
+     *
+     * @param featureId The desired features, defined as constants by Window.
+     * @return The features that are now set.
+     */
+    public boolean requestFeature(int featureId) {
+        final int flag = 1<<featureId;
+        mFeatures |= flag;
+        mLocalFeatures |= mContainer != null ? (flag&~mContainer.mFeatures) : flag;
+        return (mFeatures&flag) != 0;
+    }
+
+    public final void makeActive() {
+        if (mContainer != null) {
+            if (mContainer.mActiveChild != null) {
+                mContainer.mActiveChild.mIsActive = false;
+            }
+            mContainer.mActiveChild = this;
+        }
+        mIsActive = true;
+        onActive();
+    }
+
+    public final boolean isActive()
+    {
+        return mIsActive;
+    }
+
+    /**
+     * Finds a view that was identified by the id attribute from the XML that
+     * was processed in {@link android.app.Activity#onCreate}.  This will
+     * implicitly call {@link #getDecorView} for you, with all of the
+     * associated side-effects.
+     *
+     * @return The view if found or null otherwise.
+     */
+    public View findViewById(int id) {
+        return getDecorView().findViewById(id);
+    }
+
+    /**
+     * Convenience for
+     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
+     * to set the screen content from a layout resource.  The resource will be
+     * inflated, adding all top-level views to the screen.
+     *
+     * @param layoutResID Resource ID to be inflated.
+     * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
+     */
+    public abstract void setContentView(int layoutResID);
+
+    /**
+     * Convenience for
+     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
+     * set the screen content to an explicit view.  This view is placed
+     * directly into the screen's view hierarchy.  It can itself be a complex
+     * view hierarhcy.
+     *
+     * @param view The desired content to display.
+     * @see #setContentView(View, android.view.ViewGroup.LayoutParams)
+     */
+    public abstract void setContentView(View view);
+
+    /**
+     * Set the screen content to an explicit view.  This view is placed
+     * directly into the screen's view hierarchy.  It can itself be a complex
+     * view hierarchy.
+     *
+     * <p>Note that calling this function "locks in" various characteristics
+     * of the window that can not, from this point forward, be changed: the
+     * features that have been requested with {@link #requestFeature(int)},
+     * and certain window flags as described in {@link #setFlags(int, int)}.
+     * 
+     * @param view The desired content to display.
+     * @param params Layout parameters for the view.
+     */
+    public abstract void setContentView(View view, ViewGroup.LayoutParams params);
+
+    /**
+     * Variation on
+     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}
+     * to add an additional content view to the screen.  Added after any existing
+     * ones in the screen -- existing views are NOT removed.
+     *
+     * @param view The desired content to display.
+     * @param params Layout parameters for the view.
+     */
+    public abstract void addContentView(View view, ViewGroup.LayoutParams params);
+
+    /**
+     * Return the view in this Window that currently has focus, or null if
+     * there are none.  Note that this does not look in any containing
+     * Window.
+     *
+     * @return View The current View with focus or null.
+     */
+    public abstract View getCurrentFocus();
+
+    /**
+     * Quick access to the {@link LayoutInflater} instance that this Window
+     * retrieved from its Context.
+     *
+     * @return LayoutInflater The shared LayoutInflater.
+     */
+    public abstract LayoutInflater getLayoutInflater();
+
+    public abstract void setTitle(CharSequence title);
+
+    public abstract void setTitleColor(int textColor);
+
+    public abstract void openPanel(int featureId, KeyEvent event);
+
+    public abstract void closePanel(int featureId);
+
+    public abstract void togglePanel(int featureId, KeyEvent event);
+
+    public abstract boolean performPanelShortcut(int featureId,
+                                                 int keyCode,
+                                                 KeyEvent event,
+                                                 int flags);
+    public abstract boolean performPanelIdentifierAction(int featureId,
+                                                 int id,
+                                                 int flags);
+
+    public abstract void closeAllPanels();
+
+    public abstract boolean performContextMenuIdentifierAction(int id, int flags);
+
+    /**
+     * Should be called when the configuration is changed.
+     * 
+     * @param newConfig The new configuration.
+     */
+    public abstract void onConfigurationChanged(Configuration newConfig);
+    
+    /**
+     * Change the background of this window to a Drawable resource. Setting the
+     * background to null will make the window be opaque. To make the window
+     * transparent, you can use an empty drawable (for instance a ColorDrawable
+     * with the color 0 or the system drawable android:drawable/empty.)
+     * 
+     * @param resid The resource identifier of a drawable resource which will be
+     *              installed as the new background.
+     */
+    public void setBackgroundDrawableResource(int resid)
+    {
+        setBackgroundDrawable(mContext.getResources().getDrawable(resid));
+    }
+
+    /**
+     * Change the background of this window to a custom Drawable. Setting the
+     * background to null will make the window be opaque. To make the window
+     * transparent, you can use an empty drawable (for instance a ColorDrawable
+     * with the color 0 or the system drawable android:drawable/empty.)
+     *
+     * @param drawable The new Drawable to use for this window's background.
+     */
+    public abstract void setBackgroundDrawable(Drawable drawable);
+
+    /**
+     * Set the value for a drawable feature of this window, from a resource
+     * identifier.  You must have called requestFeauture(featureId) before
+     * calling this function.
+     *
+     * @see android.content.res.Resources#getDrawable(int)
+     *
+     * @param featureId The desired drawable feature to change, defined as a
+     * constant by Window.
+     * @param resId Resource identifier of the desired image.
+     */
+    public abstract void setFeatureDrawableResource(int featureId, int resId);
+
+    /**
+     * Set the value for a drawable feature of this window, from a URI. You
+     * must have called requestFeature(featureId) before calling this
+     * function.
+     *
+     * <p>The only URI currently supported is "content:", specifying an image
+     * in a content provider.
+     *
+     * @see android.widget.ImageView#setImageURI
+     *
+     * @param featureId The desired drawable feature to change. Features are
+     * constants defined by Window.
+     * @param uri The desired URI.
+     */
+    public abstract void setFeatureDrawableUri(int featureId, Uri uri);
+
+    /**
+     * Set an explicit Drawable value for feature of this window. You must
+     * have called requestFeature(featureId) before calling this function.
+     *
+     * @param featureId The desired drawable feature to change.
+     * Features are constants defined by Window.
+     * @param drawable A Drawable object to display.
+     */
+    public abstract void setFeatureDrawable(int featureId, Drawable drawable);
+
+    /**
+     * Set a custom alpha value for the given drawale feature, controlling how
+     * much the background is visible through it.
+     *
+     * @param featureId The desired drawable feature to change.
+     * Features are constants defined by Window.
+     * @param alpha The alpha amount, 0 is completely transparent and 255 is
+     *              completely opaque.
+     */
+    public abstract void setFeatureDrawableAlpha(int featureId, int alpha);
+
+    /**
+     * Set the integer value for a feature.  The range of the value depends on
+     * the feature being set.  For FEATURE_PROGRESSS, it should go from 0 to
+     * 10000. At 10000 the progress is complete and the indicator hidden.
+     *
+     * @param featureId The desired feature to change.
+     * Features are constants defined by Window.
+     * @param value The value for the feature.  The interpretation of this
+     *              value is feature-specific.
+     */
+    public abstract void setFeatureInt(int featureId, int value);
+
+    /**
+     * Request that key events come to this activity. Use this if your
+     * activity has no views with focus, but the activity still wants
+     * a chance to process key events.
+     */
+    public abstract void takeKeyEvents(boolean get);
+
+    /**
+     * Used by custom windows, such as Dialog, to pass the key press event
+     * further down the view hierarchy. Application developers should
+     * not need to implement or call this.
+     *
+     */
+    public abstract boolean superDispatchKeyEvent(KeyEvent event);
+
+    /**
+     * Used by custom windows, such as Dialog, to pass the touch screen event
+     * further down the view hierarchy. Application developers should
+     * not need to implement or call this.
+     *
+     */
+    public abstract boolean superDispatchTouchEvent(MotionEvent event);
+    
+    /**
+     * Used by custom windows, such as Dialog, to pass the trackball event
+     * further down the view hierarchy. Application developers should
+     * not need to implement or call this.
+     *
+     */
+    public abstract boolean superDispatchTrackballEvent(MotionEvent event);
+    
+    /**
+     * Retrieve the top-level window decor view (containing the standard
+     * window frame/decorations and the client's content inside of that), which
+     * can be added as a window to the window manager.
+     * 
+     * <p><em>Note that calling this function for the first time "locks in"
+     * various window characteristics as described in
+     * {@link #setContentView(View, android.view.ViewGroup.LayoutParams)}.</em></p>
+     * 
+     * @return Returns the top-level window decor view.
+     */
+    public abstract View getDecorView();
+
+    /**
+     * Retrieve the current decor view, but only if it has already been created;
+     * otherwise returns null.
+     * 
+     * @return Returns the top-level window decor or null.
+     * @see #getDecorView
+     */
+    public abstract View peekDecorView();
+
+    public abstract Bundle saveHierarchyState();
+    
+    public abstract void restoreHierarchyState(Bundle savedInstanceState);
+    
+    protected abstract void onActive();
+
+    /**
+     * Return the feature bits that are enabled.  This is the set of features
+     * that were given to requestFeature(), and are being handled by this
+     * Window itself or its container.  That is, it is the set of
+     * requested features that you can actually use.
+     *
+     * <p>To do: add a public version of this API that allows you to check for
+     * features by their feature ID.
+     *
+     * @return int The feature bits.
+     */
+    protected final int getFeatures()
+    {
+        return mFeatures;
+    }
+
+    /**
+     * Return the feature bits that are being implemented by this Window.
+     * This is the set of features that were given to requestFeature(), and are
+     * being handled by only this Window itself, not by its containers.
+     *
+     * @return int The feature bits.
+     */
+    protected final int getLocalFeatures()
+    {
+        return mLocalFeatures;
+    }
+
+    /**
+     * Set the default format of window, as per the PixelFormat types.  This
+     * is the format that will be used unless the client specifies in explicit
+     * format with setFormat();
+     *
+     * @param format The new window format (see PixelFormat).
+     *
+     * @see #setFormat
+     * @see PixelFormat
+     */
+    protected void setDefaultWindowFormat(int format) {
+        mDefaultWindowFormat = format;
+        if (!mHaveWindowFormat) {
+            final WindowManager.LayoutParams attrs = getAttributes();
+            attrs.format = format;
+            if (mCallback != null) {
+                mCallback.onWindowAttributesChanged(attrs);
+            }
+        }
+    }
+
+    public abstract void setChildDrawable(int featureId, Drawable drawable);
+
+    public abstract void setChildInt(int featureId, int value);
+
+    /**
+     * Is a keypress one of the defined shortcut keys for this window.
+     * @param keyCode the key code from {@link android.view.KeyEvent} to check.
+     * @param event the {@link android.view.KeyEvent} to use to help check.
+     */
+    public abstract boolean isShortcutKey(int keyCode, KeyEvent event);
+    
+    /**
+     * @see android.app.Activity#setVolumeControlStream(int) 
+     */
+    public abstract void setVolumeControlStream(int streamType);
+
+    /**
+     * @see android.app.Activity#getVolumeControlStream()
+     */
+    public abstract int getVolumeControlStream();
+    
+}
diff --git a/core/java/android/view/WindowManager.aidl b/core/java/android/view/WindowManager.aidl
new file mode 100755
index 0000000..556dc72
--- /dev/null
+++ b/core/java/android/view/WindowManager.aidl
@@ -0,0 +1,21 @@
+/* //device/java/android/android/view/WindowManager.aidl
+**
+** Copyright 2007, 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;
+
+parcelable WindowManager.LayoutParams;
+
diff --git a/core/java/android/view/WindowManager.java b/core/java/android/view/WindowManager.java
new file mode 100644
index 0000000..b87cc42
--- /dev/null
+++ b/core/java/android/view/WindowManager.java
@@ -0,0 +1,959 @@
+/*
+ * Copyright (C) 2006 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.pm.ActivityInfo;
+import android.graphics.PixelFormat;
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+import android.util.Log;
+
+
+/**
+ * The interface that apps use to talk to the window manager.
+ * <p>
+ * Use <code>Context.getSystemService(Context.WINDOW_SERVICE)</code> to get one of these.
+ *
+ * @see android.content.Context#getSystemService
+ * @see android.content.Context#WINDOW_SERVICE
+ */
+public interface WindowManager extends ViewManager {
+    /**
+     * Exception that is thrown when trying to add view whose
+     * {@link WindowManager.LayoutParams} {@link WindowManager.LayoutParams#token}
+     * is invalid.
+     */
+    public static class BadTokenException extends RuntimeException {
+        public BadTokenException() {
+        }
+
+        public BadTokenException(String name) {
+            super(name);
+        }
+    }
+
+    /**
+     * Use this method to get the default Display object.
+     * 
+     * @return default Display object
+     */
+    public Display getDefaultDisplay();
+    
+    /**
+     * Special variation of {@link #removeView} that immediately invokes
+     * the given view hierarchy's {@link View#onDetachedFromWindow()
+     * View.onDetachedFromWindow()} methods before returning.  This is not
+     * for normal applications; using it correctly requires great care.
+     * 
+     * @param view The view to be removed.
+     */
+    public void removeViewImmediate(View view);
+    
+    public static class LayoutParams extends ViewGroup.LayoutParams
+            implements Parcelable {
+        /**
+         * X position for this window.  With the default gravity it is ignored.
+         * When using {@link Gravity#LEFT} or {@link Gravity#RIGHT} it provides
+         * an offset from the given edge.
+         */
+        public int x;
+        
+        /**
+         * Y position for this window.  With the default gravity it is ignored.
+         * When using {@link Gravity#TOP} or {@link Gravity#BOTTOM} it provides
+         * an offset from the given edge.
+         */
+        public int y;
+
+        /**
+         * Indicates how much of the extra space will be allocated horizontally
+         * to the view associated with these LayoutParams. Specify 0 if the view
+         * should not be stretched. Otherwise the extra pixels will be pro-rated
+         * among all views whose weight is greater than 0.
+         */
+        public float horizontalWeight;
+
+        /**
+         * Indicates how much of the extra space will be allocated vertically
+         * to the view associated with these LayoutParams. Specify 0 if the view
+         * should not be stretched. Otherwise the extra pixels will be pro-rated
+         * among all views whose weight is greater than 0.
+         */
+        public float verticalWeight;
+        
+        /**
+         * The general type of window.  There are three main classes of
+         * window types:
+         * <ul>
+         * <li> <strong>Application windows</strong> (ranging from
+         * {@link #FIRST_APPLICATION_WINDOW} to
+         * {@link #LAST_APPLICATION_WINDOW}) are normal top-level application
+         * windows.  For these types of windows, the {@link #token} must be
+         * set to the token of the activity they are a part of (this will
+         * normally be done for you if {@link #token} is null).
+         * <li> <strong>Sub-windows</strong> (ranging from
+         * {@link #FIRST_SUB_WINDOW} to
+         * {@link #LAST_SUB_WINDOW}) are associated with another top-level
+         * window.  For these types of windows, the {@link #token} must be
+         * the token of the window it is attached to.
+         * <li> <strong>System windows</strong> (ranging from
+         * {@link #FIRST_SYSTEM_WINDOW} to
+         * {@link #LAST_SYSTEM_WINDOW}) are special types of windows for
+         * use by the system for specific purposes.  They should not normally
+         * be used by applications, and a special permission is required
+         * to use them.
+         * </ul>
+         * 
+         * @see #TYPE_BASE_APPLICATION
+         * @see #TYPE_APPLICATION
+         * @see #TYPE_APPLICATION_STARTING
+         * @see #TYPE_APPLICATION_PANEL
+         * @see #TYPE_APPLICATION_MEDIA
+         * @see #TYPE_APPLICATION_SUB_PANEL
+         * @see #TYPE_APPLICATION_ATTACHED_DIALOG
+         * @see #TYPE_STATUS_BAR
+         * @see #TYPE_SEARCH_BAR
+         * @see #TYPE_PHONE
+         * @see #TYPE_SYSTEM_ALERT
+         * @see #TYPE_KEYGUARD
+         * @see #TYPE_TOAST
+         * @see #TYPE_SYSTEM_OVERLAY
+         * @see #TYPE_PRIORITY_PHONE
+         * @see #TYPE_STATUS_BAR_PANEL
+         * @see #TYPE_SYSTEM_DIALOG
+         * @see #TYPE_KEYGUARD_DIALOG
+         * @see #TYPE_SYSTEM_ERROR
+         * @see #TYPE_INPUT_METHOD
+         * @see #TYPE_INPUT_METHOD_DIALOG
+         */
+        public int type;
+    
+        /**
+         * Start of window types that represent normal application windows.
+         */
+        public static final int FIRST_APPLICATION_WINDOW = 1;
+        
+        /**
+         * Window type: an application window that serves as the "base" window
+         * of the overall application; all other application windows will
+         * appear on top of it.
+         */
+        public static final int TYPE_BASE_APPLICATION   = 1;
+        
+        /**
+         * Window type: a normal application window.  The {@link #token} must be
+         * an Activity token identifying who the window belongs to.
+         */
+        public static final int TYPE_APPLICATION        = 2;
+    
+        /**
+         * Window type: special application window that is displayed while the
+         * application is starting.  Not for use by applications themselves;
+         * this is used by the system to display something until the
+         * application can show its own windows.
+         */
+        public static final int TYPE_APPLICATION_STARTING = 3;
+    
+        /**
+         * End of types of application windows.
+         */
+        public static final int LAST_APPLICATION_WINDOW = 99;
+    
+        /**
+         * Start of types of sub-windows.  The {@link #token} of these windows
+         * must be set to the window they are attached to.  These types of
+         * windows are kept next to their attached window in Z-order, and their
+         * coordinate space is relative to their attached window.
+         */
+        public static final int FIRST_SUB_WINDOW        = 1000;
+    
+        /**
+         * Window type: a panel on top of an application window.  These windows
+         * appear on top of their attached window.
+         */
+        public static final int TYPE_APPLICATION_PANEL  = FIRST_SUB_WINDOW;
+    
+        /**
+         * Window type: window for showing media (e.g. video).  These windows
+         * are displayed behind their attached window.
+         */
+        public static final int TYPE_APPLICATION_MEDIA  = FIRST_SUB_WINDOW+1;
+    
+        /**
+         * Window type: a sub-panel on top of an application window.  These
+         * windows are displayed on top their attached window and any
+         * {@link #TYPE_APPLICATION_PANEL} panels.
+         */
+        public static final int TYPE_APPLICATION_SUB_PANEL = FIRST_SUB_WINDOW+2;
+
+        /** Window type: like {@link #TYPE_APPLICATION_PANEL}, but layout
+         * of the window happens as that of a top-level window, <em>not</em>
+         * as a child of its container.
+         */
+        public static final int TYPE_APPLICATION_ATTACHED_DIALOG = FIRST_SUB_WINDOW+3;
+        
+        /**
+         * End of types of sub-windows.
+         */
+        public static final int LAST_SUB_WINDOW         = 1999;
+        
+        /**
+         * Start of system-specific window types.  These are not normally
+         * created by applications.
+         */
+        public static final int FIRST_SYSTEM_WINDOW     = 2000;
+    
+        /**
+         * Window type: the status bar.  There can be only one status bar
+         * window; it is placed at the top of the screen, and all other
+         * windows are shifted down so they are below it.
+         */
+        public static final int TYPE_STATUS_BAR         = FIRST_SYSTEM_WINDOW;
+    
+        /**
+         * Window type: the search bar.  There can be only one search bar
+         * window; it is placed at the top of the screen.
+         */
+        public static final int TYPE_SEARCH_BAR         = FIRST_SYSTEM_WINDOW+1;
+    
+        /**
+         * Window type: phone.  These are non-application windows providing
+         * user interaction with the phone (in particular incoming calls).
+         * These windows are normally placed above all applications, but behind
+         * the status bar.
+         */
+        public static final int TYPE_PHONE              = FIRST_SYSTEM_WINDOW+2;
+    
+        /**
+         * Window type: system window, such as low power alert. These windows
+         * are always on top of application windows.
+         */
+        public static final int TYPE_SYSTEM_ALERT       = FIRST_SYSTEM_WINDOW+3;
+        
+        /**
+         * Window type: keyguard window.
+         */
+        public static final int TYPE_KEYGUARD           = FIRST_SYSTEM_WINDOW+4;
+        
+        /**
+         * Window type: transient notifications.
+         */
+        public static final int TYPE_TOAST              = FIRST_SYSTEM_WINDOW+5;
+        
+        /**
+         * Window type: system overlay windows, which need to be displayed
+         * on top of everything else.  These windows must not take input
+         * focus, or they will interfere with the keyguard.
+         */
+        public static final int TYPE_SYSTEM_OVERLAY     = FIRST_SYSTEM_WINDOW+6;
+        
+        /**
+         * Window type: priority phone UI, which needs to be displayed even if
+         * the keyguard is active.  These windows must not take input
+         * focus, or they will interfere with the keyguard.
+         */
+        public static final int TYPE_PRIORITY_PHONE     = FIRST_SYSTEM_WINDOW+7;
+        
+        /**
+         * Window type: panel that slides out from the status bar
+         */
+        public static final int TYPE_STATUS_BAR_PANEL   = FIRST_SYSTEM_WINDOW+8;
+        
+        /**
+         * Window type: panel that slides out from the status bar
+         */
+        public static final int TYPE_SYSTEM_DIALOG      = FIRST_SYSTEM_WINDOW+8;
+    
+        /**
+         * Window type: dialogs that the keyguard shows
+         */
+        public static final int TYPE_KEYGUARD_DIALOG    = FIRST_SYSTEM_WINDOW+9;
+        
+        /**
+         * Window type: internal system error windows, appear on top of
+         * everything they can.
+         */
+        public static final int TYPE_SYSTEM_ERROR       = FIRST_SYSTEM_WINDOW+10;
+        
+        /**
+         * Window type: internal input methods windows, which appear above
+         * the normal UI.  Application windows may be resized or panned to keep
+         * the input focus visible while this window is displayed.
+         */
+        public static final int TYPE_INPUT_METHOD       = FIRST_SYSTEM_WINDOW+11;
+
+        /**
+         * Window type: internal input methods dialog windows, which appear above
+         * the current input method window.
+         */
+        public static final int TYPE_INPUT_METHOD_DIALOG= FIRST_SYSTEM_WINDOW+12;
+
+        /**
+         * End of types of system windows.
+         */
+        public static final int LAST_SYSTEM_WINDOW      = 2999;
+        
+        /**
+         * Specifies what type of memory buffers should be used by this window.
+         * Default is normal.
+         * 
+         * @see #MEMORY_TYPE_NORMAL
+         * @see #MEMORY_TYPE_HARDWARE
+         * @see #MEMORY_TYPE_GPU
+         * @see #MEMORY_TYPE_PUSH_BUFFERS
+         */
+        public int memoryType;
+
+        /** Memory type: The window's surface is allocated in main memory. */
+        public static final int MEMORY_TYPE_NORMAL = 0;
+        /** Memory type: The window's surface is configured to be accessible
+         * by DMA engines and hardware accelerators. */
+        public static final int MEMORY_TYPE_HARDWARE = 1;
+        /** Memory type: The window's surface is configured to be accessible
+         * by graphics accelerators. */
+        public static final int MEMORY_TYPE_GPU = 2;
+        /** Memory type: The window's surface doesn't own its buffers and
+         * therefore cannot be locked. Instead the buffers are pushed to
+         * it through native binder calls. */
+        public static final int MEMORY_TYPE_PUSH_BUFFERS = 3;
+
+        /**
+         * Various behavioral options/flags.  Default is none.
+         * 
+         * @see #FLAG_BLUR_BEHIND
+         * @see #FLAG_DIM_BEHIND
+         * @see #FLAG_NOT_FOCUSABLE
+         * @see #FLAG_NOT_TOUCHABLE
+         * @see #FLAG_NOT_TOUCH_MODAL
+         * @see #FLAG_LAYOUT_IN_SCREEN
+         * @see #FLAG_DITHER
+         * @see #FLAG_KEEP_SCREEN_ON
+         * @see #FLAG_FULLSCREEN
+         * @see #FLAG_FORCE_NOT_FULLSCREEN
+         * @see #FLAG_IGNORE_CHEEK_PRESSES
+         */
+        public int flags;
+        
+        /** Window flag: everything behind this window will be dimmed.
+         *  Use {@link #dimAmount} to control the amount of dim. */
+        public static final int FLAG_DIM_BEHIND        = 0x00000002;
+        
+        /** Window flag: blur everything behind this window. */
+        public static final int FLAG_BLUR_BEHIND        = 0x00000004;
+        
+        /** Window flag: this window won't ever get key input focus, so the
+         * user can not send key or other button events to it.  Those will
+         * instead go to whatever focusable window is behind it.  This flag
+         * will also enable {@link #FLAG_NOT_TOUCH_MODAL} whether or not that
+         * is explicitly set.
+         * 
+         * <p>Setting this flag also implies that the window will not need to
+         * interact with
+         * a soft input method, so it will be Z-ordered and positioned 
+         * independently of any active input method (typically this means it
+         * gets Z-ordered on top of the input method, so it can use the full
+         * screen for its content and cover the input method if needed.  You
+         * can use {@link #FLAG_ALT_FOCUSABLE_IM} to modify this behavior. */
+        public static final int FLAG_NOT_FOCUSABLE      = 0x00000008;
+        
+        /** Window flag: this window can never receive touch events. */
+        public static final int FLAG_NOT_TOUCHABLE      = 0x00000010;
+        
+        /** Window flag: Even when this window is focusable (its
+         * {@link #FLAG_NOT_FOCUSABLE is not set), allow any pointer events
+         * outside of the window to be sent to the windows behind it.  Otherwise
+         * it will consume all pointer events itself, regardless of whether they
+         * are inside of the window. */
+        public static final int FLAG_NOT_TOUCH_MODAL    = 0x00000020;
+        
+        /** Window flag: When set, if the device is asleep when the touch
+         * screen is pressed, you will receive this first touch event.  Usually
+         * the first touch event is consumed by the system since the user can
+         * not see what they are pressing on.
+         */
+        public static final int FLAG_TOUCHABLE_WHEN_WAKING = 0x00000040;
+        
+        /** Window flag: as long as this window is visible to the user, keep
+         *  the device's screen turned on and bright. */
+        public static final int FLAG_KEEP_SCREEN_ON     = 0x00000080;
+        
+        /** Window flag: place the window within the entire screen, ignoring
+         *  decorations around the border (a.k.a. the status bar).  The
+         *  window must correctly position its contents to take the screen
+         *  decoration into account.  This flag is normally set for you
+         *  by Window as described in {@link Window#setFlags}. */
+        public static final int FLAG_LAYOUT_IN_SCREEN   = 0x00000100;
+        
+        /** Window flag: allow window to extend outside of the screen. */
+        public static final int FLAG_LAYOUT_NO_LIMITS   = 0x00000200;
+        
+        /** Window flag: Hide all screen decorations (e.g. status bar) while
+         * this window is displayed.  This allows the window to use the entire
+         * display space for itself -- the status bar will be hidden when
+         * an app window with this flag set is on the top layer. */
+        public static final int FLAG_FULLSCREEN      = 0x00000400;
+        
+        /** Window flag: Override {@link #FLAG_FULLSCREEN and force the
+         *  screen decorations (such as status bar) to be shown. */
+        public static final int FLAG_FORCE_NOT_FULLSCREEN   = 0x00000800;
+        
+        /** Window flag: turn on dithering when compositing this window to
+         *  the screen. */
+        public static final int FLAG_DITHER             = 0x00001000;
+        
+        /** Window flag: don't allow screen shots while this window is
+         * displayed. */
+        public static final int FLAG_SECURE             = 0x00002000;
+        
+        /** Window flag: a special mode where the layout parameters are used
+         * to perform scaling of the surface when it is composited to the
+         * screen. */
+        public static final int FLAG_SCALED             = 0x00004000;
+        
+        /** Window flag: intended for windows that will often be used when the user is
+         * holding the screen against their face, it will aggressively filter the event
+         * stream to prevent unintended presses in this situation that may not be
+         * desired for a particular window, when such an event stream is detected, the 
+         * application will receive a CANCEL motion event to indicate this so applications
+         * can handle this accordingly by taking no action on the event 
+         * until the finger is released. */
+        public static final int FLAG_IGNORE_CHEEK_PRESSES    = 0x00008000;
+        
+        /** Window flag: a special option only for use in combination with
+         * {@link #FLAG_LAYOUT_IN_SCREEN}.  When requesting layout in the
+         * screen your window may appear on top of or behind screen decorations
+         * such as the status bar.  By also including this flag, the window
+         * manager will report the inset rectangle needed to ensure your
+         * content is not covered by screen decorations.  This flag is normally
+         * set for you by Window as described in {@link Window#setFlags}.*/
+        public static final int FLAG_LAYOUT_INSET_DECOR = 0x00010000;
+        
+        /** Window flag: invert the state of {@link #FLAG_NOT_FOCUSABLE} with
+         * respect to how this window interacts with the current method.  That
+         * is, if FLAG_NOT_FOCUSABLE is set and this flag is set, then the
+         * window will behave as if it needs to interact with the input method
+         * and thus be placed behind/away from it; if FLAG_NOT_FOCUSABLE is
+         * not set and this flag is set, then the window will behave as if it
+         * doesn't need to interact with the input method and can be placed
+         * to use more space and cover the input method.
+         */
+        public static final int FLAG_ALT_FOCUSABLE_IM = 0x00020000;
+        
+        /** Window flag: if you have set {@link #FLAG_NOT_TOUCH_MODAL}, you
+         * can set this flag to receive a single special MotionEvent with
+         * the action
+         * {@link MotionEvent#ACTION_OUTSIDE MotionEvent.ACTION_OUTSIDE} for
+         * touches that occur outside of your window.  Note that you will not
+         * receive the full down/move/up gesture, only the location of the
+         * first down as an ACTION_OUTSIDE.
+         */
+        public static final int FLAG_WATCH_OUTSIDE_TOUCH = 0x00040000;
+        
+        /** Window flag: a special option intended for system dialogs.  When
+         * this flag is set, the window will demand focus unconditionally when
+         * it is created.
+         * {@hide} */
+        public static final int FLAG_SYSTEM_ERROR = 0x40000000;
+
+        /**
+         * Given a particular set of window manager flags, determine whether
+         * such a window may be a target for an input method when it has
+         * focus.  In particular, this checks the
+         * {@link #FLAG_NOT_FOCUSABLE} and {@link #FLAG_ALT_FOCUSABLE_IM}
+         * flags and returns true if the combination of the two corresponds
+         * to a window that needs to be behind the input method so that the
+         * user can type into it.
+         * 
+         * @param flags The current window manager flags.
+         * 
+         * @return Returns true if such a window should be behind/interact
+         * with an input method, false if not.
+         */
+        public static boolean mayUseInputMethod(int flags) {
+            switch (flags&(FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM)) {
+                case 0:
+                case FLAG_NOT_FOCUSABLE|FLAG_ALT_FOCUSABLE_IM:
+                    return true;
+            }
+            return false;
+        }
+        
+        /**
+         * Mask for {@link #softInputMode} of the bits that determine the
+         * desired visibility state of the soft input area for this window.
+         */
+        public static final int SOFT_INPUT_MASK_STATE = 0x0f;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: no state has been specified.
+         */
+        public static final int SOFT_INPUT_STATE_UNSPECIFIED = 0;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: please don't change the state of
+         * the soft input area.
+         */
+        public static final int SOFT_INPUT_STATE_UNCHANGED = 1;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: please hide any soft input
+         * area when normally appropriate (when the user is navigating
+         * forward to your window).
+         */
+        public static final int SOFT_INPUT_STATE_HIDDEN = 2;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: please always hide any
+         * soft input area when this window receives focus.
+         */
+        public static final int SOFT_INPUT_STATE_ALWAYS_HIDDEN = 3;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: please show the soft
+         * input area when normally appropriate (when the user is navigating
+         * forward to your window).
+         */
+        public static final int SOFT_INPUT_STATE_VISIBLE = 4;
+        
+        /**
+         * Visibility state for {@link #softInputMode}: please always make the
+         * soft input area visible when this window receives input focus.
+         */
+        public static final int SOFT_INPUT_STATE_ALWAYS_VISIBLE = 5;
+        
+        /**
+         * Mask for {@link #softInputMode} of the bits that determine the
+         * way that the window should be adjusted to accommodate the soft
+         * input window.
+         */
+        public static final int SOFT_INPUT_MASK_ADJUST = 0xf0;
+        
+        /** Adjustment option for {@link #softInputMode}: nothing specified.
+         * The system will try to pick one or
+         * the other depending on the contents of the window.
+         */
+        public static final int SOFT_INPUT_ADJUST_UNSPECIFIED = 0x00;
+        
+        /** Adjustment option for {@link #softInputMode}: set to allow the
+         * window to be resized when an input
+         * method is shown, so that its contents are not covered by the input
+         * method.  This can <em>not<em> be combined with
+         * {@link #SOFT_INPUT_ADJUST_PAN}; if
+         * neither of these are set, then the system will try to pick one or
+         * the other depending on the contents of the window.
+         */
+        public static final int SOFT_INPUT_ADJUST_RESIZE = 0x10;
+        
+        /** Adjustment option for {@link #softInputMode}: set to have a window
+         * pan when an input method is
+         * shown, so it doesn't need to deal with resizing but just panned
+         * by the framework to ensure the current input focus is visible.  This
+         * can <em>not<em> be combined with {@link #SOFT_INPUT_ADJUST_RESIZE}; if
+         * neither of these are set, then the system will try to pick one or
+         * the other depending on the contents of the window.
+         */
+        public static final int SOFT_INPUT_ADJUST_PAN = 0x20;
+        
+        /**
+         * Bit for {@link #softInputMode}: set when the user has navigated
+         * forward to the window.  This is normally set automatically for
+         * you by the system, though you may want to set it in certain cases
+         * when you are displaying a window yourself.  This flag will always
+         * be cleared automatically after the window is displayed.
+         */
+        public static final int SOFT_INPUT_IS_FORWARD_NAVIGATION = 0x100;
+        
+        /**
+         * Desired operating mode for any soft input area.  May any combination
+         * of:
+         * 
+         * <ul>
+         * <li> One of the visibility states
+         * {@link #SOFT_INPUT_STATE_UNSPECIFIED}, {@link #SOFT_INPUT_STATE_UNCHANGED},
+         * {@link #SOFT_INPUT_STATE_HIDDEN}, {@link #SOFT_INPUT_STATE_ALWAYS_VISIBLE}, or
+         * {@link #SOFT_INPUT_STATE_VISIBLE}.
+         * <li> One of the adjustment options
+         * {@link #SOFT_INPUT_ADJUST_UNSPECIFIED},
+         * {@link #SOFT_INPUT_ADJUST_RESIZE}, or
+         * {@link #SOFT_INPUT_ADJUST_PAN}.
+         */
+        public int softInputMode;
+        
+        /**
+         * Placement of window within the screen as per {@link Gravity}
+         *
+         * @see Gravity
+         */
+        public int gravity;
+    
+        /**
+         * The horizontal margin, as a percentage of the container's width,
+         * between the container and the widget.
+         */
+        public float horizontalMargin;
+    
+        /**
+         * The vertical margin, as a percentage of the container's height,
+         * between the container and the widget.
+         */
+        public float verticalMargin;
+    
+        /**
+         * The desired bitmap format.  May be one of the constants in
+         * {@link android.graphics.PixelFormat}.  Default is OPAQUE.
+         */
+        public int format;
+    
+        /**
+         * A style resource defining the animations to use for this window.
+         * This must be a system resource; it can not be an application resource
+         * because the window manager does not have access to applications.
+         */
+        public int windowAnimations;
+    
+        /**
+         * An alpha value to apply to this entire window.
+         * An alpha of 1.0 means fully opaque and 0.0 means fully transparent
+         */
+        public float alpha = 1.0f;
+    
+        /**
+         * When {@link #FLAG_DIM_BEHIND} is set, this is the amount of dimming
+         * to apply.  Range is from 1.0 for completely opaque to 0.0 for no
+         * dim.
+         */
+        public float dimAmount = 1.0f;
+    
+        /**
+         * This can be used to override the user's preferred brightness of
+         * the screen.  A value of less than 0, the default, means to use the
+         * preferred screen brightness.  0 to 1 adjusts the brightness from
+         * dark to full bright.
+         */
+        public float screenBrightness = -1.0f;
+        
+        /**
+         * Identifier for this window.  This will usually be filled in for
+         * you.
+         */
+        public IBinder token = null;
+    
+        /**
+         * Name of the package owning this window.
+         */
+        public String packageName = null;
+        
+        /**
+         * Specific orientation value for a window.
+         * May be any of the same values allowed
+         * for {@link android.content.pm.ActivityInfo#screenOrientation}. 
+         * If not set, a default value of 
+         * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_UNSPECIFIED} 
+         * will be used.
+         */
+        public int screenOrientation = ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+        
+        
+        public LayoutParams() {
+            super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+            type = TYPE_APPLICATION;
+            format = PixelFormat.OPAQUE;
+        }
+        
+        public LayoutParams(int _type) {
+            super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+            type = _type;
+            format = PixelFormat.OPAQUE;
+        }
+    
+        public LayoutParams(int _type, int _flags) {
+            super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+            type = _type;
+            flags = _flags;
+            format = PixelFormat.OPAQUE;
+        }
+    
+        public LayoutParams(int _type, int _flags, int _format) {
+            super(LayoutParams.FILL_PARENT, LayoutParams.FILL_PARENT);
+            type = _type;
+            flags = _flags;
+            format = _format;
+        }
+        
+        public LayoutParams(int w, int h, int _type, int _flags, int _format) {
+            super(w, h);
+            type = _type;
+            flags = _flags;
+            format = _format;
+        }
+        
+        public LayoutParams(int w, int h, int xpos, int ypos, int _type,
+                int _flags, int _format) {
+            super(w, h);
+            x = xpos;
+            y = ypos;
+            type = _type;
+            flags = _flags;
+            format = _format;
+        }
+    
+        public final void setTitle(CharSequence title) {
+            if (null == title)
+                title = "";
+    
+            mTitle = TextUtils.stringOrSpannedString(title);
+        }
+    
+        public final CharSequence getTitle() {
+            return mTitle;
+        }
+    
+        public int describeContents() {
+            return 0;
+        }
+
+        public void writeToParcel(Parcel out, int parcelableFlags) {
+            out.writeInt(width);
+            out.writeInt(height);
+            out.writeInt(x);
+            out.writeInt(y);
+            out.writeInt(type);
+            out.writeInt(memoryType);
+            out.writeInt(flags);
+            out.writeInt(softInputMode);
+            out.writeInt(gravity);
+            out.writeFloat(horizontalMargin);
+            out.writeFloat(verticalMargin);
+            out.writeInt(format);
+            out.writeInt(windowAnimations);
+            out.writeFloat(alpha);
+            out.writeFloat(dimAmount);
+            out.writeFloat(screenBrightness);
+            out.writeStrongBinder(token);
+            out.writeString(packageName);
+            TextUtils.writeToParcel(mTitle, out, parcelableFlags);
+            out.writeInt(screenOrientation);
+        }
+        
+        public static final Parcelable.Creator<LayoutParams> CREATOR
+                    = new Parcelable.Creator<LayoutParams>() {
+            public LayoutParams createFromParcel(Parcel in) {
+                return new LayoutParams(in);
+            }
+    
+            public LayoutParams[] newArray(int size) {
+                return new LayoutParams[size];
+            }
+        };
+    
+    
+        public LayoutParams(Parcel in) {
+            width = in.readInt();
+            height = in.readInt();
+            x = in.readInt();
+            y = in.readInt();
+            type = in.readInt();
+            memoryType = in.readInt();
+            flags = in.readInt();
+            softInputMode = in.readInt();
+            gravity = in.readInt();
+            horizontalMargin = in.readFloat();
+            verticalMargin = in.readFloat();
+            format = in.readInt();
+            windowAnimations = in.readInt();
+            alpha = in.readFloat();
+            dimAmount = in.readFloat();
+            screenBrightness = in.readFloat();
+            token = in.readStrongBinder();
+            packageName = in.readString();
+            mTitle = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(in);
+            screenOrientation = in.readInt();
+        }
+    
+        public static final int LAYOUT_CHANGED = 1<<0;
+        public static final int TYPE_CHANGED = 1<<1;
+        public static final int FLAGS_CHANGED = 1<<2;
+        public static final int FORMAT_CHANGED = 1<<3;
+        public static final int ANIMATION_CHANGED = 1<<4;
+        public static final int DIM_AMOUNT_CHANGED = 1<<5;
+        public static final int TITLE_CHANGED = 1<<6;
+        public static final int ALPHA_CHANGED = 1<<7;
+        public static final int MEMORY_TYPE_CHANGED = 1<<8;
+        public static final int SOFT_INPUT_MODE_CHANGED = 1<<9;
+        public static final int SCREEN_ORIENTATION_CHANGED = 1<<10;
+        public static final int SCREEN_BRIGHTNESS_CHANGED = 1<<11;
+    
+        public final int copyFrom(LayoutParams o) {
+            int changes = 0;
+    
+            if (width != o.width) {
+                width = o.width;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (height != o.height) {
+                height = o.height;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (x != o.x) {
+                x = o.x;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (y != o.y) {
+                y = o.y;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (horizontalWeight != o.horizontalWeight) {
+                horizontalWeight = o.horizontalWeight;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (verticalWeight != o.verticalWeight) {
+                verticalWeight = o.verticalWeight;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (horizontalMargin != o.horizontalMargin) {
+                horizontalMargin = o.horizontalMargin;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (verticalMargin != o.verticalMargin) {
+                verticalMargin = o.verticalMargin;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (type != o.type) {
+                type = o.type;
+                changes |= TYPE_CHANGED;
+            }
+            if (memoryType != o.memoryType) {
+                memoryType = o.memoryType;
+                changes |= MEMORY_TYPE_CHANGED;
+            }
+            if (flags != o.flags) {
+                flags = o.flags;
+                changes |= FLAGS_CHANGED;
+            }
+            if (softInputMode != o.softInputMode) {
+                softInputMode = o.softInputMode;
+                changes |= SOFT_INPUT_MODE_CHANGED;
+            }
+            if (gravity != o.gravity) {
+                gravity = o.gravity;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (horizontalMargin != o.horizontalMargin) {
+                horizontalMargin = o.horizontalMargin;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (verticalMargin != o.verticalMargin) {
+                verticalMargin = o.verticalMargin;
+                changes |= LAYOUT_CHANGED;
+            }
+            if (format != o.format) {
+                format = o.format;
+                changes |= FORMAT_CHANGED;
+            }
+            if (windowAnimations != o.windowAnimations) {
+                windowAnimations = o.windowAnimations;
+                changes |= ANIMATION_CHANGED;
+            }
+            if (token == null) {
+                // NOTE: token only copied if the recipient doesn't
+                // already have one.
+                token = o.token;
+            }
+            if (packageName == null) {
+                // NOTE: packageName only copied if the recipient doesn't
+                // already have one.
+                packageName = o.packageName;
+            }
+            if (!mTitle.equals(o.mTitle)) {
+                mTitle = o.mTitle;
+                changes |= TITLE_CHANGED;
+            }
+            if (alpha != o.alpha) {
+                alpha = o.alpha;
+                changes |= ALPHA_CHANGED;
+            }
+            if (dimAmount != o.dimAmount) {
+                dimAmount = o.dimAmount;
+                changes |= DIM_AMOUNT_CHANGED;
+            }
+            if (screenBrightness != o.screenBrightness) {
+                screenBrightness = o.screenBrightness;
+                changes |= SCREEN_BRIGHTNESS_CHANGED;
+            }
+    
+            if (screenOrientation != o.screenOrientation) {
+                screenOrientation = o.screenOrientation;
+                changes |= SCREEN_ORIENTATION_CHANGED;
+            }
+            return changes;
+        }
+    
+        @Override
+        public String debug(String output) {
+            output += "Contents of " + this + ":";
+            Log.d("Debug", output);
+            output = super.debug("");
+            Log.d("Debug", output);
+            Log.d("Debug", "");
+            Log.d("Debug", "WindowManager.LayoutParams={title=" + mTitle + "}");
+            return "";
+        }
+    
+        @Override
+        public String toString() {
+            StringBuilder sb = new StringBuilder(256);
+            sb.append("WM.LayoutParams{");
+            sb.append("(");
+            sb.append(x);
+            sb.append(',');
+            sb.append(y);
+            sb.append(")(");
+            sb.append((width==FILL_PARENT?"fill":(width==WRAP_CONTENT?"wrap":width)));
+            sb.append('x');
+            sb.append((height==FILL_PARENT?"fill":(height==WRAP_CONTENT?"wrap":height)));
+            sb.append(")");
+            if (softInputMode != 0) {
+                sb.append(" sim=#");
+                sb.append(Integer.toHexString(softInputMode));
+            }
+            if (gravity != 0) {
+                sb.append(" gr=#");
+                sb.append(Integer.toHexString(gravity));
+            }
+            sb.append(" ty=");
+            sb.append(type);
+            sb.append(" fl=#");
+            sb.append(Integer.toHexString(flags));
+            sb.append(" fmt=");
+            sb.append(format);
+            if (windowAnimations != 0) {
+                sb.append(" wanim=0x");
+                sb.append(Integer.toHexString(windowAnimations));
+            }
+            if (screenOrientation != ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED) {
+                sb.append(" or=");
+                sb.append(screenOrientation);
+            }
+            sb.append('}');
+            return sb.toString();
+        }
+    
+        private CharSequence mTitle = "";
+    }
+}
diff --git a/core/java/android/view/WindowManagerImpl.java b/core/java/android/view/WindowManagerImpl.java
new file mode 100644
index 0000000..755d7b8
--- /dev/null
+++ b/core/java/android/view/WindowManagerImpl.java
@@ -0,0 +1,364 @@
+/*
+ * Copyright (C) 2006 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.graphics.PixelFormat;
+import android.os.IBinder;
+import android.util.AndroidRuntimeException;
+import android.util.Config;
+import android.util.Log;
+import android.view.WindowManager;
+import android.view.inputmethod.InputMethodManager;
+
+final class WindowLeaked extends AndroidRuntimeException {
+    public WindowLeaked(String msg) {
+        super(msg);
+    }
+}
+
+/**
+ * Low-level communication with the global system window manager.  It implements
+ * the ViewManager interface, allowing you to add any View subclass as a
+ * top-level window on the screen. Additional window manager specific layout
+ * parameters are defined for control over how windows are displayed.
+ * It also implemens the WindowManager interface, allowing you to control the
+ * displays attached to the device.
+ * 
+ * <p>Applications will not normally use WindowManager directly, instead relying
+ * on the higher-level facilities in {@link android.app.Activity} and
+ * {@link android.app.Dialog}.
+ * 
+ * <p>Even for low-level window manager access, it is almost never correct to use
+ * this class.  For example, {@link android.app.Activity#getWindowManager}
+ * provides a ViewManager for adding windows that are associated with that
+ * activity -- the window manager will not normally allow you to add arbitrary
+ * windows that are not associated with an activity.
+ * 
+ * @hide
+ */
+public class WindowManagerImpl implements WindowManager {
+    /**
+     * The user is navigating with keys (not the touch screen), so
+     * navigational focus should be shown.
+     */
+    public static final int RELAYOUT_IN_TOUCH_MODE = 0x1;
+    /**
+     * This is the first time the window is being drawn,
+     * so the client must call drawingFinished() when done
+     */
+    public static final int RELAYOUT_FIRST_TIME = 0x2;
+    
+    public static final int ADD_FLAG_APP_VISIBLE = 0x2;
+    public static final int ADD_FLAG_IN_TOUCH_MODE = RELAYOUT_IN_TOUCH_MODE;
+    
+    public static final int ADD_OKAY = 0;
+    public static final int ADD_BAD_APP_TOKEN = -1;
+    public static final int ADD_BAD_SUBWINDOW_TOKEN = -2;
+    public static final int ADD_NOT_APP_TOKEN = -3;
+    public static final int ADD_APP_EXITING = -4;
+    public static final int ADD_DUPLICATE_ADD = -5;
+    public static final int ADD_STARTING_NOT_NEEDED = -6;
+    public static final int ADD_MULTIPLE_SINGLETON = -7;
+    public static final int ADD_PERMISSION_DENIED = -8;
+
+    public static WindowManagerImpl getDefault()
+    {
+        return mWindowManager;
+    }
+    
+    public void addView(View view)
+    {
+        addView(view, new WindowManager.LayoutParams(
+            WindowManager.LayoutParams.TYPE_APPLICATION, 0, PixelFormat.OPAQUE));
+    }
+
+    public void addView(View view, ViewGroup.LayoutParams params)
+    {
+        addView(view, params, false);
+    }
+    
+    public void addViewNesting(View view, ViewGroup.LayoutParams params)
+    {
+        addView(view, params, false);
+    }
+    
+    private void addView(View view, ViewGroup.LayoutParams params, boolean nest)
+    {
+        if (Config.LOGV) Log.v("WindowManager", "addView view=" + view);
+
+        if (!(params instanceof WindowManager.LayoutParams)) {
+            throw new IllegalArgumentException(
+                    "Params must be WindowManager.LayoutParams");
+        }
+
+        final WindowManager.LayoutParams wparams
+                = (WindowManager.LayoutParams)params;
+        
+        ViewRoot root;
+        View panelParentView = null;
+        
+        synchronized (this) {
+            // Here's an odd/questionable case: if someone tries to add a
+            // view multiple times, then we simply bump up a nesting count
+            // and they need to remove the view the corresponding number of
+            // times to have it actually removed from the window manager.
+            // This is useful specifically for the notification manager,
+            // which can continually add/remove the same view as a
+            // notification gets updated.
+            int index = findViewLocked(view, false);
+            if (index >= 0) {
+                if (!nest) {
+                    throw new IllegalStateException("View " + view
+                            + " has already been added to the window manager.");
+                }
+                root = mRoots[index];
+                root.mAddNesting++;
+                // Update layout parameters.
+                view.setLayoutParams(wparams);
+                root.setLayoutParams(wparams, true);
+                return;
+            }
+            
+            // If this is a panel window, then find the window it is being
+            // attached to for future reference.
+            if (wparams.type >= WindowManager.LayoutParams.FIRST_SUB_WINDOW &&
+                    wparams.type <= WindowManager.LayoutParams.LAST_SUB_WINDOW) {
+                final int count = mViews != null ? mViews.length : 0;
+                for (int i=0; i<count; i++) {
+                    if (mRoots[i].mWindow.asBinder() == wparams.token) {
+                        panelParentView = mViews[i];
+                    }
+                }
+            }
+            
+            root = new ViewRoot(view.getContext());
+            root.mAddNesting = 1;
+
+            view.setLayoutParams(wparams);
+            
+            if (mViews == null) {
+                index = 1;
+                mViews = new View[1];
+                mRoots = new ViewRoot[1];
+                mParams = new WindowManager.LayoutParams[1];
+            } else {
+                index = mViews.length + 1;
+                Object[] old = mViews;
+                mViews = new View[index];
+                System.arraycopy(old, 0, mViews, 0, index-1);
+                old = mRoots;
+                mRoots = new ViewRoot[index];
+                System.arraycopy(old, 0, mRoots, 0, index-1);
+                old = mParams;
+                mParams = new WindowManager.LayoutParams[index];
+                System.arraycopy(old, 0, mParams, 0, index-1);
+            }
+            index--;
+
+            mViews[index] = view;
+            mRoots[index] = root;
+            mParams[index] = wparams;
+        }
+
+        // do this last because it fires off messages to start doing things
+        root.setView(view, wparams, panelParentView);
+    }
+
+    public void updateViewLayout(View view, ViewGroup.LayoutParams params) {
+        if (!(params instanceof WindowManager.LayoutParams)) {
+            throw new IllegalArgumentException("Params must be WindowManager.LayoutParams");
+        }
+
+        final WindowManager.LayoutParams wparams
+                = (WindowManager.LayoutParams)params;
+        
+        view.setLayoutParams(wparams);
+
+        synchronized (this) {
+            int index = findViewLocked(view, true);
+            ViewRoot root = mRoots[index];
+            mParams[index] = wparams;
+            root.setLayoutParams(wparams, false);
+        }
+    }
+
+    public void removeView(View view) {
+        synchronized (this) {
+            int index = findViewLocked(view, true);
+            View curView = removeViewLocked(index);
+            if (curView == view) {
+                return;
+            }
+            
+            throw new IllegalStateException("Calling with view " + view
+                    + " but the ViewRoot is attached to " + curView);
+        }
+    }
+
+    public void removeViewImmediate(View view) {
+        synchronized (this) {
+            int index = findViewLocked(view, true);
+            ViewRoot root = mRoots[index];
+            View curView = root.getView();
+            
+            root.mAddNesting = 0;
+            root.die(true);
+            finishRemoveViewLocked(curView, index);
+            if (curView == view) {
+                return;
+            }
+            
+            throw new IllegalStateException("Calling with view " + view
+                    + " but the ViewRoot is attached to " + curView);
+        }
+    }
+    
+    View removeViewLocked(int index) {
+        ViewRoot root = mRoots[index];
+        View view = root.getView();
+        
+        // Don't really remove until we have matched all calls to add().
+        root.mAddNesting--;
+        if (root.mAddNesting > 0) {
+            return view;
+        }
+
+        InputMethodManager imm = InputMethodManager.getInstance(view.getContext());
+        if (imm != null) {
+            imm.windowDismissed(mViews[index].getWindowToken());
+        }
+        root.die(false);
+        finishRemoveViewLocked(view, index);
+        return view;
+    }
+    
+    void finishRemoveViewLocked(View view, int index) {
+        final int count = mViews.length;
+
+        // remove it from the list
+        View[] tmpViews = new View[count-1];
+        removeItem(tmpViews, mViews, index);
+        mViews = tmpViews;
+        
+        ViewRoot[] tmpRoots = new ViewRoot[count-1];
+        removeItem(tmpRoots, mRoots, index);
+        mRoots = tmpRoots;
+        
+        WindowManager.LayoutParams[] tmpParams
+                = new WindowManager.LayoutParams[count-1];
+        removeItem(tmpParams, mParams, index);
+        mParams = tmpParams;
+
+        view.assignParent(null);
+        // func doesn't allow null...  does it matter if we clear them?
+        //view.setLayoutParams(null);
+    }
+
+    public void closeAll(IBinder token, String who, String what) {
+        synchronized (this) {
+            if (mViews == null)
+                return;
+            
+            int count = mViews.length;
+            //Log.i("foo", "Closing all windows of " + token);
+            for (int i=0; i<count; i++) {
+                //Log.i("foo", "@ " + i + " token " + mParams[i].token
+                //        + " view " + mRoots[i].getView());
+                if (token == null || mParams[i].token == token) {
+                    ViewRoot root = mRoots[i];
+                    root.mAddNesting = 1;
+                    
+                    //Log.i("foo", "Force closing " + root);
+                    if (who != null) {
+                        WindowLeaked leak = new WindowLeaked(
+                                what + " " + who + " has leaked window "
+                                + root.getView() + " that was originally added here");
+                        leak.setStackTrace(root.getLocation().getStackTrace());
+                        Log.e("WindowManager", leak.getMessage(), leak);
+                    }
+                    
+                    removeViewLocked(i);
+                    i--;
+                    count--;
+                }
+            }
+        }
+    }
+    
+    public WindowManager.LayoutParams getRootViewLayoutParameter(View view) {
+        ViewParent vp = view.getParent();
+        while (vp != null && !(vp instanceof ViewRoot)) {
+            vp = vp.getParent();
+        }
+        
+        if (vp == null) return null;
+        
+        ViewRoot vr = (ViewRoot)vp;
+        
+        int N = mRoots.length;
+        for (int i = 0; i < N; ++i) {
+            if (mRoots[i] == vr) {
+                return mParams[i];
+            }
+        }
+        
+        return null;
+    }
+    
+    public void closeAll() {
+        closeAll(null, null, null);
+    }
+    
+    public Display getDefaultDisplay() {
+        return new Display(Display.DEFAULT_DISPLAY);
+    }
+
+    private View[] mViews;
+    private ViewRoot[] mRoots;
+    private WindowManager.LayoutParams[] mParams;
+
+    private static void removeItem(Object[] dst, Object[] src, int index)
+    {
+        if (dst.length > 0) {
+            if (index > 0) {
+                System.arraycopy(src, 0, dst, 0, index);
+            }
+            if (index < dst.length) {
+                System.arraycopy(src, index+1, dst, index, src.length-index-1);
+            }
+        }
+    }
+
+    private int findViewLocked(View view, boolean required)
+    {
+        synchronized (this) {
+            final int count = mViews != null ? mViews.length : 0;
+            for (int i=0; i<count; i++) {
+                if (mViews[i] == view) {
+                    return i;
+                }
+            }
+            if (required) {
+                throw new IllegalArgumentException(
+                        "View not attached to window manager");
+            }
+            return -1;
+        }
+    }
+
+    private static WindowManagerImpl mWindowManager = new WindowManagerImpl();
+}
diff --git a/core/java/android/view/WindowManagerPolicy.java b/core/java/android/view/WindowManagerPolicy.java
new file mode 100644
index 0000000..0f15b17
--- /dev/null
+++ b/core/java/android/view/WindowManagerPolicy.java
@@ -0,0 +1,796 @@
+/*
+ * Copyright (C) 2006 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.content.res.Configuration;
+import android.graphics.Rect;
+import android.os.IBinder;
+import android.os.LocalPowerManager;
+import android.view.animation.Animation;
+
+/**
+ * This interface supplies all UI-specific behavior of the window manager.  An
+ * instance of it is created by the window manager when it starts up, and allows
+ * customization of window layering, special window types, key dispatching, and
+ * layout.
+ * 
+ * <p>Because this provides deep interaction with the system window manager,
+ * specific methods on this interface can be called from a variety of contexts
+ * with various restrictions on what they can do.  These are encoded through
+ * a suffixes at the end of a method encoding the thread the method is called
+ * from and any locks that are held when it is being called; if no suffix
+ * is attached to a method, then it is not called with any locks and may be
+ * called from the main window manager thread or another thread calling into
+ * the window manager.
+ * 
+ * <p>The current suffixes are:
+ * 
+ * <dl>
+ * <dt> Ti <dd> Called from the input thread.  This is the thread that
+ * collects pending input events and dispatches them to the appropriate window.
+ * It may block waiting for events to be processed, so that the input stream is
+ * properly serialized.
+ * <dt> Tq <dd> Called from the low-level input queue thread.  This is the
+ * thread that reads events out of the raw input devices and places them
+ * into the global input queue that is read by the <var>Ti</var> thread.
+ * This thread should not block for a long period of time on anything but the
+ * key driver.
+ * <dt> Lw <dd> Called with the main window manager lock held.  Because the
+ * window manager is a very low-level system service, there are few other
+ * system services you can call with this lock held.  It is explicitly okay to
+ * make calls into the package manager and power manager; it is explicitly not
+ * okay to make calls into the activity manager.  Note that
+ * {@link android.content.Context#checkPermission(String, int, int)} and
+ * variations require calling into the activity manager.
+ * <dt> Li <dd> Called with the input thread lock held.  This lock can be
+ * acquired by the window manager while it holds the window lock, so this is
+ * even more restrictive than <var>Lw</var>.
+ * </dl>
+ * 
+ * @hide
+ */
+public interface WindowManagerPolicy {
+    public final static int FLAG_WAKE = 0x00000001;
+    public final static int FLAG_WAKE_DROPPED = 0x00000002;
+    public final static int FLAG_SHIFT = 0x00000004;
+    public final static int FLAG_CAPS_LOCK = 0x00000008;
+    public final static int FLAG_ALT = 0x00000010;
+    public final static int FLAG_ALT_GR = 0x00000020;
+    public final static int FLAG_MENU = 0x00000040;
+    public final static int FLAG_LAUNCHER = 0x00000080;
+
+    public final static int FLAG_WOKE_HERE = 0x10000000;
+    public final static int FLAG_BRIGHT_HERE = 0x20000000;
+
+    public final static boolean WATCH_POINTER = false;
+
+    // flags for interceptKeyTq
+    /**
+     * Pass this event to the user / app.  To be returned from {@link #interceptKeyTq}.
+     */
+    public final static int ACTION_PASS_TO_USER = 0x00000001;
+
+    /**
+     * This key event should extend the user activity timeout and turn the lights on.
+     * To be returned from {@link #interceptKeyTq}. Do not return this and
+     * {@link #ACTION_GO_TO_SLEEP} or {@link #ACTION_PASS_TO_USER}.
+     */
+    public final static int ACTION_POKE_USER_ACTIVITY = 0x00000002;
+
+    /**
+     * This key event should put the device to sleep (and engage keyguard if necessary)
+     * To be returned from {@link #interceptKeyTq}.  Do not return this and
+     * {@link #ACTION_POKE_USER_ACTIVITY} or {@link #ACTION_PASS_TO_USER}.
+     */
+    public final static int ACTION_GO_TO_SLEEP = 0x00000004;
+
+    /**
+     * Interface to the Window Manager state associated with a particular
+     * window.  You can hold on to an instance of this interface from the call
+     * to prepareAddWindow() until removeWindow().
+     */
+    public interface WindowState {
+        /**
+         * Perform standard frame computation.  The result can be obtained with
+         * getFrame() if so desired.  Must be called with the window manager
+         * lock held.
+         * 
+         * @param parentFrame The frame of the parent container this window
+         * is in, used for computing its basic position.
+         * @param displayFrame The frame of the overall display in which this
+         * window can appear, used for constraining the overall dimensions
+         * of the window.
+         * @param contentFrame The frame within the display in which we would
+         * like active content to appear.  This will cause windows behind to
+         * be resized to match the given content frame.
+         * @param visibleFrame The frame within the display that the window
+         * is actually visible, used for computing its visible insets to be
+         * given to windows behind.
+         * This can be used as a hint for scrolling (avoiding resizing)
+         * the window to make certain that parts of its content
+         * are visible.
+         */
+        public void computeFrameLw(Rect parentFrame, Rect displayFrame,
+                Rect contentFrame, Rect visibleFrame);
+
+        /**
+         * Retrieve the current frame of the window that has been assigned by
+         * the window manager.  Must be called with the window manager lock held.
+         * 
+         * @return Rect The rectangle holding the window frame.
+         */
+        public Rect getFrameLw();
+
+        /**
+         * Retrieve the current frame of the window that is actually shown.
+         * Must be called with the window manager lock held.
+         * 
+         * @return Rect The rectangle holding the shown window frame.
+         */
+        public Rect getShownFrameLw();
+
+        /**
+         * Retrieve the frame of the display that this window was last
+         * laid out in.  Must be called with the
+         * window manager lock held.
+         * 
+         * @return Rect The rectangle holding the display frame.
+         */
+        public Rect getDisplayFrameLw();
+
+        /**
+         * Retrieve the frame of the content area that this window was last
+         * laid out in.  This is the area in which the content of the window
+         * should be placed.  It will be smaller than the display frame to
+         * account for screen decorations such as a status bar or soft
+         * keyboard.  Must be called with the
+         * window manager lock held.
+         * 
+         * @return Rect The rectangle holding the content frame.
+         */
+        public Rect getContentFrameLw();
+
+        /**
+         * Retrieve the frame of the visible area that this window was last
+         * laid out in.  This is the area of the screen in which the window
+         * will actually be fully visible.  It will be smaller than the
+         * content frame to account for transient UI elements blocking it
+         * such as an input method's candidates UI.  Must be called with the
+         * window manager lock held.
+         * 
+         * @return Rect The rectangle holding the visible frame.
+         */
+        public Rect getVisibleFrameLw();
+
+        /**
+         * Returns true if this window is waiting to receive its given
+         * internal insets from the client app, and so should not impact the
+         * layout of other windows.
+         */
+        public boolean getGivenInsetsPendingLw();
+        
+        /**
+         * Retrieve the insets given by this window's client for the content
+         * area of windows behind it.  Must be called with the
+         * window manager lock held.
+         * 
+         * @return Rect The left, top, right, and bottom insets, relative
+         * to the window's frame, of the actual contents.
+         */
+        public Rect getGivenContentInsetsLw();
+
+        /**
+         * Retrieve the insets given by this window's client for the visible
+         * area of windows behind it.  Must be called with the
+         * window manager lock held.
+         * 
+         * @return Rect The left, top, right, and bottom insets, relative
+         * to the window's frame, of the actual visible area.
+         */
+        public Rect getGivenVisibleInsetsLw();
+
+        /**
+         * Retrieve the current LayoutParams of the window.
+         * 
+         * @return WindowManager.LayoutParams The window's internal LayoutParams
+         *         instance.
+         */
+        public WindowManager.LayoutParams getAttrs();
+
+        /**
+         * Get the layer at which this window's surface will be Z-ordered.
+         */
+        public int getSurfaceLayer();
+        
+        /**
+         * Return the token for the application (actually activity) that owns 
+         * this window.  May return null for system windows. 
+         * 
+         * @return An IApplicationToken identifying the owning activity.
+         */
+        public IApplicationToken getAppToken();
+
+        /**
+         * Return true if, at any point, the application token associated with 
+         * this window has actually displayed any windows.  This is most useful 
+         * with the "starting up" window to determine if any windows were 
+         * displayed when it is closed. 
+         * 
+         * @return Returns true if one or more windows have been displayed, 
+         *         else false.
+         */
+        public boolean hasAppShownWindows();
+
+        /**
+         * Return true if the application token has been asked to display an 
+         * app starting icon as the application is starting up. 
+         * 
+         * @return Returns true if setAppStartingIcon() was called for this 
+         *         window's token.
+         */
+        public boolean hasAppStartingIcon();
+
+        /**
+         * Return the Window that is being displayed as this window's 
+         * application token is being started. 
+         * 
+         * @return Returns the currently displayed starting window, or null if 
+         *         it was not requested, has not yet been displayed, or has
+         *         been removed.
+         */
+        public WindowState getAppStartingWindow();
+
+        /**
+         * Is this window visible?  It is not visible if there is no
+         * surface, or we are in the process of running an exit animation
+         * that will remove the surface.
+         */
+        boolean isVisibleLw();
+        
+        /**
+         * Is this window currently visible to the user on-screen?  It is 
+         * displayed either if it is visible or it is currently running an 
+         * animation before no longer being visible.  Must be called with the
+         * window manager lock held.
+         */
+        boolean isDisplayedLw();
+
+        /**
+         * Returns true if the window is both full screen and opaque.  Must be
+         * called with the window manager lock held.
+         * 
+         * @param width The width of the screen
+         * @param height The height of the screen 
+         * @param shownFrame If true, this is based on the actual shown frame of 
+         *                   the window (taking into account animations); if
+         *                   false, this is based on the currently requested
+         *                   frame, which any current animation will be moving
+         *                   towards.
+         * @param onlyOpaque If true, this will only pass if the window is
+         * also opaque.
+         * @return Returns true if the window is both full screen and opaque
+         */
+        public boolean fillsScreenLw(int width, int height, boolean shownFrame,
+                boolean onlyOpaque);
+
+        /**
+         * Returns true if this window has been shown on screen at some time in 
+         * the past.  Must be called with the window manager lock held.
+         * 
+         * @return boolean
+         */
+        public boolean hasDrawnLw();
+
+        /**
+         * Can be called by the policy to force a window to be hidden,
+         * regardless of whether the client or window manager would like
+         * it shown.  Must be called with the window manager lock held.
+         * Returns true if {@link #showLw} was last called for the window.
+         */
+        public boolean hideLw(boolean doAnimation);
+        
+        /**
+         * Can be called to undo the effect of {@link #hideLw}, allowing a
+         * window to be shown as long as the window manager and client would
+         * also like it to be shown.  Must be called with the window manager
+         * lock held.
+         * Returns true if {@link #hideLw} was last called for the window.
+         */
+        public boolean showLw(boolean doAnimation);
+    }
+
+    /** No transition happening. */
+    public final int TRANSIT_NONE = 0;
+    /** Window has been added to the screen. */
+    public final int TRANSIT_ENTER = 1;
+    /** Window has been removed from the screen. */
+    public final int TRANSIT_EXIT = 2;
+    /** Window has been made visible. */
+    public final int TRANSIT_SHOW = 3;
+    /** Window has been made invisible. */
+    public final int TRANSIT_HIDE = 4;
+    /** The "application starting" preview window is no longer needed, and will
+     * animate away to show the real window. */
+    public final int TRANSIT_PREVIEW_DONE = 5;
+    /** A window in a new activity is being opened on top of an existing one
+     * in the same task. */
+    public final int TRANSIT_ACTIVITY_OPEN = 6;
+    /** The window in the top-most activity is being closed to reveal the
+     * previous activity in the same task. */
+    public final int TRANSIT_ACTIVITY_CLOSE = 7;
+    /** A window in a new task is being opened on top of an existing one
+     * in another activity's task. */
+    public final int TRANSIT_TASK_OPEN = 8;
+    /** A window in the top-most activity is being closed to reveal the
+     * previous activity in a different task. */
+    public final int TRANSIT_TASK_CLOSE = 9;
+    /** A window in an existing task is being displayed on top of an existing one
+     * in another activity's task. */
+    public final int TRANSIT_TASK_TO_FRONT = 10;
+    /** A window in an existing task is being put below all other tasks. */
+    public final int TRANSIT_TASK_TO_BACK = 11;
+    
+    /** Screen turned off because of power button */
+    public final int OFF_BECAUSE_OF_USER = 1;
+    /** Screen turned off because of timeout */
+    public final int OFF_BECAUSE_OF_TIMEOUT = 2;
+
+    /**
+     * Magic constant to {@link IWindowManager#setRotation} to not actually
+     * modify the rotation.
+     */
+    public final int USE_LAST_ROTATION = -1000;
+    
+    /**
+     * Perform initialization of the policy.
+     * 
+     * @param context The system context we are running in.
+     * @param powerManager 
+     */
+    public void init(Context context, IWindowManager windowManager,
+            LocalPowerManager powerManager);
+
+    /**
+     * Check permissions when adding a window.
+     * 
+     * @param attrs The window's LayoutParams. 
+     *  
+     * @return {@link WindowManagerImpl#ADD_OKAY} if the add can proceed;
+     *      else an error code, usually
+     *      {@link WindowManagerImpl#ADD_PERMISSION_DENIED}, to abort the add.
+     */
+    public int checkAddPermission(WindowManager.LayoutParams attrs);
+
+    /**
+     * Sanitize the layout parameters coming from a client.  Allows the policy
+     * to do things like ensure that windows of a specific type can't take
+     * input focus.
+     * 
+     * @param attrs The window layout parameters to be modified.  These values
+     * are modified in-place.
+     */
+    public void adjustWindowParamsLw(WindowManager.LayoutParams attrs);
+    
+    /**
+     * After the window manager has computed the current configuration based
+     * on its knowledge of the display and input devices, it gives the policy
+     * a chance to adjust the information contained in it.  If you want to
+     * leave it as-is, simply do nothing.
+     * 
+     * <p>This method may be called by any thread in the window manager, but
+     * no internal locks in the window manager will be held.
+     * 
+     * @param config The Configuration being computed, for you to change as
+     * desired.
+     */
+    public void adjustConfigurationLw(Configuration config);
+    
+    /**
+     * Assign a window type to a layer.  Allows you to control how different
+     * kinds of windows are ordered on-screen.
+     * 
+     * @param type The type of window being assigned.
+     * 
+     * @return int An arbitrary integer used to order windows, with lower
+     *         numbers below higher ones.
+     */
+    public int windowTypeToLayerLw(int type);
+
+    /**
+     * Return how to Z-order sub-windows in relation to the window they are
+     * attached to.  Return positive to have them ordered in front, negative for
+     * behind.
+     * 
+     * @param type The sub-window type code.
+     * 
+     * @return int Layer in relation to the attached window, where positive is
+     *         above and negative is below.
+     */
+    public int subWindowTypeToLayerLw(int type);
+
+    /**
+     * Called when the system would like to show a UI to indicate that an
+     * application is starting.  You can use this to add a
+     * APPLICATION_STARTING_TYPE window with the given appToken to the window
+     * manager (using the normal window manager APIs) that will be shown until
+     * the application displays its own window.  This is called without the
+     * window manager locked so that you can call back into it.
+     * 
+     * @param appToken Token of the application being started.
+     * @param packageName The name of the application package being started. 
+     * @param theme Resource defining the application's overall visual theme.
+     * @param nonLocalizedLabel The default title label of the application if
+     *        no data is found in the resource.
+     * @param labelRes The resource ID the application would like to use as its name.
+     * @param icon The resource ID the application would like to use as its icon.
+     * 
+     * @return Optionally you can return the View that was used to create the
+     *         window, for easy removal in removeStartingWindow.
+     * 
+     * @see #removeStartingWindow
+     */
+    public View addStartingWindow(IBinder appToken, String packageName,
+            int theme, CharSequence nonLocalizedLabel,
+            int labelRes, int icon);
+
+    /**
+     * Called when the first window of an application has been displayed, while
+     * {@link #addStartingWindow} has created a temporary initial window for
+     * that application.  You should at this point remove the window from the
+     * window manager.  This is called without the window manager locked so
+     * that you can call back into it.
+     * 
+     * <p>Note: due to the nature of these functions not being called with the
+     * window manager locked, you must be prepared for this function to be
+     * called multiple times and/or an initial time with a null View window
+     * even if you previously returned one.
+     * 
+     * @param appToken Token of the application that has started.
+     * @param window Window View that was returned by createStartingWindow.
+     * 
+     * @see #addStartingWindow
+     */
+    public void removeStartingWindow(IBinder appToken, View window);
+
+    /**
+     * Prepare for a window being added to the window manager.  You can throw an
+     * exception here to prevent the window being added, or do whatever setup
+     * you need to keep track of the window.
+     * 
+     * @param win The window being added.
+     * @param attrs The window's LayoutParams. 
+     *  
+     * @return {@link WindowManagerImpl#ADD_OKAY} if the add can proceed, else an 
+     *         error code to abort the add.
+     */
+    public int prepareAddWindowLw(WindowState win,
+            WindowManager.LayoutParams attrs);
+
+    /**
+     * Called when a window is being removed from a window manager.  Must not
+     * throw an exception -- clean up as much as possible.
+     * 
+     * @param win The window being removed.
+     */
+    public void removeWindowLw(WindowState win);
+
+    /**
+     * Control the animation to run when a window's state changes.  Return a
+     * non-0 number to force the animation to a specific resource ID, or 0
+     * to use the default animation.
+     * 
+     * @param win The window that is changing.
+     * @param transit What is happening to the window: {@link #TRANSIT_ENTER},
+     *                {@link #TRANSIT_EXIT}, {@link #TRANSIT_SHOW}, or
+     *                {@link #TRANSIT_HIDE}.
+     * 
+     * @return Resource ID of the actual animation to use, or 0 for none.
+     */
+    public int selectAnimationLw(WindowState win, int transit);
+
+    /**
+     * Called from the key queue thread before a key is dispatched to the
+     * input thread.
+     *
+     * <p>There are some actions that need to be handled here because they
+     * affect the power state of the device, for example, the power keys.
+     * Generally, it's best to keep as little as possible in the queue thread
+     * because it's the most fragile.
+     *
+     * @param event the raw input event as read from the driver
+     * @param screenIsOn true if the screen is already on
+     * @return The bitwise or of the {@link #ACTION_PASS_TO_USER},
+     *          {@link #ACTION_POKE_USER_ACTIVITY} and {@link #ACTION_GO_TO_SLEEP} flags.
+     */
+    public int interceptKeyTq(RawInputEvent event, boolean screenIsOn);
+    
+    /**
+     * Called from the input thread before a key is dispatched to a window.
+     *
+     * <p>Allows you to define
+     * behavior for keys that can not be overridden by applications or redirect
+     * key events to a different window.  This method is called from the
+     * input thread, with no locks held.
+     * 
+     * <p>Note that if you change the window a key is dispatched to, the new
+     * target window will receive the key event without having input focus.
+     * 
+     * @param win The window that currently has focus.  This is where the key
+     *            event will normally go.
+     * @param code Key code.
+     * @param metaKeys TODO
+     * @param down Is this a key press (true) or release (false)?
+     * @param repeatCount Number of times a key down has repeated.
+     * @return Returns true if the policy consumed the event and it should
+     * not be further dispatched.
+     */
+    public boolean interceptKeyTi(WindowState win, int code,
+                               int metaKeys, boolean down, int repeatCount);
+
+    /**
+     * Called when layout of the windows is about to start.
+     * 
+     * @param displayWidth The current full width of the screen.
+     * @param displayHeight The current full height of the screen.
+     */
+    public void beginLayoutLw(int displayWidth, int displayHeight);
+
+    /**
+     * Called for each window attached to the window manager as layout is
+     * proceeding.  The implementation of this function must take care of
+     * setting the window's frame, either here or in finishLayout().
+     * 
+     * @param win The window being positioned.
+     * @param attrs The LayoutParams of the window.
+     * @param attached For sub-windows, the window it is attached to; this
+     *                 window will already have had layoutWindow() called on it
+     *                 so you can use its Rect.  Otherwise null.
+     */
+    public void layoutWindowLw(WindowState win,
+            WindowManager.LayoutParams attrs, WindowState attached);
+
+    
+    /**
+     * Return the insets for the areas covered by system windows. These values
+     * are computed on the most recent layout, so they are not guaranteed to
+     * be correct.
+     * 
+     * @param attrs The LayoutParams of the window.
+     * @param contentInset The areas covered by system windows, expressed as positive insets
+     * 
+     */
+    public void getContentInsetHintLw(WindowManager.LayoutParams attrs, Rect contentInset);
+    
+    /**
+     * Called when layout of the windows is finished.  After this function has
+     * returned, all windows given to layoutWindow() <em>must</em> have had a
+     * frame assigned.
+     */
+    public void finishLayoutLw();
+
+    /**
+     * Called when animation of the windows is about to start.
+     * 
+     * @param displayWidth The current full width of the screen.
+     * @param displayHeight The current full height of the screen.
+     */
+    public void beginAnimationLw(int displayWidth, int displayHeight);
+
+    /**
+     * Called each time a window is animating.
+     * 
+     * @param win The window being positioned.
+     * @param attrs The LayoutParams of the window. 
+     */
+    public void animatingWindowLw(WindowState win,
+            WindowManager.LayoutParams attrs);
+
+    /**
+     * Called when animation of the windows is finished.  If in this function you do 
+     * something that may have modified the animation state of another window, 
+     * be sure to return true in order to perform another animation frame. 
+     *  
+     * @return Return true if animation state may have changed (so that another 
+     *         frame of animation will be run).
+     */
+    public boolean finishAnimationLw();
+
+    /**
+     * Called after the screen turns off.
+     *
+     * @param why {@link #OFF_BECAUSE_OF_USER} or
+     * {@link #OFF_BECAUSE_OF_TIMEOUT}.
+     */
+    public void screenTurnedOff(int why);
+
+    /**
+     * Called after the screen turns on.
+     */
+    public void screenTurnedOn();
+
+    /**
+     * Perform any initial processing of a low-level input event before the
+     * window manager handles special keys and generates a high-level event
+     * that is dispatched to the application.
+     * 
+     * @param event The input event that has occurred.
+     * 
+     * @return Return true if you have consumed the event and do not want
+     * further processing to occur; return false for normal processing.
+     */
+    public boolean preprocessInputEventTq(RawInputEvent event);
+    
+    /**
+     * Determine whether a given key code is used to cause an app switch
+     * to occur (most often the HOME key, also often ENDCALL).  If you return
+     * true, then the system will go into a special key processing state
+     * where it drops any pending events that it cans and adjusts timeouts to
+     * try to get to this key as quickly as possible.
+     * 
+     * <p>Note that this function is called from the low-level input queue
+     * thread, with either/or the window or input lock held; be very careful
+     * about what you do here.  You absolutely should never acquire a lock
+     * that you would ever hold elsewhere while calling out into the window
+     * manager or view hierarchy.
+     * 
+     * @param keycode The key that should be checked for performing an
+     * app switch before delivering to the application.
+     * 
+     * @return Return true if this is an app switch key and special processing
+     * should happen; return false for normal processing.
+     */
+    public boolean isAppSwitchKeyTqTiLwLi(int keycode);
+    
+    /**
+     * Determine whether a given key code is used for movement within a UI,
+     * and does not generally cause actions to be performed (normally the DPAD
+     * movement keys, NOT the DPAD center press key).  This is called
+     * when {@link #isAppSwitchKeyTiLi} returns true to remove any pending events
+     * in the key queue that are not needed to switch applications.
+     * 
+     * <p>Note that this function is called from the low-level input queue
+     * thread; be very careful about what you do here.
+     * 
+     * @param keycode The key that is waiting to be delivered to the
+     * application.
+     * 
+     * @return Return true if this is a purely navigation key and can be
+     * dropped without negative consequences; return false to keep it.
+     */
+    public boolean isMovementKeyTi(int keycode);
+    
+    /**
+     * Given the current state of the world, should this relative movement
+     * wake up the device?
+     * 
+     * @param device The device the movement came from.
+     * @param classes The input classes associated with the device.
+     * @param event The input event that occurred.
+     * @return
+     */
+    public boolean isWakeRelMovementTq(int device, int classes,
+            RawInputEvent event);
+    
+    /**
+     * Given the current state of the world, should this absolute movement
+     * wake up the device?
+     * 
+     * @param device The device the movement came from.
+     * @param classes The input classes associated with the device.
+     * @param event The input event that occurred.
+     * @return
+     */
+    public boolean isWakeAbsMovementTq(int device, int classes,
+            RawInputEvent event);
+    
+    /**
+     * Tell the policy if anyone is requesting that keyguard not come on.
+     *
+     * @param enabled Whether keyguard can be on or not.  does not actually
+     * turn it on, unless it was previously disabled with this function.
+     *
+     * @see android.app.KeyguardManager.KeyguardLock#disableKeyguard()
+     * @see android.app.KeyguardManager.KeyguardLock#reenableKeyguard()
+     */
+    public void enableKeyguard(boolean enabled);
+
+    /**
+     * Callback used by {@link WindowManagerPolicy#exitKeyguardSecurely}
+     */
+    interface OnKeyguardExitResult {
+        void onKeyguardExitResult(boolean success);
+    }
+
+    /**
+     * Tell the policy if anyone is requesting the keyguard to exit securely
+     * (this would be called after the keyguard was disabled)
+     * @param callback Callback to send the result back.
+     * @see android.app.KeyguardManager#exitKeyguardSecurely(android.app.KeyguardManager.OnKeyguardExitResult)
+     */
+    void exitKeyguardSecurely(OnKeyguardExitResult callback);
+
+    /**
+     * Return if keyguard is currently showing.
+     */
+    public boolean keyguardIsShowingTq();
+
+    /**
+     * inKeyguardRestrictedKeyInputMode
+     *
+     * if keyguard screen is showing or in restricted key input mode (i.e. in
+     * keyguard password emergency screen). When in such mode, certain keys,
+     * such as the Home key and the right soft keys, don't work.
+     *
+     * @return true if in keyguard restricted input mode.
+     */
+    public boolean inKeyguardRestrictedKeyInputMode();
+
+    /**
+     * Given an orientation constant
+     * ({@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_LANDSCAPE
+     * ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE} or
+     * {@link android.content.pm.ActivityInfo#SCREEN_ORIENTATION_PORTRAIT
+     * ActivityInfo.SCREEN_ORIENTATION_PORTRAIT}), return a surface
+     * rotation.
+     */
+    public int rotationForOrientation(int orientation, int lastRotation,
+            boolean displayEnabled);
+    
+    /**
+     * Called when the system is mostly done booting to dentermine whether
+     * the system should go into safe mode.
+     */
+    public boolean detectSafeMode();
+    
+    /**
+     * Called when the system is mostly done booting.
+     */
+    public void systemReady();
+
+    /**
+     * Called when we have finished booting and can now display the home
+     * screen to the user.  This wilWl happen after systemReady(), and at
+     * this point the display is active.
+     */
+    public void enableScreenAfterBoot();
+    
+    /**
+     * Returns true if the user's cheek has been pressed against the phone. This is 
+     * determined by comparing the event's size attribute with a threshold value.
+     * For example for a motion event like down or up or move, if the size exceeds
+     * the threshold, it is considered as cheek press.
+     * @param ev the motion event generated when the cheek is pressed 
+     * against the phone
+     * @return Returns true if the user's cheek has been pressed against the phone
+     * screen resulting in an invalid motion event
+     */
+    public boolean isCheekPressedAgainstScreen(MotionEvent ev);
+    
+    public void setCurrentOrientation(int newOrientation);
+    
+    /**
+     * Call from application to perform haptic feedback on its window.
+     */
+    public boolean performHapticFeedback(WindowState win, int effectId, boolean always);
+    
+    /**
+     * Called when we have stopped keeping the screen on because a window
+     * requesting this is no longer visible.
+     */
+    public void screenOnStopped();
+}
diff --git a/core/java/android/view/WindowOrientationListener.java b/core/java/android/view/WindowOrientationListener.java
new file mode 100755
index 0000000..5877932
--- /dev/null
+++ b/core/java/android/view/WindowOrientationListener.java
@@ -0,0 +1,180 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view;
+
+import android.content.Context;
+import android.hardware.Sensor;
+import android.hardware.SensorEvent;
+import android.hardware.SensorEventListener;
+import android.hardware.SensorManager;
+import android.util.Config;
+import android.util.Log;
+
+/**
+ * A special helper class used by the WindowManager
+ *  for receiving notifications from the SensorManager when
+ * the orientation of the device has changed.
+ * @hide
+ */
+public abstract class WindowOrientationListener {
+    private static final String TAG = "WindowOrientationListener";
+    private static final boolean DEBUG = false;
+    private static final boolean localLOGV = DEBUG ? Config.LOGD : Config.LOGV;
+    private int mOrientation = ORIENTATION_UNKNOWN;
+    private SensorManager mSensorManager;
+    private boolean mEnabled = false;
+    private int mRate;
+    private Sensor mSensor;
+    private SensorEventListener mSensorEventListener;
+    
+    /**
+     * Returned from onOrientationChanged when the device orientation cannot be determined
+     * (typically when the device is in a close to flat position).
+     *
+     *  @see #onOrientationChanged
+     */
+    public static final int ORIENTATION_UNKNOWN = -1;
+    /*
+     * Returned when the device is almost lying flat on a surface
+     */
+    public static final int ORIENTATION_FLAT = -2;
+
+    /**
+     * Creates a new WindowOrientationListener.
+     * 
+     * @param context for the WindowOrientationListener.
+     */
+    public WindowOrientationListener(Context context) {
+        this(context, SensorManager.SENSOR_DELAY_NORMAL);
+    }
+    
+    /**
+     * Creates a new WindowOrientationListener.
+     * 
+     * @param context for the WindowOrientationListener.
+     * @param rate at which sensor events are processed (see also
+     * {@link android.hardware.SensorManager SensorManager}). Use the default
+     * value of {@link android.hardware.SensorManager#SENSOR_DELAY_NORMAL 
+     * SENSOR_DELAY_NORMAL} for simple screen orientation change detection.
+     */
+    public WindowOrientationListener(Context context, int rate) {
+        mSensorManager = (SensorManager)context.getSystemService(Context.SENSOR_SERVICE);
+        mRate = rate;
+        mSensor = mSensorManager.getDefaultSensor(Sensor.TYPE_ACCELEROMETER);
+        if (mSensor != null) {
+            // Create listener only if sensors do exist
+            mSensorEventListener = new SensorEventListenerImpl();
+        }
+    }
+
+    /**
+     * Enables the WindowOrientationListener so it will monitor the sensor and call
+     * {@link #onOrientationChanged} when the device orientation changes.
+     */
+    public void enable() {
+        if (mSensor == null) {
+            Log.w(TAG, "Cannot detect sensors. Not enabled");
+            return;
+        }
+        if (mEnabled == false) {
+            if (localLOGV) Log.d(TAG, "WindowOrientationListener enabled");
+            mSensorManager.registerListener(mSensorEventListener, mSensor, mRate);
+            mEnabled = true;
+        }
+    }
+
+    /**
+     * Disables the WindowOrientationListener.
+     */
+    public void disable() {
+        if (mSensor == null) {
+            Log.w(TAG, "Cannot detect sensors. Invalid disable");
+            return;
+        }
+        if (mEnabled == true) {
+            if (localLOGV) Log.d(TAG, "WindowOrientationListener disabled");
+            mSensorManager.unregisterListener(mSensorEventListener);
+            mEnabled = false;
+        }
+    }
+
+    class SensorEventListenerImpl implements SensorEventListener {
+        private static final int _DATA_X = 0;
+        private static final int _DATA_Y = 1;
+        private static final int _DATA_Z = 2;
+        
+        public void onSensorChanged(SensorEvent event) {
+            float[] values = event.values;
+            int orientation = ORIENTATION_UNKNOWN;
+            float X = values[_DATA_X];
+            float Y = values[_DATA_Y];
+            float Z = values[_DATA_Z];
+            float OneEightyOverPi = 57.29577957855f;
+            float gravity = (float) Math.sqrt(X*X+Y*Y+Z*Z);
+            float zyangle = Math.abs((float)Math.asin(Z/gravity)*OneEightyOverPi);
+            // The device is considered flat if the angle is more than 75
+            // if the angle is less than 40, its considered too flat to switch
+            // orientation. if the angle is between 40 - 75, the orientation is unknown
+            if (zyangle < 40) {
+                // Check orientation only if the phone is flat enough
+                // Don't trust the angle if the magnitude is small compared to the y value
+                float angle = (float)Math.atan2(Y, -X) * OneEightyOverPi;
+                orientation = 90 - (int)Math.round(angle);
+                // normalize to 0 - 359 range
+                while (orientation >= 360) {
+                    orientation -= 360;
+                } 
+                while (orientation < 0) {
+                    orientation += 360;
+                }
+            } else if (zyangle >= 75){
+                orientation = ORIENTATION_FLAT;
+            }
+            
+            if (orientation != mOrientation) {
+                mOrientation = orientation;
+                onOrientationChanged(orientation);
+            }
+        }
+
+        public void onAccuracyChanged(Sensor sensor, int accuracy) {
+
+        }
+    }
+
+    /*
+     * Returns true if sensor is enabled and false otherwise
+     */
+    public boolean canDetectOrientation() {
+        return mSensor != null;
+    }
+    
+    /**
+     * Called when the orientation of the device has changed.
+     * orientation parameter is in degrees, ranging from 0 to 359.
+     * orientation is 0 degrees when the device is oriented in its natural position,
+     * 90 degrees when its left side is at the top, 180 degrees when it is upside down, 
+     * and 270 degrees when its right side is to the top.
+     * {@link #ORIENTATION_UNKNOWN} is returned when the device is close to flat
+     * and the orientation cannot be determined.
+     *
+     * @param orientation The new orientation of the device.
+     *
+     *  @see #ORIENTATION_UNKNOWN
+     */
+    abstract public void onOrientationChanged(int orientation);
+}
diff --git a/core/java/android/view/animation/AccelerateDecelerateInterpolator.java b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
new file mode 100644
index 0000000..fdb6f9d
--- /dev/null
+++ b/core/java/android/view/animation/AccelerateDecelerateInterpolator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * An interpolator where the rate of change starts and ends slowly but
+ * accelerates through the middle.
+ * 
+ */
+public class AccelerateDecelerateInterpolator implements Interpolator {
+    public AccelerateDecelerateInterpolator() {
+    }
+    
+    public AccelerateDecelerateInterpolator(Context context, AttributeSet attrs) {
+    }
+    
+    public float getInterpolation(float input) {
+        return (float)(Math.cos((input + 1) * Math.PI) / 2.0f) + 0.5f;
+    }
+}
diff --git a/core/java/android/view/animation/AccelerateInterpolator.java b/core/java/android/view/animation/AccelerateInterpolator.java
new file mode 100644
index 0000000..b9e293f0
--- /dev/null
+++ b/core/java/android/view/animation/AccelerateInterpolator.java
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An interpolator where the rate of change starts out slowly and 
+ * and then accelerates.
+ *
+ */
+public class AccelerateInterpolator implements Interpolator {
+    public AccelerateInterpolator() {
+    }
+    
+    /**
+     * Constructor
+     * 
+     * @param factor Degree to which the animation should be eased. Seting
+     *        factor to 1.0f produces a y=x^2 parabola. Increasing factor above
+     *        1.0f  exaggerates the ease-in effect (i.e., it starts even
+     *        slower and ends evens faster)
+     */
+    public AccelerateInterpolator(float factor) {
+        mFactor = factor;
+    }
+    
+    public AccelerateInterpolator(Context context, AttributeSet attrs) {
+        TypedArray a =
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AccelerateInterpolator);
+        
+        mFactor = a.getFloat(com.android.internal.R.styleable.AccelerateInterpolator_factor, 1.0f);
+        
+        a.recycle();
+    }
+    
+    public float getInterpolation(float input) {
+        if (mFactor == 1.0f) {
+            return (float)(input * input);
+        } else {
+            return (float)Math.pow(input, 2 * mFactor);
+        }
+    }
+    
+    private float mFactor = 1.0f;
+}
diff --git a/core/java/android/view/animation/AlphaAnimation.java b/core/java/android/view/animation/AlphaAnimation.java
new file mode 100644
index 0000000..16a10a4
--- /dev/null
+++ b/core/java/android/view/animation/AlphaAnimation.java
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the alpha level of an object.
+ * Useful for fading things in and out. This animation ends up
+ * changing the alpha property of a {@link Transformation}
+ *
+ */
+public class AlphaAnimation extends Animation {
+    private float mFromAlpha;
+    private float mToAlpha;
+
+    /**
+     * Constructor used whan an AlphaAnimation is loaded from a resource. 
+     * 
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public AlphaAnimation(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        TypedArray a =
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AlphaAnimation);
+        
+        mFromAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_fromAlpha, 1.0f);
+        mToAlpha = a.getFloat(com.android.internal.R.styleable.AlphaAnimation_toAlpha, 1.0f);
+        
+        a.recycle();
+    }
+    
+    /**
+     * Constructor to use when building an AlphaAnimation from code
+     * 
+     * @param fromAlpha Starting alpha value for the animation, where 1.0 means
+     *        fully opaque and 0.0 means fully transparent.
+     * @param toAlpha Ending alpha value for the animation.
+     */
+    public AlphaAnimation(float fromAlpha, float toAlpha) {
+        mFromAlpha = fromAlpha;
+        mToAlpha = toAlpha;
+    }
+    
+    /**
+     * Changes the alpha property of the supplied {@link Transformation}
+     */
+    @Override
+    protected void applyTransformation(float interpolatedTime, Transformation t) {
+        final float alpha = mFromAlpha;
+        t.setAlpha(alpha + ((mToAlpha - alpha) * interpolatedTime));
+    }
+
+    @Override
+    public boolean willChangeTransformationMatrix() {
+        return false;
+    }
+
+    @Override
+    public boolean willChangeBounds() {
+        return false;
+    }
+}
diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java
new file mode 100644
index 0000000..b9c8ec3
--- /dev/null
+++ b/core/java/android/view/animation/Animation.java
@@ -0,0 +1,925 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.util.TypedValue;
+import android.graphics.RectF;
+
+/**
+ * Abstraction for an Animation that can be applied to Views, Surfaces, or
+ * other objects. See the {@link android.view.animation animation package
+ * description file}.
+ */
+public abstract class Animation implements Cloneable {
+    /**
+     * Repeat the animation indefinitely.
+     */
+    public static final int INFINITE = -1;
+
+    /**
+     * When the animation reaches the end and the repeat count is INFINTE_REPEAT
+     * or a positive value, the animation restarts from the beginning.
+     */
+    public static final int RESTART = 1;
+
+    /**
+     * When the animation reaches the end and the repeat count is INFINTE_REPEAT
+     * or a positive value, the animation plays backward (and then forward again).
+     */
+    public static final int REVERSE = 2;
+
+    /**
+     * Can be used as the start time to indicate the start time should be the current
+     * time when {@link #getTransformation(long, Transformation)} is invoked for the
+     * first animation frame. This can is useful for short animations.
+     */
+    public static final int START_ON_FIRST_FRAME = -1;
+
+    /**
+     * The specified dimension is an absolute number of pixels.
+     */
+    public static final int ABSOLUTE = 0;
+
+    /**
+     * The specified dimension holds a float and should be multiplied by the
+     * height or width of the object being animated.
+     */
+    public static final int RELATIVE_TO_SELF = 1;
+
+    /**
+     * The specified dimension holds a float and should be multiplied by the
+     * height or width of the parent of the object being animated.
+     */
+    public static final int RELATIVE_TO_PARENT = 2;
+
+    /**
+     * Requests that the content being animated be kept in its current Z
+     * order.
+     */
+    public static final int ZORDER_NORMAL = 0;
+    
+    /**
+     * Requests that the content being animated be forced on top of all other
+     * content for the duration of the animation.
+     */
+    public static final int ZORDER_TOP = 1;
+    
+    /**
+     * Requests that the content being animated be forced under all other
+     * content for the duration of the animation.
+     */
+    public static final int ZORDER_BOTTOM = -1;
+    
+    /**
+     * Set by {@link #getTransformation(long, Transformation)} when the animation ends.
+     */
+    boolean mEnded = false;
+
+    /**
+     * Set by {@link #getTransformation(long, Transformation)} when the animation starts.
+     */
+    boolean mStarted = false;
+
+    /**
+     * Set by {@link #getTransformation(long, Transformation)} when the animation repeats
+     * in REVERSE mode.
+     */
+    boolean mCycleFlip = false;
+
+    /**
+     * This value must be set to true by {@link #initialize(int, int, int, int)}. It
+     * indicates the animation was successfully initialized and can be played.
+     */
+    boolean mInitialized = false;
+
+    /**
+     * Indicates whether the animation transformation should be applied before the
+     * animation starts.
+     */
+    boolean mFillBefore = true;
+
+    /**
+     * Indicates whether the animation transformation should be applied after the
+     * animation ends.
+     */
+    boolean mFillAfter = false;
+
+    /**
+     * Indicates whether fillAfter should be taken into account.
+     */
+    boolean mFillEnabled = false;    
+
+    /**
+     * The time in milliseconds at which the animation must start;
+     */
+    long mStartTime = -1;
+
+    /**
+     * The delay in milliseconds after which the animation must start. When the
+     * start offset is > 0, the start time of the animation is startTime + startOffset.
+     */
+    long mStartOffset;
+
+    /**
+     * The duration of one animation cycle in milliseconds.
+     */
+    long mDuration;
+
+    /**
+     * The number of times the animation must repeat. By default, an animation repeats
+     * indefinitely.
+     */
+    int mRepeatCount = 0;
+
+    /**
+     * Indicates how many times the animation was repeated.
+     */
+    int mRepeated = 0;
+
+    /**
+     * The behavior of the animation when it repeats. The repeat mode is either
+     * {@link #RESTART} or {@link #REVERSE}.
+     *
+     */
+    int mRepeatMode = RESTART;
+
+    /**
+     * The interpolator used by the animation to smooth the movement.
+     */
+    Interpolator mInterpolator;
+
+    /**
+     * The animation listener to be notified when the animation starts, ends or repeats.
+     */
+    AnimationListener mListener;
+
+    /**
+     * Desired Z order mode during animation.
+     */
+    private int mZAdjustment;
+    
+    private boolean mMore = true;
+    private boolean mOneMoreTime = true;
+
+    RectF mPreviousRegion = new RectF();
+    RectF mRegion = new RectF();
+    Transformation mTransformation = new Transformation();
+    Transformation mPreviousTransformation = new Transformation();
+
+    /**
+     * Creates a new animation with a duration of 0ms, the default interpolator, with
+     * fillBefore set to true and fillAfter set to false
+     */
+    public Animation() {
+        ensureInterpolator();
+    }
+
+    /**
+     * Creates a new animation whose parameters come from the specified context and
+     * attributes set.
+     *
+     * @param context the application environment
+     * @param attrs the set of attributes holding the animation parameters
+     */
+    public Animation(Context context, AttributeSet attrs) {
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.Animation);
+
+        setDuration((long) a.getInt(com.android.internal.R.styleable.Animation_duration, 0));
+        setStartOffset((long) a.getInt(com.android.internal.R.styleable.Animation_startOffset, 0));
+        
+        setFillEnabled(a.getBoolean(com.android.internal.R.styleable.Animation_fillEnabled, mFillEnabled));
+        setFillBefore(a.getBoolean(com.android.internal.R.styleable.Animation_fillBefore, mFillBefore));
+        setFillAfter(a.getBoolean(com.android.internal.R.styleable.Animation_fillAfter, mFillAfter));
+
+        final int resID = a.getResourceId(com.android.internal.R.styleable.Animation_interpolator, 0);
+        if (resID > 0) {
+            setInterpolator(context, resID);
+        }
+
+        setRepeatCount(a.getInt(com.android.internal.R.styleable.Animation_repeatCount, mRepeatCount));
+        setRepeatMode(a.getInt(com.android.internal.R.styleable.Animation_repeatMode, RESTART));
+
+        setZAdjustment(a.getInt(com.android.internal.R.styleable.Animation_zAdjustment, ZORDER_NORMAL));
+        
+        ensureInterpolator();
+
+        a.recycle();
+    }
+
+    @Override
+    protected Animation clone() throws CloneNotSupportedException {
+        final Animation animation = (Animation) super.clone();
+        animation.mPreviousRegion = new RectF();
+        animation.mRegion = new RectF();
+        animation.mTransformation = new Transformation();
+        animation.mPreviousTransformation = new Transformation();
+        return animation;
+    }
+
+    /**
+     * Reset the initialization state of this animation.
+     *
+     * @see #initialize(int, int, int, int)
+     */
+    public void reset() {
+        mPreviousRegion.setEmpty();
+        mPreviousTransformation.clear();
+        mInitialized = false;
+        mCycleFlip = false;
+        mRepeated = 0;
+        mMore = true;
+        mOneMoreTime = true;
+    }
+
+    /**
+     * Whether or not the animation has been initialized.
+     *
+     * @return Has this animation been initialized.
+     * @see #initialize(int, int, int, int)
+     */
+    public boolean isInitialized() {
+        return mInitialized;
+    }
+
+    /**
+     * Initialize this animation with the dimensions of the object being
+     * animated as well as the objects parents. (This is to support animation
+     * sizes being specifed relative to these dimensions.)
+     *
+     * <p>Objects that interpret a Animations should call this method when
+     * the sizes of the object being animated and its parent are known, and
+     * before calling {@link #getTransformation}.
+     *
+     *
+     * @param width Width of the object being animated
+     * @param height Height of the object being animated
+     * @param parentWidth Width of the animated object's parent
+     * @param parentHeight Height of the animated object's parent
+     */
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        reset();
+        mInitialized = true;
+    }
+
+    /**
+     * Sets the acceleration curve for this animation. The interpolator is loaded as
+     * a resource from the specified context.
+     *
+     * @param context The application environment
+     * @param resID The resource identifier of the interpolator to load
+     * @attr ref android.R.styleable#Animation_interpolator
+     */
+    public void setInterpolator(Context context, int resID) {
+        setInterpolator(AnimationUtils.loadInterpolator(context, resID));
+    }
+
+    /**
+     * Sets the acceleration curve for this animation. Defaults to a linear
+     * interpolation.
+     *
+     * @param i The interpolator which defines the acceleration curve
+     * @attr ref android.R.styleable#Animation_interpolator
+     */
+    public void setInterpolator(Interpolator i) {
+        mInterpolator = i;
+    }
+
+    /**
+     * When this animation should start relative to the start time. This is most
+     * useful when composing complex animations using an {@link AnimationSet }
+     * where some of the animations components start at different times.
+     *
+     * @param startOffset When this Animation should start, in milliseconds from
+     *                    the start time of the root AnimationSet.
+     * @attr ref android.R.styleable#Animation_startOffset
+     */
+    public void setStartOffset(long startOffset) {
+        mStartOffset = startOffset;
+    }
+
+    /**
+     * How long this animation should last. The duration cannot be negative.
+     * 
+     * @param durationMillis Duration in milliseconds
+     *
+     * @throw java.lang.IllegalArgumentException if the duration is < 0
+     *
+     * @attr ref android.R.styleable#Animation_duration
+     */
+    public void setDuration(long durationMillis) {
+        if (durationMillis < 0) {
+            throw new IllegalArgumentException("Animation duration cannot be negative");
+        }
+        mDuration = durationMillis;
+    }
+
+    /**
+     * Ensure that the duration that this animation will run is not longer
+     * than <var>durationMillis</var>.  In addition to adjusting the duration
+     * itself, this ensures that the repeat count also will not make it run
+     * longer than the given time.
+     * 
+     * @param durationMillis The maximum duration the animation is allowed
+     * to run.
+     */
+    public void restrictDuration(long durationMillis) {
+        if (mStartOffset > durationMillis) {
+            mStartOffset = durationMillis;
+            mDuration = 0;
+            mRepeatCount = 0;
+            return;
+        }
+        
+        long dur = mDuration + mStartOffset;
+        if (dur > durationMillis) {
+            mDuration = dur = durationMillis-mStartOffset;
+        }
+        if (mRepeatCount < 0 || mRepeatCount > durationMillis
+                || (dur*mRepeatCount) > durationMillis) {
+            mRepeatCount = (int)(durationMillis/dur);
+        }
+    }
+    
+    /**
+     * How much to scale the duration by.
+     *
+     * @param scale The amount to scale the duration.
+     */
+    public void scaleCurrentDuration(float scale) {
+        mDuration = (long) (mDuration * scale);
+    }
+
+    /**
+     * When this animation should start. When the start time is set to
+     * {@link #START_ON_FIRST_FRAME}, the animation will start the first time
+     * {@link #getTransformation(long, Transformation)} is invoked. The time passed
+     * to this method should be obtained by calling
+     * {@link AnimationUtils#currentAnimationTimeMillis()} instead of
+     * {@link System#currentTimeMillis()}.
+     *
+     * @param startTimeMillis the start time in milliseconds
+     */
+    public void setStartTime(long startTimeMillis) {
+        mStartTime = startTimeMillis;
+        mStarted = mEnded = false;
+        mCycleFlip = false;
+        mRepeated = 0;
+        mMore = true;
+    }
+
+    /**
+     * Convenience method to start the animation the first time
+     * {@link #getTransformation(long, Transformation)} is invoked.
+     */
+    public void start() {
+        setStartTime(-1);
+    }
+
+    /**
+     * Convenience method to start the animation at the current time in
+     * milliseconds.
+     */
+    public void startNow() {
+        setStartTime(AnimationUtils.currentAnimationTimeMillis());
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end. This
+     * setting is applied only when the repeat count is either greater than
+     * 0 or {@link #INFINITE}. Defaults to {@link #RESTART}. 
+     *
+     * @param repeatMode {@link #RESTART} or {@link #REVERSE}
+     * @attr ref android.R.styleable#Animation_repeatMode
+     */
+    public void setRepeatMode(int repeatMode) {
+        mRepeatMode = repeatMode;
+    }
+
+    /**
+     * Sets how many times the animation should be repeated. If the repeat
+     * count is 0, the animation is never repeated. If the repeat count is
+     * greater than 0 or {@link #INFINITE}, the repeat mode will be taken
+     * into account. The repeat count if 0 by default.
+     *
+     * @param repeatCount the number of times the animation should be repeated
+     * @attr ref android.R.styleable#Animation_repeatCount
+     */
+    public void setRepeatCount(int repeatCount) {
+        if (repeatCount < 0) {
+            repeatCount = INFINITE;
+        }
+        mRepeatCount = repeatCount;
+    }
+
+    /**
+     * If fillEnabled is true, this animation will apply fillBefore and fillAfter.
+     *
+     * @return true if the animation will take fillBefore and fillAfter into account
+     * @attr ref android.R.styleable#Animation_fillEnabled
+     */
+    public boolean isFillEnabled() {
+        return mFillEnabled;
+    }
+
+    /**
+     * If fillEnabled is true, the animation will apply the value of fillBefore and
+     * fillAfter. Otherwise, fillBefore and fillAfter are ignored and the animation
+     * transformation is always applied.
+     *
+     * @param fillEnabled true if the animation should take fillBefore and fillAfter into account
+     * @attr ref android.R.styleable#Animation_fillEnabled
+     *
+     * @see #setFillBefore(boolean)
+     * @see #setFillAfter(boolean)
+     */
+    public void setFillEnabled(boolean fillEnabled) {
+        mFillEnabled = fillEnabled;
+    }
+
+    /**
+     * If fillBefore is true, this animation will apply its transformation
+     * before the start time of the animation. Defaults to true if not set.
+     * Note that this applies when using an {@link
+     * android.view.animation.AnimationSet AnimationSet} to chain
+     * animations. The transformation is not applied before the AnimationSet
+     * itself starts.
+     *
+     * @param fillBefore true if the animation should apply its transformation before it starts
+     * @attr ref android.R.styleable#Animation_fillBefore
+     *
+     * @see #setFillEnabled(boolean)
+     */
+    public void setFillBefore(boolean fillBefore) {
+        mFillBefore = fillBefore;
+    }
+
+    /**
+     * If fillAfter is true, the transformation that this animation performed
+     * will persist when it is finished. Defaults to false if not set.
+     * Note that this applies when using an {@link
+     * android.view.animation.AnimationSet AnimationSet} to chain
+     * animations. The transformation is not applied before the AnimationSet
+     * itself starts.
+     *
+     * @param fillAfter true if the animation should apply its transformation after it ends
+     * @attr ref android.R.styleable#Animation_fillAfter
+     *
+     * @see #setFillEnabled(boolean) 
+     */
+    public void setFillAfter(boolean fillAfter) {
+        mFillAfter = fillAfter;
+    }
+
+    /**
+     * Set the Z ordering mode to use while running the animation.
+     * 
+     * @param zAdjustment The desired mode, one of {@link #ZORDER_NORMAL},
+     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
+     * @attr ref android.R.styleable#Animation_zAdjustment
+     */
+    public void setZAdjustment(int zAdjustment) {
+        mZAdjustment = zAdjustment;
+    }
+    
+    /**
+     * Gets the acceleration curve type for this animation.
+     *
+     * @return the {@link Interpolator} associated to this animation
+     * @attr ref android.R.styleable#Animation_interpolator
+     */
+    public Interpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
+     * When this animation should start. If the animation has not startet yet,
+     * this method might return {@link #START_ON_FIRST_FRAME}.
+     *
+     * @return the time in milliseconds when the animation should start or
+     *         {@link #START_ON_FIRST_FRAME}
+     */
+    public long getStartTime() {
+        return mStartTime;
+    }
+
+    /**
+     * How long this animation should last
+     *
+     * @return the duration in milliseconds of the animation
+     * @attr ref android.R.styleable#Animation_duration
+     */
+    public long getDuration() {
+        return mDuration;
+    }
+
+    /**
+     * When this animation should start, relative to StartTime
+     *
+     * @return the start offset in milliseconds
+     * @attr ref android.R.styleable#Animation_startOffset
+     */
+    public long getStartOffset() {
+        return mStartOffset;
+    }
+
+    /**
+     * Defines what this animation should do when it reaches the end.
+     *
+     * @return either one of {@link #REVERSE} or {@link #RESTART}
+     * @attr ref android.R.styleable#Animation_repeatMode
+     */
+    public int getRepeatMode() {
+        return mRepeatMode;
+    }
+
+    /**
+     * Defines how many times the animation should repeat. The default value
+     * is 0.
+     *
+     * @return the number of times the animation should repeat, or {@link #INFINITE}
+     * @attr ref android.R.styleable#Animation_repeatCount
+     */
+    public int getRepeatCount() {
+        return mRepeatCount;
+    }
+
+    /**
+     * If fillBefore is true, this animation will apply its transformation
+     * before the start time of the animation.
+     *
+     * @return true if the animation applies its transformation before it starts
+     * @attr ref android.R.styleable#Animation_fillBefore
+     */
+    public boolean getFillBefore() {
+        return mFillBefore;
+    }
+
+    /**
+     * If fillAfter is true, this animation will apply its transformation
+     * after the end time of the animation.
+     *
+     * @return true if the animation applies its transformation after it ends
+     * @attr ref android.R.styleable#Animation_fillAfter
+     */
+    public boolean getFillAfter() {
+        return mFillAfter;
+    }
+
+    /**
+     * Returns the Z ordering mode to use while running the animation as
+     * previously set by {@link #setZAdjustment}.
+     * 
+     * @return Returns one of {@link #ZORDER_NORMAL},
+     * {@link #ZORDER_TOP}, or {@link #ZORDER_BOTTOM}.
+     * @attr ref android.R.styleable#Animation_zAdjustment
+     */
+    public int getZAdjustment() {
+        return mZAdjustment;
+    }
+
+    /**
+     * <p>Indicates whether or not this animation will affect the transformation
+     * matrix. For instance, a fade animation will not affect the matrix whereas
+     * a scale animation will.</p>
+     *
+     * @return true if this animation will change the transformation matrix
+     */
+    public boolean willChangeTransformationMatrix() {
+        // assume we will change the matrix
+        return true;
+    }
+
+    /**
+     * <p>Indicates whether or not this animation will affect the bounds of the
+     * animated view. For instance, a fade animation will not affect the bounds
+     * whereas a 200% scale animation will.</p>
+     *
+     * @return true if this animation will change the view's bounds
+     */
+    public boolean willChangeBounds() {
+        // assume we will change the bounds
+        return true;
+    }
+
+    /**
+     * <p>Binds an animation listener to this animation. The animation listener
+     * is notified of animation events such as the end of the animation or the
+     * repetition of the animation.</p>
+     *
+     * @param listener the animation listener to be notified
+     */
+    public void setAnimationListener(AnimationListener listener) {
+        mListener = listener;
+    }
+
+    /**
+     * Gurantees that this animation has an interpolator. Will use
+     * a AccelerateDecelerateInterpolator is nothing else was specified.
+     */
+    protected void ensureInterpolator() {
+        if (mInterpolator == null) {
+            mInterpolator = new AccelerateDecelerateInterpolator();
+        }
+    }
+
+    /**
+     * Compute a hint at how long the entire animation may last, in milliseconds.
+     * Animations can be written to cause themselves to run for a different
+     * duration than what is computed here, but generally this should be
+     * accurate.
+     */
+    public long computeDurationHint() {
+        return (getStartOffset() + getDuration()) * (getRepeatCount() + 1);
+    }
+    
+    /**
+     * Gets the transformation to apply at a specified point in time. Implementations of this
+     * method should always replace the specified Transformation or document they are doing
+     * otherwise.
+     *
+     * @param currentTime Where we are in the animation. This is wall clock time.
+     * @param outTransformation A tranformation object that is provided by the
+     *        caller and will be filled in by the animation.
+     * @return True if the animation is still running
+     */
+    public boolean getTransformation(long currentTime, Transformation outTransformation) {
+        if (mStartTime == -1) {
+            mStartTime = currentTime;
+        }
+
+        final long startOffset = getStartOffset();
+        final long duration = mDuration;
+        float normalizedTime;
+        if (duration != 0) {
+            normalizedTime = ((float) (currentTime - (mStartTime + startOffset))) /
+                    (float) duration;
+        } else {
+            // time is a step-change with a zero duration
+            normalizedTime = currentTime < mStartTime ? 0.0f : 1.0f;
+        }
+
+        final boolean expired = normalizedTime >= 1.0f;
+        mMore = !expired;
+
+        if (!mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
+
+        if ((normalizedTime >= 0.0f || mFillBefore) && (normalizedTime <= 1.0f || mFillAfter)) {
+            if (!mStarted) {
+                if (mListener != null) {
+                    mListener.onAnimationStart(this);
+                }
+                mStarted = true;
+            }
+
+            if (mFillEnabled) normalizedTime = Math.max(Math.min(normalizedTime, 1.0f), 0.0f);
+
+            if (mCycleFlip) {
+                normalizedTime = 1.0f - normalizedTime;
+            }
+
+            final float interpolatedTime = mInterpolator.getInterpolation(normalizedTime);
+            applyTransformation(interpolatedTime, outTransformation);
+        }
+
+        if (expired) {
+            if (mRepeatCount == mRepeated) {
+                if (!mEnded) {
+                    if (mListener != null) {
+                        mListener.onAnimationEnd(this);
+                    }
+                    mEnded = true;
+                }
+            } else {
+                if (mRepeatCount > 0) {
+                    mRepeated++;
+                }
+
+                if (mRepeatMode == REVERSE) {
+                    mCycleFlip = !mCycleFlip;
+                }
+
+                mStartTime = -1;
+                mMore = true;
+
+                if (mListener != null) {
+                    mListener.onAnimationRepeat(this);
+                }
+            }
+        }
+
+        if (!mMore && mOneMoreTime) {
+            mOneMoreTime = false;
+            return true;
+        }
+
+        return mMore;
+    }
+
+    /**
+     * <p>Indicates whether this animation has started or not.</p>
+     *
+     * @return true if the animation has started, false otherwise
+     */
+    public boolean hasStarted() {
+        return mStarted;
+    }
+
+    /**
+     * <p>Indicates whether this animation has ended or not.</p>
+     *
+     * @return true if the animation has ended, false otherwise
+     */
+    public boolean hasEnded() {
+        return mEnded;
+    }
+
+    /**
+     * Helper for getTransformation. Subclasses should implement this to apply
+     * their transforms given an interpolation value.  Implementations of this
+     * method should always replace the specified Transformation or document
+     * they are doing otherwise.
+     * 
+     * @param interpolatedTime The value of the normalized time (0.0 to 1.0)
+     *        after it has been run through the interpolation function.
+     * @param t The Transofrmation object to fill in with the current
+     *        transforms.
+     */
+    protected void applyTransformation(float interpolatedTime, Transformation t) {
+    }
+
+    /**
+     * Convert the information in the description of a size to an actual
+     * dimension
+     *
+     * @param type One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *             Animation.RELATIVE_TO_PARENT.
+     * @param value The dimension associated with the type parameter
+     * @param size The size of the object being animated
+     * @param parentSize The size of the parent of the object being animated
+     * @return The dimension to use for the animation
+     */
+    protected float resolveSize(int type, float value, int size, int parentSize) {
+        switch (type) {
+            case ABSOLUTE:
+                return value;
+            case RELATIVE_TO_SELF:
+                return size * value;
+            case RELATIVE_TO_PARENT:
+                return parentSize * value;
+            default:
+                return value;
+        }
+    }
+
+    /**
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     * @param invalidate
+     * @param transformation
+     * 
+     * @hide
+     */
+    public void getInvalidateRegion(int left, int top, int right, int bottom,
+            RectF invalidate, Transformation transformation) {
+
+        final RectF tempRegion = mRegion;
+        final RectF previousRegion = mPreviousRegion;
+
+        invalidate.set(left, top, right, bottom);
+        transformation.getMatrix().mapRect(invalidate);
+        tempRegion.set(invalidate);
+        invalidate.union(previousRegion);
+
+        previousRegion.set(tempRegion);
+
+        final Transformation tempTransformation = mTransformation;
+        final Transformation previousTransformation = mPreviousTransformation;
+
+        tempTransformation.set(transformation);
+        transformation.set(previousTransformation);
+        previousTransformation.set(tempTransformation);
+    }
+
+    /**
+     * @param left
+     * @param top
+     * @param right
+     * @param bottom
+     *
+     * @hide
+     */
+    public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
+        final RectF region = mPreviousRegion;
+        region.set(left, top, right, bottom);
+        if (mFillBefore) {
+            final Transformation previousTransformation = mPreviousTransformation;
+            applyTransformation(0.0f, previousTransformation);
+        }
+    }
+
+    /**
+     * Utility class to parse a string description of a size.
+     */
+    protected static class Description {
+        /**
+         * One of Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+         * Animation.RELATIVE_TO_PARENT.
+         */
+        public int type;
+
+        /**
+         * The absolute or relative dimension for this Description.
+         */
+        public float value;
+
+        /**
+         * Size descriptions can appear inthree forms:
+         * <ol>
+         * <li>An absolute size. This is represented by a number.</li>
+         * <li>A size relative to the size of the object being animated. This
+         * is represented by a number followed by "%".</li> *
+         * <li>A size relative to the size of the parent of object being
+         * animated. This is represented by a number followed by "%p".</li>
+         * </ol>
+         * @param value The typed value to parse
+         * @return The parsed version of the description
+         */
+        static Description parseValue(TypedValue value) {
+            Description d = new Description();
+            if (value == null) {
+                d.type = ABSOLUTE;
+                d.value = 0;
+            } else {
+                if (value.type == TypedValue.TYPE_FRACTION) {
+                    d.type = (value.data & TypedValue.COMPLEX_UNIT_MASK) ==
+                            TypedValue.COMPLEX_UNIT_FRACTION_PARENT ?
+                                    RELATIVE_TO_PARENT : RELATIVE_TO_SELF;
+                    d.value = TypedValue.complexToFloat(value.data);
+                    return d;
+                } else if (value.type == TypedValue.TYPE_FLOAT) {
+                    d.type = ABSOLUTE;
+                    d.value = value.getFloat();
+                    return d;
+                } else if (value.type >= TypedValue.TYPE_FIRST_INT &&
+                        value.type <= TypedValue.TYPE_LAST_INT) {
+                    d.type = ABSOLUTE;
+                    d.value = value.data;
+                    return d;
+                }
+            }
+
+            d.type = ABSOLUTE;
+            d.value = 0.0f;
+
+            return d;
+        }
+    }
+
+    /**
+     * <p>An animation listener receives notifications from an animation.
+     * Notifications indicate animation related events, such as the end or the
+     * repetition of the animation.</p>
+     */
+    public static interface AnimationListener {
+        /**
+         * <p>Notifies the start of the animation.</p>
+         *
+         * @param animation The started animation.
+         */
+        void onAnimationStart(Animation animation);
+
+        /**
+         * <p>Notifies the end of the animation. This callback is not invoked
+         * for animations with repeat count set to INFINITE.</p>
+         *
+         * @param animation The animation which reached its end.
+         */
+        void onAnimationEnd(Animation animation);
+
+        /**
+         * <p>Notifies the repetition of the animation.</p>
+         *
+         * @param animation The animation which was repeated.
+         */
+        void onAnimationRepeat(Animation animation);
+    }
+}
diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java
new file mode 100644
index 0000000..7b56f00
--- /dev/null
+++ b/core/java/android/view/animation/AnimationSet.java
@@ -0,0 +1,472 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.graphics.RectF;
+
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * Represents a group of Animations that should be played together.
+ * The transformation of each individual animation are composed 
+ * together into a single transform. 
+ * If AnimationSet sets any properties that its children also set
+ * (for example, duration or fillBefore), the values of AnimationSet
+ * override the child values.
+ */
+public class AnimationSet extends Animation {
+    private static final int PROPERTY_FILL_AFTER_MASK         = 0x1;
+    private static final int PROPERTY_FILL_BEFORE_MASK        = 0x2;
+    private static final int PROPERTY_REPEAT_MODE_MASK        = 0x4;
+    private static final int PROPERTY_START_OFFSET_MASK       = 0x8;
+    private static final int PROPERTY_SHARE_INTERPOLATOR_MASK = 0x10;
+    private static final int PROPERTY_DURATION_MASK           = 0x20;
+    private static final int PROPERTY_MORPH_MATRIX_MASK       = 0x40;
+    private static final int PROPERTY_CHANGE_BOUNDS_MASK      = 0x80;
+
+    private int mFlags = 0;
+
+    private ArrayList<Animation> mAnimations = new ArrayList<Animation>();
+
+    private Transformation mTempTransformation = new Transformation();
+
+    private long mLastEnd;
+
+    private long[] mStoredOffsets;
+
+    /**
+     * Constructor used whan an AnimationSet is loaded from a resource. 
+     * 
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public AnimationSet(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        
+        TypedArray a =
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.AnimationSet);
+        
+        setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK,
+                a.getBoolean(com.android.internal.R.styleable.AnimationSet_shareInterpolator, true));
+        init();
+        
+        a.recycle();
+    }
+    
+    
+    /**
+     * Constructor to use when building an AnimationSet from code
+     * 
+     * @param shareInterpolator Pass true if all of the animations in this set
+     *        should use the interpolator assocciated with this AnimationSet.
+     *        Pass false if each animation should use its own interpolator.
+     */
+    public AnimationSet(boolean shareInterpolator) {
+        setFlag(PROPERTY_SHARE_INTERPOLATOR_MASK, shareInterpolator);
+        init();
+    }
+
+    @Override
+    protected AnimationSet clone() throws CloneNotSupportedException {
+        final AnimationSet animation = (AnimationSet) super.clone();
+        animation.mTempTransformation = new Transformation();
+        animation.mAnimations = new ArrayList<Animation>();
+
+        final int count = mAnimations.size();
+        final ArrayList<Animation> animations = mAnimations;
+
+        for (int i = 0; i < count; i++) {
+            animation.mAnimations.add(animations.get(i).clone());
+        }
+
+        return animation;
+    }
+
+    private void setFlag(int mask, boolean value) {
+        if (value) {
+            mFlags |= mask;
+        } else {
+            mFlags &= ~mask;
+        }
+    }
+
+    private void init() {
+        mStartTime = 0;
+        mDuration = 0;
+    }
+
+    @Override
+    public void setFillAfter(boolean fillAfter) {
+        mFlags |= PROPERTY_FILL_AFTER_MASK;
+        super.setFillAfter(fillAfter);
+    }
+
+    @Override
+    public void setFillBefore(boolean fillBefore) {
+        mFlags |= PROPERTY_FILL_BEFORE_MASK;
+        super.setFillBefore(fillBefore);
+    }
+
+    @Override
+    public void setRepeatMode(int repeatMode) {
+        mFlags |= PROPERTY_REPEAT_MODE_MASK;
+        super.setRepeatMode(repeatMode);
+    }
+
+    @Override
+    public void setStartOffset(long startOffset) {
+        mFlags |= PROPERTY_START_OFFSET_MASK;
+        super.setStartOffset(startOffset);
+    }
+
+    /**
+     * <p>Sets the duration of every child animation.</p>
+     *
+     * @param durationMillis the duration of the animation, in milliseconds, for
+     *        every child in this set
+     */
+    @Override
+    public void setDuration(long durationMillis) {
+        mFlags |= PROPERTY_DURATION_MASK;
+        super.setDuration(durationMillis);
+    }
+
+    /**
+     * Add a child animation to this animation set.
+     * The transforms of the child animations are applied in the order
+     * that they were added
+     * @param a Animation to add.
+     */
+    public void addAnimation(Animation a) {
+        mAnimations.add(a);
+
+        boolean noMatrix = (mFlags & PROPERTY_MORPH_MATRIX_MASK) == 0;
+        if (noMatrix && a.willChangeTransformationMatrix()) {
+            mFlags |= PROPERTY_MORPH_MATRIX_MASK;
+        }
+
+        boolean changeBounds = (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == 0;
+        if (changeBounds && a.willChangeTransformationMatrix()) {
+            mFlags |= PROPERTY_CHANGE_BOUNDS_MASK;
+        }
+
+        if (mAnimations.size() == 1) {
+            mDuration = a.getStartOffset() + a.getDuration();
+            mLastEnd = mStartOffset + mDuration;
+        } else {
+            mLastEnd = Math.max(mLastEnd, a.getStartOffset() + a.getDuration());
+            mDuration = mLastEnd - mStartOffset;
+        }
+    }
+    
+    /**
+     * Sets the start time of this animation and all child animations
+     * 
+     * @see android.view.animation.Animation#setStartTime(long)
+     */
+    @Override
+    public void setStartTime(long startTimeMillis) {
+        super.setStartTime(startTimeMillis);
+
+        final int count = mAnimations.size();
+        final ArrayList<Animation> animations = mAnimations;
+
+        for (int i = 0; i < count; i++) {
+            Animation a = animations.get(i);
+            a.setStartTime(startTimeMillis);
+        }
+    }
+
+    @Override
+    public long getStartTime() {
+        long startTime = Long.MAX_VALUE;
+
+        final int count = mAnimations.size();
+        final ArrayList<Animation> animations = mAnimations;
+
+        for (int i = 0; i < count; i++) {
+            Animation a = animations.get(i);
+            startTime = Math.min(startTime, a.getStartTime());
+        }
+
+        return startTime;
+    }
+
+    @Override
+    public void restrictDuration(long durationMillis) {
+        super.restrictDuration(durationMillis);
+
+        final ArrayList<Animation> animations = mAnimations;
+        int count = animations.size();
+
+        for (int i = 0; i < count; i++) {
+            animations.get(i).restrictDuration(durationMillis);
+        }
+    }
+    
+    /**
+     * The duration of an AnimationSet is defined to be the 
+     * duration of the longest child animation.
+     * 
+     * @see android.view.animation.Animation#getDuration()
+     */
+    @Override
+    public long getDuration() {
+        final ArrayList<Animation> animations = mAnimations;
+        final int count = animations.size();
+        long duration = 0;
+
+        boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
+        if (durationSet) {
+            duration = mDuration;
+        } else {
+            for (int i = 0; i < count; i++) {
+                duration = Math.max(duration, animations.get(i).getDuration());
+            }
+        }
+
+        return duration;
+    }
+
+    /**
+     * The duration hint of an animation set is the maximum of the duration
+     * hints of all of its component animations.
+     * 
+     * @see android.view.animation.Animation#computeDurationHint
+     */
+    public long computeDurationHint() {
+        long duration = 0;
+        final int count = mAnimations.size();
+        final ArrayList<Animation> animations = mAnimations;
+        for (int i = count - 1; i >= 0; --i) {
+            final long d = animations.get(i).computeDurationHint();
+            if (d > duration) duration = d;
+        }
+        return duration;
+    }
+
+    /**
+     * @hide
+     */
+    public void getInvalidateRegion(int left, int top, int right, int bottom,
+            RectF invalidate, Transformation transformation) {
+
+        final RectF previousRegion = mPreviousRegion;
+
+        invalidate.set(left, top, right, bottom);
+        transformation.getMatrix().mapRect(invalidate);
+        invalidate.union(previousRegion);
+
+        previousRegion.set(left, top, right, bottom);
+        transformation.getMatrix().mapRect(previousRegion);
+
+        final Transformation tempTransformation = mTransformation;
+        final Transformation previousTransformation = mPreviousTransformation;
+
+        tempTransformation.set(transformation);
+        transformation.set(previousTransformation);
+        previousTransformation.set(tempTransformation);
+    }
+
+    /**
+     * @hide
+     */
+    public void initializeInvalidateRegion(int left, int top, int right, int bottom) {
+        final RectF region = mPreviousRegion;
+        region.set(left, top, right, bottom);
+
+        if (mFillBefore) {
+            final int count = mAnimations.size();
+            final ArrayList<Animation> animations = mAnimations;
+            final Transformation temp = mTempTransformation;
+
+            final Transformation previousTransformation = mPreviousTransformation;
+
+            for (int i = count - 1; i >= 0; --i) {
+                final Animation a = animations.get(i);
+
+                temp.clear();
+                a.applyTransformation(0.0f, temp);
+                previousTransformation.compose(temp);
+            }
+        }
+    }
+
+    /**
+     * The transformation of an animation set is the concatenation of all of its
+     * component animations.
+     * 
+     * @see android.view.animation.Animation#getTransformation
+     */
+    @Override
+    public boolean getTransformation(long currentTime, Transformation t) {
+        final int count = mAnimations.size();
+        final ArrayList<Animation> animations = mAnimations;
+        final Transformation temp = mTempTransformation;
+
+        boolean more = false;
+        boolean started = false;
+        boolean ended = true;
+
+        t.clear();
+
+        for (int i = count - 1; i >= 0; --i) {
+            final Animation a = animations.get(i);
+
+            temp.clear();
+            more = a.getTransformation(currentTime, temp) || more;
+            t.compose(temp);
+
+            started = started || a.hasStarted();
+            ended = a.hasEnded() && ended;
+        }
+
+        if (started && !mStarted) {
+            if (mListener != null) {
+                mListener.onAnimationStart(this);
+            }
+            mStarted = true;
+        }
+
+        if (ended != mEnded) {
+            if (mListener != null) {
+                mListener.onAnimationEnd(this);
+            }
+            mEnded = ended;
+        }
+
+        return more;
+    }
+    
+    /**
+     * @see android.view.animation.Animation#scaleCurrentDuration(float)
+     */
+    @Override
+    public void scaleCurrentDuration(float scale) {
+        final ArrayList<Animation> animations = mAnimations;
+        int count = animations.size();
+        for (int i = 0; i < count; i++) {
+            animations.get(i).scaleCurrentDuration(scale);
+        }
+    }
+
+    /**
+     * @see android.view.animation.Animation#initialize(int, int, int, int)
+     */
+    @Override
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        super.initialize(width, height, parentWidth, parentHeight);
+
+        boolean durationSet = (mFlags & PROPERTY_DURATION_MASK) == PROPERTY_DURATION_MASK;
+        boolean fillAfterSet = (mFlags & PROPERTY_FILL_AFTER_MASK) == PROPERTY_FILL_AFTER_MASK;
+        boolean fillBeforeSet = (mFlags & PROPERTY_FILL_BEFORE_MASK) == PROPERTY_FILL_BEFORE_MASK;
+        boolean repeatModeSet = (mFlags & PROPERTY_REPEAT_MODE_MASK) == PROPERTY_REPEAT_MODE_MASK;
+        boolean shareInterpolator = (mFlags & PROPERTY_SHARE_INTERPOLATOR_MASK)
+                == PROPERTY_SHARE_INTERPOLATOR_MASK;
+        boolean startOffsetSet = (mFlags & PROPERTY_START_OFFSET_MASK)
+                == PROPERTY_START_OFFSET_MASK;
+
+        if (shareInterpolator) {
+            ensureInterpolator();
+        }
+
+        final ArrayList<Animation> children = mAnimations;
+        final int count = children.size();
+
+        final long duration = mDuration;
+        final boolean fillAfter = mFillAfter;
+        final boolean fillBefore = mFillBefore;
+        final int repeatMode = mRepeatMode;
+        final Interpolator interpolator = mInterpolator;
+        final long startOffset = mStartOffset;
+
+
+        long[] storedOffsets = mStoredOffsets;
+        if (storedOffsets == null || storedOffsets.length != count) {
+            storedOffsets = mStoredOffsets = new long[count];
+        }
+
+        for (int i = 0; i < count; i++) {
+            Animation a = children.get(i);
+            if (durationSet) {
+                a.setDuration(duration);
+            }
+            if (fillAfterSet) {
+                a.setFillAfter(fillAfter);
+            }
+            if (fillBeforeSet) {
+                a.setFillBefore(fillBefore);
+            }
+            if (repeatModeSet) {
+                a.setRepeatMode(repeatMode);
+            }
+            if (shareInterpolator) {
+                a.setInterpolator(interpolator);
+            }
+            if (startOffsetSet) {
+                long offset = a.getStartOffset();
+                a.setStartOffset(offset + startOffset);
+                storedOffsets[i] = offset;
+            }
+            a.initialize(width, height, parentWidth, parentHeight);
+        }
+    }
+
+    @Override
+    public void reset() {
+        super.reset();
+        restoreChildrenStartOffset();
+    }
+
+    /**
+     * @hide
+     */
+    void restoreChildrenStartOffset() {
+        final long[] offsets = mStoredOffsets;
+        if (offsets == null) return;
+
+        final ArrayList<Animation> children = mAnimations;
+        final int count = children.size();
+
+
+        for (int i = 0; i < count; i++) {
+            children.get(i).setStartOffset(offsets[i]);
+        }
+    }
+
+    /**
+     * @return All the child animations in this AnimationSet. Note that
+     * this may include other AnimationSets, which are not expanded.
+     */
+    public List<Animation> getAnimations() {
+        return mAnimations;
+    }
+
+    @Override
+    public boolean willChangeTransformationMatrix() {
+        return (mFlags & PROPERTY_MORPH_MATRIX_MASK) == PROPERTY_MORPH_MATRIX_MASK;
+    }
+
+    @Override
+    public boolean willChangeBounds() {
+        return (mFlags & PROPERTY_CHANGE_BOUNDS_MASK) == PROPERTY_CHANGE_BOUNDS_MASK;
+    }
+}
diff --git a/core/java/android/view/animation/AnimationUtils.java b/core/java/android/view/animation/AnimationUtils.java
new file mode 100644
index 0000000..ce3cdc5
--- /dev/null
+++ b/core/java/android/view/animation/AnimationUtils.java
@@ -0,0 +1,329 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.Context;
+import android.content.res.XmlResourceParser;
+import android.content.res.Resources.NotFoundException;
+import android.util.AttributeSet;
+import android.util.Xml;
+import android.os.SystemClock;
+
+import java.io.IOException;
+
+/**
+ * Defines common utilities for working with animations.
+ *
+ */
+public class AnimationUtils {
+    /**
+     * Returns the current animation time in milliseconds. This time should be used when invoking
+     * {@link Animation#setStartTime(long)}. Refer to {@link android.os.SystemClock} for more
+     * information about the different available clocks. The clock used by this method is
+     * <em>not</em> the "wall" clock (it is not {@link System#currentTimeMillis}).
+     *
+     * @return the current animation time in milliseconds
+     *
+     * @see android.os.SystemClock
+     */
+    public static long currentAnimationTimeMillis() {
+        return SystemClock.uptimeMillis();
+    }
+
+    /**
+     * Loads an {@link Animation} object from a resource
+     * 
+     * @param context Application context used to access resources
+     * @param id The resource id of the animation to load
+     * @return The animation object reference by the specified id
+     * @throws NotFoundException when the animation cannot be loaded
+     */
+    public static Animation loadAnimation(Context context, int id)
+            throws NotFoundException {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createAnimationFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x"
+                            + Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x"
+                            + Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private static Animation createAnimationFromXml(Context c, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        return createAnimationFromXml(c, parser, null, Xml.asAttributeSet(parser));
+    }
+    
+    private static Animation createAnimationFromXml(Context c, XmlPullParser parser, AnimationSet parent, AttributeSet attrs)
+    throws XmlPullParserException, IOException {
+        
+        Animation anim = null;
+ 
+        // Make sure we are on a start tag.
+        int type = parser.getEventType();
+        int depth = parser.getDepth();
+
+        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+               && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String  name = parser.getName();
+    
+            if (name.equals("set")) {
+                anim = new AnimationSet(c, attrs);
+                createAnimationFromXml(c, parser, (AnimationSet)anim, attrs);
+            } else if (name.equals("alpha")) {
+                anim = new AlphaAnimation(c, attrs);
+            } else if (name.equals("scale")) {
+                anim = new ScaleAnimation(c, attrs);
+            }  else if (name.equals("rotate")) {
+                anim = new RotateAnimation(c, attrs);
+            }  else if (name.equals("translate")) {
+                anim = new TranslateAnimation(c, attrs);
+            } else {
+                throw new RuntimeException("Unknown animation name: " + parser.getName());
+            }
+
+            if (parent != null) {
+                parent.addAnimation(anim);
+            }
+        }
+    
+        return anim;
+
+    }
+
+    public static LayoutAnimationController loadLayoutAnimation(
+            Context context, int id) throws NotFoundException {
+        
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createLayoutAnimationFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x" +
+                            Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x" +
+                            Integer .toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+    }
+
+    private static LayoutAnimationController createLayoutAnimationFromXml(
+            Context c, XmlPullParser parser)
+            throws XmlPullParserException, IOException {
+        return createLayoutAnimationFromXml(c, parser,
+                Xml.asAttributeSet(parser));
+    }
+
+    private static LayoutAnimationController createLayoutAnimationFromXml(
+            Context c, XmlPullParser parser, AttributeSet attrs)
+            throws XmlPullParserException, IOException {
+
+        LayoutAnimationController controller = null;
+
+        int type;
+        int depth = parser.getDepth();
+
+        while (((type = parser.next()) != XmlPullParser.END_TAG
+                || parser.getDepth() > depth)
+                && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            String name = parser.getName();
+
+            if ("layoutAnimation".equals(name)) {
+                controller = new LayoutAnimationController(c, attrs);
+            } else if ("gridLayoutAnimation".equals(name)) {
+                controller = new GridLayoutAnimationController(c, attrs);
+            } else {
+                throw new RuntimeException("Unknown layout animation name: " +
+                        name);
+            }
+        }
+
+        return controller;
+    }
+
+    /**
+     * Make an animation for objects becoming visible. Uses a slide and fade
+     * effect.
+     * 
+     * @param c Context for loading resources
+     * @param fromLeft is the object to be animated coming from the left
+     * @return The new animation
+     */
+    public static Animation makeInAnimation(Context c, boolean fromLeft)
+    {
+        
+        Animation a;
+        if (fromLeft) {
+            a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_left);
+        } else {
+            a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_right);
+        }
+
+        a.setInterpolator(new DecelerateInterpolator());
+        a.setStartTime(currentAnimationTimeMillis());
+        return a;
+    }
+    
+    /**
+     * Make an animation for objects becoming invisible. Uses a slide and fade
+     * effect.
+     * 
+     * @param c Context for loading resources
+     * @param toRight is the object to be animated exiting to the right
+     * @return The new animation
+     */
+    public static Animation makeOutAnimation(Context c, boolean toRight)
+    {   
+        
+        Animation a;
+        if (toRight) {
+            a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_right);
+        } else {
+            a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_out_left);
+        }
+        
+        a.setInterpolator(new AccelerateInterpolator());
+        a.setStartTime(currentAnimationTimeMillis());
+        return a;
+    }
+
+    
+    /**
+     * Make an animation for objects becoming visible. Uses a slide up and fade
+     * effect.
+     * 
+     * @param c Context for loading resources
+     * @return The new animation
+     */
+    public static Animation makeInChildBottomAnimation(Context c)
+    {   
+
+        Animation a;
+        a = AnimationUtils.loadAnimation(c, com.android.internal.R.anim.slide_in_child_bottom);
+        a.setInterpolator(new AccelerateInterpolator());
+        a.setStartTime(currentAnimationTimeMillis());
+        return a;
+    }
+    
+    /**
+     * Loads an {@link Interpolator} object from a resource
+     * 
+     * @param context Application context used to access resources
+     * @param id The resource id of the animation to load
+     * @return The animation object reference by the specified id
+     * @throws NotFoundException
+     */
+    public static Interpolator loadInterpolator(Context context, int id)
+            throws NotFoundException {
+
+        XmlResourceParser parser = null;
+        try {
+            parser = context.getResources().getAnimation(id);
+            return createInterpolatorFromXml(context, parser);
+        } catch (XmlPullParserException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x"
+                            + Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } catch (IOException ex) {
+            NotFoundException rnf = new NotFoundException(
+                    "Can't load animation resource ID #0x"
+                            + Integer.toHexString(id));
+            rnf.initCause(ex);
+            throw rnf;
+        } finally {
+            if (parser != null) parser.close();
+        }
+
+    }
+    
+    private static Interpolator createInterpolatorFromXml(Context c, XmlPullParser parser)
+    throws XmlPullParserException, IOException {
+        
+        Interpolator interpolator = null;
+ 
+        // Make sure we are on a start tag.
+        int type = parser.getEventType();
+        int depth = parser.getDepth();
+
+        while (((type=parser.next()) != XmlPullParser.END_TAG || parser.getDepth() > depth)
+               && type != XmlPullParser.END_DOCUMENT) {
+
+            if (type != XmlPullParser.START_TAG) {
+                continue;
+            }
+
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            
+            String  name = parser.getName();
+    
+            
+            if (name.equals("linearInterpolator")) {
+                interpolator = new LinearInterpolator(c, attrs);
+            } else if (name.equals("accelerateInterpolator")) {
+                interpolator = new AccelerateInterpolator(c, attrs);
+            } else if (name.equals("decelerateInterpolator")) {
+                interpolator = new DecelerateInterpolator(c, attrs);
+            }  else if (name.equals("accelerateDecelerateInterpolator")) {
+                interpolator = new AccelerateDecelerateInterpolator(c, attrs);
+            }  else if (name.equals("cycleInterpolator")) {
+                interpolator = new CycleInterpolator(c, attrs);
+            } else {
+                throw new RuntimeException("Unknown interpolator name: " + parser.getName());
+            }
+
+        }
+    
+        return interpolator;
+
+    }
+}
diff --git a/core/java/android/view/animation/CycleInterpolator.java b/core/java/android/view/animation/CycleInterpolator.java
new file mode 100644
index 0000000..d355c23
--- /dev/null
+++ b/core/java/android/view/animation/CycleInterpolator.java
@@ -0,0 +1,47 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * Repeats the animation for a specified number of cycles. The
+ * rate of change follows a sinusoidal pattern.
+ *
+ */
+public class CycleInterpolator implements Interpolator {
+    public CycleInterpolator(float cycles) {
+        mCycles = cycles;
+    }
+    
+    public CycleInterpolator(Context context, AttributeSet attrs) {
+        TypedArray a =
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.CycleInterpolator);
+        
+        mCycles = a.getFloat(com.android.internal.R.styleable.CycleInterpolator_cycles, 1.0f);
+        
+        a.recycle();
+    }
+    
+    public float getInterpolation(float input) {
+        return (float)(Math.sin(2 * mCycles * Math.PI * input));
+    }
+    
+    private float mCycles;
+}
diff --git a/core/java/android/view/animation/DecelerateInterpolator.java b/core/java/android/view/animation/DecelerateInterpolator.java
new file mode 100644
index 0000000..176169e
--- /dev/null
+++ b/core/java/android/view/animation/DecelerateInterpolator.java
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An interpolator where the rate of change starts out quickly and 
+ * and then decelerates.
+ *
+ */
+public class DecelerateInterpolator implements Interpolator {
+    public DecelerateInterpolator() {
+    }
+    
+    /**
+     * Constructor
+     * 
+     * @param factor Degree to which the animation should be eased. Seting factor to 1.0f produces
+     *        an upside-down y=x^2 parabola. Increasing factor above 1.0f makes exaggerates the
+     *        ease-out effect (i.e., it starts even faster and ends evens slower)
+     */
+    public DecelerateInterpolator(float factor) {
+        mFactor = factor;
+    }
+    
+    public DecelerateInterpolator(Context context, AttributeSet attrs) {
+        TypedArray a =
+            context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.DecelerateInterpolator);
+        
+        mFactor = a.getFloat(com.android.internal.R.styleable.DecelerateInterpolator_factor, 1.0f);
+        
+        a.recycle();
+    }
+    
+    public float getInterpolation(float input) {
+        if (mFactor == 1.0f) {
+            return (float)(1.0f - (1.0f - input) * (1.0f - input));
+        } else {
+            return (float)(1.0f - Math.pow((1.0f - input), 2 * mFactor));
+        }
+    }
+    
+    private float mFactor = 1.0f;
+}
diff --git a/core/java/android/view/animation/GridLayoutAnimationController.java b/core/java/android/view/animation/GridLayoutAnimationController.java
new file mode 100644
index 0000000..9161d8b
--- /dev/null
+++ b/core/java/android/view/animation/GridLayoutAnimationController.java
@@ -0,0 +1,424 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import android.view.View;
+import android.view.ViewGroup;
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+import java.util.Random;
+
+/**
+ * A layout animation controller is used to animated a grid layout's children.
+ *
+ * While {@link LayoutAnimationController} relies only on the index of the child
+ * in the view group to compute the animation delay, this class uses both the
+ * X and Y coordinates of the child within a grid.
+ *
+ * In addition, the animation direction can be controlled. The default direction
+ * is <code>DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM</code>. You can
+ * also set the animation priority to columns or rows. The default priority is
+ * none.
+ *
+ * Information used to compute the animation delay of each child are stored
+ * in an instance of
+ * {@link android.view.animation.GridLayoutAnimationController.AnimationParameters},
+ * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view.
+ *
+ * @see LayoutAnimationController
+ * @see android.widget.GridView
+ * 
+ * @attr ref android.R.styleable#GridLayoutAnimation_columnDelay
+ * @attr ref android.R.styleable#GridLayoutAnimation_rowDelay
+ * @attr ref android.R.styleable#GridLayoutAnimation_direction
+ * @attr ref android.R.styleable#GridLayoutAnimation_directionPriority
+ */
+public class GridLayoutAnimationController extends LayoutAnimationController {
+    /**
+     * Animates the children starting from the left of the grid to the right.
+     */
+    public static final int DIRECTION_LEFT_TO_RIGHT = 0x0;
+
+    /**
+     * Animates the children starting from the right of the grid to the left.
+     */
+    public static final int DIRECTION_RIGHT_TO_LEFT = 0x1;
+
+    /**
+     * Animates the children starting from the top of the grid to the bottom.
+     */
+    public static final int DIRECTION_TOP_TO_BOTTOM = 0x0;
+
+    /**
+     * Animates the children starting from the bottom of the grid to the top.
+     */
+    public static final int DIRECTION_BOTTOM_TO_TOP = 0x2;
+
+    /**
+     * Bitmask used to retrieve the horizontal component of the direction.
+     */
+    public static final int DIRECTION_HORIZONTAL_MASK = 0x1;
+
+    /**
+     * Bitmask used to retrieve the vertical component of the direction.
+     */
+    public static final int DIRECTION_VERTICAL_MASK   = 0x2;
+
+    /**
+     * Rows and columns are animated at the same time.
+     */
+    public static final int PRIORITY_NONE   = 0;
+
+    /**
+     * Columns are animated first.
+     */
+    public static final int PRIORITY_COLUMN = 1;
+
+    /**
+     * Rows are animated first.
+     */
+    public static final int PRIORITY_ROW    = 2;
+
+    private float mColumnDelay;
+    private float mRowDelay;
+
+    private int mDirection;
+    private int mDirectionPriority;
+
+    /**
+     * Creates a new grid layout animation controller from external resources.
+     *
+     * @param context the Context the view  group is running in, through which
+     *        it can access the resources
+     * @param attrs the attributes of the XML tag that is inflating the
+     *        layout animation controller
+     */
+    public GridLayoutAnimationController(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.GridLayoutAnimation);
+
+        Animation.Description d = Animation.Description.parseValue(
+                a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_columnDelay));
+        mColumnDelay = d.value;
+        d = Animation.Description.parseValue(
+                a.peekValue(com.android.internal.R.styleable.GridLayoutAnimation_rowDelay));
+        mRowDelay = d.value;
+        //noinspection PointlessBitwiseExpression
+        mDirection = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_direction,
+                DIRECTION_LEFT_TO_RIGHT | DIRECTION_TOP_TO_BOTTOM);
+        mDirectionPriority = a.getInt(com.android.internal.R.styleable.GridLayoutAnimation_directionPriority,
+                PRIORITY_NONE);
+
+        a.recycle();
+    }
+
+    /**
+     * Creates a new layout animation controller with a delay of 50%
+     * for both rows and columns and the specified animation.
+     *
+     * @param animation the animation to use on each child of the view group
+     */
+    public GridLayoutAnimationController(Animation animation) {
+        this(animation, 0.5f, 0.5f);
+    }
+
+    /**
+     * Creates a new layout animation controller with the specified delays
+     * and the specified animation.
+     *
+     * @param animation the animation to use on each child of the view group
+     * @param columnDelay the delay by which each column animation must be offset
+     * @param rowDelay the delay by which each row animation must be offset
+     */
+    public GridLayoutAnimationController(Animation animation, float columnDelay, float rowDelay) {
+        super(animation);
+        mColumnDelay = columnDelay;
+        mRowDelay = rowDelay;
+    }
+
+    /**
+     * Returns the delay by which the children's animation are offset from one
+     * column to the other. The delay is expressed as a fraction of the
+     * animation duration.
+     *
+     * @return a fraction of the animation duration
+     *
+     * @see #setColumnDelay(float)
+     * @see #getRowDelay()
+     * @see #setRowDelay(float)
+     */
+    public float getColumnDelay() {
+        return mColumnDelay;
+    }
+
+    /**
+     * Sets the delay, as a fraction of the animation duration, by which the
+     * children's animations are offset from one column to the other.
+     *
+     * @param columnDelay a fraction of the animation duration
+     *
+     * @see #getColumnDelay()
+     * @see #getRowDelay()
+     * @see #setRowDelay(float)
+     */
+    public void setColumnDelay(float columnDelay) {
+        mColumnDelay = columnDelay;
+    }
+
+    /**
+     * Returns the delay by which the children's animation are offset from one
+     * row to the other. The delay is expressed as a fraction of the
+     * animation duration.
+     *
+     * @return a fraction of the animation duration
+     *
+     * @see #setRowDelay(float)
+     * @see #getColumnDelay()
+     * @see #setColumnDelay(float)
+     */
+    public float getRowDelay() {
+        return mRowDelay;
+    }
+
+    /**
+     * Sets the delay, as a fraction of the animation duration, by which the
+     * children's animations are offset from one row to the other.
+     *
+     * @param rowDelay a fraction of the animation duration
+     *
+     * @see #getRowDelay()
+     * @see #getColumnDelay()
+     * @see #setColumnDelay(float) 
+     */
+    public void setRowDelay(float rowDelay) {
+        mRowDelay = rowDelay;
+    }
+
+    /**
+     * Returns the direction of the animation. {@link #DIRECTION_HORIZONTAL_MASK}
+     * and {@link #DIRECTION_VERTICAL_MASK} can be used to retrieve the
+     * horizontal and vertical components of the direction.
+     *
+     * @return the direction of the animation
+     *
+     * @see #setDirection(int)
+     * @see #DIRECTION_BOTTOM_TO_TOP
+     * @see #DIRECTION_TOP_TO_BOTTOM
+     * @see #DIRECTION_LEFT_TO_RIGHT
+     * @see #DIRECTION_RIGHT_TO_LEFT
+     * @see #DIRECTION_HORIZONTAL_MASK
+     * @see #DIRECTION_VERTICAL_MASK
+     */
+    public int getDirection() {
+        return mDirection;
+    }
+
+    /**
+     * Sets the direction of the animation. The direction is expressed as an
+     * integer containing a horizontal and vertical component. For instance,
+     * <code>DIRECTION_BOTTOM_TO_TOP | DIRECTION_RIGHT_TO_LEFT</code>.
+     *
+     * @param direction the direction of the animation
+     *
+     * @see #getDirection()
+     * @see #DIRECTION_BOTTOM_TO_TOP
+     * @see #DIRECTION_TOP_TO_BOTTOM
+     * @see #DIRECTION_LEFT_TO_RIGHT
+     * @see #DIRECTION_RIGHT_TO_LEFT
+     * @see #DIRECTION_HORIZONTAL_MASK
+     * @see #DIRECTION_VERTICAL_MASK
+     */
+    public void setDirection(int direction) {
+        mDirection = direction;
+    }
+
+    /**
+     * Returns the direction priority for the animation. The priority can
+     * be either {@link #PRIORITY_NONE}, {@link #PRIORITY_COLUMN} or
+     * {@link #PRIORITY_ROW}.
+     *
+     * @return the priority of the animation direction
+     *
+     * @see #setDirectionPriority(int)
+     * @see #PRIORITY_COLUMN
+     * @see #PRIORITY_NONE
+     * @see #PRIORITY_ROW
+     */
+    public int getDirectionPriority() {
+        return mDirectionPriority;
+    }
+
+    /**
+     * Specifies the direction priority of the animation. For instance,
+     * {@link #PRIORITY_COLUMN} will give priority to columns: the animation
+     * will first play on the column, then on the rows.Z
+     *
+     * @param directionPriority the direction priority of the animation
+     *
+     * @see #getDirectionPriority()
+     * @see #PRIORITY_COLUMN
+     * @see #PRIORITY_NONE
+     * @see #PRIORITY_ROW
+     */
+    public void setDirectionPriority(int directionPriority) {
+        mDirectionPriority = directionPriority;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    public boolean willOverlap() {
+        return mColumnDelay < 1.0f || mRowDelay < 1.0f;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    @Override
+    protected long getDelayForView(View view) {
+        ViewGroup.LayoutParams lp = view.getLayoutParams();
+        AnimationParameters params = (AnimationParameters) lp.layoutAnimationParameters;
+
+        if (params == null) {
+            return 0;
+        }
+
+        final int column = getTransformedColumnIndex(params);
+        final int row = getTransformedRowIndex(params);
+
+        final int rowsCount = params.rowsCount;
+        final int columnsCount = params.columnsCount;
+
+        final long duration = mAnimation.getDuration();
+        final float columnDelay = mColumnDelay * duration;
+        final float rowDelay = mRowDelay * duration;
+
+        float totalDelay;
+        long viewDelay;
+
+        if (mInterpolator == null) {
+            mInterpolator = new LinearInterpolator();
+        }
+
+        switch (mDirectionPriority) {
+            case PRIORITY_COLUMN:
+                viewDelay = (long) (row * rowDelay + column * rowsCount * rowDelay);
+                totalDelay = rowsCount * rowDelay + columnsCount * rowsCount * rowDelay;
+                break;
+            case PRIORITY_ROW:
+                viewDelay = (long) (column * columnDelay + row * columnsCount * columnDelay);
+                totalDelay = columnsCount * columnDelay + rowsCount * columnsCount * columnDelay;
+                break;
+            case PRIORITY_NONE:
+            default:
+                viewDelay = (long) (column * columnDelay + row * rowDelay);
+                totalDelay = columnsCount * columnDelay + rowsCount * rowDelay;
+                break;
+        }
+
+        float normalizedDelay = viewDelay / totalDelay;
+        normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
+
+        return (long) (normalizedDelay * totalDelay);
+    }
+
+    private int getTransformedColumnIndex(AnimationParameters params) {
+        int index;
+        switch (getOrder()) {
+            case ORDER_REVERSE:
+                index = params.columnsCount - 1 - params.column;
+                break;
+            case ORDER_RANDOM:
+                if (mRandomizer == null) {
+                    mRandomizer = new Random();
+                }
+                index = (int) (params.columnsCount * mRandomizer.nextFloat());
+                break;
+            case ORDER_NORMAL:
+            default:
+                index = params.column;
+                break;
+        }
+
+        int direction = mDirection & DIRECTION_HORIZONTAL_MASK;
+        if (direction == DIRECTION_RIGHT_TO_LEFT) {
+            index = params.columnsCount - 1 - index;
+        }
+
+        return index;
+    }
+
+    private int getTransformedRowIndex(AnimationParameters params) {
+        int index;
+        switch (getOrder()) {
+            case ORDER_REVERSE:
+                index = params.rowsCount - 1 - params.row;
+                break;
+            case ORDER_RANDOM:
+                if (mRandomizer == null) {
+                    mRandomizer = new Random();
+                }
+                index = (int) (params.rowsCount * mRandomizer.nextFloat());
+                break;
+            case ORDER_NORMAL:
+            default:
+                index = params.row;
+                break;
+        }
+
+        int direction = mDirection & DIRECTION_VERTICAL_MASK;
+        if (direction == DIRECTION_BOTTOM_TO_TOP) {
+            index = params.rowsCount - 1 - index;
+        }
+
+        return index;
+    }
+
+    /**
+     * The set of parameters that has to be attached to each view contained in
+     * the view group animated by the grid layout animation controller. These
+     * parameters are used to compute the start time of each individual view's
+     * animation.
+     */
+    public static class AnimationParameters extends
+            LayoutAnimationController.AnimationParameters {
+        /**
+         * The view group's column to which the view belongs.
+         */
+        public int column;
+
+        /**
+         * The view group's row to which the view belongs.
+         */
+        public int row;
+
+        /**
+         * The number of columns in the view's enclosing grid layout.
+         */
+        public int columnsCount;
+
+        /**
+         * The number of rows in the view's enclosing grid layout.
+         */
+        public int rowsCount;
+    }
+}
diff --git a/core/java/android/view/animation/Interpolator.java b/core/java/android/view/animation/Interpolator.java
new file mode 100644
index 0000000..d14c3e33
--- /dev/null
+++ b/core/java/android/view/animation/Interpolator.java
@@ -0,0 +1,39 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+/**
+ * An interpolator defines the rate of change of an animation. This allows
+ * the basic animation effects (alpha, scale, translate, rotate) to be 
+ * accelerated, decelerated, repeated, etc.
+ */
+public interface Interpolator {
+    
+    /**
+     * Maps a point on the timeline to a multiplier to be applied to the
+     * transformations of an animation.
+     * 
+     * @param input A value between 0 and 1.0 indicating our current point
+     *        in the animation where 0 represents the start and 1.0 represents
+     *        the end
+     * @return The interpolation value. This value can be more than 1.0 for
+     *         Interpolators which overshoot their targets, or less than 0 for
+     *         Interpolators that undershoot their targets.
+     */
+    float getInterpolation(float input);
+    
+}
diff --git a/core/java/android/view/animation/LayoutAnimationController.java b/core/java/android/view/animation/LayoutAnimationController.java
new file mode 100644
index 0000000..882e738
--- /dev/null
+++ b/core/java/android/view/animation/LayoutAnimationController.java
@@ -0,0 +1,435 @@
+/*
+ * Copyright (C) 2007 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+import android.view.View;
+import android.view.ViewGroup;
+
+import java.util.Random;
+
+/**
+ * A layout animation controller is used to animated a layout's, or a view
+ * group's, children. Each child uses the same animation but for every one of
+ * them, the animation starts at a different time. A layout animation controller
+ * is used by {@link android.view.ViewGroup} to compute the delay by which each
+ * child's animation start must be offset. The delay is computed by using
+ * characteristics of each child, like its index in the view group.
+ *
+ * This standard implementation computes the delay by multiplying a fixed
+ * amount of miliseconds by the index of the child in its parent view group.
+ * Subclasses are supposed to override
+ * {@link #getDelayForView(android.view.View)} to implement a different way
+ * of computing the delay. For instance, a
+ * {@link android.view.animation.GridLayoutAnimationController} will compute the
+ * delay based on the column and row indices of the child in its parent view
+ * group.
+ *
+ * Information used to compute the animation delay of each child are stored
+ * in an instance of
+ * {@link android.view.animation.LayoutAnimationController.AnimationParameters},
+ * itself stored in the {@link android.view.ViewGroup.LayoutParams} of the view.
+ *
+ * @attr ref android.R.styleable#LayoutAnimation_delay
+ * @attr ref android.R.styleable#LayoutAnimation_animationOrder
+ * @attr ref android.R.styleable#LayoutAnimation_interpolator
+ * @attr ref android.R.styleable#LayoutAnimation_animation
+ */
+public class LayoutAnimationController {
+    /**
+     * Distributes the animation delays in the order in which view were added
+     * to their view group.
+     */
+    public static final int ORDER_NORMAL  = 0;
+
+    /**
+     * Distributes the animation delays in the reverse order in which view were
+     * added to their view group.
+     */
+    public static final int ORDER_REVERSE = 1;
+
+    /**
+     * Randomly distributes the animation delays.
+     */
+    public static final int ORDER_RANDOM  = 2;
+
+    /**
+     * The animation applied on each child of the view group on which this
+     * layout animation controller is set.
+     */
+    protected Animation mAnimation;
+
+    /**
+     * The randomizer used when the order is set to random. Subclasses should
+     * use this object to avoid creating their own.
+     */
+    protected Random mRandomizer;
+
+    /**
+     * The interpolator used to interpolate the delays.
+     */
+    protected Interpolator mInterpolator;
+
+    private float mDelay;
+    private int mOrder;
+
+    private long mDuration;
+    private long mMaxDelay;    
+
+    /**
+     * Creates a new layout animation controller from external resources.
+     *
+     * @param context the Context the view  group is running in, through which
+     *        it can access the resources
+     * @param attrs the attributes of the XML tag that is inflating the
+     *        layout animation controller
+     */
+    public LayoutAnimationController(Context context, AttributeSet attrs) {
+        TypedArray a = context.obtainStyledAttributes(attrs, com.android.internal.R.styleable.LayoutAnimation);
+
+        Animation.Description d = Animation.Description.parseValue(
+                a.peekValue(com.android.internal.R.styleable.LayoutAnimation_delay));
+        mDelay = d.value;
+
+        mOrder = a.getInt(com.android.internal.R.styleable.LayoutAnimation_animationOrder, ORDER_NORMAL);
+
+        int resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_animation, 0);
+        if (resource > 0) {
+            setAnimation(context, resource);
+        }
+
+        resource = a.getResourceId(com.android.internal.R.styleable.LayoutAnimation_interpolator, 0);
+        if (resource > 0) {
+            setInterpolator(context, resource);
+        }
+
+        a.recycle();
+    }
+
+    /**
+     * Creates a new layout animation controller with a delay of 50%
+     * and the specified animation.
+     *
+     * @param animation the animation to use on each child of the view group
+     */
+    public LayoutAnimationController(Animation animation) {
+        this(animation, 0.5f);
+    }
+
+    /**
+     * Creates a new layout animation controller with the specified delay
+     * and the specified animation.
+     *
+     * @param animation the animation to use on each child of the view group
+     * @param delay the delay by which each child's animation must be offset
+     */
+    public LayoutAnimationController(Animation animation, float delay) {
+        mDelay = delay;
+        setAnimation(animation);
+    }
+
+    /**
+     * Returns the order used to compute the delay of each child's animation.
+     *
+     * @return one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or
+     *         {@link #ORDER_RANDOM)
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_animationOrder
+     */
+    public int getOrder() {
+        return mOrder;
+    }
+
+    /**
+     * Sets the order used to compute the delay of each child's animation.
+     *
+     * @param order one of {@link #ORDER_NORMAL}, {@link #ORDER_REVERSE} or
+     *        {@link #ORDER_RANDOM}
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_animationOrder
+     */
+    public void setOrder(int order) {
+        mOrder = order;
+    }
+
+    /**
+     * Sets the animation to be run on each child of the view group on which
+     * this layout animation controller is .
+     *
+     * @param context the context from which the animation must be inflated
+     * @param resourceID the resource identifier of the animation
+     *
+     * @see #setAnimation(Animation)
+     * @see #getAnimation() 
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_animation
+     */
+    public void setAnimation(Context context, int resourceID) {
+        setAnimation(AnimationUtils.loadAnimation(context, resourceID));
+    }
+
+    /**
+     * Sets the animation to be run on each child of the view group on which
+     * this layout animation controller is .
+     *
+     * @param animation the animation to run on each child of the view group
+
+     * @see #setAnimation(android.content.Context, int)
+     * @see #getAnimation()
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_animation
+     */
+    public void setAnimation(Animation animation) {
+        mAnimation = animation;
+        mAnimation.setFillBefore(true);
+    }
+
+    /**
+     * Returns the animation applied to each child of the view group on which
+     * this controller is set.
+     *
+     * @return an {@link android.view.animation.Animation} instance
+     *
+     * @see #setAnimation(android.content.Context, int)
+     * @see #setAnimation(Animation)
+     */
+    public Animation getAnimation() {
+        return mAnimation;
+    }
+
+    /**
+     * Sets the interpolator used to interpolate the delays between the
+     * children.
+     *
+     * @param context the context from which the interpolator must be inflated
+     * @param resourceID the resource identifier of the interpolator
+     *
+     * @see #getInterpolator()
+     * @see #setInterpolator(Interpolator)
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_interpolator
+     */
+    public void setInterpolator(Context context, int resourceID) {
+        setInterpolator(AnimationUtils.loadInterpolator(context, resourceID));
+    }
+
+    /**
+     * Sets the interpolator used to interpolate the delays between the
+     * children.
+     *
+     * @param interpolator the interpolator
+     *
+     * @see #getInterpolator()
+     * @see #setInterpolator(Interpolator)
+     *
+     * @attr ref android.R.styleable#LayoutAnimation_interpolator
+     */
+    public void setInterpolator(Interpolator interpolator) {
+        mInterpolator = interpolator;
+    }
+
+    /**
+     * Returns the interpolator used to interpolate the delays between the
+     * children.
+     *
+     * @return an {@link android.view.animation.Interpolator}
+     */
+    public Interpolator getInterpolator() {
+        return mInterpolator;
+    }
+
+    /**
+     * Returns the delay by which the children's animation are offset. The
+     * delay is expressed as a fraction of the animation duration.
+     *
+     * @return a fraction of the animation duration
+     *
+     * @see #setDelay(float)
+     */
+    public float getDelay() {
+        return mDelay;
+    }
+
+    /**
+     * Sets the delay, as a fraction of the animation duration, by which the
+     * children's animations are offset. The general formula is:
+     *
+     * <pre>
+     * child animation delay = child index * delay * animation duration
+     * </pre>
+     *
+     * @param delay a fraction of the animation duration
+     *
+     * @see #getDelay()
+     */
+    public void setDelay(float delay) {
+        mDelay = delay;
+    }
+
+    /**
+     * Indicates whether two children's animations will overlap. Animations
+     * overlap when the delay is lower than 100% (or 1.0).
+     *
+     * @return true if animations will overlap, false otherwise
+     */
+    public boolean willOverlap() {
+        return mDelay < 1.0f;
+    }
+
+    /**
+     * Starts the animation.
+     */
+    public void start() {
+        mDuration = mAnimation.getDuration();
+        mMaxDelay = Long.MIN_VALUE;
+        mAnimation.setStartTime(-1);
+    }
+
+    /**
+     * Returns the animation to be applied to the specified view. The returned
+     * animation is delayed by an offset computed according to the information
+     * provided by
+     * {@link android.view.animation.LayoutAnimationController.AnimationParameters}.
+     * This method is called by view groups to obtain the animation to set on
+     * a specific child.
+     *
+     * @param view the view to animate
+     * @return an animation delayed by the number of milliseconds returned by
+     *         {@link #getDelayForView(android.view.View)}
+     *
+     * @see #getDelay()
+     * @see #setDelay(float)
+     * @see #getDelayForView(android.view.View)
+     */
+    public final Animation getAnimationForView(View view) {
+        final long delay = getDelayForView(view) + mAnimation.getStartOffset();
+        mMaxDelay = Math.max(mMaxDelay, delay);
+
+        try {
+            final Animation animation = mAnimation.clone();
+            animation.setStartOffset(delay);
+            return animation;
+        } catch (CloneNotSupportedException e) {
+            return null;
+        }
+    }
+
+    /**
+     * Indicates whether the layout animation is over or not. A layout animation
+     * is considered done when the animation with the longest delay is done.
+     *
+     * @return true if all of the children's animations are over, false otherwise
+     */
+    public boolean isDone() {
+        return AnimationUtils.currentAnimationTimeMillis() >
+                mAnimation.getStartTime() + mMaxDelay + mDuration;
+    }
+
+    /**
+     * Returns the amount of milliseconds by which the specified view's
+     * animation must be delayed or offset. Subclasses should override this
+     * method to return a suitable value.
+     *
+     * This implementation returns <code>child animation delay</code>
+     * milliseconds where:
+     *
+     * <pre>
+     * child animation delay = child index * delay
+     * </pre>
+     *
+     * The index is retrieved from the
+     * {@link android.view.animation.LayoutAnimationController.AnimationParameters}
+     * found in the view's {@link android.view.ViewGroup.LayoutParams}.
+     *
+     * @param view the view for which to obtain the animation's delay
+     * @return a delay in milliseconds
+     *
+     * @see #getAnimationForView(android.view.View)
+     * @see #getDelay()
+     * @see #getTransformedIndex(android.view.animation.LayoutAnimationController.AnimationParameters)
+     * @see android.view.ViewGroup.LayoutParams
+     */
+    protected long getDelayForView(View view) {
+        ViewGroup.LayoutParams lp = view.getLayoutParams();
+        AnimationParameters params = lp.layoutAnimationParameters;
+
+        if (params == null) {
+            return 0;
+        }
+
+        final float delay = mDelay * mAnimation.getDuration();
+        final long viewDelay = (long) (getTransformedIndex(params) * delay);
+        final float totalDelay = delay * params.count;
+
+        if (mInterpolator == null) {
+            mInterpolator = new LinearInterpolator();
+        }
+
+        float normalizedDelay = viewDelay / totalDelay;
+        normalizedDelay = mInterpolator.getInterpolation(normalizedDelay);
+
+        return (long) (normalizedDelay * totalDelay);
+    }
+
+    /**
+     * Transforms the index stored in
+     * {@link android.view.animation.LayoutAnimationController.AnimationParameters}
+     * by the order returned by {@link #getOrder()}. Subclasses should override
+     * this method to provide additional support for other types of ordering.
+     * This method should be invoked by
+     * {@link #getDelayForView(android.view.View)} prior to any computation. 
+     *
+     * @param params the animation parameters containing the index
+     * @return a transformed index
+     */
+    protected int getTransformedIndex(AnimationParameters params) {
+        switch (getOrder()) {
+            case ORDER_REVERSE:
+                return params.count - 1 - params.index;
+            case ORDER_RANDOM:
+                if (mRandomizer == null) {
+                    mRandomizer = new Random();
+                }
+                return (int) (params.count * mRandomizer.nextFloat());
+            case ORDER_NORMAL:
+            default:
+                return params.index;
+        }
+    }
+
+    /**
+     * The set of parameters that has to be attached to each view contained in
+     * the view group animated by the layout animation controller. These
+     * parameters are used to compute the start time of each individual view's
+     * animation.
+     */
+    public static class AnimationParameters {
+        /**
+         * The number of children in the view group containing the view to which
+         * these parameters are attached.
+         */
+        public int count;
+
+        /**
+         * The index of the view to which these parameters are attached in its
+         * containing view group.
+         */
+        public int index;
+    }
+}
diff --git a/core/java/android/view/animation/LinearInterpolator.java b/core/java/android/view/animation/LinearInterpolator.java
new file mode 100644
index 0000000..96a039f
--- /dev/null
+++ b/core/java/android/view/animation/LinearInterpolator.java
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.util.AttributeSet;
+
+/**
+ * An interpolator where the rate of change is constant
+ *
+ */
+public class LinearInterpolator implements Interpolator {
+
+    public LinearInterpolator() {
+    }
+    
+    public LinearInterpolator(Context context, AttributeSet attrs) {
+    }
+    
+    public float getInterpolation(float input) {
+        return input;
+    }
+}
diff --git a/core/java/android/view/animation/RotateAnimation.java b/core/java/android/view/animation/RotateAnimation.java
new file mode 100644
index 0000000..2f51b91
--- /dev/null
+++ b/core/java/android/view/animation/RotateAnimation.java
@@ -0,0 +1,165 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the rotation of an object. This rotation takes
+ * place int the X-Y plane. You can specify the point to use for the center of
+ * the rotation, where (0,0) is the top left point. If not specified, (0,0) is
+ * the default rotation point.
+ * 
+ */
+public class RotateAnimation extends Animation {
+    private float mFromDegrees;
+    private float mToDegrees;
+
+    private int mPivotXType = ABSOLUTE;
+    private int mPivotYType = ABSOLUTE;
+    private float mPivotXValue = 0.0f;
+    private float mPivotYValue = 0.0f;
+
+    private float mPivotX;
+    private float mPivotY;
+
+    /**
+     * Constructor used whan an RotateAnimation is loaded from a resource.
+     * 
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public RotateAnimation(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.RotateAnimation);
+
+        mFromDegrees = a.getFloat(
+                com.android.internal.R.styleable.RotateAnimation_fromDegrees, 0.0f);
+        mToDegrees = a.getFloat(com.android.internal.R.styleable.RotateAnimation_toDegrees, 0.0f);
+
+        Description d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.RotateAnimation_pivotX));
+        mPivotXType = d.type;
+        mPivotXValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.RotateAnimation_pivotY));
+        mPivotYType = d.type;
+        mPivotYValue = d.value;
+
+        a.recycle();
+    }
+
+    /**
+     * Constructor to use when building a RotateAnimation from code.
+     * Default pivotX/pivotY point is (0,0).
+     * 
+     * @param fromDegrees Rotation offset to apply at the start of the
+     *        animation.
+     * 
+     * @param toDegrees Rotation offset to apply at the end of the animation.
+     */
+    public RotateAnimation(float fromDegrees, float toDegrees) {
+        mFromDegrees = fromDegrees;
+        mToDegrees = toDegrees;
+        mPivotX = 0.0f;
+        mPivotY = 0.0f;
+    }
+
+    /**
+     * Constructor to use when building a RotateAnimation from code
+     * 
+     * @param fromDegrees Rotation offset to apply at the start of the
+     *        animation.
+     * 
+     * @param toDegrees Rotation offset to apply at the end of the animation.
+     * 
+     * @param pivotX The X coordinate of the point about which the object is
+     *        being rotated, specified as an absolute number where 0 is the left
+     *        edge.
+     * @param pivotY The Y coordinate of the point about which the object is
+     *        being rotated, specified as an absolute number where 0 is the top
+     *        edge.
+     */
+    public RotateAnimation(float fromDegrees, float toDegrees, float pivotX, float pivotY) {
+        mFromDegrees = fromDegrees;
+        mToDegrees = toDegrees;
+
+        mPivotXType = ABSOLUTE;
+        mPivotYType = ABSOLUTE;
+        mPivotXValue = pivotX;
+        mPivotYValue = pivotY;
+    }
+
+    /**
+     * Constructor to use when building a RotateAnimation from code
+     * 
+     * @param fromDegrees Rotation offset to apply at the start of the
+     *        animation.
+     * 
+     * @param toDegrees Rotation offset to apply at the end of the animation.
+     * 
+     * @param pivotXType Specifies how pivotXValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param pivotXValue The X coordinate of the point about which the object
+     *        is being rotated, specified as an absolute number where 0 is the
+     *        left edge. This value can either be an absolute number if
+     *        pivotXType is ABSOLUTE, or a percentage (where 1.0 is 100%)
+     *        otherwise.
+     * @param pivotYType Specifies how pivotYValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param pivotYValue The Y coordinate of the point about which the object
+     *        is being rotated, specified as an absolute number where 0 is the
+     *        top edge. This value can either be an absolute number if
+     *        pivotYType is ABSOLUTE, or a percentage (where 1.0 is 100%)
+     *        otherwise.
+     */
+    public RotateAnimation(float fromDegrees, float toDegrees, int pivotXType, float pivotXValue,
+            int pivotYType, float pivotYValue) {
+        mFromDegrees = fromDegrees;
+        mToDegrees = toDegrees;
+
+        mPivotXValue = pivotXValue;
+        mPivotXType = pivotXType;
+        mPivotYValue = pivotYValue;
+        mPivotYType = pivotYType;
+    }
+
+    @Override
+    protected void applyTransformation(float interpolatedTime, Transformation t) {
+        float degrees = mFromDegrees + ((mToDegrees - mFromDegrees) * interpolatedTime);
+
+        if (mPivotX == 0.0f && mPivotY == 0.0f) {
+            t.getMatrix().setRotate(degrees);
+        } else {
+            t.getMatrix().setRotate(degrees, mPivotX, mPivotY);
+        }
+    }
+
+    @Override
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        super.initialize(width, height, parentWidth, parentHeight);
+        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
+        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
+    }
+}
diff --git a/core/java/android/view/animation/ScaleAnimation.java b/core/java/android/view/animation/ScaleAnimation.java
new file mode 100644
index 0000000..122ed6d
--- /dev/null
+++ b/core/java/android/view/animation/ScaleAnimation.java
@@ -0,0 +1,186 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the scale of an object. You can specify the point
+ * to use for the center of scaling.
+ * 
+ */
+public class ScaleAnimation extends Animation {
+    private float mFromX;
+    private float mToX;
+    private float mFromY;
+    private float mToY;
+
+    private int mPivotXType = ABSOLUTE;
+    private int mPivotYType = ABSOLUTE;
+    private float mPivotXValue = 0.0f;
+    private float mPivotYValue = 0.0f;
+
+    private float mPivotX;
+    private float mPivotY;
+
+    /**
+     * Constructor used whan an ScaleAnimation is loaded from a resource.
+     * 
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public ScaleAnimation(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.ScaleAnimation);
+
+        mFromX = a.getFloat(com.android.internal.R.styleable.ScaleAnimation_fromXScale, 0.0f);
+        mToX = a.getFloat(com.android.internal.R.styleable.ScaleAnimation_toXScale, 0.0f);
+
+        mFromY = a.getFloat(com.android.internal.R.styleable.ScaleAnimation_fromYScale, 0.0f);
+        mToY = a.getFloat(com.android.internal.R.styleable.ScaleAnimation_toYScale, 0.0f);
+
+        Description d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.ScaleAnimation_pivotX));
+        mPivotXType = d.type;
+        mPivotXValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.ScaleAnimation_pivotY));
+        mPivotYType = d.type;
+        mPivotYValue = d.value;
+
+        a.recycle();
+    }
+
+    /**
+     * Constructor to use when building a ScaleAnimation from code
+     * 
+     * @param fromX Horizontal scaling factor to apply at the start of the
+     *        animation
+     * @param toX Horizontal scaling factor to apply at the end of the animation
+     * @param fromY Vertical scaling factor to apply at the start of the
+     *        animation
+     * @param toY Vertical scaling factor to apply at the end of the animation
+     */
+    public ScaleAnimation(float fromX, float toX, float fromY, float toY) {
+        mFromX = fromX;
+        mToX = toX;
+        mFromY = fromY;
+        mToY = toY;
+        mPivotX = 0;
+        mPivotY = 0;
+    }
+
+    /**
+     * Constructor to use when building a ScaleAnimation from code
+     * 
+     * @param fromX Horizontal scaling factor to apply at the start of the
+     *        animation
+     * @param toX Horizontal scaling factor to apply at the end of the animation
+     * @param fromY Vertical scaling factor to apply at the start of the
+     *        animation
+     * @param toY Vertical scaling factor to apply at the end of the animation
+     * @param pivotX The X coordinate of the point about which the object is
+     *        being scaled, specified as an absolute number where 0 is the left
+     *        edge. (This point remains fixed while the object changes size.)
+     * @param pivotY The Y coordinate of the point about which the object is
+     *        being scaled, specified as an absolute number where 0 is the top
+     *        edge. (This point remains fixed while the object changes size.)
+     */
+    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
+            float pivotX, float pivotY) {
+        mFromX = fromX;
+        mToX = toX;
+        mFromY = fromY;
+        mToY = toY;
+
+        mPivotXType = ABSOLUTE;
+        mPivotYType = ABSOLUTE;
+        mPivotXValue = pivotX;
+        mPivotYValue = pivotY;
+    }
+
+    /**
+     * Constructor to use when building a ScaleAnimation from code
+     * 
+     * @param fromX Horizontal scaling factor to apply at the start of the
+     *        animation
+     * @param toX Horizontal scaling factor to apply at the end of the animation
+     * @param fromY Vertical scaling factor to apply at the start of the
+     *        animation
+     * @param toY Vertical scaling factor to apply at the end of the animation
+     * @param pivotXType Specifies how pivotXValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param pivotXValue The X coordinate of the point about which the object
+     *        is being scaled, specified as an absolute number where 0 is the
+     *        left edge. (This point remains fixed while the object changes
+     *        size.) This value can either be an absolute number if pivotXType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     * @param pivotYType Specifies how pivotYValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param pivotYValue The Y coordinate of the point about which the object
+     *        is being scaled, specified as an absolute number where 0 is the
+     *        top edge. (This point remains fixed while the object changes
+     *        size.) This value can either be an absolute number if pivotYType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     */
+    public ScaleAnimation(float fromX, float toX, float fromY, float toY,
+            int pivotXType, float pivotXValue, int pivotYType, float pivotYValue) {
+        mFromX = fromX;
+        mToX = toX;
+        mFromY = fromY;
+        mToY = toY;
+
+        mPivotXValue = pivotXValue;
+        mPivotXType = pivotXType;
+        mPivotYValue = pivotYValue;
+        mPivotYType = pivotYType;
+    }
+
+    @Override
+    protected void applyTransformation(float interpolatedTime, Transformation t) {
+        float sx = 1.0f;
+        float sy = 1.0f;
+
+        if (mFromX != 1.0f || mToX != 1.0f) {
+            sx = mFromX + ((mToX - mFromX) * interpolatedTime);
+        }
+        if (mFromY != 1.0f || mToY != 1.0f) {
+            sy = mFromY + ((mToY - mFromY) * interpolatedTime);
+        }
+
+        if (mPivotX == 0 && mPivotY == 0) {
+            t.getMatrix().setScale(sx, sy);
+        } else {
+            t.getMatrix().setScale(sx, sy, mPivotX, mPivotY);
+        }
+    }
+
+    @Override
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        super.initialize(width, height, parentWidth, parentHeight);
+
+        mPivotX = resolveSize(mPivotXType, mPivotXValue, width, parentWidth);
+        mPivotY = resolveSize(mPivotYType, mPivotYValue, height, parentHeight);
+    }
+}
diff --git a/core/java/android/view/animation/Transformation.java b/core/java/android/view/animation/Transformation.java
new file mode 100644
index 0000000..f9e85bf
--- /dev/null
+++ b/core/java/android/view/animation/Transformation.java
@@ -0,0 +1,147 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.graphics.Matrix;
+
+/**
+ * Defines the transformation to be applied at
+ * one point in time of an Animation.
+ *
+ */
+public class Transformation {
+    /**
+     * Indicates a transformation that has no effect (alpha = 1 and identity matrix.)
+     */
+    public static int TYPE_IDENTITY = 0x0;
+    /**
+     * Indicates a transformation that applies an alpha only (uses an identity matrix.)
+     */
+    public static int TYPE_ALPHA = 0x1;
+    /**
+     * Indicates a transformation that applies a matrix only (alpha = 1.)
+     */
+    public static int TYPE_MATRIX = 0x2;
+    /**
+     * Indicates a transformation that applies an alpha and a matrix.
+     */
+    public static int TYPE_BOTH = TYPE_ALPHA | TYPE_MATRIX;
+
+    protected Matrix mMatrix;
+    protected float mAlpha;
+    protected int mTransformationType;
+
+    /**
+     * Creates a new transformation with alpha = 1 and the identity matrix.
+     */
+    public Transformation() {
+        clear();
+    }
+
+    /**
+     * Reset the transformation to a state that leaves the object
+     * being animated in an unmodified state. The transformation type is
+     * {@link #TYPE_BOTH} by default.
+     */
+    public void clear() {
+        if (mMatrix == null) {
+            mMatrix = new Matrix();
+        } else {
+            mMatrix.reset();
+        }
+        mAlpha = 1.0f;
+        mTransformationType = TYPE_BOTH;
+    }
+
+    /**
+     * Indicates the nature of this transformation.
+     *
+     * @return {@link #TYPE_ALPHA}, {@link #TYPE_MATRIX},
+     *         {@link #TYPE_BOTH} or {@link #TYPE_IDENTITY}.
+     */
+    public int getTransformationType() {
+        return mTransformationType;
+    }
+
+    /**
+     * Sets the transformation type.
+     *
+     * @param transformationType One of {@link #TYPE_ALPHA},
+     *        {@link #TYPE_MATRIX}, {@link #TYPE_BOTH} or
+     *        {@link #TYPE_IDENTITY}.
+     */
+    public void setTransformationType(int transformationType) {
+        mTransformationType = transformationType;
+    }
+
+    /**
+     * Clones the specified transformation.
+     *
+     * @param t The transformation to clone.
+     */
+    public void set(Transformation t) {
+        mAlpha = t.getAlpha();
+        mMatrix.set(t.getMatrix());
+        mTransformationType = t.getTransformationType();
+    }
+    
+    /**
+     * Apply this Transformation to an existing Transformation, e.g. apply
+     * a scale effect to something that has already been rotated.
+     * @param t
+     */
+    public void compose(Transformation t) {
+        mAlpha *= t.getAlpha();
+        mMatrix.preConcat(t.getMatrix());
+    }
+    
+    /**
+     * @return The 3x3 Matrix representing the trnasformation to apply to the
+     * coordinates of the object being animated
+     */
+    public Matrix getMatrix() {
+        return mMatrix;
+    }
+    
+    /**
+     * Sets the degree of transparency
+     * @param alpha 1.0 means fully opaqe and 0.0 means fully transparent
+     */
+    public void setAlpha(float alpha) {
+        mAlpha = alpha;
+    }
+
+    /**
+     * @return The degree of transparency
+     */
+    public float getAlpha() {
+        return mAlpha;
+    }
+    
+    @Override
+    public String toString() {
+        return "Transformation{alpha=" + mAlpha + " matrix="
+                + mMatrix.toShortString() + "}";
+    }
+    
+    /**
+     * Return a string representation of the transformation in a compact form.
+     */
+    public String toShortString() {
+        return "{alpha=" + mAlpha + " matrix=" + mMatrix.toShortString() + "}";
+    }
+}
diff --git a/core/java/android/view/animation/TranslateAnimation.java b/core/java/android/view/animation/TranslateAnimation.java
new file mode 100644
index 0000000..ca936cb
--- /dev/null
+++ b/core/java/android/view/animation/TranslateAnimation.java
@@ -0,0 +1,171 @@
+/*
+ * Copyright (C) 2006 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.animation;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.util.AttributeSet;
+
+/**
+ * An animation that controls the position of an object. See the
+ * {@link android.view.animation full package} description for details and
+ * sample code.
+ * 
+ */
+public class TranslateAnimation extends Animation {
+    private int mFromXType = ABSOLUTE;
+    private int mToXType = ABSOLUTE;
+
+    private int mFromYType = ABSOLUTE;
+    private int mToYType = ABSOLUTE;
+
+    private float mFromXValue = 0.0f;
+    private float mToXValue = 0.0f;
+
+    private float mFromYValue = 0.0f;
+    private float mToYValue = 0.0f;
+
+    private float mFromXDelta;
+    private float mToXDelta;
+    private float mFromYDelta;
+    private float mToYDelta;
+
+    /**
+     * Constructor used when a TranslateAnimation is loaded from a resource.
+     * 
+     * @param context Application context to use
+     * @param attrs Attribute set from which to read values
+     */
+    public TranslateAnimation(Context context, AttributeSet attrs) {
+        super(context, attrs);
+
+        TypedArray a = context.obtainStyledAttributes(attrs,
+                com.android.internal.R.styleable.TranslateAnimation);
+
+        Description d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.TranslateAnimation_fromXDelta));
+        mFromXType = d.type;
+        mFromXValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+                com.android.internal.R.styleable.TranslateAnimation_toXDelta));
+        mToXType = d.type;
+        mToXValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.TranslateAnimation_fromYDelta));
+        mFromYType = d.type;
+        mFromYValue = d.value;
+
+        d = Description.parseValue(a.peekValue(
+            com.android.internal.R.styleable.TranslateAnimation_toYDelta));
+        mToYType = d.type;
+        mToYValue = d.value;
+
+        a.recycle();
+    }
+
+    /**
+     * Constructor to use when building a ScaleAnimation from code
+     * 
+     * @param fromXDelta Change in X coordinate to apply at the start of the
+     *        animation
+     * @param toXDelta Change in X coordinate to apply at the end of the
+     *        animation
+     * @param fromYDelta Change in Y coordinate to apply at the start of the
+     *        animation
+     * @param toYDelta Change in Y coordinate to apply at the end of the
+     *        animation
+     */
+    public TranslateAnimation(float fromXDelta, float toXDelta, float fromYDelta, float toYDelta) {
+        mFromXValue = fromXDelta;
+        mToXValue = toXDelta;
+        mFromYValue = fromYDelta;
+        mToYValue = toYDelta;
+
+        mFromXType = ABSOLUTE;
+        mToXType = ABSOLUTE;
+        mFromYType = ABSOLUTE;
+        mToYType = ABSOLUTE;
+    }
+
+    /**
+     * Constructor to use when building a ScaleAnimation from code
+     * 
+     * @param fromXType Specifies how fromXValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param fromXValue Change in X coordinate to apply at the start of the
+     *        animation. This value can either be an absolute number if fromXType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     * @param toXType Specifies how toXValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param toXValue Change in X coordinate to apply at the end of the
+     *        animation. This value can either be an absolute number if toXType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     * @param fromYType Specifies how fromYValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param fromYValue Change in Y coordinate to apply at the start of the
+     *        animation. This value can either be an absolute number if fromYType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     * @param toYType Specifies how toYValue should be interpreted. One of
+     *        Animation.ABSOLUTE, Animation.RELATIVE_TO_SELF, or
+     *        Animation.RELATIVE_TO_PARENT.
+     * @param toYValue Change in Y coordinate to apply at the end of the
+     *        animation. This value can either be an absolute number if toYType
+     *        is ABSOLUTE, or a percentage (where 1.0 is 100%) otherwise.
+     */
+    public TranslateAnimation(int fromXType, float fromXValue, int toXType, float toXValue,
+            int fromYType, float fromYValue, int toYType, float toYValue) {
+
+        mFromXValue = fromXValue;
+        mToXValue = toXValue;
+        mFromYValue = fromYValue;
+        mToYValue = toYValue;
+
+        mFromXType = fromXType;
+        mToXType = toXType;
+        mFromYType = fromYType;
+        mToYType = toYType;
+    }
+
+
+    @Override
+    protected void applyTransformation(float interpolatedTime, Transformation t) {
+        float dx = mFromXDelta;
+        float dy = mFromYDelta;
+        if (mFromXDelta != mToXDelta) {
+            dx = mFromXDelta + ((mToXDelta - mFromXDelta) * interpolatedTime);
+        }
+        if (mFromYDelta != mToYDelta) {
+            dy = mFromYDelta + ((mToYDelta - mFromYDelta) * interpolatedTime);
+        }
+
+        t.getMatrix().setTranslate(dx, dy);
+    }
+
+    @Override
+    public void initialize(int width, int height, int parentWidth, int parentHeight) {
+        super.initialize(width, height, parentWidth, parentHeight);
+        mFromXDelta = resolveSize(mFromXType, mFromXValue, width, parentWidth);
+        mToXDelta = resolveSize(mToXType, mToXValue, width, parentWidth);
+        mFromYDelta = resolveSize(mFromYType, mFromYValue, height, parentHeight);
+        mToYDelta = resolveSize(mToYType, mToYValue, height, parentHeight);
+    }
+}
diff --git a/core/java/android/view/animation/package.html b/core/java/android/view/animation/package.html
new file mode 100755
index 0000000..87c99bb
--- /dev/null
+++ b/core/java/android/view/animation/package.html
@@ -0,0 +1,20 @@
+<html>
+<body>
+<p>Provides classes that handle tweened animations.</p>
+<p>Android provides two mechanisms
+    that you can use to create simple animations: <strong>tweened
+        animation</strong>, in which you tell Android to perform a series of simple
+    transformations (position, size, rotation, and so on) to the content of a
+    View; and <strong>frame-by-frame animation</strong>, which loads a series of Drawable resources
+    one after the other. Both animation types can be used in any View object
+    to provide simple rotating timers, activity icons, and other useful UI elements.
+    Tweened animation is handled by this package (android.view.animation); frame-by-frame animation is
+    handled by the {@link android.graphics.drawable.AnimationDrawable} class.
+    </p>
+
+<p>For more information on creating tweened or frame-by-frame animations, read the discussion in the
+<a href="{@docRoot}guide/topics/graphics/2d-graphics.html#tween-animation">2D Graphics</a>
+Dev Guide.</p>
+
+</body>
+</html>
diff --git a/core/java/android/view/inputmethod/BaseInputConnection.java b/core/java/android/view/inputmethod/BaseInputConnection.java
new file mode 100644
index 0000000..52b4107
--- /dev/null
+++ b/core/java/android/view/inputmethod/BaseInputConnection.java
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.content.Context;
+import android.content.res.TypedArray;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.SystemClock;
+import android.text.Editable;
+import android.text.NoCopySpan;
+import android.text.Selection;
+import android.text.Spannable;
+import android.text.SpannableStringBuilder;
+import android.text.Spanned;
+import android.text.TextUtils;
+import android.text.method.MetaKeyKeyListener;
+import android.util.Log;
+import android.util.LogPrinter;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+import android.view.View;
+import android.view.ViewRoot;
+
+class ComposingText implements NoCopySpan {
+}
+
+/**
+ * Base class for implementors of the InputConnection interface, taking care
+ * of most of the common behavior for providing a connection to an Editable.
+ * Implementors of this class will want to be sure to implement
+ * {@link #getEditable} to provide access to their own editable object.
+ */
+public class BaseInputConnection implements InputConnection {
+    private static final boolean DEBUG = false;
+    private static final String TAG = "BaseInputConnection";
+    static final Object COMPOSING = new ComposingText();
+    
+    final InputMethodManager mIMM;
+    final Handler mH;
+    final View mTargetView;
+    final boolean mDummyMode;
+    
+    private Object[] mDefaultComposingSpans;
+    
+    Editable mEditable;
+    KeyCharacterMap mKeyCharacterMap;
+    
+    BaseInputConnection(InputMethodManager mgr, boolean dummyMode) {
+        mIMM = mgr;
+        mTargetView = null;
+        mH = null;
+        mDummyMode = dummyMode;
+    }
+    
+    public BaseInputConnection(View targetView, boolean dummyMode) {
+        mIMM = (InputMethodManager)targetView.getContext().getSystemService(
+                Context.INPUT_METHOD_SERVICE);
+        mH = targetView.getHandler();
+        mTargetView = targetView;
+        mDummyMode = dummyMode;
+    }
+    
+    public static final void removeComposingSpans(Spannable text) {
+        text.removeSpan(COMPOSING);
+        Object[] sps = text.getSpans(0, text.length(), Object.class);
+        if (sps != null) {
+            for (int i=sps.length-1; i>=0; i--) {
+                Object o = sps[i];
+                if ((text.getSpanFlags(o)&Spanned.SPAN_COMPOSING) != 0) {
+                    text.removeSpan(o);
+                }
+            }
+        }
+    }
+    
+    public static void setComposingSpans(Spannable text) {
+        final Object[] sps = text.getSpans(0, text.length(), Object.class);
+        if (sps != null) {
+            for (int i=sps.length-1; i>=0; i--) {
+                final Object o = sps[i];
+                if (o == COMPOSING) {
+                    text.removeSpan(o);
+                    continue;
+                }
+                final int fl = text.getSpanFlags(o);
+                if ((fl&(Spanned.SPAN_COMPOSING|Spanned.SPAN_POINT_MARK_MASK)) 
+                        != (Spanned.SPAN_COMPOSING|Spanned.SPAN_EXCLUSIVE_EXCLUSIVE)) {
+                    text.setSpan(o, text.getSpanStart(o), text.getSpanEnd(o),
+                            (fl&Spanned.SPAN_POINT_MARK_MASK)
+                                    | Spanned.SPAN_COMPOSING
+                                    | Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                }
+            }
+        }
+        
+        text.setSpan(COMPOSING, 0, text.length(),
+                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE | Spanned.SPAN_COMPOSING);
+    }
+    
+    public static int getComposingSpanStart(Spannable text) {
+        return text.getSpanStart(COMPOSING);
+    }
+    
+    public static int getComposingSpanEnd(Spannable text) {
+        return text.getSpanEnd(COMPOSING);
+    }
+    
+    /**
+     * Return the target of edit operations.  The default implementation
+     * returns its own fake editable that is just used for composing text;
+     * subclasses that are real text editors should override this and
+     * supply their own.
+     */
+    public Editable getEditable() {
+        if (mEditable == null) {
+            mEditable = Editable.Factory.getInstance().newEditable("");
+            Selection.setSelection(mEditable, 0);
+        }
+        return mEditable;
+    }
+    
+    /**
+     * Default implementation does nothing.
+     */
+    public boolean beginBatchEdit() {
+        return false;
+    }
+
+    /**
+     * Default implementation does nothing.
+     */
+    public boolean endBatchEdit() {
+        return false;
+    }
+
+    /**
+     * Default implementation uses
+     * {@link MetaKeyKeyListener#clearMetaKeyState(long, int)
+     * MetaKeyKeyListener.clearMetaKeyState(long, int)} to clear the state.
+     */
+    public boolean clearMetaKeyStates(int states) {
+        final Editable content = getEditable();
+        if (content == null) return false;
+        MetaKeyKeyListener.clearMetaKeyState(content, states);
+        return true;
+    }
+
+    /**
+     * Default implementation does nothing.
+     */
+    public boolean commitCompletion(CompletionInfo text) {
+        return false;
+    }
+
+    /**
+     * Default implementation replaces any existing composing text with
+     * the given text.  In addition, only if dummy mode, a key event is
+     * sent for the new text and the current editable buffer cleared.
+     */
+    public boolean commitText(CharSequence text, int newCursorPosition) {
+        if (DEBUG) Log.v(TAG, "commitText " + text);
+        replaceText(text, newCursorPosition, false);
+        sendCurrentText();
+        return true;
+    }
+
+    /**
+     * The default implementation performs the deletion around the current
+     * selection position of the editable text.
+     */
+    public boolean deleteSurroundingText(int leftLength, int rightLength) {
+        if (DEBUG) Log.v(TAG, "deleteSurroundingText " + leftLength
+                + " / " + rightLength);
+        final Editable content = getEditable();
+        if (content == null) return false;
+
+        beginBatchEdit();
+        
+        int a = Selection.getSelectionStart(content);
+        int b = Selection.getSelectionEnd(content);
+
+        if (a > b) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        // ignore the composing text.
+        int ca = getComposingSpanStart(content);
+        int cb = getComposingSpanEnd(content);
+        if (cb < ca) {
+            int tmp = ca;
+            ca = cb;
+            cb = tmp;
+        }
+        if (ca != -1 && cb != -1) {
+            if (ca < a) a = ca;
+            if (cb > b) b = cb;
+        }
+
+        int deleted = 0;
+
+        if (leftLength > 0) {
+            int start = a - leftLength;
+            if (start < 0) start = 0;
+            content.delete(start, a);
+            deleted = a - start;
+        }
+
+        if (rightLength > 0) {
+            b = b - deleted;
+
+            int end = b + rightLength;
+            if (end > content.length()) end = content.length();
+
+            content.delete(b, end);
+        }
+        
+        endBatchEdit();
+        
+        return true;
+    }
+
+    /**
+     * The default implementation removes the composing state from the
+     * current editable text.  In addition, only if dummy mode, a key event is
+     * sent for the new text and the current editable buffer cleared.
+     */
+    public boolean finishComposingText() {
+        if (DEBUG) Log.v(TAG, "finishComposingText");
+        final Editable content = getEditable();
+        if (content != null) {
+            beginBatchEdit();
+            removeComposingSpans(content);
+            endBatchEdit();
+            sendCurrentText();
+        }
+        return true;
+    }
+
+    /**
+     * The default implementation uses TextUtils.getCapsMode to get the
+     * cursor caps mode for the current selection position in the editable
+     * text, unless in dummy mode in which case 0 is always returned.
+     */
+    public int getCursorCapsMode(int reqModes) {
+        if (mDummyMode) return 0;
+        
+        final Editable content = getEditable();
+        if (content == null) return 0;
+        
+        int a = Selection.getSelectionStart(content);
+        int b = Selection.getSelectionEnd(content);
+
+        if (a > b) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        return TextUtils.getCapsMode(content, a, reqModes);
+    }
+
+    /**
+     * The default implementation always returns null.
+     */
+    public ExtractedText getExtractedText(ExtractedTextRequest request, int flags) {
+        return null;
+    }
+
+    /**
+     * The default implementation returns the given amount of text from the
+     * current cursor position in the buffer.
+     */
+    public CharSequence getTextBeforeCursor(int length, int flags) {
+        final Editable content = getEditable();
+        if (content == null) return null;
+
+        int a = Selection.getSelectionStart(content);
+        int b = Selection.getSelectionEnd(content);
+
+        if (a > b) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        if (length > a) {
+            length = a;
+        }
+
+        if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+            return content.subSequence(a - length, a);
+        }
+        return TextUtils.substring(content, a - length, a);
+    }
+
+    /**
+     * The default implementation returns the given amount of text from the
+     * current cursor position in the buffer.
+     */
+    public CharSequence getTextAfterCursor(int length, int flags) {
+        final Editable content = getEditable();
+        if (content == null) return null;
+
+        int a = Selection.getSelectionStart(content);
+        int b = Selection.getSelectionEnd(content);
+
+        if (a > b) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        if (b + length > content.length()) {
+            length = content.length() - b;
+        }
+
+
+        if ((flags&GET_TEXT_WITH_STYLES) != 0) {
+            return content.subSequence(b, b + length);
+        }
+        return TextUtils.substring(content, b, b + length);
+    }
+
+    /**
+     * The default implementation does nothing.
+     */
+    public boolean performEditorAction(int actionCode) {
+        return false;
+    }
+
+    /**
+     * The default implementation does nothing.
+     */
+    public boolean performContextMenuAction(int id) {
+        return false;
+    }
+
+    /**
+     * The default implementation does nothing.
+     */
+    public boolean performPrivateCommand(String action, Bundle data) {
+        return false;
+    }
+
+    /**
+     * The default implementation places the given text into the editable,
+     * replacing any existing composing text.  The new text is marked as
+     * in a composing state with the composing style.
+     */
+    public boolean setComposingText(CharSequence text, int newCursorPosition) {
+        if (DEBUG) Log.v(TAG, "setComposingText " + text);
+        replaceText(text, newCursorPosition, true);
+        return true;
+    }
+
+    /**
+     * The default implementation changes the selection position in the
+     * current editable text.
+     */
+    public boolean setSelection(int start, int end) {
+        if (DEBUG) Log.v(TAG, "setSelection " + start + ", " + end);
+        final Editable content = getEditable();
+        if (content == null) return false;
+        int len = content.length();
+        if (start > len || end > len) {
+            // If the given selection is out of bounds, just ignore it.
+            // Most likely the text was changed out from under the IME,
+            // the the IME is going to have to update all of its state
+            // anyway.
+            return true;
+        }
+        Selection.setSelection(content, start, end);
+        return true;
+    }
+
+    /**
+     * Provides standard implementation for sending a key event to the window
+     * attached to the input connection's view.
+     */
+    public boolean sendKeyEvent(KeyEvent event) {
+        synchronized (mIMM.mH) {
+            Handler h = mH;
+            if (h == null) {
+                if (mIMM.mServedView != null) {
+                    h = mIMM.mServedView.getHandler();
+                }
+            }
+            if (h != null) {
+                h.sendMessage(h.obtainMessage(ViewRoot.DISPATCH_KEY_FROM_IME,
+                        event));
+            }
+        }
+        return false;
+    }
+    
+    /**
+     * Updates InputMethodManager with the current fullscreen mode.
+     */
+    public boolean reportFullscreenMode(boolean enabled) {
+        mIMM.setFullscreenMode(enabled);
+        return true;
+    }
+    
+    private void sendCurrentText() {
+        if (!mDummyMode) {
+            return;
+        }
+        
+        Editable content = getEditable();
+        if (content != null) {
+            final int N = content.length();
+            if (N == 0) {
+                return;
+            }
+            if (N == 1) {
+                // If it's 1 character, we have a chance of being
+                // able to generate normal key events...
+                if (mKeyCharacterMap == null) {
+                    mKeyCharacterMap = KeyCharacterMap.load(
+                            KeyCharacterMap.BUILT_IN_KEYBOARD);
+                }
+                char[] chars = new char[1];
+                content.getChars(0, 1, chars, 0);
+                KeyEvent[] events = mKeyCharacterMap.getEvents(chars);
+                if (events != null) {
+                    for (int i=0; i<events.length; i++) {
+                        if (DEBUG) Log.v(TAG, "Sending: " + events[i]);
+                        sendKeyEvent(events[i]);
+                    }
+                    content.clear();
+                    return;
+                }
+            }
+            
+            // Otherwise, revert to the special key event containing
+            // the actual characters.
+            KeyEvent event = new KeyEvent(SystemClock.uptimeMillis(),
+                    content.toString(), KeyCharacterMap.BUILT_IN_KEYBOARD, 0);
+            sendKeyEvent(event);
+            content.clear();
+        }
+    }
+    
+    private void replaceText(CharSequence text, int newCursorPosition,
+            boolean composing) {
+        final Editable content = getEditable();
+        if (content == null) {
+            return;
+        }
+        
+        beginBatchEdit();
+        
+        // delete composing text set previously.
+        int a = getComposingSpanStart(content);
+        int b = getComposingSpanEnd(content);
+
+        if (DEBUG) Log.v(TAG, "Composing span: " + a + " to " + b);
+        
+        if (b < a) {
+            int tmp = a;
+            a = b;
+            b = tmp;
+        }
+
+        if (a != -1 && b != -1) {
+            removeComposingSpans(content);
+        } else {
+            a = Selection.getSelectionStart(content);
+            b = Selection.getSelectionEnd(content);
+            if (a >=0 && b>= 0 && a != b) {
+                if (b < a) {
+                    int tmp = a;
+                    a = b;
+                    b = tmp;
+                }
+            }
+        }
+
+        if (composing) {
+            Spannable sp = null;
+            if (!(text instanceof Spannable)) {
+                sp = new SpannableStringBuilder(text);
+                text = sp;
+                if (mDefaultComposingSpans == null) {
+                    Context context;
+                    if (mTargetView != null) {
+                        context = mTargetView.getContext();
+                    } else if (mIMM.mServedView != null) {
+                        context = mIMM.mServedView.getContext();
+                    } else {
+                        context = null;
+                    }
+                    if (context != null) {
+                        TypedArray ta = context.getTheme()
+                                .obtainStyledAttributes(new int[] {
+                                        com.android.internal.R.attr.candidatesTextStyleSpans
+                                });
+                        CharSequence style = ta.getText(0);
+                        ta.recycle();
+                        if (style != null && style instanceof Spanned) {
+                            mDefaultComposingSpans = ((Spanned)style).getSpans(
+                                    0, style.length(), Object.class);
+                        }
+                    }
+                }
+                if (mDefaultComposingSpans != null) {
+                    for (int i = 0; i < mDefaultComposingSpans.length; ++i) {
+                        sp.setSpan(mDefaultComposingSpans[i], 0, sp.length(),
+                                Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
+                    }
+                }
+            } else {
+                sp = (Spannable)text;
+            }
+            setComposingSpans(sp);
+        }
+        
+        if (DEBUG) Log.v(TAG, "Replacing from " + a + " to " + b + " with \""
+                + text + "\", composing=" + composing
+                + ", type=" + text.getClass().getCanonicalName());
+        
+        if (DEBUG) {
+            LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+            lp.println("Current text:");
+            TextUtils.dumpSpans(content, lp, "  ");
+            lp.println("Composing text:");
+            TextUtils.dumpSpans(text, lp, "  ");
+        }
+        
+        // Position the cursor appropriately, so that after replacing the
+        // desired range of text it will be located in the correct spot.
+        // This allows us to deal with filters performing edits on the text
+        // we are providing here.
+        if (newCursorPosition > 0) {
+            newCursorPosition += b - 1;
+        } else {
+            newCursorPosition += a;
+        }
+        if (newCursorPosition < 0) newCursorPosition = 0;
+        if (newCursorPosition > content.length())
+            newCursorPosition = content.length();
+        Selection.setSelection(content, newCursorPosition);
+
+        content.replace(a, b, text);
+        
+        if (DEBUG) {
+            LogPrinter lp = new LogPrinter(Log.VERBOSE, TAG);
+            lp.println("Final text:");
+            TextUtils.dumpSpans(content, lp, "  ");
+        }
+        
+        endBatchEdit();
+    }
+}
diff --git a/core/java/android/view/inputmethod/CompletionInfo.aidl b/core/java/android/view/inputmethod/CompletionInfo.aidl
new file mode 100644
index 0000000..e601054
--- /dev/null
+++ b/core/java/android/view/inputmethod/CompletionInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable CompletionInfo;
diff --git a/core/java/android/view/inputmethod/CompletionInfo.java b/core/java/android/view/inputmethod/CompletionInfo.java
new file mode 100644
index 0000000..3a8fe72
--- /dev/null
+++ b/core/java/android/view/inputmethod/CompletionInfo.java
@@ -0,0 +1,131 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information about a single text completion that an editor has reported to
+ * an input method.
+ */
+public final class CompletionInfo implements Parcelable {
+    static final String TAG = "CompletionInfo";
+    
+    final long mId;
+    final int mPosition;
+    final CharSequence mText;
+    final CharSequence mLabel;
+    
+    /**
+     * Create a simple completion with just text, no label.
+     */
+    public CompletionInfo(long id, int index, CharSequence text) {
+        mId = id;
+        mPosition = index;
+        mText = text;
+        mLabel = null;
+    }
+
+    /**
+     * Create a full completion with both text and label.
+     */
+    public CompletionInfo(long id, int index, CharSequence text, CharSequence label) {
+        mId = id;
+        mPosition = index;
+        mText = text;
+        mLabel = label;
+    }
+
+    CompletionInfo(Parcel source) {
+        mId = source.readLong();
+        mPosition = source.readInt();
+        mText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+        mLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+    }
+    
+    /**
+     * Return the abstract identifier for this completion, typically
+     * corresponding to the id associated with it in the original adapter.
+     */
+    public long getId() {
+        return mId;
+    }
+    
+    /**
+     * Return the original position of this completion, typically
+     * corresponding to its position in the original adapter.
+     */
+    public int getPosition() {
+        return mPosition;
+    }
+    
+    /**
+     * Return the actual text associated with this completion.  This is the
+     * real text that will be inserted into the editor if the user selects it.
+     */
+    public CharSequence getText() {
+        return mText;
+    }
+    
+    /**
+     * Return the user-visible label for the completion, or null if the plain
+     * text should be shown.  If non-null, this will be what the user sees as
+     * the completion option instead of the actual text.
+     */
+    public CharSequence getLabel() {
+        return mLabel;
+    }
+    
+    @Override
+    public String toString() {
+        return "CompletionInfo{#" + mPosition + " \"" + mText
+                + "\" id=" + mId + " label=" + mLabel + "}";
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeLong(mId);
+        dest.writeInt(mPosition);
+        TextUtils.writeToParcel(mText, dest, flags);
+        TextUtils.writeToParcel(mLabel, dest, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<CompletionInfo> CREATOR
+            = new Parcelable.Creator<CompletionInfo>() {
+        public CompletionInfo createFromParcel(Parcel source) {
+            return new CompletionInfo(source);
+        }
+
+        public CompletionInfo[] newArray(int size) {
+            return new CompletionInfo[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/inputmethod/EditorInfo.aidl b/core/java/android/view/inputmethod/EditorInfo.aidl
new file mode 100644
index 0000000..48068f0
--- /dev/null
+++ b/core/java/android/view/inputmethod/EditorInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable EditorInfo;
diff --git a/core/java/android/view/inputmethod/EditorInfo.java b/core/java/android/view/inputmethod/EditorInfo.java
new file mode 100644
index 0000000..0405371
--- /dev/null
+++ b/core/java/android/view/inputmethod/EditorInfo.java
@@ -0,0 +1,263 @@
+package android.view.inputmethod;
+
+import android.os.Bundle;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.InputType;
+import android.text.TextUtils;
+import android.util.Printer;
+
+/**
+ * An EditorInfo describes several attributes of a text editing object
+ * that an input method is communicating with (typically an EditText), most
+ * importantly the type of text content it contains.
+ */
+public class EditorInfo implements InputType, Parcelable {
+    /**
+     * The content type of the text box, whose bits are defined by
+     * {@link InputType}.
+     * 
+     * @see InputType
+     * @see #TYPE_MASK_CLASS
+     * @see #TYPE_MASK_VARIATION
+     * @see #TYPE_MASK_FLAGS
+     */
+    public int inputType = TYPE_NULL;
+
+    /**
+     * Set of bits in {@link #imeOptions} that provide alternative actions
+     * associated with the "enter" key.  This both helps the IME provide
+     * better feedback about what the enter key will do, and also allows it
+     * to provide alternative mechanisms for providing that command.
+     */
+    public static final int IME_MASK_ACTION = 0x000000ff;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: there is no special action
+     * associated with this editor.
+     */
+    public static final int IME_ACTION_NONE = 0x00000000;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: the action key performs a "go"
+     * operation to take the user to the target of the text they typed.
+     * Typically used, for example, when entering a URL.
+     */
+    public static final int IME_ACTION_GO = 0x00000001;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: the action key performs a "search"
+     * operation, taking the user to the results of searching for the text
+     * the have typed (in whatever context is appropriate).
+     */
+    public static final int IME_ACTION_SEARCH = 0x00000002;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: the action key performs a "send"
+     * operation, delivering the text to its target.  This is typically used
+     * when composing a message.
+     */
+    public static final int IME_ACTION_SEND = 0x00000003;
+    
+    /**
+     * Bits of {@link #IME_MASK_ACTION}: the action key performs a "next"
+     * operation, taking the user to the next field that will accept text.
+     */
+    public static final int IME_ACTION_NEXT = 0x00000004;
+    
+    /**
+     * Flag of {@link #imeOptions}: used in conjunction with
+     * {@link #IME_MASK_ACTION}, this indicates that the action should not
+     * be available in-line as the same as a "enter" key.  Typically this is
+     * because the action has such a significant impact or is not recoverable
+     * enough that accidentally hitting it should be avoided, such as sending
+     * a message.
+     */
+    public static final int IME_FLAG_NO_ENTER_ACTION = 0x40000000;
+    
+    /**
+     * Generic non-special type for {@link #imeOptions}.
+     */
+    public static final int IME_NORMAL = 0x00000000;
+    
+    /**
+     * Special code for when the ime option has been undefined.  This is not
+     * used with the EditorInfo structure, but can be used elsewhere.
+     */
+    public static final int IME_UNDEFINED = 0x80000000;
+    
+    /**
+     * Extended type information for the editor, to help the IME better
+     * integrate with it.
+     */
+    public int imeOptions = IME_NORMAL;
+    
+    /**
+     * A string supplying additional information options that are
+     * private to a particular IME implementation.  The string must be
+     * scoped to a package owned by the implementation, to ensure there are
+     * no conflicts between implementations, but other than that you can put
+     * whatever you want in it to communicate with the IME.  For example,
+     * you could have a string that supplies an argument like
+     * <code>"com.example.myapp.SpecialMode=3"</code>.  This field is can be
+     * filled in from the {@link android.R.attr#privateImeOptions}
+     * attribute of a TextView.
+     */
+    public String privateImeOptions = null;
+    
+    /**
+     * In some cases an IME may be able to display an arbitrary label for
+     * a command the user can perform, which you can specify here.  You can
+     * not count on this being used.
+     */
+    public CharSequence actionLabel = null;
+    
+    /**
+     * If {@link #actionLabel} has been given, this is the id for that command
+     * when the user presses its button that is delivered back with
+     * {@link InputConnection#performEditorAction(int)
+     * InputConnection.performEditorAction()}.
+     */
+    public int actionId = 0;
+    
+    /**
+     * The text offset of the start of the selection at the time editing
+     * began; -1 if not known.
+     */
+    public int initialSelStart = -1;
+    
+    /**
+     * The text offset of the end of the selection at the time editing
+     * began; -1 if not known.
+     */
+    public int initialSelEnd = -1;
+    
+    /**
+     * The capitalization mode of the first character being edited in the
+     * text.  Values may be any combination of
+     * {@link TextUtils#CAP_MODE_CHARACTERS TextUtils.CAP_MODE_CHARACTERS},
+     * {@link TextUtils#CAP_MODE_WORDS TextUtils.CAP_MODE_WORDS}, and
+     * {@link TextUtils#CAP_MODE_SENTENCES TextUtils.CAP_MODE_SENTENCES}, though
+     * you should generally just take a non-zero value to mean start out in
+     * caps mode.
+     */
+    public int initialCapsMode = 0;
+    
+    /**
+     * The "hint" text of the text view, typically shown in-line when the
+     * text is empty to tell the user what to enter.
+     */
+    public CharSequence hintText;
+    
+    /**
+     * A label to show to the user describing the text they are writing.
+     */
+    public CharSequence label;
+    
+    /**
+     * Name of the package that owns this editor.
+     */
+    public String packageName;
+    
+    /**
+     * Identifier for the editor's field.  This is optional, and may be
+     * 0.  By default it is filled in with the result of
+     * {@link android.view.View#getId() View.getId()} on the View that
+     * is being edited.
+     */
+    public int fieldId;
+    
+    /**
+     * Additional name for the editor's field.  This can supply additional
+     * name information for the field.  By default it is null.  The actual
+     * contents have no meaning.
+     */
+    public String fieldName;
+    
+    /**
+     * Any extra data to supply to the input method.  This is for extended
+     * communication with specific input methods; the name fields in the
+     * bundle should be scoped (such as "com.mydomain.im.SOME_FIELD") so
+     * that they don't conflict with others.  This field is can be
+     * filled in from the {@link android.R.attr#editorExtras}
+     * attribute of a TextView.
+     */
+    public Bundle extras;
+    
+    /**
+     * Write debug output of this object.
+     */
+    public void dump(Printer pw, String prefix) {
+        pw.println(prefix + "inputType=0x" + Integer.toHexString(inputType)
+                + " imeOptions=0x" + Integer.toHexString(imeOptions)
+                + " privateImeOptions=" + privateImeOptions);
+        pw.println(prefix + "actionLabel=" + actionLabel
+                + " actionId=" + actionId);
+        pw.println(prefix + "initialSelStart=" + initialSelStart
+                + " initialSelEnd=" + initialSelEnd
+                + " initialCapsMode=0x"
+                + Integer.toHexString(initialCapsMode));
+        pw.println(prefix + "hintText=" + hintText
+                + " label=" + label);
+        pw.println(prefix + "packageName=" + packageName
+                + " fieldId=" + fieldId
+                + " fieldName=" + fieldName);
+        pw.println(prefix + "extras=" + extras);
+    }
+    
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(inputType);
+        dest.writeInt(imeOptions);
+        dest.writeString(privateImeOptions);
+        TextUtils.writeToParcel(actionLabel, dest, flags);
+        dest.writeInt(actionId);
+        dest.writeInt(initialSelStart);
+        dest.writeInt(initialSelEnd);
+        dest.writeInt(initialCapsMode);
+        TextUtils.writeToParcel(hintText, dest, flags);
+        TextUtils.writeToParcel(label, dest, flags);
+        dest.writeString(packageName);
+        dest.writeInt(fieldId);
+        dest.writeString(fieldName);
+        dest.writeBundle(extras);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<EditorInfo> CREATOR = new Parcelable.Creator<EditorInfo>() {
+        public EditorInfo createFromParcel(Parcel source) {
+            EditorInfo res = new EditorInfo();
+            res.inputType = source.readInt();
+            res.imeOptions = source.readInt();
+            res.privateImeOptions = source.readString();
+            res.actionLabel = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            res.actionId = source.readInt();
+            res.initialSelStart = source.readInt();
+            res.initialSelEnd = source.readInt();
+            res.initialCapsMode = source.readInt();
+            res.hintText = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            res.label = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            res.packageName = source.readString();
+            res.fieldId = source.readInt();
+            res.fieldName = source.readString();
+            res.extras = source.readBundle();
+            return res;
+        }
+
+        public EditorInfo[] newArray(int size) {
+            return new EditorInfo[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+
+}
diff --git a/core/java/android/view/inputmethod/ExtractedText.aidl b/core/java/android/view/inputmethod/ExtractedText.aidl
new file mode 100644
index 0000000..95e56d7
--- /dev/null
+++ b/core/java/android/view/inputmethod/ExtractedText.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable ExtractedText;
diff --git a/core/java/android/view/inputmethod/ExtractedText.java b/core/java/android/view/inputmethod/ExtractedText.java
new file mode 100644
index 0000000..e5d3cae
--- /dev/null
+++ b/core/java/android/view/inputmethod/ExtractedText.java
@@ -0,0 +1,102 @@
+package android.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information about text that has been extracted for use by an input method.
+ */
+public class ExtractedText implements Parcelable {
+    /**
+     * The text that has been extracted.
+     */
+    public CharSequence text;
+
+    /**
+     * The offset in the overall text at which the extracted text starts.
+     */
+    public int startOffset;
+    
+    /**
+     * If the content is a report of a partial text change, this is the
+     * offset where the change starts and it runs until
+     * {@link #partialEndOffset}.  If the content is the full text, this
+     * field is -1.
+     */
+    public int partialStartOffset;
+    
+    /**
+     * If the content is a report of a partial text change, this is the offset
+     * where the change ends.  Note that the actual text may be larger or
+     * smaller than the difference between this and {@link #partialEndOffset},
+     * meaning a reduction or increase, respectively, in the total text.
+     */
+    public int partialEndOffset;
+    
+    /**
+     * The offset where the selection currently starts within the extracted
+     * text.  The real selection start position is at
+     * <var>startOffset</var>+<var>selectionStart</var>.
+     */
+    public int selectionStart;
+    
+    /**
+     * The offset where the selection currently ends within the extracted
+     * text.  The real selection end position is at
+     * <var>startOffset</var>+<var>selectionEnd</var>.
+     */
+    public int selectionEnd;
+    
+    /**
+     * Bit for {@link #flags}: set if the text being edited can only be on
+     * a single line.
+     */
+    public static final int FLAG_SINGLE_LINE = 0x0001;
+    
+    /**
+     * Additional bit flags of information about the edited text.
+     */
+    public int flags;
+    
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        TextUtils.writeToParcel(text, dest, flags);
+        dest.writeInt(startOffset);
+        dest.writeInt(partialStartOffset);
+        dest.writeInt(partialEndOffset);
+        dest.writeInt(selectionStart);
+        dest.writeInt(selectionEnd);
+        dest.writeInt(flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<ExtractedText> CREATOR = new Parcelable.Creator<ExtractedText>() {
+        public ExtractedText createFromParcel(Parcel source) {
+            ExtractedText res = new ExtractedText();
+            res.text = TextUtils.CHAR_SEQUENCE_CREATOR.createFromParcel(source);
+            res.startOffset = source.readInt();
+            res.partialStartOffset = source.readInt();
+            res.partialEndOffset = source.readInt();
+            res.selectionStart = source.readInt();
+            res.selectionEnd = source.readInt();
+            res.flags = source.readInt();
+            return res;
+        }
+
+        public ExtractedText[] newArray(int size) {
+            return new ExtractedText[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.aidl b/core/java/android/view/inputmethod/ExtractedTextRequest.aidl
new file mode 100644
index 0000000..c69acc7
--- /dev/null
+++ b/core/java/android/view/inputmethod/ExtractedTextRequest.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable ExtractedTextRequest;
diff --git a/core/java/android/view/inputmethod/ExtractedTextRequest.java b/core/java/android/view/inputmethod/ExtractedTextRequest.java
new file mode 100644
index 0000000..e84b094
--- /dev/null
+++ b/core/java/android/view/inputmethod/ExtractedTextRequest.java
@@ -0,0 +1,70 @@
+package android.view.inputmethod;
+
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Description of what an input method would like from an application when
+ * extract text from its input editor.
+ */
+public class ExtractedTextRequest implements Parcelable {
+    /**
+     * Arbitrary integer that can be supplied in the request, which will be
+     * delivered back when reporting updates.
+     */
+    public int token;
+    
+    /**
+     * Additional request flags, having the same possible values as the
+     * flags parameter of {@link InputConnection#getTextBeforeCursor
+     * InputConnection.getTextBeforeCursor()}.
+     */
+    public int flags;
+    
+    /**
+     * Hint for the maximum number of lines to return.
+     */
+    public int hintMaxLines;
+    
+    /**
+     * Hint for the maximum number of characters to return.
+     */
+    public int hintMaxChars;
+    
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeInt(token);
+        dest.writeInt(this.flags);
+        dest.writeInt(hintMaxLines);
+        dest.writeInt(hintMaxChars);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<ExtractedTextRequest> CREATOR
+            = new Parcelable.Creator<ExtractedTextRequest>() {
+        public ExtractedTextRequest createFromParcel(Parcel source) {
+            ExtractedTextRequest res = new ExtractedTextRequest();
+            res.token = source.readInt();
+            res.flags = source.readInt();
+            res.hintMaxLines = source.readInt();
+            res.hintMaxChars = source.readInt();
+            return res;
+        }
+
+        public ExtractedTextRequest[] newArray(int size) {
+            return new ExtractedTextRequest[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputBinding.aidl b/core/java/android/view/inputmethod/InputBinding.aidl
new file mode 100644
index 0000000..ea09d8b
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputBinding.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable InputBinding;
diff --git a/core/java/android/view/inputmethod/InputBinding.java b/core/java/android/view/inputmethod/InputBinding.java
new file mode 100644
index 0000000..f4209ef
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputBinding.java
@@ -0,0 +1,152 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.IBinder;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.text.TextUtils;
+
+/**
+ * Information given to an {@link InputMethod} about a client connecting
+ * to it.
+ */
+public final class InputBinding implements Parcelable {
+    static final String TAG = "InputBinding";
+    
+    /**
+     * The connection back to the client.
+     */
+    final InputConnection mConnection;
+    
+    /**
+     * A remotable token for the connection back to the client.
+     */
+    final IBinder mConnectionToken;
+    
+    /**
+     * The UID where this binding came from.
+     */
+    final int mUid;
+    
+    /**
+     * The PID where this binding came from.
+     */
+    final int mPid;
+    
+    /**
+     * Constructor.
+     * 
+     * @param conn The interface for communicating back with the application.
+     * @param connToken A remoteable token for communicating across processes.
+     * @param uid The user id of the client of this binding.
+     * @param pid The process id of where the binding came from.
+     */
+    public InputBinding(InputConnection conn, IBinder connToken,
+            int uid, int pid) {
+        mConnection = conn;
+        mConnectionToken = connToken;
+        mUid = uid;
+        mPid = pid;
+    }
+
+    /**
+     * Constructor from an existing InputBinding taking a new local input
+     * connection interface.
+     * 
+     * @param conn The new connection interface.
+     * @param binding Existing binding to copy.
+     */
+    public InputBinding(InputConnection conn, InputBinding binding) {
+        mConnection = conn;
+        mConnectionToken = binding.getConnectionToken();
+        mUid = binding.getUid();
+        mPid = binding.getPid();
+    }
+
+    InputBinding(Parcel source) {
+        mConnection = null;
+        mConnectionToken = source.readStrongBinder();
+        mUid = source.readInt();
+        mPid = source.readInt();
+    }
+    
+    /**
+     * Return the connection for interacting back with the application.
+     */
+    public InputConnection getConnection() {
+        return mConnection;
+    }
+    
+    /**
+     * Return the token for the connection back to the application.  You can
+     * not use this directly, it must be converted to a {@link InputConnection}
+     * for you.
+     */
+    public IBinder getConnectionToken() {
+        return mConnectionToken;
+    }
+    
+    /**
+     * Return the user id of the client associated with this binding.
+     */
+    public int getUid() {
+        return mUid;
+    }
+    
+    /**
+     * Return the process id where this binding came from.
+     */
+    public int getPid() {
+        return mPid;
+    }
+    
+    @Override
+    public String toString() {
+        return "InputBinding{" + mConnectionToken
+                + " / uid " + mUid + " / pid " + mPid + "}";
+    }
+
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeStrongBinder(mConnectionToken);
+        dest.writeInt(mUid);
+        dest.writeInt(mPid);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<InputBinding> CREATOR = new Parcelable.Creator<InputBinding>() {
+        public InputBinding createFromParcel(Parcel source) {
+            return new InputBinding(source);
+        }
+
+        public InputBinding[] newArray(int size) {
+            return new InputBinding[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputConnection.java b/core/java/android/view/inputmethod/InputConnection.java
new file mode 100644
index 0000000..32cce35
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputConnection.java
@@ -0,0 +1,320 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.os.Bundle;
+import android.text.Spanned;
+import android.view.KeyCharacterMap;
+import android.view.KeyEvent;
+
+/**
+ * The InputConnection interface is the communication channel from an
+ * {@link InputMethod} back to the application that is receiving its input. It
+ * is used to perform such things as reading text around the cursor,
+ * committing text to the text box, and sending raw key events to the application.
+ * 
+ * <p>Implementations of this interface should generally be done by
+ * subclassing {@link BaseInputConnection}.
+ */
+public interface InputConnection {
+    /**
+     * Flag for use with {@link #getTextAfterCursor} and
+     * {@link #getTextBeforeCursor} to have style information returned along
+     * with the text.  If not set, you will receive only the raw text.  If
+     * set, you may receive a complex CharSequence of both text and style
+     * spans.
+     */
+    static final int GET_TEXT_WITH_STYLES = 0x0001;
+    
+    /**
+     * Flag for use with {@link #getExtractedText} to indicate you would
+     * like to receive updates when the extracted text changes.
+     */
+    public static final int GET_EXTRACTED_TEXT_MONITOR = 0x0001;
+    
+    /**
+     * Get <var>n</var> characters of text before the current cursor position.
+     * 
+     * <p>This method may fail either if the input connection has become invalid
+     * (such as its process crashing) or the client is taking too long to
+     * respond with the text (it is given a couple seconds to return).
+     * In either case, a null is returned.
+     * 
+     * @param n The expected length of the text.
+     * @param flags Supplies additional options controlling how the text is
+     * returned.  May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
+     * 
+     * @return Returns the text before the cursor position; the length of the
+     * returned text might be less than <var>n</var>.
+     */
+    public CharSequence getTextBeforeCursor(int n, int flags);
+
+    /**
+     * Get <var>n</var> characters of text after the current cursor position.
+     * 
+     * <p>This method may fail either if the input connection has become invalid
+     * (such as its process crashing) or the client is taking too long to
+     * respond with the text (it is given a couple seconds to return).
+     * In either case, a null is returned.
+     * 
+     * @param n The expected length of the text.
+     * @param flags Supplies additional options controlling how the text is
+     * returned.  May be either 0 or {@link #GET_TEXT_WITH_STYLES}.
+     * 
+     * @return Returns the text after the cursor position; the length of the
+     * returned text might be less than <var>n</var>.
+     */
+    public CharSequence getTextAfterCursor(int n, int flags);
+
+    /**
+     * Retrieve the current capitalization mode in effect at the current
+     * cursor position in the text.  See
+     * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode} for
+     * more information.
+     * 
+     * <p>This method may fail either if the input connection has become invalid
+     * (such as its process crashing) or the client is taking too long to
+     * respond with the text (it is given a couple seconds to return).
+     * In either case, a 0 is returned.
+     * 
+     * @param reqModes The desired modes to retrieve, as defined by
+     * {@link android.text.TextUtils#getCapsMode TextUtils.getCapsMode}.  These
+     * constants are defined so that you can simply pass the current
+     * {@link EditorInfo#inputType TextBoxAttribute.contentType} value
+     * directly in to here.
+     * 
+     * @return Returns the caps mode flags that are in effect.
+     */
+    public int getCursorCapsMode(int reqModes);
+    
+    /**
+     * Retrieve the current text in the input connection's editor, and monitor
+     * for any changes to it.  This function returns with the current text,
+     * and optionally the input connection can send updates to the
+     * input method when its text changes.
+     * 
+     * <p>This method may fail either if the input connection has become invalid
+     * (such as its process crashing) or the client is taking too long to
+     * respond with the text (it is given a couple seconds to return).
+     * In either case, a null is returned.
+     * 
+     * @param request Description of how the text should be returned.
+     * @param flags Additional options to control the client, either 0 or
+     * {@link #GET_EXTRACTED_TEXT_MONITOR}.
+     * 
+     * @return Returns an ExtractedText object describing the state of the
+     * text view and containing the extracted text itself.
+     */
+    public ExtractedText getExtractedText(ExtractedTextRequest request,
+            int flags);
+
+    /**
+     * Delete <var>leftLength</var> characters of text before the current cursor
+     * position, and delete <var>rightLength</var> characters of text after the
+     * current cursor position, excluding composing text.
+     * 
+     * @param leftLength The number of characters to be deleted before the
+     *        current cursor position.
+     * @param rightLength The number of characters to be deleted after the
+     *        current cursor position.
+     *        
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    boolean deleteSurroundingText(int leftLength, int rightLength);
+
+    /**
+     * Set composing text around the current cursor position with the given text,
+     * and set the new cursor position.  Any composing text set previously will
+     * be removed automatically.
+     * 
+     * @param text The composing text with styles if necessary. If no style
+     *        object attached to the text, the default style for composing text
+     *        is used. See {#link android.text.Spanned} for how to attach style
+     *        object to the text. {#link android.text.SpannableString} and
+     *        {#link android.text.SpannableStringBuilder} are two
+     *        implementations of the interface {#link android.text.Spanned}.
+     * @param newCursorPosition The new cursor position around the text.  If
+     *        > 0, this is relative to the end of the text - 1; if <= 0, this
+     *        is relative to the start of the text.  So a value of 1 will
+     *        always advance you to the position after the full text being
+     *        inserted.  Note that this means you can't position the cursor
+     *        within the text, because the editor can make modifications to
+     *        the text you are providing so it is not possible to correctly
+     *        specify locations there.
+     * 
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean setComposingText(CharSequence text, int newCursorPosition);
+
+    /**
+     * Have the text editor finish whatever composing text is currently
+     * active.  This simply leaves the text as-is, removing any special
+     * composing styling or other state that was around it.  The cursor
+     * position remains unchanged.
+     */
+    public boolean finishComposingText();
+    
+    /**
+     * Commit text to the text box and set the new cursor position.
+     * Any composing text set previously will be removed
+     * automatically.
+     * 
+     * @param text The committed text.
+     * @param newCursorPosition The new cursor position around the text.  If
+     *        > 0, this is relative to the end of the text - 1; if <= 0, this
+     *        is relative to the start of the text.  So a value of 1 will
+     *        always advance you to the position after the full text being
+     *        inserted.  Note that this means you can't position the cursor
+     *        within the text, because the editor can make modifications to
+     *        the text you are providing so it is not possible to correctly
+     *        specify locations there.
+     * 
+     *        
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean commitText(CharSequence text, int newCursorPosition);
+
+    /**
+     * Commit a completion the user has selected from the possible ones
+     * previously reported to {@link InputMethodSession#displayCompletions
+     * InputMethodSession.displayCompletions()}.  This will result in the
+     * same behavior as if the user had selected the completion from the
+     * actual UI.
+     * 
+     * @param text The committed completion.
+     *        
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean commitCompletion(CompletionInfo text);
+
+    /**
+     * Set the selection of the text editor.  To set the cursor position,
+     * start and end should have the same value.
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean setSelection(int start, int end);
+    
+    /**
+     * Have the editor perform an action it has said it can do.
+     * 
+     * @param editorAction This must be one of the action constants for
+     * {@link EditorInfo#imeOptions EditorInfo.editorType}, such as
+     * {@link EditorInfo#IME_ACTION_GO EditorInfo.EDITOR_ACTION_GO}.
+     * 
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean performEditorAction(int editorAction);
+    
+    /**
+     * Perform a context menu action on the field.  The given id may be one of:
+     * {@link android.R.id#selectAll},
+     * {@link android.R.id#startSelectingText}, {@link android.R.id#stopSelectingText},
+     * {@link android.R.id#cut}, {@link android.R.id#copy},
+     * {@link android.R.id#paste}, {@link android.R.id#copyUrl},
+     * or {@link android.R.id#switchInputMethod}
+     */
+    public boolean performContextMenuAction(int id);
+    
+    /**
+     * Tell the editor that you are starting a batch of editor operations.
+     * The editor will try to avoid sending you updates about its state
+     * until {@link #endBatchEdit} is called.
+     */
+    public boolean beginBatchEdit();
+    
+    /**
+     * Tell the editor that you are done with a batch edit previously
+     * initiated with {@link #endBatchEdit}.
+     */
+    public boolean endBatchEdit();
+    
+    /**
+     * Send a key event to the process that is currently attached through
+     * this input connection.  The event will be dispatched like a normal
+     * key event, to the currently focused; this generally is the view that
+     * is providing this InputConnection, but due to the asynchronous nature
+     * of this protocol that can not be guaranteed and the focus may have
+     * changed by the time the event is received.
+     * 
+     * <p>
+     * This method can be used to send key events to the application. For
+     * example, an on-screen keyboard may use this method to simulate a hardware
+     * keyboard. There are three types of standard keyboards, numeric (12-key),
+     * predictive (20-key) and ALPHA (QWERTY). You can specify the keyboard type
+     * by specify the device id of the key event.
+     * 
+     * <p>
+     * You will usually want to set the flag
+     * {@link KeyEvent#FLAG_SOFT_KEYBOARD KeyEvent.FLAG_SOFT_KEYBOARD} on all
+     * key event objects you give to this API; the flag will not be set
+     * for you.
+     * 
+     * @param event The key event.
+     *        
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     * 
+     * @see KeyEvent
+     * @see KeyCharacterMap#NUMERIC
+     * @see KeyCharacterMap#PREDICTIVE
+     * @see KeyCharacterMap#ALPHA
+     */
+    public boolean sendKeyEvent(KeyEvent event);
+
+    /**
+     * Clear the given meta key pressed states in the given input connection.
+     * 
+     * @param states The states to be cleared, may be one or more bits as
+     * per {@link KeyEvent#getMetaState() KeyEvent.getMetaState()}.
+     * 
+     * @return Returns true on success, false if the input connection is no longer
+     * valid.
+     */
+    public boolean clearMetaKeyStates(int states);
+    
+    /**
+     * Called by the IME to tell the client when it switches between fullscreen
+     * and normal modes.  This will normally be called for you by the standard
+     * implementation of {@link android.inputmethodservice.InputMethodService}.
+     */
+    public boolean reportFullscreenMode(boolean enabled);
+    
+    /**
+     * API to send private commands from an input method to its connected
+     * editor.  This can be used to provide domain-specific features that are
+     * only known between certain input methods and their clients.  Note that
+     * because the InputConnection protocol is asynchronous, you have no way
+     * to get a result back or know if the client understood the command; you
+     * can use the information in {@link EditorInfo} to determine if
+     * a client supports a particular command.
+     * 
+     * @param action Name of the command to be performed.  This <em>must</em>
+     * be a scoped name, i.e. prefixed with a package name you own, so that
+     * different developers will not create conflicting commands.
+     * @param data Any data to include with the command.
+     * @return Returns true if the command was sent (whether or not the
+     * associated editor understood it), false if the input connection is no longer
+     * valid.
+     */
+    public boolean performPrivateCommand(String action, Bundle data);
+}
diff --git a/core/java/android/view/inputmethod/InputMethod.java b/core/java/android/view/inputmethod/InputMethod.java
new file mode 100644
index 0000000..740dca8
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethod.java
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.inputmethodservice.InputMethodService;
+import android.os.IBinder;
+
+/**
+ * The InputMethod interface represents an input method which can generate key
+ * events and text, such as digital, email addresses, CJK characters, other
+ * language characters, and etc., while handling various input events, and send
+ * the text back to the application that requests text input.  See
+ * {@link InputMethodManager} for more general information about the
+ * architecture.
+ *
+ * <p>Applications will not normally use this interface themselves, instead
+ * relying on the standard interaction provided by
+ * {@link android.widget.TextView} and {@link android.widget.EditText}.
+ * 
+ * <p>Those implementing input methods should normally do so by deriving from
+ * {@link InputMethodService} or one of its subclasses.  When implementing
+ * an input method, the service component containing it must also supply
+ * a {@link #SERVICE_META_DATA} meta-data field, referencing an XML resource
+ * providing details about the input method.  All input methods also must
+ * require that clients hold the
+ * {@link android.Manifest.permission#BIND_INPUT_METHOD} in order to interact
+ * with the service; if this is not required, the system will not use that
+ * input method, because it can not trust that it is not compromised.
+ * 
+ * <p>The InputMethod interface is actually split into two parts: the interface
+ * here is the top-level interface to the input method, providing all
+ * access to it, which only the system can access (due to the BIND_INPUT_METHOD
+ * permission requirement).  In addition its method
+ * {@link #createSession(android.view.inputmethod.InputMethod.SessionCallback)}
+ * can be called to instantate a secondary {@link InputMethodSession} interface
+ * which is what clients use to communicate with the input method.
+ */
+public interface InputMethod {
+    /**
+     * This is the interface name that a service implementing an input
+     * method should say that it supports -- that is, this is the action it
+     * uses for its intent filter.  (Note: this name is used because this
+     * interface should be moved to the view package.)
+     */
+    public static final String SERVICE_INTERFACE = "android.view.InputMethod";
+    
+    /**
+     * Name under which an InputMethod service component publishes information
+     * about itself.  This meta-data must reference an XML resource containing
+     * an
+     * <code>&lt;{@link android.R.styleable#InputMethod input-method}&gt;</code>
+     * tag.
+     */
+    public static final String SERVICE_META_DATA = "android.view.im";
+    
+    public interface SessionCallback {
+        public void sessionCreated(InputMethodSession session);
+    }
+    
+    /**
+     * Called first thing after an input method is created, this supplies a
+     * unique token for the session it has with the system service.  It is
+     * needed to identify itself with the service to validate its operations.
+     * This token <strong>must not</strong> be passed to applications, since
+     * it grants special priviledges that should not be given to applications.
+     * 
+     * <p>Note: to protect yourself from malicious clients, you should only
+     * accept the first token given to you.  Any after that may come from the
+     * client.
+     */
+    public void attachToken(IBinder token);
+    
+    /**
+     * Bind a new application environment in to the input method, so that it
+     * can later start and stop input processing.
+     * Typically this method is called when this input method is enabled in an
+     * application for the first time.
+     * 
+     * @param binding Information about the application window that is binding
+     * to the input method.
+     * 
+     * @see InputBinding
+     * @see #unbindInput()
+     */
+    public void bindInput(InputBinding binding);
+
+    /**
+     * Unbind an application environment, called when the information previously
+     * set by {@link #bindInput} is no longer valid for this input method.
+     * 
+     * <p>
+     * Typically this method is called when the application changes to be
+     * non-foreground.
+     */
+    public void unbindInput();
+
+    /**
+     * This method is called when the application starts to receive text and it
+     * is ready for this input method to process received events and send result
+     * text back to the application.
+     * 
+     * @param inputConnection Optional specific input connection for
+     * communicating with the text box; if null, you should use the generic
+     * bound input connection.
+     * @param info Information about the text box (typically, an EditText)
+     *        that requests input.
+     * 
+     * @see EditorInfo
+     */
+    public void startInput(InputConnection inputConnection, EditorInfo info);
+
+    /**
+     * This method is called when the state of this input method needs to be
+     * reset.
+     * 
+     * <p>
+     * Typically, this method is called when the input focus is moved from one
+     * text box to another.
+     * 
+     * @param inputConnection Optional specific input connection for
+     * communicating with the text box; if null, you should use the generic
+     * bound input connection.
+     * @param attribute The attribute of the text box (typically, a EditText)
+     *        that requests input.
+     * 
+     * @see EditorInfo
+     */
+    public void restartInput(InputConnection inputConnection, EditorInfo attribute);
+
+    /**
+     * Create a new {@link InputMethodSession} that can be handed to client
+     * applications for interacting with the input method.  You can later
+     * use {@link #revokeSession(InputMethodSession)} to destroy the session
+     * so that it can no longer be used by any clients.
+     * 
+     * @param callback Interface that is called with the newly created session.
+     */
+    public void createSession(SessionCallback callback);
+    
+    /**
+     * Control whether a particular input method session is active.
+     * 
+     * @param session The {@link InputMethodSession} previously provided through
+     * SessionCallback.sessionCreated() that is to be changed.
+     */
+    public void setSessionEnabled(InputMethodSession session, boolean enabled);
+    
+    /**
+     * Disable and destroy a session that was previously created with
+     * {@link #createSession(android.view.inputmethod.InputMethod.SessionCallback)}.
+     * After this call, the given session interface is no longer active and
+     * calls on it will fail.
+     * 
+     * @param session The {@link InputMethodSession} previously provided through
+     * SessionCallback.sessionCreated() that is to be revoked.
+     */
+    public void revokeSession(InputMethodSession session);
+    
+    /**
+     * Flag for {@link #showSoftInput(int)}: this show has been explicitly
+     * requested by the user.  If not set, the system has decided it may be
+     * a good idea to show the input method based on a navigation operation
+     * in the UI.
+     */
+    public static final int SHOW_EXPLICIT = 0x00001;
+    
+    /**
+     * Flag for {@link #showSoftInput(int)}: this show has been forced to
+     * happen by the user.  If set, the input method should remain visible
+     * until deliberated dismissed by the user in its UI.
+     */
+    public static final int SHOW_FORCED = 0x00002;
+    
+    /**
+     * Request that any soft input part of the input method be shown to the user.
+     * 
+     * @param flags Provide additional information about the show request.
+     * Currently may be 0 or have the bit {@link #SHOW_EXPLICIT} set.
+     */
+    public void showSoftInput(int flags);
+    
+    /**
+     * Request that any soft input part of the input method be hidden from the user.
+     */
+    public void hideSoftInput();
+}
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.aidl b/core/java/android/view/inputmethod/InputMethodInfo.aidl
new file mode 100644
index 0000000..5f4d6b6
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodInfo.aidl
@@ -0,0 +1,19 @@
+/*
+ * Copyright (C) 2008 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package android.view.inputmethod;
+
+parcelable InputMethodInfo;
diff --git a/core/java/android/view/inputmethod/InputMethodInfo.java b/core/java/android/view/inputmethod/InputMethodInfo.java
new file mode 100644
index 0000000..4e98591
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodInfo.java
@@ -0,0 +1,291 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import org.xmlpull.v1.XmlPullParser;
+import org.xmlpull.v1.XmlPullParserException;
+
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.pm.ApplicationInfo;
+import android.content.pm.PackageManager;
+import android.content.pm.ResolveInfo;
+import android.content.pm.ServiceInfo;
+import android.content.res.TypedArray;
+import android.content.res.XmlResourceParser;
+import android.graphics.drawable.Drawable;
+import android.os.Parcel;
+import android.os.Parcelable;
+import android.util.AttributeSet;
+import android.util.Printer;
+import android.util.Xml;
+
+import java.io.IOException;
+
+/**
+ * This class is used to specify meta information of an input method.
+ */
+public final class InputMethodInfo implements Parcelable {
+    static final String TAG = "InputMethodMetaInfo";
+    
+    /**
+     * The Service that implements this input method component.
+     */
+    final ResolveInfo mService;
+    
+    /**
+     * The unique string Id to identify the input method.  This is generated
+     * from the input method component.
+     */
+    final String mId;
+
+    /**
+     * The input method setting activity's name, used by the system settings to
+     * launch the setting activity of this input method.
+     */
+    final String mSettingsActivityName;
+
+    /**
+     * The resource in the input method's .apk that holds a boolean indicating
+     * whether it should be considered the default input method for this
+     * system.  This is a resource ID instead of the final value so that it
+     * can change based on the configuration (in particular locale).
+     */
+    final int mIsDefaultResId;
+    
+    /**
+     * Constructor.
+     * 
+     * @param context The Context in which we are parsing the input method.
+     * @param service The ResolveInfo returned from the package manager about
+     * this input method's component.
+     */
+    public InputMethodInfo(Context context, ResolveInfo service)
+            throws XmlPullParserException, IOException {
+        mService = service;
+        ServiceInfo si = service.serviceInfo;
+        mId = new ComponentName(si.packageName, si.name).flattenToShortString();
+        
+        PackageManager pm = context.getPackageManager();
+        String settingsActivityComponent = null;
+        int isDefaultResId = 0;
+        
+        XmlResourceParser parser = null;
+        try {
+            parser = si.loadXmlMetaData(pm, InputMethod.SERVICE_META_DATA);
+            if (parser == null) {
+                throw new XmlPullParserException("No "
+                        + InputMethod.SERVICE_META_DATA + " meta-data");
+            }
+        
+            AttributeSet attrs = Xml.asAttributeSet(parser);
+            
+            int type;
+            while ((type=parser.next()) != XmlPullParser.END_DOCUMENT
+                    && type != XmlPullParser.START_TAG) {
+            }
+            
+            String nodeName = parser.getName();
+            if (!"input-method".equals(nodeName)) {
+                throw new XmlPullParserException(
+                        "Meta-data does not start with input-method tag");
+            }
+            
+            TypedArray sa = context.getResources().obtainAttributes(attrs,
+                    com.android.internal.R.styleable.InputMethod);
+            settingsActivityComponent = sa.getString(
+                    com.android.internal.R.styleable.InputMethod_settingsActivity);
+            isDefaultResId = sa.getResourceId(
+                    com.android.internal.R.styleable.InputMethod_isDefault, 0);
+            sa.recycle();
+        } finally {
+            if (parser != null) parser.close();
+        }
+        
+        mSettingsActivityName = settingsActivityComponent;
+        mIsDefaultResId = isDefaultResId;
+    }
+
+    InputMethodInfo(Parcel source) {
+        mId = source.readString();
+        mSettingsActivityName = source.readString();
+        mIsDefaultResId = source.readInt();
+        mService = ResolveInfo.CREATOR.createFromParcel(source);
+    }
+    
+    /**
+     * Temporary API for creating a built-in input method.
+     */
+    public InputMethodInfo(String packageName, String className,
+            CharSequence label, String settingsActivity) {
+        ResolveInfo ri = new ResolveInfo();
+        ServiceInfo si = new ServiceInfo();
+        ApplicationInfo ai = new ApplicationInfo();
+        ai.packageName = packageName;
+        ai.enabled = true;
+        si.applicationInfo = ai;
+        si.enabled = true;
+        si.packageName = packageName;
+        si.name = className;
+        si.exported = true;
+        si.nonLocalizedLabel = label;
+        ri.serviceInfo = si;
+        mService = ri;
+        mId = new ComponentName(si.packageName, si.name).flattenToShortString();
+        mSettingsActivityName = settingsActivity;
+        mIsDefaultResId = 0;
+    }
+    
+    /**
+     * Return a unique ID for this input method.  The ID is generated from
+     * the package and class name implementing the method.
+     */
+    public String getId() {
+        return mId;
+    }
+    
+    /**
+     * Return the .apk package that implements this input method.
+     */
+    public String getPackageName() {
+        return mService.serviceInfo.packageName;
+    }
+    
+    /**
+     * Return the class name of the service component that implements
+     * this input method.
+     */
+    public String getServiceName() {
+        return mService.serviceInfo.name;
+    }
+
+    /**
+     * Return the component of the service that implements this input
+     * method.
+     */
+    public ComponentName getComponent() {
+        return new ComponentName(mService.serviceInfo.packageName,
+                mService.serviceInfo.name);
+    }
+    
+    /**
+     * Load the user-displayed label for this input method.
+     * 
+     * @param pm Supply a PackageManager used to load the input method's
+     * resources.
+     */
+    public CharSequence loadLabel(PackageManager pm) {
+        return mService.loadLabel(pm);
+    }
+    
+    /**
+     * Load the user-displayed icon for this input method.
+     * 
+     * @param pm Supply a PackageManager used to load the input method's
+     * resources.
+     */
+    public Drawable loadIcon(PackageManager pm) {
+        return mService.loadIcon(pm);
+    }
+    
+    /**
+     * Return the class name of an activity that provides a settings UI for
+     * the input method.  You can launch this activity be starting it with
+     * an {@link android.content.Intent} whose action is MAIN and with an
+     * explicit {@link android.content.ComponentName}
+     * composed of {@link #getPackageName} and the class name returned here.
+     * 
+     * <p>A null will be returned if there is no settings activity associated
+     * with the input method.
+     */
+    public String getSettingsActivity() {
+        return mSettingsActivityName;
+    }
+    
+    /**
+     * Return the resource identifier of a resource inside of this input
+     * method's .apk that determines whether it should be considered a
+     * default input method for the system.
+     */
+    public int getIsDefaultResourceId() {
+        return mIsDefaultResId;
+    }
+    
+    public void dump(Printer pw, String prefix) {
+        pw.println(prefix + "mId=" + mId
+                + " mSettingsActivityName=" + mSettingsActivityName);
+        pw.println(prefix + "mIsDefaultResId=0x"
+                + Integer.toHexString(mIsDefaultResId));
+        pw.println(prefix + "Service:");
+        mService.dump(pw, prefix + "  ");
+    }
+    
+    @Override
+    public String toString() {
+        return "InputMethodMetaInfo{" + mId
+                + ", settings: "
+                + mSettingsActivityName + "}";
+    }
+
+    /**
+     * Used to test whether the given parameter object is an
+     * {@link InputMethodInfo} and its Id is the same to this one.
+     * 
+     * @return true if the given parameter object is an
+     *         {@link InputMethodInfo} and its Id is the same to this one.
+     */
+    @Override
+    public boolean equals(Object o) {
+        if (o == this) return true;
+        if (o == null) return false;
+
+        if (!(o instanceof InputMethodInfo)) return false;
+
+        InputMethodInfo obj = (InputMethodInfo) o;
+        return mId.equals(obj.mId);
+    }
+    
+    /**
+     * Used to package this object into a {@link Parcel}.
+     * 
+     * @param dest The {@link Parcel} to be written.
+     * @param flags The flags used for parceling.
+     */
+    public void writeToParcel(Parcel dest, int flags) {
+        dest.writeString(mId);
+        dest.writeString(mSettingsActivityName);
+        dest.writeInt(mIsDefaultResId);
+        mService.writeToParcel(dest, flags);
+    }
+
+    /**
+     * Used to make this class parcelable.
+     */
+    public static final Parcelable.Creator<InputMethodInfo> CREATOR = new Parcelable.Creator<InputMethodInfo>() {
+        public InputMethodInfo createFromParcel(Parcel source) {
+            return new InputMethodInfo(source);
+        }
+
+        public InputMethodInfo[] newArray(int size) {
+            return new InputMethodInfo[size];
+        }
+    };
+
+    public int describeContents() {
+        return 0;
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodManager.java b/core/java/android/view/inputmethod/InputMethodManager.java
new file mode 100644
index 0000000..0aa1d6c
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodManager.java
@@ -0,0 +1,1239 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.IBinder;
+import android.os.Looper;
+import android.os.Message;
+import android.os.RemoteException;
+import android.os.ServiceManager;
+import android.util.Log;
+import android.util.PrintWriterPrinter;
+import android.util.Printer;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewRoot;
+
+import com.android.internal.os.HandlerCaller;
+import com.android.internal.view.IInputConnectionWrapper;
+import com.android.internal.view.IInputContext;
+import com.android.internal.view.IInputMethodCallback;
+import com.android.internal.view.IInputMethodClient;
+import com.android.internal.view.IInputMethodManager;
+import com.android.internal.view.IInputMethodSession;
+import com.android.internal.view.InputBindResult;
+
+import java.io.FileDescriptor;
+import java.io.PrintWriter;
+import java.util.List;
+import java.util.concurrent.CountDownLatch;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * Central system API to the overall input method framework (IMF) architecture,
+ * which arbitrates interaction between applications and the current input method.
+ * You can retrieve an instance of this interface with
+ * {@link Context#getSystemService(String) Context.getSystemService()}.
+ * 
+ * <p>Topics covered here:
+ * <ol>
+ * <li><a href="#ArchitectureOverview">Architecture Overview</a>
+ * </ol>
+ * 
+ * <a name="ArchitectureOverview"></a>
+ * <h3>Architecture Overview</h3>
+ * 
+ * <p>There are three primary parties involved in the input method
+ * framework (IMF) architecture:</p>
+ * 
+ * <ul>
+ * <li> The <strong>input method manager</strong> as expressed by this class
+ * is the central point of the system that manages interaction between all
+ * other parts.  It is expressed as the client-side API here which exists
+ * in each application context and communicates with a global system service
+ * that manages the interaction across all processes.
+ * <li> An <strong>input method (IME)</strong> implements a particular
+ * interaction model allowing the user to generate text.  The system binds
+ * to the current input method that is use, causing it to be created and run,
+ * and tells it when to hide and show its UI.  Only one IME is running at a time.
+ * <li> Multiple <strong>client applications</strong> arbitrate with the input
+ * method manager for input focus and control over the state of the IME.  Only
+ * one such client is ever active (working with the IME) at a time.
+ * </ul>
+ * 
+ * 
+ * <a name="Applications"></a>
+ * <h3>Applications</h3>
+ * 
+ * <p>In most cases, applications that are using the standard
+ * {@link android.widget.TextView} or its subclasses will have little they need
+ * to do to work well with soft input methods.  The main things you need to
+ * be aware of are:</p>
+ * 
+ * <ul>
+ * <li> Properly set the {@link android.R.attr#inputType} if your editable
+ * text views, so that the input method will have enough context to help the
+ * user in entering text into them.
+ * <li> Deal well with losing screen space when the input method is
+ * displayed.  Ideally an application should handle its window being resized
+ * smaller, but it can rely on the system performing panning of the window
+ * if needed.  You should set the {@link android.R.attr#windowSoftInputMode}
+ * attribute on your activity or the corresponding values on windows you
+ * create to help the system determine whether to pan or resize (it will
+ * try to determine this automatically but may get it wrong).
+ * <li> You can also control the preferred soft input state (open, closed, etc)
+ * for your window using the same {@link android.R.attr#windowSoftInputMode}
+ * attribute.
+ * </ul>
+ * 
+ * <p>More finer-grained control is available through the APIs here to directly
+ * interact with the IMF and its IME -- either showing or hiding the input
+ * area, letting the user pick an input method, etc.</p>
+ * 
+ * <p>For the rare people amongst us writing their own text editors, you
+ * will need to implement {@link android.view.View#onCreateInputConnection}
+ * to return a new instance of your own {@link InputConnection} interface
+ * allowing the IME to interact with your editor.</p>
+ * 
+ * 
+ * <a name="InputMethods"></a>
+ * <h3>Input Methods</h3>
+ * 
+ * <p>An input method (IME) is implemented
+ * as a {@link android.app.Service}, typically deriving from
+ * {@link android.inputmethodservice.InputMethodService}.  It must provide
+ * the core {@link InputMethod} interface, though this is normally handled by
+ * {@link android.inputmethodservice.InputMethodService} and implementors will
+ * only need to deal with the higher-level API there.</p>
+ * 
+ * See the {@link android.inputmethodservice.InputMethodService} class for
+ * more information on implementing IMEs.
+ * 
+ * 
+ * <a name="Security"></a>
+ * <h3>Security</h3>
+ * 
+ * <p>There are a lot of security issues associated with input methods,
+ * since they essentially have freedom to completely drive the UI and monitor
+ * everything the user enters.  The Android input method framework also allows
+ * arbitrary third party IMEs, so care must be taken to restrict their
+ * selection and interactions.</p>
+ * 
+ * <p>Here are some key points about the security architecture behind the
+ * IMF:</p>
+ * 
+ * <ul>
+ * <li> <p>Only the system is allowed to directly access an IME's
+ * {@link InputMethod} interface, via the
+ * {@link android.Manifest.permission#BIND_INPUT_METHOD} permission.  This is
+ * enforced in the system by not binding to an input method service that does
+ * not require this permission, so the system can guarantee no other untrusted
+ * clients are accessing the current input method outside of its control.</p>
+ * 
+ * <li> <p>There may be many client processes of the IMF, but only one may
+ * be active at a time.  The inactive clients can not interact with key
+ * parts of the IMF through the mechanisms described below.</p>
+ * 
+ * <li> <p>Clients of an input method are only given access to its
+ * {@link InputMethodSession} interface.  One instance of this interface is
+ * created for each client, and only calls from the session associated with
+ * the active client will be processed by the current IME.  This is enforced
+ * by {@link android.inputmethodservice.AbstractInputMethodService} for normal
+ * IMEs, but must be explicitly handled by an IME that is customizing the
+ * raw {@link InputMethodSession} implementation.</p>
+ * 
+ * <li> <p>Only the active client's {@link InputConnection} will accept
+ * operations.  The IMF tells each client process whether it is active, and
+ * the framework enforces that in inactive processes calls on to the current
+ * InputConnection will be ignored.  This ensures that the current IME can
+ * only deliver events and text edits to the UI that the user sees as
+ * being in focus.</p>
+ * 
+ * <li> <p>An IME can never interact with an {@link InputConnection} while
+ * the screen is off.  This is enforced by making all clients inactive while
+ * the screen is off, and prevents bad IMEs from driving the UI when the user
+ * can not be aware of its behavior.</p>
+ * 
+ * <li> <p>A client application can ask that the system let the user pick a
+ * new IME, but can not programmatically switch to one itself.  This avoids
+ * malicious applications from switching the user to their own IME, which
+ * remains running when the user navigates away to another application.  An
+ * IME, on the other hand, <em>is</em> allowed to programmatically switch
+ * the system to another IME, since it already has full control of user
+ * input.</p>
+ * 
+ * <li> <p>The user must explicitly enable a new IME in settings before
+ * they can switch to it, to confirm with the system that they know about it
+ * and want to make it available for use.</p>
+ * </ul>
+ */
+public final class InputMethodManager {
+    static final boolean DEBUG = false;
+    static final String TAG = "InputMethodManager";
+
+    static final Object mInstanceSync = new Object();
+    static InputMethodManager mInstance;
+    
+    final IInputMethodManager mService;
+    final Looper mMainLooper;
+    
+    // For scheduling work on the main thread.  This also serves as our
+    // global lock.
+    final H mH;
+    
+    // Our generic input connection if the current target does not have its own.
+    final IInputContext mIInputContext;
+
+    /**
+     * True if this input method client is active, initially false.
+     */
+    boolean mActive = false;
+    
+    /**
+     * Set whenever this client becomes inactive, to know we need to reset
+     * state with the IME then next time we receive focus.
+     */
+    boolean mHasBeenInactive = true;
+    
+    /**
+     * As reported by IME through InputConnection.
+     */
+    boolean mFullscreenMode;
+    
+    // -----------------------------------------------------------
+    
+    /**
+     * This is the root view of the overall window that currently has input
+     * method focus.
+     */
+    View mCurRootView;
+    /**
+     * This is the view that should currently be served by an input method,
+     * regardless of the state of setting that up.
+     */
+    View mServedView;
+    /**
+     * This is then next view that will be served by the input method, when
+     * we get around to updating things.
+     */
+    View mNextServedView;
+    /**
+     * True if we should restart input in the next served view, even if the
+     * view hasn't actually changed from the current serve view.
+     */
+    boolean mNextServedNeedsStart;
+    /**
+     * This is set when we are in the process of connecting, to determine
+     * when we have actually finished.
+     */
+    boolean mServedConnecting;
+    /**
+     * This is non-null when we have connected the served view; it holds
+     * the attributes that were last retrieved from the served view and given
+     * to the input connection.
+     */
+    EditorInfo mCurrentTextBoxAttribute;
+    /**
+     * The InputConnection that was last retrieved from the served view.
+     */
+    InputConnection mServedInputConnection;
+    /**
+     * The completions that were last provided by the served view.
+     */
+    CompletionInfo[] mCompletions;
+    
+    // Cursor position on the screen.
+    Rect mTmpCursorRect = new Rect();
+    Rect mCursorRect = new Rect();
+    int mCursorSelStart;
+    int mCursorSelEnd;
+    int mCursorCandStart;
+    int mCursorCandEnd;
+
+    // -----------------------------------------------------------
+    
+    /**
+     * Sequence number of this binding, as returned by the server.
+     */
+    int mBindSequence = -1;
+    /**
+     * ID of the method we are bound to.
+     */
+    String mCurId;
+    /**
+     * The actual instance of the method to make calls on it.
+     */
+    IInputMethodSession mCurMethod;
+
+    // -----------------------------------------------------------
+    
+    static final int MSG_DUMP = 1;
+    
+    class H extends Handler {
+        H(Looper looper) {
+            super(looper);
+        }
+        
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case MSG_DUMP: {
+                    HandlerCaller.SomeArgs args = (HandlerCaller.SomeArgs)msg.obj;
+                    try {
+                        doDump((FileDescriptor)args.arg1,
+                                (PrintWriter)args.arg2, (String[])args.arg3);
+                    } catch (RuntimeException e) {
+                        ((PrintWriter)args.arg2).println("Exception: " + e);
+                    }
+                    synchronized (args.arg4) {
+                        ((CountDownLatch)args.arg4).countDown();
+                    }
+                    return;
+                }
+            }
+        }
+    }
+    
+    class ControlledInputConnectionWrapper extends IInputConnectionWrapper {
+        public ControlledInputConnectionWrapper(Looper mainLooper, InputConnection conn) {
+            super(mainLooper, conn);
+        }
+
+        public boolean isActive() {
+            return mActive;
+        }
+    }
+    
+    final IInputMethodClient.Stub mClient = new IInputMethodClient.Stub() {
+        @Override protected void dump(FileDescriptor fd, PrintWriter fout, String[] args) {
+            // No need to check for dump permission, since we only give this
+            // interface to the system.
+            
+            CountDownLatch latch = new CountDownLatch(1);
+            HandlerCaller.SomeArgs sargs = new HandlerCaller.SomeArgs();
+            sargs.arg1 = fd;
+            sargs.arg2 = fout;
+            sargs.arg3 = args;
+            sargs.arg4 = latch;
+            mH.sendMessage(mH.obtainMessage(MSG_DUMP, sargs));
+            try {
+                if (!latch.await(5, TimeUnit.SECONDS)) {
+                    fout.println("Timeout waiting for dump");
+                }
+            } catch (InterruptedException e) {
+                fout.println("Interrupted waiting for dump");
+            }
+        }
+        
+        public void setUsingInputMethod(boolean state) {
+        }
+        
+        public void onBindMethod(InputBindResult res) {
+            synchronized (mH) {
+                if (mBindSequence < 0 || mBindSequence != res.sequence) {
+                    Log.w(TAG, "Ignoring onBind: cur seq=" + mBindSequence
+                            + ", given seq=" + res.sequence);
+                    return;
+                }
+                
+                mCurMethod = res.method;
+                mCurId = res.id;
+                mBindSequence = res.sequence;
+            }
+            startInputInner();
+        }
+        
+        public void onUnbindMethod(int sequence) {
+            synchronized (mH) {
+                if (mBindSequence == sequence) {
+                    if (false) {
+                        // XXX the server has already unbound!
+                        if (mCurMethod != null && mCurrentTextBoxAttribute != null) {
+                            try {
+                                mCurMethod.finishInput();
+                            } catch (RemoteException e) {
+                                Log.w(TAG, "IME died: " + mCurId, e);
+                            }
+                        }
+                    }
+                    clearBindingLocked();
+                    
+                    // If we were actively using the last input method, then
+                    // we would like to re-connect to the next input method.
+                    if (mServedView != null && mServedView.isFocused()) {
+                        mServedConnecting = true;
+                    }
+                }
+                startInputInner();
+            }
+        }
+        
+        public void setActive(boolean active) {
+            synchronized (mH) {
+                mActive = active;
+                mFullscreenMode = false;
+                if (!active) {
+                    // Some other client has starting using the IME, so note
+                    // that this happened and make sure our own editor's
+                    // state is reset.
+                    mHasBeenInactive = true;
+                    try {
+                        // Note that finishComposingText() is allowed to run
+                        // even when we are not active.
+                        mIInputContext.finishComposingText();
+                    } catch (RemoteException e) {
+                    }
+                }
+            }
+        }
+    };    
+    
+    final InputConnection mDummyInputConnection = new BaseInputConnection(this, true);
+    
+    InputMethodManager(IInputMethodManager service, Looper looper) {
+        mService = service;
+        mMainLooper = looper;
+        mH = new H(looper);
+        mIInputContext = new ControlledInputConnectionWrapper(looper,
+                mDummyInputConnection);
+        
+        if (mInstance == null) {
+            mInstance = this;
+        }
+    }
+
+    /**
+     * Retrieve the global InputMethodManager instance, creating it if it
+     * doesn't already exist.
+     * @hide
+     */
+    static public InputMethodManager getInstance(Context context) {
+        synchronized (mInstanceSync) {
+            if (mInstance != null) {
+                return mInstance;
+            }
+            IBinder b = ServiceManager.getService(Context.INPUT_METHOD_SERVICE);
+            IInputMethodManager service = IInputMethodManager.Stub.asInterface(b);
+            mInstance = new InputMethodManager(service, context.getMainLooper());
+        }
+        return mInstance;
+    }
+    
+    /**
+     * Private optimization: retrieve the global InputMethodManager instance,
+     * if it exists.
+     * @hide
+     */
+    static public InputMethodManager peekInstance() {
+        return mInstance;
+    }
+    
+    /** @hide */
+    public IInputMethodClient getClient() {
+        return mClient;
+    }
+    
+    /** @hide */
+    public IInputContext getInputContext() {
+        return mIInputContext;
+    }
+    
+    public List<InputMethodInfo> getInputMethodList() {
+        try {
+            return mService.getInputMethodList();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public List<InputMethodInfo> getEnabledInputMethodList() {
+        try {
+            return mService.getEnabledInputMethodList();
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void showStatusIcon(IBinder imeToken, String packageName, int iconId) {
+        try {
+            mService.updateStatusIcon(imeToken, packageName, iconId);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    public void hideStatusIcon(IBinder imeToken) {
+        try {
+            mService.updateStatusIcon(imeToken, null, 0);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+
+    /** @hide */
+    public void setFullscreenMode(boolean fullScreen) {
+        mFullscreenMode = fullScreen;
+    }
+    
+    /**
+     * Allows you to discover whether the attached input method is running
+     * in fullscreen mode.  Return true if it is fullscreen, entirely covering
+     * your UI, else returns false.
+     */
+    public boolean isFullscreenMode() {
+        return mFullscreenMode;
+    }
+    
+    /**
+     * Return true if the given view is the currently active view for the
+     * input method.
+     */
+    public boolean isActive(View view) {
+        checkFocus();
+        synchronized (mH) {
+            return mServedView == view && mCurrentTextBoxAttribute != null;
+        }
+    }
+    
+    /**
+     * Return true if any view is currently active in the input method.
+     */
+    public boolean isActive() {
+        checkFocus();
+        synchronized (mH) {
+            return mServedView != null && mCurrentTextBoxAttribute != null;
+        }
+    }
+    
+    /**
+     * Return true if the currently served view is accepting full text edits.
+     * If false, it has no input connection, so can only handle raw key events.
+     */
+    public boolean isAcceptingText() {
+        checkFocus();
+        return mServedInputConnection != null;
+    }
+
+    /**
+     * Reset all of the state associated with being bound to an input method.
+     */
+    void clearBindingLocked() {
+        clearConnectionLocked();
+        mBindSequence = -1;
+        mCurId = null;
+        mCurMethod = null;
+    }
+    
+    /**
+     * Reset all of the state associated with a served view being connected
+     * to an input method
+     */
+    void clearConnectionLocked() {
+        mCurrentTextBoxAttribute = null;
+        mServedInputConnection = null;
+    }
+    
+    /**
+     * Disconnect any existing input connection, clearing the served view.
+     */
+    void finishInputLocked() {
+        mNextServedView = null;
+        if (mServedView != null) {
+            if (DEBUG) Log.v(TAG, "FINISH INPUT: " + mServedView);
+            
+            if (mCurrentTextBoxAttribute != null) {
+                try {
+                    mService.finishInput(mClient);
+                } catch (RemoteException e) {
+                }
+            }
+            
+            if (mServedInputConnection != null) {
+                // We need to tell the previously served view that it is no
+                // longer the input target, so it can reset its state.  Schedule
+                // this call on its window's Handler so it will be on the correct
+                // thread and outside of our lock.
+                Handler vh = mServedView.getHandler();
+                if (vh != null) {
+                    // This will result in a call to reportFinishInputConnection()
+                    // below.
+                    vh.sendMessage(vh.obtainMessage(ViewRoot.FINISH_INPUT_CONNECTION,
+                            mServedInputConnection));
+                }
+            }
+            
+            mServedView = null;
+            mCompletions = null;
+            mServedConnecting = false;
+            clearConnectionLocked();
+        }
+    }
+    
+    /**
+     * Called from the FINISH_INPUT_CONNECTION message above.
+     * @hide
+     */
+    public void reportFinishInputConnection(InputConnection ic) {
+        if (mServedInputConnection != ic) {
+            ic.finishComposingText();
+        }
+    }
+    
+    public void displayCompletions(View view, CompletionInfo[] completions) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view) {
+                return;
+            }
+            
+            mCompletions = completions;
+            if (mCurMethod != null) {
+                try {
+                    mCurMethod.displayCompletions(mCompletions);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+    
+    public void updateExtractedText(View view, int token, ExtractedText text) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view) {
+                return;
+            }
+            
+            if (mCurMethod != null) {
+                try {
+                    mCurMethod.updateExtractedText(token, text);
+                } catch (RemoteException e) {
+                }
+            }
+        }
+    }
+    
+    /**
+     * Flag for {@link #showSoftInput} to indicate that this is an implicit
+     * request to show the input window, not as the result of a direct request
+     * by the user.  The window may not be shown in this case.
+     */
+    public static final int SHOW_IMPLICIT = 0x0001;
+    
+    /**
+     * Flag for {@link #showSoftInput} to indicate that the user has forced
+     * the input method open (such as by long-pressing menu) so it should
+     * not be closed until they explicitly do so.
+     */
+    public static final int SHOW_FORCED = 0x0002;
+    
+    /**
+     * Explicitly request that the current input method's soft input area be
+     * shown to the user, if needed.  Call this if the user interacts with
+     * your view in such a way that they have expressed they would like to
+     * start performing input into it.
+     * 
+     * @param view The currently focused view, which would like to receive
+     * soft keyboard input.
+     * @param flags Provides additional operating flags.  Currently may be
+     * 0 or have the {@link #SHOW_IMPLICIT} bit set.
+     */
+    public void showSoftInput(View view, int flags) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view) {
+                return;
+            }
+
+            try {
+                mService.showSoftInput(mClient, flags);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
+    /** @hide */
+    public void showSoftInputUnchecked(int flags) {
+        try {
+            mService.showSoftInput(mClient, flags);
+        } catch (RemoteException e) {
+        }
+    }
+    
+    /**
+     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
+     * input window should only be hidden if it was not explicitly shown
+     * by the user.
+     */
+    public static final int HIDE_IMPLICIT_ONLY = 0x0001;
+    
+    /**
+     * Flag for {@link #hideSoftInputFromWindow} to indicate that the soft
+     * input window should normally be hidden, unless it was originally
+     * shown with {@link #SHOW_FORCED}.
+     */
+    public static final int HIDE_NOT_ALWAYS = 0x0002;
+    
+    /**
+     * Request to hide the soft input window from the context of the window
+     * that is currently accepting input.  This should be called as a result
+     * of the user doing some actually than fairly explicitly requests to
+     * have the input window hidden.
+     * 
+     * @param windowToken The token of the window that is making the request,
+     * as returned by {@link View#getWindowToken() View.getWindowToken()}.
+     * @param flags Provides additional operating flags.  Currently may be
+     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
+     */
+    public void hideSoftInputFromWindow(IBinder windowToken, int flags) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView == null || mServedView.getWindowToken() != windowToken) {
+                return;
+            }
+
+            try {
+                mService.hideSoftInput(mClient, flags);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
+    /**
+     * If the input method is currently connected to the given view,
+     * restart it with its new contents.  You should call this when the text
+     * within your view changes outside of the normal input method or key
+     * input flow, such as when an application calls TextView.setText().
+     * 
+     * @param view The view whose text has changed.
+     */
+    public void restartInput(View view) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view) {
+                return;
+            }
+            
+            mServedConnecting = true;
+        }
+        
+        startInputInner();
+    }
+    
+    void startInputInner() {
+        final View view;
+        synchronized (mH) {
+            view = mServedView;
+            
+            // Make sure we have a window token for the served view.
+            if (DEBUG) Log.v(TAG, "Starting input: view=" + view);
+            if (view == null) {
+                if (DEBUG) Log.v(TAG, "ABORT input: no served view!");
+                return;
+            }
+        }
+        
+        // Now we need to get an input connection from the served view.
+        // This is complicated in a couple ways: we can't be holding our lock
+        // when calling out to the view, and we need to make sure we call into
+        // the view on the same thread that is driving its view hierarchy.
+        Handler vh = view.getHandler();
+        if (vh == null) {
+            // If the view doesn't have a handler, something has changed out
+            // from under us, so just bail.
+            if (DEBUG) Log.v(TAG, "ABORT input: no handler for view!");
+            return;
+        }
+        if (vh.getLooper() != Looper.myLooper()) {
+            // The view is running on a different thread than our own, so
+            // we need to reschedule our work for over there.
+            if (DEBUG) Log.v(TAG, "Starting input: reschedule to view thread");
+            vh.post(new Runnable() {
+                public void run() {
+                    startInputInner();
+                }
+            });
+        }
+        
+        // Okay we are now ready to call into the served view and have it
+        // do its stuff.
+        // Life is good: let's hook everything up!
+        EditorInfo tba = new EditorInfo();
+        tba.packageName = view.getContext().getPackageName();
+        tba.fieldId = view.getId();
+        InputConnection ic = view.onCreateInputConnection(tba);
+        if (DEBUG) Log.v(TAG, "Starting input: tba=" + tba + " ic=" + ic);
+        
+        synchronized (mH) {
+            // Now that we are locked again, validate that our state hasn't
+            // changed.
+            if (mServedView != view || !mServedConnecting) {
+                // Something else happened, so abort.
+                if (DEBUG) Log.v(TAG, 
+                        "Starting input: finished by someone else (view="
+                        + mServedView + " conn=" + mServedConnecting + ")");
+                return;
+            }
+            
+            // If we already have a text box, then this view is already
+            // connected so we want to restart it.
+            final boolean initial = mCurrentTextBoxAttribute == null;
+            
+            // Hook 'em up and let 'er rip.
+            mCurrentTextBoxAttribute = tba;
+            mServedConnecting = false;
+            mServedInputConnection = ic;
+            IInputContext servedContext;
+            if (ic != null) {
+                mCursorSelStart = tba.initialSelStart;
+                mCursorSelEnd = tba.initialSelEnd;
+                mCursorCandStart = -1;
+                mCursorCandEnd = -1;
+                mCursorRect.setEmpty();
+                servedContext = new ControlledInputConnectionWrapper(vh.getLooper(), ic);
+            } else {
+                servedContext = null;
+            }
+            
+            try {
+                if (DEBUG) Log.v(TAG, "START INPUT: " + view + " ic="
+                        + ic + " tba=" + tba + " initial=" + initial);
+                InputBindResult res = mService.startInput(mClient,
+                        servedContext, tba, initial, mCurMethod == null);
+                if (DEBUG) Log.v(TAG, "Starting input: Bind result=" + res);
+                if (res != null) {
+                    if (res.id != null) {
+                        mBindSequence = res.sequence;
+                        mCurMethod = res.method;
+                    } else {
+                        // This means there is no input method available.
+                        if (DEBUG) Log.v(TAG, "ABORT input: no input method!");
+                        return;
+                    }
+                }
+                if (mCurMethod != null && mCompletions != null) {
+                    try {
+                        mCurMethod.displayCompletions(mCompletions);
+                    } catch (RemoteException e) {
+                    }
+                }
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+
+    /**
+     * When the focused window is dismissed, this method is called to finish the
+     * input method started before.
+     * @hide
+     */
+    public void windowDismissed(IBinder appWindowToken) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != null &&
+                    mServedView.getWindowToken() == appWindowToken) {
+                finishInputLocked();
+            }
+        }
+    }
+
+    /**
+     * Call this when a view receives focus.
+     * @hide
+     */
+    public void focusIn(View view) {
+        synchronized (mH) {
+            focusInLocked(view);
+        }
+    }
+
+    void focusInLocked(View view) {
+        if (DEBUG) Log.v(TAG, "focusIn: " + view);
+        
+        if (mCurRootView != view.getRootView()) {
+            // This is a request from a window that isn't in the window with
+            // IME focus, so ignore it.
+            if (DEBUG) Log.v(TAG, "Not IME target window, ignoring");
+            return;
+        }
+        
+        mNextServedView = view;
+        scheduleCheckFocusLocked(view);
+    }
+
+    /**
+     * Call this when a view loses focus.
+     * @hide
+     */
+    public void focusOut(View view) {
+        synchronized (mH) {
+            if (DEBUG) Log.v(TAG, "focusOut: " + view
+                    + " mServedView=" + mServedView
+                    + " winFocus=" + view.hasWindowFocus());
+            if (mServedView == view) {
+                // The following code would auto-hide the IME if we end up
+                // with no more views with focus.  This can happen, however,
+                // whenever we go into touch mode, so it ends up hiding
+                // at times when we don't really want it to.  For now it
+                // seems better to just turn it all off.
+                if (false && view.hasWindowFocus()) {
+                    mNextServedView = null;
+                    scheduleCheckFocusLocked(view);
+                }
+            }
+        }
+    }
+
+    void scheduleCheckFocusLocked(View view) {
+        Handler vh = view.getHandler();
+        if (vh != null && !vh.hasMessages(ViewRoot.CHECK_FOCUS)) {
+            // This will result in a call to checkFocus() below.
+            vh.sendMessage(vh.obtainMessage(ViewRoot.CHECK_FOCUS));
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void checkFocus() {
+        // This is called a lot, so short-circuit before locking.
+        if (mServedView == mNextServedView && !mNextServedNeedsStart) {
+            return;
+        }
+        
+        InputConnection ic = null;
+        synchronized (mH) {
+            if (mServedView == mNextServedView && !mNextServedNeedsStart) {
+                return;
+            }
+            if (DEBUG) Log.v(TAG, "checkFocus: view=" + mServedView
+                    + " next=" + mNextServedView
+                    + " restart=" + mNextServedNeedsStart);
+            
+            mNextServedNeedsStart = false;
+            if (mNextServedView == null) {
+                finishInputLocked();
+                // In this case, we used to have a focused view on the window,
+                // but no longer do.  We should make sure the input method is
+                // no longer shown, since it serves no purpose.
+                closeCurrentInput();
+                return;
+            }
+            
+            ic = mServedInputConnection;
+            
+            mServedView = mNextServedView;
+            mCurrentTextBoxAttribute = null;
+            mCompletions = null;
+            mServedConnecting = true;
+        }
+        
+        if (ic != null) {
+            ic.finishComposingText();
+        }
+        
+        startInputInner();
+    }
+    
+    void closeCurrentInput() {
+        try {
+            mService.hideSoftInput(mClient, HIDE_NOT_ALWAYS);
+        } catch (RemoteException e) {
+        }
+    }
+    
+    /**
+     * Called by ViewRoot when its window gets input focus.
+     * @hide
+     */
+    public void onWindowFocus(View rootView, View focusedView, int softInputMode,
+            boolean first, int windowFlags) {
+        synchronized (mH) {
+            if (DEBUG) Log.v(TAG, "onWindowFocus: " + focusedView
+                    + " softInputMode=" + softInputMode
+                    + " first=" + first + " flags=#"
+                    + Integer.toHexString(windowFlags));
+            if (mHasBeenInactive) {
+                if (DEBUG) Log.v(TAG, "Has been inactive!  Starting fresh");
+                mHasBeenInactive = false;
+                mNextServedNeedsStart = true;
+            }
+            focusInLocked(focusedView != null ? focusedView : rootView);
+        }
+        
+        checkFocus();
+        
+        synchronized (mH) {
+            try {
+                final boolean isTextEditor = focusedView != null &&
+                        focusedView.onCheckIsTextEditor();
+                mService.windowGainedFocus(mClient, focusedView != null,
+                        isTextEditor, softInputMode, first, windowFlags);
+            } catch (RemoteException e) {
+            }
+        }
+    }
+    
+    /** @hide */
+    public void startGettingWindowFocus(View rootView) {
+        synchronized (mH) {
+            mCurRootView = rootView;
+        }
+    }
+    
+    /**
+     * Report the current selection range.
+     */
+    public void updateSelection(View view, int selStart, int selEnd,
+            int candidatesStart, int candidatesEnd) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
+                return;
+            }
+            
+            if (mCursorSelStart != selStart || mCursorSelEnd != selEnd
+                    || mCursorCandStart != candidatesStart
+                    || mCursorCandEnd != candidatesEnd) {
+                if (DEBUG) Log.d(TAG, "updateSelection");
+
+                try {
+                    if (DEBUG) Log.v(TAG, "SELECTION CHANGE: " + mCurMethod);
+                    mCurMethod.updateSelection(mCursorSelStart, mCursorSelEnd,
+                            selStart, selEnd, candidatesStart, candidatesEnd);
+                    mCursorSelStart = selStart;
+                    mCursorSelEnd = selEnd;
+                    mCursorCandStart = candidatesStart;
+                    mCursorCandEnd = candidatesEnd;
+                } catch (RemoteException e) {
+                    Log.w(TAG, "IME died: " + mCurId, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Returns true if the current input method wants to watch the location
+     * of the input editor's cursor in its window.
+     */
+    public boolean isWatchingCursor(View view) {
+        return false;
+    }
+    
+    /**
+     * Report the current cursor location in its window.
+     */
+    public void updateCursor(View view, int left, int top, int right, int bottom) {
+        checkFocus();
+        synchronized (mH) {
+            if (mServedView != view || mCurrentTextBoxAttribute == null
+                    || mCurMethod == null) {
+                return;
+            }
+            
+            mTmpCursorRect.set(left, top, right, bottom);
+            if (!mCursorRect.equals(mTmpCursorRect)) {
+                if (DEBUG) Log.d(TAG, "updateCursor");
+
+                try {
+                    if (DEBUG) Log.v(TAG, "CURSOR CHANGE: " + mCurMethod);
+                    mCurMethod.updateCursor(mTmpCursorRect);
+                    mCursorRect.set(mTmpCursorRect);
+                } catch (RemoteException e) {
+                    Log.w(TAG, "IME died: " + mCurId, e);
+                }
+            }
+        }
+    }
+
+    /**
+     * Call {@link InputMethodSession#appPrivateCommand(String, Bundle)
+     * InputMethodSession.appPrivateCommand()} on the current Input Method.
+     * @param view Optional View that is sending the command, or null if
+     * you want to send the command regardless of the view that is attached
+     * to the input method.
+     * @param action Name of the command to be performed.  This <em>must</em>
+     * be a scoped name, i.e. prefixed with a package name you own, so that
+     * different developers will not create conflicting commands.
+     * @param data Any data to include with the command.
+     */
+    public void sendAppPrivateCommand(View view, String action, Bundle data) {
+        checkFocus();
+        synchronized (mH) {
+            if ((view != null && mServedView != view)
+                    || mCurrentTextBoxAttribute == null || mCurMethod == null) {
+                return;
+            }
+            try {
+                if (DEBUG) Log.v(TAG, "APP PRIVATE COMMAND " + action + ": " + data);
+                mCurMethod.appPrivateCommand(action, data);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+    
+    /**
+     * Force switch to a new input method component.  This can only be called
+     * from the currently active input method, as validated by the given token.
+     * @param token Supplies the identifying token given to an input method
+     * when it was started, which allows it to perform this operation on
+     * itself.
+     * @param id The unique identifier for the new input method to be switched to.
+     */
+    public void setInputMethod(IBinder token, String id) {
+        try {
+            mService.setInputMethod(token, id);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * Close/hide the input method's soft input area, so the user no longer
+     * sees it or can interact with it.  This can only be called
+     * from the currently active input method, as validated by the given token.
+     * 
+     * @param token Supplies the identifying token given to an input method
+     * when it was started, which allows it to perform this operation on
+     * itself.
+     * @param flags Provides additional operating flags.  Currently may be
+     * 0 or have the {@link #HIDE_IMPLICIT_ONLY} bit set.
+     */
+    public void hideSoftInputFromInputMethod(IBinder token, int flags) {
+        try {
+            mService.hideMySoftInput(token, flags);
+        } catch (RemoteException e) {
+            throw new RuntimeException(e);
+        }
+    }
+    
+    /**
+     * @hide
+     */
+    public void dispatchKeyEvent(Context context, int seq, KeyEvent key,
+            IInputMethodCallback callback) {
+        synchronized (mH) {
+            if (DEBUG) Log.d(TAG, "dispatchKeyEvent");
+    
+            if (mCurMethod == null) {
+                try {
+                    callback.finishedEvent(seq, false);
+                } catch (RemoteException e) {
+                }
+                return;
+            }
+    
+            if (key.getAction() == KeyEvent.ACTION_DOWN
+                    && key.getKeyCode() == KeyEvent.KEYCODE_SYM) {
+                showInputMethodPicker();
+                try {
+                    callback.finishedEvent(seq, true);
+                } catch (RemoteException e) {
+                }
+                return;
+            }
+            try {
+                if (DEBUG) Log.v(TAG, "DISPATCH KEY: " + mCurMethod);
+                mCurMethod.dispatchKeyEvent(seq, key, callback);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId + " dropping: " + key, e);
+                try {
+                    callback.finishedEvent(seq, false);
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+    }
+
+    /**
+     * @hide
+     */
+    void dispatchTrackballEvent(Context context, int seq, MotionEvent motion,
+            IInputMethodCallback callback) {
+        synchronized (mH) {
+            if (DEBUG) Log.d(TAG, "dispatchTrackballEvent");
+    
+            if (mCurMethod == null || mCurrentTextBoxAttribute == null) {
+                try {
+                    callback.finishedEvent(seq, false);
+                } catch (RemoteException e) {
+                }
+                return;
+            }
+    
+            try {
+                if (DEBUG) Log.v(TAG, "DISPATCH TRACKBALL: " + mCurMethod);
+                mCurMethod.dispatchTrackballEvent(seq, motion, callback);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId + " dropping trackball: " + motion, e);
+                try {
+                    callback.finishedEvent(seq, false);
+                } catch (RemoteException ex) {
+                }
+            }
+        }
+    }
+    
+    public void showInputMethodPicker() {
+        synchronized (mH) {
+            try {
+                mService.showInputMethodPickerFromClient(mClient);
+            } catch (RemoteException e) {
+                Log.w(TAG, "IME died: " + mCurId, e);
+            }
+        }
+    }
+    
+    void doDump(FileDescriptor fd, PrintWriter fout, String[] args) {
+        final Printer p = new PrintWriterPrinter(fout);
+        p.println("Input method client state for " + this + ":");
+        
+        p.println("  mService=" + mService);
+        p.println("  mMainLooper=" + mMainLooper);
+        p.println("  mIInputContext=" + mIInputContext);
+        p.println("  mActive=" + mActive
+                + " mHasBeenInactive=" + mHasBeenInactive
+                + " mBindSequence=" + mBindSequence
+                + " mCurId=" + mCurId);
+        p.println("  mCurMethod=" + mCurMethod);
+        p.println("  mCurRootView=" + mCurRootView);
+        p.println("  mServedView=" + mServedView);
+        p.println("  mNextServedNeedsStart=" + mNextServedNeedsStart
+                + " mNextServedView=" + mNextServedView);
+        p.println("  mServedConnecting=" + mServedConnecting);
+        if (mCurrentTextBoxAttribute != null) {
+            p.println("  mCurrentTextBoxAttribute:");
+            mCurrentTextBoxAttribute.dump(p, "    ");
+        } else {
+            p.println("  mCurrentTextBoxAttribute: null");
+        }
+        p.println("  mServedInputConnection=" + mServedInputConnection);
+        p.println("  mCompletions=" + mCompletions);
+        p.println("  mCursorRect=" + mCursorRect);
+        p.println("  mCursorSelStart=" + mCursorSelStart
+                + " mCursorSelEnd=" + mCursorSelEnd
+                + " mCursorCandStart=" + mCursorCandStart
+                + " mCursorCandEnd=" + mCursorCandEnd);
+    }
+}
diff --git a/core/java/android/view/inputmethod/InputMethodSession.java b/core/java/android/view/inputmethod/InputMethodSession.java
new file mode 100644
index 0000000..b5bbaff
--- /dev/null
+++ b/core/java/android/view/inputmethod/InputMethodSession.java
@@ -0,0 +1,142 @@
+/*
+ * Copyright (C) 2007-2008 The Android Open Source Project
+ * 
+ * Licensed under the Apache License, Version 2.0 (the "License"); you may not
+ * use this file except in compliance with the License. You may obtain a copy of
+ * the License at
+ * 
+ * http://www.apache.org/licenses/LICENSE-2.0
+ * 
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+
+package android.view.inputmethod;
+
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.view.KeyEvent;
+import android.view.MotionEvent;
+
+/**
+ * The InputMethodSession interface provides the per-client functionality
+ * of {@link InputMethod} that is safe to expose to applications.
+ * 
+ * <p>Applications will not normally use this interface themselves, instead
+ * relying on the standard interaction provided by
+ * {@link android.widget.TextView} and {@link android.widget.EditText}.
+ */
+public interface InputMethodSession {
+    
+    public interface EventCallback {
+        void finishedEvent(int seq, boolean handled);
+    }
+    
+    /**
+     * This method is called when the application would like to stop
+     * receiving text input.
+     */
+    public void finishInput();
+
+    /**
+     * This method is called when the selection or cursor in the current
+     * target input field has changed.
+     * 
+     * @param oldSelStart The previous text offset of the cursor selection
+     * start position.
+     * @param oldSelEnd The previous text offset of the cursor selection
+     * end position.
+     * @param newSelStart The new text offset of the cursor selection
+     * start position.
+     * @param newSelEnd The new text offset of the cursor selection
+     * end position.
+     * @param candidatesStart The text offset of the current candidate
+     * text start position.
+     * @param candidatesEnd The text offset of the current candidate
+     * text end position.
+     */
+    public void updateSelection(int oldSelStart, int oldSelEnd,
+            int newSelStart, int newSelEnd,
+            int candidatesStart, int candidatesEnd);
+
+    /**
+     * This method is called when cursor location of the target input field
+     * has changed within its window.  This is not normally called, but will
+     * only be reported if requested by the input method.
+     * 
+     * @param newCursor The rectangle of the cursor currently being shown in
+     * the input field's window coordinates.
+     */
+    public void updateCursor(Rect newCursor);
+    
+    /**
+     * Called by a text editor that performs auto completion, to tell the
+     * input method about the completions it has available.  This can be used
+     * by the input method to display them to the user to select the text to
+     * be inserted.
+     * 
+     * @param completions Array of text completions that are available, starting with
+     * the best.  If this array is null, any existing completions will be
+     * removed.
+     */
+    public void displayCompletions(CompletionInfo[] completions);
+    
+    /**
+     * Called by a text editor to report its new extracted text when its
+     * contents change.  This will only be called if the input method
+     * calls {@link InputConnection#getExtractedText(ExtractedTextRequest, int)
+     * InputConnection.getExtractedText()} with the option to report updates.
+     * 
+     * @param token The input method supplied token for identifying its request.
+     * @param text The new extracted text.
+     */
+    public void updateExtractedText(int token, ExtractedText text);
+    
+    /**
+     * This method is called when a key is pressed.  When done with the event,
+     * the implementation must call back on <var>callback</var> with its
+     * result.
+     * 
+     * <p>
+     * If the input method wants to handle this event, return true, otherwise
+     * return false and the caller (i.e. the application) will handle the event.
+     * 
+     * @param event The key event.
+     * 
+     * @return Whether the input method wants to handle this event.
+     * 
+     * @see #dispatchKeyUp
+     * @see android.view.KeyEvent
+     */
+    public void dispatchKeyEvent(int seq, KeyEvent event, EventCallback callback);
+
+    /**
+     * This method is called when there is a track ball event.
+     * 
+     * <p>
+     * If the input method wants to handle this event, return true, otherwise
+     * return false and the caller (i.e. the application) will handle the event.
+     * 
+     * @param event The motion event.
+     * 
+     * @return Whether the input method wants to handle this event.
+     * 
+     * @see android.view.MotionEvent
+     */
+    public void dispatchTrackballEvent(int seq, MotionEvent event, EventCallback callback);
+
+    /**
+     * Process a private command sent from the application to the input method.
+     * This can be used to provide domain-specific features that are
+     * only known between certain input methods and their clients.
+     * 
+     * @param action Name of the command to be performed.  This <em>must</em>
+     * be a scoped name, i.e. prefixed with a package name you own, so that
+     * different developers will not create conflicting commands.
+     * @param data Any data to include with the command.
+     */
+    public void appPrivateCommand(String action, Bundle data);
+}
diff --git a/core/java/android/view/inputmethod/package.html b/core/java/android/view/inputmethod/package.html
new file mode 100644
index 0000000..328c7b3
--- /dev/null
+++ b/core/java/android/view/inputmethod/package.html
@@ -0,0 +1,12 @@
+<html>
+<body>
+Framework classes for interaction between views and input methods (such
+as soft keyboards).  See {@link android.view.inputmethod.InputMethodManager} for
+an overview.  In most cases the main classes here are not needed for
+most applications, since they are dealt with for you by
+{@link android.widget.TextView}.  When implementing a custom text editor,
+however, you will need to implement the
+{@link android.view.inputmethod.InputConnection} class to allow the current
+input method to interact with your view.
+</body>
+</html>
diff --git a/core/java/android/view/package.html b/core/java/android/view/package.html
new file mode 100644
index 0000000..1c58765
--- /dev/null
+++ b/core/java/android/view/package.html
@@ -0,0 +1,6 @@
+<HTML>
+<BODY>
+Provides classes that expose basic user interface classes that handle
+screen layout and interaction with the user.
+</BODY>
+</HTML>
\ No newline at end of file