Add RotarySelector widget to android.internal for use by lock screen and incoming call screen.
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
new file mode 100644
index 0000000..7b940c9
--- /dev/null
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -0,0 +1,542 @@
+/*
+ * 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 com.android.internal.widget;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Color;
+import android.graphics.drawable.Drawable;
+import android.os.Vibrator;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.animation.AccelerateInterpolator;
+
+import com.android.internal.R;
+
+
+/**
+ * Custom view that presents up to two items that are selectable by rotating a semi-circle from
+ * left to right, or right to left.  Used by incoming call screen, and the lock screen when no
+ * security pattern is set.
+ */
+public class RotarySelector extends View {
+    private static final String LOG_TAG = "RotarySelector";
+    private static final boolean DBG = false;
+
+    // Listener for onDialTrigger() callbacks.
+    private OnDialTriggerListener mOnDialTriggerListener;
+
+    private float mDensity;
+
+    // UI elements
+    private Drawable mBackground;
+    private Drawable mDimple;
+
+    private Drawable mLeftHandleIcon;
+    private Drawable mRightHandleIcon;
+
+    private Drawable mArrowShortLeftAndRight;
+    private Drawable mArrowLongLeft;  // Long arrow starting on the left, pointing clockwise
+    private Drawable mArrowLongRight;  // Long arrow starting on the right, pointing CCW
+
+    // positions of the left and right handle
+    private int mLeftHandleX;
+    private int mRightHandleX;
+
+    // current offset of user's dragging
+    private int mTouchDragOffset = 0;
+
+    // state of the animation used to bring the handle back to its start position when
+    // the user lets go before triggering an action
+    private boolean mAnimating = false;
+    private long mAnimationEndTime;
+    private int mAnimatingDelta;
+    AccelerateInterpolator mInterpolator;
+
+    /**
+     * True after triggering an action if the user of {@link OnDialTriggerListener} wants to
+     * freeze the UI (until they transition to another screen).
+     */
+    private boolean mFrozen = false;
+
+    /**
+     * If the user is currently dragging something.
+     */
+    private int mGrabbedState = NOTHING_GRABBED;
+    private static final int NOTHING_GRABBED = 0;
+    private static final int LEFT_HANDLE_GRABBED = 1;
+    private static final int RIGHT_HANDLE_GRABBED = 2;
+
+    /**
+     * Whether the user has triggered something (e.g dragging the left handle all the way over to
+     * the right).
+     */
+    private boolean mTriggered = false;
+
+    // Vibration (haptic feedback)
+    private Vibrator mVibrator;
+    private static final long VIBRATE_SHORT = 60;  // msec
+    private static final long VIBRATE_LONG = 100;  // msec
+
+    // Various tweakable layout or behavior parameters:
+
+    // How close to the edge of the screen, we let the handle get before
+    // triggering an action:
+    private static final int EDGE_THRESHOLD_DIP = 70;
+
+    /**
+     * The drawable for the arrows need to be scrunched this many dips towards the rotary bg below
+     * it.
+     */
+    private static final int ARROW_SCRUNCH_DIP = 6;
+
+    /**
+     * How far inset the left and right circles should be
+     */
+    private static final int EDGE_PADDING_DIP = 9;
+
+    /**
+     * Dimensions of arc in background drawable.
+     */
+    static final int OUTER_ROTARY_RADIUS_DIP = 390;
+    static final int ROTARY_STROKE_WIDTH_DIP = 83;
+    private static final int ANIMATION_DURATION_MILLIS = 300;
+
+    private static final boolean DRAW_CENTER_DIMPLE = false;
+
+    /**
+     * Constructor used when this widget is created from a layout file.
+     */
+    public RotarySelector(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        if (DBG) log("IncomingCallDialWidget constructor...");
+
+        Resources r = getResources();
+        mDensity = r.getDisplayMetrics().density;
+        if (DBG) log("- Density: " + mDensity);
+        // Density is 1.0 on HVGA (like Dream), and 1.5 on WVGA.
+        // Usage: raw_pixel_value = (int) (dpi_value * mDensity + 0.5f)
+
+        // Assets (all are BitmapDrawables).
+        mBackground = r.getDrawable(R.drawable.jog_dial_bg_cropped);
+        mDimple = r.getDrawable(R.drawable.jog_dial_dimple);
+
+        mArrowLongLeft = r.getDrawable(R.drawable.jog_dial_arrow_long_left_green);
+        mArrowLongRight = r.getDrawable(R.drawable.jog_dial_arrow_long_right_red);
+        mArrowShortLeftAndRight = r.getDrawable(R.drawable.jog_dial_arrow_short_left_and_right);
+
+        mInterpolator = new AccelerateInterpolator();
+    }
+
+    /**
+     * Sets the left handle icon to a given resource.
+     *
+     * The resource should refer to a Drawable object, or use 0 to remove
+     * the icon.
+     *
+     * @param resId the resource ID.
+     */
+    public void setLeftHandleResource(int resId) {
+        Drawable d = null;
+        if (resId != 0) {
+            d = getResources().getDrawable(resId);
+        }
+        setLeftHandleDrawable(d);
+    }
+
+    /**
+     * Sets the left handle icon to a given Drawable.
+     *
+     * @param d the Drawable to use as the icon, or null to remove the icon.
+     */
+    public void setLeftHandleDrawable(Drawable d) {
+        mLeftHandleIcon = d;
+        invalidate();
+    }
+
+    /**
+     * Sets the right handle icon to a given resource.
+     *
+     * The resource should refer to a Drawable object, or use 0 to remove
+     * the icon.
+     *
+     * @param resId the resource ID.
+     */
+    public void setRightHandleResource(int resId) {
+        Drawable d = null;
+        if (resId != 0) {
+            d = getResources().getDrawable(resId);
+        }
+        setRightHandleDrawable(d);
+    }
+
+    /**
+     * Sets the right handle icon to a given Drawable.
+     *
+     * @param d the Drawable to use as the icon, or null to remove the icon.
+     */
+    public void setRightHandleDrawable(Drawable d) {
+        mRightHandleIcon = d;
+        invalidate();
+    }
+
+    @Override
+    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int width = MeasureSpec.getSize(widthMeasureSpec);  // screen width
+
+        final int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight();
+        final int backgroundH = mBackground.getIntrinsicHeight();
+
+        // by making the height less than arrow + bg, arrow and bg will be scrunched together,
+        // overlaying somewhat (though on transparent portions of the drawable).
+        // this works because the arrows are drawn from the top, and the rotary bg is drawn
+        // from the bottom.
+        final int arrowScrunch = (int) (ARROW_SCRUNCH_DIP * mDensity);
+        setMeasuredDimension(width, backgroundH + arrowH - arrowScrunch);
+    }
+
+    @Override
+    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimple.getIntrinsicWidth() / 2;
+        mRightHandleX =
+                getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimple.getIntrinsicWidth() / 2;
+    }
+
+//    private Paint mPaint = new Paint();
+
+    @Override
+    protected void onDraw(Canvas canvas) {
+        super.onDraw(canvas);
+        if (DBG) {
+            log(String.format("onDraw: mAnimating=%s, mTouchDragOffset=%d, mGrabbedState=%d," +
+                    "mFrozen=%s",
+                    mAnimating, mTouchDragOffset, mGrabbedState, mFrozen));
+        }
+
+        final int height = getHeight();
+
+        // update animating state before we draw anything
+        if (mAnimating && !mFrozen) {
+            long millisLeft = mAnimationEndTime - System.currentTimeMillis();
+            if (DBG) log("millisleft for animating: " + millisLeft);
+            if (millisLeft <= 0) {
+                reset();
+            } else {
+                float interpolation = mInterpolator.getInterpolation(
+                        (float) millisLeft / ANIMATION_DURATION_MILLIS);
+                mTouchDragOffset = (int) (mAnimatingDelta * interpolation);
+            }
+        }
+
+
+        // Background:
+        final int backgroundW = mBackground.getIntrinsicWidth();
+        final int backgroundH = mBackground.getIntrinsicHeight();
+        final int backgroundY = height - backgroundH;
+        if (DBG) log("- Background INTRINSIC: " + backgroundW + " x " + backgroundH);
+        mBackground.setBounds(0, backgroundY,
+                              backgroundW, backgroundY + backgroundH);
+        if (DBG) log("  Background BOUNDS: " + mBackground.getBounds());
+        mBackground.draw(canvas);
+
+        // Arrows:
+        // All arrow assets are the same size (they're the full width of
+        // the screen) regardless of which arrows are actually visible.
+        int arrowW = mArrowShortLeftAndRight.getIntrinsicWidth();
+        int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight();
+
+        // Draw the correct arrow(s) depending on the current state:
+        Drawable currentArrow;
+        switch (mGrabbedState) {
+            case NOTHING_GRABBED:
+                currentArrow  = mArrowShortLeftAndRight;
+                break;
+            case LEFT_HANDLE_GRABBED:
+                currentArrow = mArrowLongLeft;
+                break;
+            case RIGHT_HANDLE_GRABBED:
+                currentArrow = mArrowLongRight;
+                break;
+            default:
+                throw new IllegalStateException("invalid mGrabbedState: " + mGrabbedState);
+        }
+        currentArrow.setBounds(0, 0, arrowW, arrowH);
+        currentArrow.draw(canvas);
+
+        // debug: draw circle that should match the outer arc (good sanity check)
+//        mPaint.setColor(Color.RED);
+//        mPaint.setStyle(Paint.Style.STROKE);
+//        float or = OUTER_ROTARY_RADIUS_DIP * mDensity;
+//        canvas.drawCircle(getWidth() / 2, or + mBackground.getBounds().top, or, mPaint);
+
+        final int outerRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP);
+        final int innerRadius =
+                (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity);
+        final int bgTop = mBackground.getBounds().top;
+        {
+            final int xOffset = mLeftHandleX + mTouchDragOffset;
+            final int drawableY = getYOnArc(
+                    mBackground,
+                    innerRadius,
+                    outerRadius,
+                    xOffset);
+
+            drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+            drawCentered(mLeftHandleIcon, canvas, xOffset, drawableY + bgTop);
+        }
+
+        if (DRAW_CENTER_DIMPLE) {
+            final int xOffset = getWidth() / 2 + mTouchDragOffset;
+            final int drawableY = getYOnArc(
+                    mBackground,
+                    innerRadius,
+                    outerRadius,
+                    xOffset);
+
+            drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+        }
+
+        {
+            final int xOffset = mRightHandleX + mTouchDragOffset;
+            final int drawableY = getYOnArc(
+                    mBackground,
+                    innerRadius,
+                    outerRadius,
+                    xOffset);
+
+            drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+            drawCentered(mRightHandleIcon, canvas, xOffset, drawableY + bgTop);
+        }
+
+        if (mAnimating) invalidate();
+    }
+
+    /**
+     * Assuming drawable is a bounding box around a piece of an arc drawn by two concentric circles
+     * (as the background drawable for the rotary widget is), and given an x coordinate along the
+     * drawable, return the y coordinate of a point on the arc that is between the two concentric
+     * circles.  The resulting y combined with the incoming x is a point along the circle in
+     * between the two concentric circles.
+     *
+     * @param drawable The drawable.
+     * @param innerRadius The radius of the circle that intersects the drawable at the bottom two
+     *        corders of the drawable (top two corners in terms of drawing coordinates).
+     * @param outerRadius The radius of the circle who's top most point is the top center of the
+     *        drawable (bottom center in terms of drawing coordinates).
+     * @param x The distance along the x axis of the desired point.
+     * @return The y coordinate, in drawing coordinates, that will place (x, y) along the circle
+     *        in between the two concentric circles.
+     */
+    private int getYOnArc(Drawable drawable, int innerRadius, int outerRadius, int x) {
+
+        // the hypotenuse
+        final int halfWidth = (outerRadius - innerRadius) / 2;
+        final int middleRadius = innerRadius + halfWidth;
+
+        // the bottom leg of the triangle
+        final int triangleBottom = (drawable.getIntrinsicWidth() / 2) - x;
+
+        // "Our offense is like the pythagorean theorem: There is no answer!" - Shaquille O'Neal
+        final int triangleY =
+                (int) Math.sqrt(middleRadius * middleRadius - triangleBottom * triangleBottom);
+
+        // convert to drawing coordinates:
+        // middleRadius - triangleY =
+        //   the vertical distance from the outer edge of the circle to the desired point
+        // from there we add the distance from the top of the drawable to the middle circle
+        return middleRadius - triangleY + halfWidth;
+    }
+
+    /**
+     * Handle touch screen events.
+     *
+     * @param event The motion event.
+     * @return True if the event was handled, false otherwise.
+     */
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        if (mAnimating || mFrozen) {
+            return true;
+        }
+
+        final int eventX = (int) event.getX();
+        final int hitWindow = mDimple.getIntrinsicWidth();
+
+        if (event.getAction() == MotionEvent.ACTION_DOWN) {
+            if (DBG) log("touch-down");
+            mTriggered = false;
+            if (mGrabbedState != RotarySelector.NOTHING_GRABBED) {
+                reset();
+                invalidate();
+            }
+            if (eventX < mLeftHandleX + hitWindow) {
+                mTouchDragOffset = eventX - mLeftHandleX;
+                mGrabbedState = RotarySelector.LEFT_HANDLE_GRABBED;
+                invalidate();
+                vibrate(VIBRATE_SHORT);
+            } else if (eventX > mRightHandleX - hitWindow) {
+                mTouchDragOffset = eventX - mRightHandleX;
+                mGrabbedState = RotarySelector.RIGHT_HANDLE_GRABBED;
+                invalidate();
+                vibrate(VIBRATE_SHORT);
+            }
+        } else if (event.getAction() == MotionEvent.ACTION_MOVE) {
+            if (DBG) log("touch-move");
+            if (mGrabbedState == RotarySelector.LEFT_HANDLE_GRABBED) {
+                mTouchDragOffset = eventX - mLeftHandleX;
+                invalidate();
+                if (eventX >= mRightHandleX - EDGE_PADDING_DIP && !mTriggered) {
+                    mTriggered = true;
+                    mFrozen = dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE);
+                }
+            } else if (mGrabbedState == RotarySelector.RIGHT_HANDLE_GRABBED) {
+                mTouchDragOffset = eventX - mRightHandleX;
+                invalidate();
+                if (eventX <= mLeftHandleX + EDGE_PADDING_DIP && !mTriggered) {
+                    mTriggered = true;
+                    mFrozen = dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE);
+                }
+            }
+        } else if ((event.getAction() == MotionEvent.ACTION_UP)) {
+            if (DBG) log("touch-up");
+            // handle animating back to start if they didn't trigger
+            if (mGrabbedState == RotarySelector.LEFT_HANDLE_GRABBED
+                    && Math.abs(eventX - mLeftHandleX) > 5) {
+                mAnimating = true;
+                mAnimationEndTime = System.currentTimeMillis() + ANIMATION_DURATION_MILLIS;
+                mAnimatingDelta = eventX - mLeftHandleX;
+            } else if (mGrabbedState == RotarySelector.RIGHT_HANDLE_GRABBED
+                    && Math.abs(eventX - mRightHandleX) > 5) {
+                mAnimating = true;
+                mAnimationEndTime = System.currentTimeMillis() + ANIMATION_DURATION_MILLIS;
+                mAnimatingDelta = eventX - mRightHandleX;
+            }
+
+            mTouchDragOffset = 0;
+            mGrabbedState = RotarySelector.NOTHING_GRABBED;
+            invalidate();
+        } else if (event.getAction() == MotionEvent.ACTION_CANCEL) {
+            if (DBG) log("touch-cancel");
+            reset();
+            invalidate();
+        }
+        return true;
+    }
+
+    private void reset() {
+        mAnimating = false;
+        mTouchDragOffset = 0;
+        mGrabbedState = RotarySelector.NOTHING_GRABBED;
+        mTriggered = false;
+    }
+
+    /**
+     * Triggers haptic feedback.
+     */
+    private synchronized void vibrate(long duration) {
+        if (mVibrator == null) {
+            mVibrator = (android.os.Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
+        }
+        mVibrator.vibrate(duration);
+    }
+
+    /**
+     * Sets the bounds of the specified Drawable so that it's centered
+     * on the point (x,y), then draws it onto the specified canvas.
+     * TODO: is there already a utility method somewhere for this?
+     */
+    private static void drawCentered(Drawable d, Canvas c, int x, int y) {
+        int w = d.getIntrinsicWidth();
+        int h = d.getIntrinsicHeight();
+
+        // if (DBG) log("--> drawCentered: " + x + " , " + y + "; intrinsic " + w + " x " + h);
+        d.setBounds(x - (w / 2), y - (h / 2),
+                    x + (w / 2), y + (h / 2));
+        d.draw(c);
+    }
+
+
+    /**
+     * Registers a callback to be invoked when the dial
+     * is "triggered" by rotating it one way or the other.
+     *
+     * @param l the OnDialTriggerListener to attach to this view
+     */
+    public void setOnDialTriggerListener(OnDialTriggerListener l) {
+        mOnDialTriggerListener = l;
+    }
+
+    /**
+     * Dispatches a trigger event to our listener.
+     */
+    private boolean dispatchTriggerEvent(int whichHandle) {
+        vibrate(VIBRATE_LONG);
+        if (mOnDialTriggerListener != null) {
+            return mOnDialTriggerListener.onDialTrigger(this, whichHandle);
+        }
+        return false;
+    }
+
+    /**
+     * Interface definition for a callback to be invoked when the dial
+     * is "triggered" by rotating it one way or the other.
+     */
+    public interface OnDialTriggerListener {
+        /**
+         * The dial was triggered because the user grabbed the left handle,
+         * and rotated the dial clockwise.
+         */
+        public static final int LEFT_HANDLE = 1;
+
+        /**
+         * The dial was triggered because the user grabbed the right handle,
+         * and rotated the dial counterclockwise.
+         */
+        public static final int RIGHT_HANDLE = 2;
+
+        /**
+         * @hide
+         * The center handle is currently unused.
+         */
+        public static final int CENTER_HANDLE = 3;
+
+        /**
+         * Called when the dial is triggered.
+         *
+         * @param v The view that was triggered
+         * @param whichHandle  Which "dial handle" the user grabbed,
+         *        either {@link #LEFT_HANDLE}, {@link #RIGHT_HANDLE}, or
+         *        {@link #CENTER_HANDLE}.
+         * @return Whether the widget should freeze (e.g when the action goes to another screen,
+         *         you want the UI to stay put until the transition occurs).
+         */
+        boolean onDialTrigger(View v, int whichHandle);
+    }
+
+
+    // Debugging / testing code
+
+    private void log(String msg) {
+        Log.d(LOG_TAG, msg);
+    }
+}
diff --git a/core/res/res/drawable/ic_jog_dial_answer.png b/core/res/res/drawable/ic_jog_dial_answer.png
new file mode 100644
index 0000000..e2bc483
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_answer.png
Binary files differ
diff --git a/core/res/res/drawable/ic_jog_dial_decline.png b/core/res/res/drawable/ic_jog_dial_decline.png
new file mode 100644
index 0000000..81c76b5
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_decline.png
Binary files differ
diff --git a/core/res/res/drawable/ic_jog_dial_silence_ringer.png b/core/res/res/drawable/ic_jog_dial_silence_ringer.png
new file mode 100644
index 0000000..6d573e6
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_silence_ringer.png
Binary files differ
diff --git a/core/res/res/drawable/ic_jog_dial_turn_ring_vol_off.png b/core/res/res/drawable/ic_jog_dial_turn_ring_vol_off.png
new file mode 100644
index 0000000..3804c25
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_turn_ring_vol_off.png
Binary files differ
diff --git a/core/res/res/drawable/ic_jog_dial_turn_ring_vol_on.png b/core/res/res/drawable/ic_jog_dial_turn_ring_vol_on.png
new file mode 100644
index 0000000..62f8e15
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_turn_ring_vol_on.png
Binary files differ
diff --git a/core/res/res/drawable/ic_jog_dial_unlock.png b/core/res/res/drawable/ic_jog_dial_unlock.png
new file mode 100644
index 0000000..e697d91
--- /dev/null
+++ b/core/res/res/drawable/ic_jog_dial_unlock.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_long_left_green.png b/core/res/res/drawable/jog_dial_arrow_long_left_green.png
new file mode 100644
index 0000000..334a8e0
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_long_left_green.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_long_left_yellow.png b/core/res/res/drawable/jog_dial_arrow_long_left_yellow.png
new file mode 100644
index 0000000..2e011ca3
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_long_left_yellow.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_long_middle_yellow.png b/core/res/res/drawable/jog_dial_arrow_long_middle_yellow.png
new file mode 100644
index 0000000..323745e
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_long_middle_yellow.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_long_right_red.png b/core/res/res/drawable/jog_dial_arrow_long_right_red.png
new file mode 100644
index 0000000..1e97c9a
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_long_right_red.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_long_right_yellow.png b/core/res/res/drawable/jog_dial_arrow_long_right_yellow.png
new file mode 100644
index 0000000..3536e58
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_long_right_yellow.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_short_left.png b/core/res/res/drawable/jog_dial_arrow_short_left.png
new file mode 100644
index 0000000..4a4ab3ae
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_short_left.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_short_left_and_right.png b/core/res/res/drawable/jog_dial_arrow_short_left_and_right.png
new file mode 100644
index 0000000..987cfa7
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_short_left_and_right.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_arrow_short_right.png b/core/res/res/drawable/jog_dial_arrow_short_right.png
new file mode 100644
index 0000000..ee79875
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_arrow_short_right.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_bg_cropped.png b/core/res/res/drawable/jog_dial_bg_cropped.png
new file mode 100644
index 0000000..60d93d2
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_bg_cropped.png
Binary files differ
diff --git a/core/res/res/drawable/jog_dial_dimple.png b/core/res/res/drawable/jog_dial_dimple.png
new file mode 100644
index 0000000..85d3a43
--- /dev/null
+++ b/core/res/res/drawable/jog_dial_dimple.png
Binary files differ
diff --git a/core/res/res/layout/keyguard_screen_rotary_unlock.xml b/core/res/res/layout/keyguard_screen_rotary_unlock.xml
new file mode 100644
index 0000000..cf97d04
--- /dev/null
+++ b/core/res/res/layout/keyguard_screen_rotary_unlock.xml
@@ -0,0 +1,136 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+**
+** Copyright 2009, The Android Open Source Project
+**
+** Licensed under the Apache License, Version 2.0 (the "License")
+** you may not use this file except in compliance with the License.
+** You may obtain a copy of the License at
+**
+**     http://www.apache.org/licenses/LICENSE-2.0
+**
+** Unless required by applicable law or agreed to in writing, software
+** distributed under the License is distributed on an "AS IS" BASIS,
+** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+** See the License for the specific language governing permissions and
+** limitations under the License.
+*/
+-->
+
+<!-- This is the general lock screen which shows information about the
+  state of the device, as well as instructions on how to get past it
+  depending on the state of the device.  It is the same for landscape
+  and portrait.-->
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+             android:layout_width="fill_parent"
+             android:layout_height="fill_parent"
+             android:id="@+id/root"
+                 >
+
+<RelativeLayout
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:background="#A0000000"
+        >
+
+    <TextView
+        android:id="@+id/carrier"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_alignParentTop="true"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="20dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorSecondary"
+        />
+
+    <TextView
+        android:id="@+id/time"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/carrier"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="25dip"
+        android:textAppearance="?android:attr/textAppearanceLarge"
+        android:textSize="55sp"
+        />
+
+    <TextView
+        android:id="@+id/date"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/time"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="-12dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        />
+
+    <View
+        android:id="@+id/divider"
+        android:layout_width="fill_parent"
+        android:layout_height="1dip"
+        android:layout_marginTop="10dip"
+        android:layout_below="@id/date"
+        android:background="@android:drawable/divider_horizontal_dark"
+            />
+
+    <TextView
+        android:id="@+id/status1"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/divider"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="6dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorSecondary"
+        />
+
+    <TextView
+        android:id="@+id/status2"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/status1"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="6dip"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorSecondary"
+        />
+
+    <TextView
+        android:id="@+id/screenLocked"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/status2"
+        android:layout_centerHorizontal="true"
+        android:textAppearance="?android:attr/textAppearanceMedium"
+        android:textColor="?android:attr/textColorSecondary"
+        android:gravity="center"
+        android:layout_marginTop="12dip"
+        />
+
+    <!-- By having the rotary selector hang below "screen locked" text, we get a layout more
+         robust for different screen sizes.  On wvga, the widget should be flush with the bottom.-->
+    <com.android.internal.widget.RotarySelector
+        android:id="@+id/rotary"
+        android:layout_width="fill_parent"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/screenLocked"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="24dip"
+        />
+
+    <!-- emergency call button shown when sim is missing or PUKd -->
+    <Button
+        android:id="@+id/emergencyCallButton"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:layout_below="@id/screenLocked"
+        android:layout_centerHorizontal="true"
+        android:layout_marginTop="24dip"
+        android:drawableLeft="@drawable/ic_emergency"
+        android:drawablePadding="8dip"
+       />
+
+</RelativeLayout>
+
+</FrameLayout>
\ No newline at end of file