summaryrefslogtreecommitdiffstats
path: root/core/java/com/android/internal/widget/RotarySelector.java
diff options
context:
space:
mode:
authorKarl Rosaen <krosaen@android.com>2009-09-23 17:00:55 -0700
committerKarl Rosaen <krosaen@android.com>2009-09-24 09:24:42 -0700
commit74646ad618a9ca289efa99b4a822e66ca61b8f95 (patch)
tree35c08d4b724a59a74e151fe77f72a6494c741f16 /core/java/com/android/internal/widget/RotarySelector.java
parent3e81ad2181132da87fb844cda28ac7b2e0738ae0 (diff)
downloadframeworks_base-74646ad618a9ca289efa99b4a822e66ca61b8f95.zip
frameworks_base-74646ad618a9ca289efa99b4a822e66ca61b8f95.tar.gz
frameworks_base-74646ad618a9ca289efa99b4a822e66ca61b8f95.tar.bz2
Update RotarySelector to support vertical orientation, and add resolution specific assets (removing old ones).
Diffstat (limited to 'core/java/com/android/internal/widget/RotarySelector.java')
-rw-r--r--core/java/com/android/internal/widget/RotarySelector.java274
1 files changed, 186 insertions, 88 deletions
diff --git a/core/java/com/android/internal/widget/RotarySelector.java b/core/java/com/android/internal/widget/RotarySelector.java
index 426cef5..a6ce7f8 100644
--- a/core/java/com/android/internal/widget/RotarySelector.java
+++ b/core/java/com/android/internal/widget/RotarySelector.java
@@ -18,7 +18,12 @@ package com.android.internal.widget;
import android.content.Context;
import android.content.res.Resources;
+import android.content.res.TypedArray;
import android.graphics.Canvas;
+import android.graphics.Paint;
+import android.graphics.Bitmap;
+import android.graphics.BitmapFactory;
+import android.graphics.Matrix;
import android.graphics.drawable.Drawable;
import android.os.Vibrator;
import android.util.AttributeSet;
@@ -38,6 +43,9 @@ import com.android.internal.R;
* security pattern is set.
*/
public class RotarySelector extends View {
+ public static final int HORIZONTAL = 0;
+ public static final int VERTICAL = 1;
+
private static final String LOG_TAG = "RotarySelector";
private static final boolean DBG = false;
@@ -47,15 +55,15 @@ public class RotarySelector extends View {
private float mDensity;
// UI elements
- private Drawable mBackground;
+ private Bitmap 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
+ private Bitmap mArrowShortLeftAndRight;
+ private Bitmap mArrowLongLeft; // Long arrow starting on the left, pointing clockwise
+ private Bitmap mArrowLongRight; // Long arrow starting on the right, pointing CCW
// positions of the left and right handle
private int mLeftHandleX;
@@ -74,6 +82,12 @@ public class RotarySelector extends View {
private DecelerateInterpolator mInterpolator;
+ private Paint mPaint = new Paint();
+
+ // used to rotate the background and arrow assets depending on orientation
+ final Matrix mBgMatrix = new Matrix();
+ final Matrix mArrowMatrix = new Matrix();
+
/**
* If the user is currently dragging something.
*/
@@ -117,7 +131,6 @@ public class RotarySelector extends View {
static final int SNAP_BACK_ANIMATION_DURATION_MILLIS = 300;
static final int SPIN_ANIMATION_DURATION_MILLIS = 800;
- private static final boolean DRAW_CENTER_DIMPLE = true;
private int mEdgeTriggerThresh;
private int mDimpleWidth;
private int mBackgroundWidth;
@@ -137,6 +150,10 @@ public class RotarySelector extends View {
*/
private int mDimplesOfFling = 0;
+ /**
+ * Either {@link #HORIZONTAL} or {@link #VERTICAL}.
+ */
+ private int mOrientation;
public RotarySelector(Context context) {
@@ -148,28 +165,23 @@ public class RotarySelector extends View {
*/
public RotarySelector(Context context, AttributeSet attrs) {
super(context, attrs);
- if (DBG) log("IncomingCallDialWidget constructor...");
+
+ TypedArray a =
+ context.obtainStyledAttributes(attrs, R.styleable.RotarySelector);
+ mOrientation = a.getInt(R.styleable.RotarySelector_orientation, HORIZONTAL);
+ a.recycle();
Resources r = getResources();
mDensity = r.getDisplayMetrics().density;
if (DBG) log("- Density: " + mDensity);
// Assets (all are BitmapDrawables).
- mBackground = r.getDrawable(R.drawable.jog_dial_bg_cropped);
+ mBackground = getBitmapFor(R.drawable.jog_dial_bg);
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);
-
- // 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();
- mArrowShortLeftAndRight.setBounds(0, 0, arrowW, arrowH);
- mArrowLongLeft.setBounds(0, 0, arrowW, arrowH);
- mArrowLongRight.setBounds(0, 0, arrowW, arrowH);
+ mArrowLongLeft = getBitmapFor(R.drawable.jog_dial_arrow_long_left_green);
+ mArrowLongRight = getBitmapFor(R.drawable.jog_dial_arrow_long_right_red);
+ mArrowShortLeftAndRight = getBitmapFor(R.drawable.jog_dial_arrow_short_left_and_right);
mInterpolator = new DecelerateInterpolator(1f);
@@ -177,8 +189,8 @@ public class RotarySelector extends View {
mDimpleWidth = mDimple.getIntrinsicWidth();
- mBackgroundWidth = mBackground.getIntrinsicWidth();
- mBackgroundHeight = mBackground.getIntrinsicHeight();
+ mBackgroundWidth = mBackground.getWidth();
+ mBackgroundHeight = mBackground.getHeight();
mOuterRadius = (int) (mDensity * OUTER_ROTARY_RADIUS_DIP);
mInnerRadius = (int) ((OUTER_ROTARY_RADIUS_DIP - ROTARY_STROKE_WIDTH_DIP) * mDensity);
@@ -187,15 +199,35 @@ public class RotarySelector extends View {
mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
}
+ private Bitmap getBitmapFor(int resId) {
+ return BitmapFactory.decodeResource(getContext().getResources(), resId);
+ }
+
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
- mLeftHandleX = (int) (EDGE_PADDING_DIP * mDensity) + mDimpleWidth / 2;
- mRightHandleX =
- getWidth() - (int) (EDGE_PADDING_DIP * mDensity) - mDimpleWidth / 2;
+ final int edgePadding = (int) (EDGE_PADDING_DIP * mDensity);
+ mLeftHandleX = edgePadding + mDimpleWidth / 2;
+ final int length = isHoriz() ? w : h;
+ mRightHandleX = length - edgePadding - mDimpleWidth / 2;
+ mDimpleSpacing = (length / 2) - mLeftHandleX;
+
+ // bg matrix only needs to be calculated once
+ mBgMatrix.setTranslate(0, 0);
+ if (!isHoriz()) {
+ // set up matrix for translating drawing of background and arrow assets
+ final int left = w - mBackgroundHeight;
+ mBgMatrix.preRotate(-90, 0, 0);
+ mBgMatrix.postTranslate(left, h);
+
+ } else {
+ mBgMatrix.postTranslate(0, h - mBackgroundHeight);
+ }
+ }
- mDimpleSpacing = (getWidth() / 2) - mLeftHandleX;
+ private boolean isHoriz() {
+ return mOrientation == HORIZONTAL;
}
/**
@@ -252,28 +284,35 @@ public class RotarySelector extends View {
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
- final int width = MeasureSpec.getSize(widthMeasureSpec); // screen width
-
- final int arrowH = mArrowShortLeftAndRight.getIntrinsicHeight();
- final int backgroundH = mBackgroundHeight;
+ final int length = isHoriz() ?
+ MeasureSpec.getSize(widthMeasureSpec) :
+ MeasureSpec.getSize(heightMeasureSpec);
+ final int arrowScrunch = (int) (ARROW_SCRUNCH_DIP * mDensity);
+ final int arrowH = mArrowShortLeftAndRight.getHeight();
// 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);
- }
+ final int height = mBackgroundHeight + arrowH - arrowScrunch;
-// private Paint mPaint = new Paint();
+ if (isHoriz()) {
+ setMeasuredDimension(length, height);
+ } else {
+ setMeasuredDimension(height, length);
+ }
+ }
@Override
protected void onDraw(Canvas canvas) {
super.onDraw(canvas);
- if (DBG) {
- log(String.format("onDraw: mAnimating=%s, mRotaryOffsetX=%d, mGrabbedState=%d",
- mAnimating, mRotaryOffsetX, mGrabbedState));
- }
+
+ final int width = getWidth();
+
+ // DEBUG: draw bounding box around widget
+// mPaint.setColor(Color.RED);
+// mPaint.setStyle(Paint.Style.STROKE);
+// canvas.drawRect(0, 0, width, getHeight(), mPaint);
final int height = getHeight();
@@ -283,76 +322,113 @@ public class RotarySelector extends View {
}
// Background:
- final int backgroundW = mBackgroundWidth;
- final int backgroundH = mBackgroundHeight;
- 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);
-
+ canvas.drawBitmap(mBackground, mBgMatrix, mPaint);
// Draw the correct arrow(s) depending on the current state:
- Drawable currentArrow;
+ mArrowMatrix.reset();
switch (mGrabbedState) {
case NOTHING_GRABBED:
- currentArrow = null; //mArrowShortLeftAndRight;
+ //mArrowShortLeftAndRight;
break;
case LEFT_HANDLE_GRABBED:
- currentArrow = mArrowLongLeft;
+ mArrowMatrix.setTranslate(0, 0);
+ if (!isHoriz()) {
+ mArrowMatrix.preRotate(-90, 0, 0);
+ mArrowMatrix.postTranslate(0, height);
+ }
+ canvas.drawBitmap(mArrowLongLeft, mArrowMatrix, mPaint);
break;
case RIGHT_HANDLE_GRABBED:
- currentArrow = mArrowLongRight;
+ mArrowMatrix.setTranslate(0, 0);
+ if (!isHoriz()) {
+ mArrowMatrix.preRotate(-90, 0, 0);
+ // since bg width is > height of screen in landscape mode...
+ mArrowMatrix.postTranslate(0, height + (mBackgroundWidth - height));
+ }
+ canvas.drawBitmap(mArrowLongRight, mArrowMatrix, mPaint);
break;
default:
throw new IllegalStateException("invalid mGrabbedState: " + mGrabbedState);
}
- if (currentArrow != null) currentArrow.draw(canvas);
- // debug: draw circle that should match the outer arc (good sanity check)
-// mPaint.setColor(Color.RED);
-// mPaint.setStyle(Paint.Style.STROKE);
+ final int bgHeight = mBackgroundHeight;
+ final int bgTop = isHoriz() ?
+ height - bgHeight:
+ width - bgHeight;
+ // DEBUG: draw circle bounding arc drawable: good sanity check we're doing the math
+ // correctly
// float or = OUTER_ROTARY_RADIUS_DIP * mDensity;
-// canvas.drawCircle(getWidth() / 2, or + mBackground.getBounds().top, or, mPaint);
-
- final int bgTop = mBackground.getBounds().top;
+// final int vOffset = mBackgroundWidth - height;
+// final int midX = isHoriz() ?
+// width / 2 :
+// mBackgroundWidth / 2 - vOffset;
+// if (isHoriz()) {
+// canvas.drawCircle(midX, or + bgTop, or, mPaint);
+// } else {
+// canvas.drawCircle(or + bgTop, midX, or, mPaint);
+// }
+
+ // left dimple / icon
{
final int xOffset = mLeftHandleX + mRotaryOffsetX;
final int drawableY = getYOnArc(
- mBackground,
+ mBackgroundWidth,
mInnerRadius,
mOuterRadius,
xOffset);
-
- drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
- if (mGrabbedState != RIGHT_HANDLE_GRABBED) {
- drawCentered(mLeftHandleIcon, canvas, xOffset, drawableY + bgTop);
+ if (isHoriz()) {
+ drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+ if (mGrabbedState != RIGHT_HANDLE_GRABBED) {
+ drawCentered(mLeftHandleIcon, canvas, xOffset, drawableY + bgTop);
+ }
+ } else {
+ // vertical
+ drawCentered(mDimple, canvas, drawableY + bgTop, height - xOffset);
+ if (mGrabbedState != RIGHT_HANDLE_GRABBED) {
+ drawCentered(mLeftHandleIcon, canvas, drawableY + bgTop, height - xOffset);
+ }
}
}
- if (DRAW_CENTER_DIMPLE) {
- final int xOffset = getWidth() / 2 + mRotaryOffsetX;
+ // center dimple
+ {
+ final int xOffset = isHoriz() ?
+ width / 2 + mRotaryOffsetX:
+ height / 2 + mRotaryOffsetX;
final int drawableY = getYOnArc(
- mBackground,
+ mBackgroundWidth,
mInnerRadius,
mOuterRadius,
xOffset);
- drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+ if (isHoriz()) {
+ drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+ } else {
+ // vertical
+ drawCentered(mDimple, canvas, drawableY + bgTop, height - xOffset);
+ }
}
+ // right dimple / icon
{
final int xOffset = mRightHandleX + mRotaryOffsetX;
final int drawableY = getYOnArc(
- mBackground,
+ mBackgroundWidth,
mInnerRadius,
mOuterRadius,
xOffset);
- drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
- if (mGrabbedState != LEFT_HANDLE_GRABBED) {
- drawCentered(mRightHandleIcon, canvas, xOffset, drawableY + bgTop);
+ if (isHoriz()) {
+ drawCentered(mDimple, canvas, xOffset, drawableY + bgTop);
+ if (mGrabbedState != LEFT_HANDLE_GRABBED) {
+ drawCentered(mRightHandleIcon, canvas, xOffset, drawableY + bgTop);
+ }
+ } else {
+ // vertical
+ drawCentered(mDimple, canvas, drawableY + bgTop, height - xOffset);
+ if (mGrabbedState != LEFT_HANDLE_GRABBED) {
+ drawCentered(mRightHandleIcon, canvas, drawableY + bgTop, height - xOffset);
+ }
}
}
@@ -361,12 +437,16 @@ public class RotarySelector extends View {
final int halfdimple = mDimpleWidth / 2;
while (dimpleLeft > -halfdimple) {
final int drawableY = getYOnArc(
- mBackground,
+ mBackgroundWidth,
mInnerRadius,
mOuterRadius,
dimpleLeft);
- drawCentered(mDimple, canvas, dimpleLeft, drawableY + bgTop);
+ if (isHoriz()) {
+ drawCentered(mDimple, canvas, dimpleLeft, drawableY + bgTop);
+ } else {
+ drawCentered(mDimple, canvas, drawableY + bgTop, height - dimpleLeft);
+ }
dimpleLeft -= mDimpleSpacing;
}
@@ -375,40 +455,43 @@ public class RotarySelector extends View {
final int rightThresh = mRight + halfdimple;
while (dimpleRight < rightThresh) {
final int drawableY = getYOnArc(
- mBackground,
+ mBackgroundWidth,
mInnerRadius,
mOuterRadius,
dimpleRight);
- drawCentered(mDimple, canvas, dimpleRight, drawableY + bgTop);
+ if (isHoriz()) {
+ drawCentered(mDimple, canvas, dimpleRight, drawableY + bgTop);
+ } else {
+ drawCentered(mDimple, canvas, drawableY + bgTop, height - dimpleRight);
+ }
dimpleRight += mDimpleSpacing;
}
}
/**
- * Assuming drawable is a bounding box around a piece of an arc drawn by two concentric circles
+ * Assuming bitmap 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 backgroundWidth The width of the asset (the bottom of the box surrounding the arc).
* @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
+ * @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) {
+ private int getYOnArc(int backgroundWidth, 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;
+ final int triangleBottom = (backgroundWidth / 2) - x;
// "Our offense is like the pythagorean theorem: There is no answer!" - Shaquille O'Neal
final int triangleY =
@@ -437,8 +520,11 @@ public class RotarySelector extends View {
}
mVelocityTracker.addMovement(event);
+ final int height = getHeight();
- final int eventX = (int) event.getX();
+ final int eventX = isHoriz() ?
+ (int) event.getX():
+ height - ((int) event.getY());
final int hitWindow = mDimpleWidth;
final int action = event.getAction();
@@ -468,12 +554,16 @@ public class RotarySelector extends View {
if (mGrabbedState == LEFT_HANDLE_GRABBED) {
mRotaryOffsetX = eventX - mLeftHandleX;
invalidate();
- if (eventX >= getRight() - mEdgeTriggerThresh && !mTriggered) {
+ final int rightThresh = isHoriz() ? getRight() : height;
+ if (eventX >= rightThresh - mEdgeTriggerThresh && !mTriggered) {
mTriggered = true;
dispatchTriggerEvent(OnDialTriggerListener.LEFT_HANDLE);
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int velocity = Math.max(mMinimumVelocity, (int) velocityTracker.getXVelocity());
+ final int rawVelocity = isHoriz() ?
+ (int) velocityTracker.getXVelocity():
+ -(int) velocityTracker.getYVelocity();
+ final int velocity = Math.max(mMinimumVelocity, rawVelocity);
mDimplesOfFling = Math.max(
8,
Math.abs(velocity / mDimpleSpacing));
@@ -490,7 +580,10 @@ public class RotarySelector extends View {
dispatchTriggerEvent(OnDialTriggerListener.RIGHT_HANDLE);
final VelocityTracker velocityTracker = mVelocityTracker;
velocityTracker.computeCurrentVelocity(1000, mMaximumVelocity);
- final int velocity = Math.min(-mMinimumVelocity, (int) velocityTracker.getXVelocity());
+ final int rawVelocity = isHoriz() ?
+ (int) velocityTracker.getXVelocity():
+ - (int) velocityTracker.getYVelocity();
+ final int velocity = Math.min(-mMinimumVelocity, rawVelocity);
mDimplesOfFling = Math.max(
8,
Math.abs(velocity / mDimpleSpacing));
@@ -559,6 +652,7 @@ public class RotarySelector extends View {
final long millisSoFar = currentAnimationTimeMillis() - mAnimationStartTime;
final long millisLeft = mAnimationDuration - millisSoFar;
final int totalDeltaX = mAnimatingDeltaXStart - mAnimatingDeltaXEnd;
+ final boolean goingRight = totalDeltaX < 0;
if (DBG) log("millisleft for animating: " + millisLeft);
if (millisLeft <= 0) {
reset();
@@ -569,13 +663,17 @@ public class RotarySelector extends View {
mInterpolator.getInterpolation((float) millisSoFar / mAnimationDuration);
final int dx = (int) (totalDeltaX * (1 - interpolation));
mRotaryOffsetX = mAnimatingDeltaXEnd + dx;
+
+ // once we have gone far enough to animate the current buttons off screen, we start
+ // wrapping the offset back to the other side so that when the animation is finished,
+ // the buttons will come back into their original places.
if (mDimplesOfFling > 0) {
- if (mRotaryOffsetX < 4 * mDimpleSpacing) {
+ if (!goingRight && mRotaryOffsetX < 3 * mDimpleSpacing) {
// wrap around on fling left
- mRotaryOffsetX += (4 + mDimplesOfFling - 4) * mDimpleSpacing;
- } else if (mRotaryOffsetX > 4 * mDimpleSpacing) {
+ mRotaryOffsetX += mDimplesOfFling * mDimpleSpacing;
+ } else if (goingRight && mRotaryOffsetX > 3 * mDimpleSpacing) {
// wrap around on fling right
- mRotaryOffsetX -= (4 + mDimplesOfFling - 4) * mDimpleSpacing;
+ mRotaryOffsetX -= mDimplesOfFling * mDimpleSpacing;
}
}
invalidate();