Type: INTEGER
*/ public static final String ERROR = "error"; + /** + * Indicates whether this thread contains any attachments. + *Type: INTEGER
+ */ + public static final String HAS_ATTACHMENT = "has_attachment"; } /** diff --git a/core/java/android/text/InputType.java b/core/java/android/text/InputType.java index f643f9256c989..d50684af911d5 100644 --- a/core/java/android/text/InputType.java +++ b/core/java/android/text/InputType.java @@ -178,10 +178,28 @@ public interface InputType { */ public static final int TYPE_TEXT_VARIATION_PASSWORD = 0x00000080; + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering a password, which should + * be visible to the user. + */ + public static final int TYPE_TEXT_VARIATION_VISIBLE_PASSWORD = 0x00000090; + /** * Variation of {@link #TYPE_CLASS_TEXT}: entering text inside of a web form. */ - public static final int TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x00000090; + public static final int TYPE_TEXT_VARIATION_WEB_EDIT_TEXT = 0x000000a0; + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering text to filter contents + * of a list etc. + */ + public static final int TYPE_TEXT_VARIATION_FILTER = 0x000000b0; + + /** + * Variation of {@link #TYPE_CLASS_TEXT}: entering text for phonetic + * pronunciation, such as a phonetic name field in contacts. + */ + public static final int TYPE_TEXT_VARIATION_PHONETIC = 0x000000c0; // ---------------------------------------------------------------------- // ---------------------------------------------------------------------- diff --git a/core/java/android/text/method/ArrowKeyMovementMethod.java b/core/java/android/text/method/ArrowKeyMovementMethod.java index 6df0b35cb497a..8aa49afe45c5b 100644 --- a/core/java/android/text/method/ArrowKeyMovementMethod.java +++ b/core/java/android/text/method/ArrowKeyMovementMethod.java @@ -204,7 +204,7 @@ implements MovementMethod MotionEvent event) { boolean handled = Touch.onTouchEvent(widget, buffer, event); - if (widget.isFocused()) { + if (widget.isFocused() && !widget.didTouchFocusSelectAll()) { if (event.getAction() == MotionEvent.ACTION_UP) { int x = (int) event.getX(); int y = (int) event.getY(); diff --git a/core/java/android/text/method/QwertyKeyListener.java b/core/java/android/text/method/QwertyKeyListener.java index 0b3951755e94c..3f8288c83e69c 100644 --- a/core/java/android/text/method/QwertyKeyListener.java +++ b/core/java/android/text/method/QwertyKeyListener.java @@ -296,20 +296,27 @@ public class QwertyKeyListener extends BaseKeyListener { String old = new String(repl[0].mText); content.removeSpan(repl[0]); - content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT, - en, en, Spannable.SPAN_POINT_POINT); - content.replace(st, en, old); - en = content.getSpanStart(TextKeyListener.INHIBIT_REPLACEMENT); - if (en - 1 >= 0) { + // only cancel the autocomplete if the cursor is at the end of + // the replaced span + if (selStart == en) { content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT, - en - 1, en, - Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); - } else { - content.removeSpan(TextKeyListener.INHIBIT_REPLACEMENT); - } + en, en, Spannable.SPAN_POINT_POINT); + content.replace(st, en, old); - adjustMetaAfterKeypress(content); + en = content.getSpanStart(TextKeyListener.INHIBIT_REPLACEMENT); + if (en - 1 >= 0) { + content.setSpan(TextKeyListener.INHIBIT_REPLACEMENT, + en - 1, en, + Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); + } else { + content.removeSpan(TextKeyListener.INHIBIT_REPLACEMENT); + } + adjustMetaAfterKeypress(content); + } else { + adjustMetaAfterKeypress(content); + return super.onKeyDown(view, content, keyCode, event); + } return true; } diff --git a/core/java/android/view/ViewGroup.java b/core/java/android/view/ViewGroup.java index 70cc2a9c3390b..dc7b299cade7a 100644 --- a/core/java/android/view/ViewGroup.java +++ b/core/java/android/view/ViewGroup.java @@ -1387,14 +1387,10 @@ public abstract class ViewGroup extends View implements ViewParent, ViewManager // 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)); + + final int left = cl + (int) region.left; + final int top = ct + (int) region.top; + invalidate(left, top, left + (int) region.width(), top + (int) region.height()); } } } else if ((flags & FLAG_SUPPORT_STATIC_TRANSFORMATIONS) == diff --git a/core/java/android/view/animation/Animation.java b/core/java/android/view/animation/Animation.java index b9c8ec35637f2..a6627609e3be9 100644 --- a/core/java/android/view/animation/Animation.java +++ b/core/java/android/view/animation/Animation.java @@ -340,6 +340,7 @@ public abstract class Animation implements Cloneable { * to run. */ public void restrictDuration(long durationMillis) { + // If we start after the duration, then we just won't run. if (mStartOffset > durationMillis) { mStartOffset = durationMillis; mDuration = 0; @@ -349,11 +350,26 @@ public abstract class Animation implements Cloneable { long dur = mDuration + mStartOffset; if (dur > durationMillis) { - mDuration = dur = durationMillis-mStartOffset; + mDuration = durationMillis-mStartOffset; + dur = durationMillis; } + // If the duration is 0 or less, then we won't run. + if (mDuration <= 0) { + mDuration = 0; + mRepeatCount = 0; + return; + } + // Reduce the number of repeats to keep below the maximum duration. + // The comparison between mRepeatCount and duration is to catch + // overflows after multiplying them. if (mRepeatCount < 0 || mRepeatCount > durationMillis || (dur*mRepeatCount) > durationMillis) { - mRepeatCount = (int)(durationMillis/dur); + // Figure out how many times to do the animation. Subtract 1 since + // repeat count is the number of times to repeat so 0 runs once. + mRepeatCount = (int)(durationMillis/dur) - 1; + if (mRepeatCount < 0) { + mRepeatCount = 0; + } } } @@ -416,7 +432,7 @@ public abstract class Animation implements Cloneable { * 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. + * into account. The repeat count is 0 by default. * * @param repeatCount the number of times the animation should be repeated * @attr ref android.R.styleable#Animation_repeatCount @@ -806,6 +822,8 @@ public abstract class Animation implements Cloneable { invalidate.set(left, top, right, bottom); transformation.getMatrix().mapRect(invalidate); + // Enlarge the invalidate region to account for rounding errors + invalidate.inset(-1.0f, -1.0f); tempRegion.set(invalidate); invalidate.union(previousRegion); @@ -830,6 +848,8 @@ public abstract class Animation implements Cloneable { public void initializeInvalidateRegion(int left, int top, int right, int bottom) { final RectF region = mPreviousRegion; region.set(left, top, right, bottom); + // Enlarge the invalidate region to account for rounding errors + region.inset(-1.0f, -1.0f); if (mFillBefore) { final Transformation previousTransformation = mPreviousTransformation; applyTransformation(0.0f, previousTransformation); diff --git a/core/java/android/view/animation/AnimationSet.java b/core/java/android/view/animation/AnimationSet.java index 590ce062eb525..98b259451ae18 100644 --- a/core/java/android/view/animation/AnimationSet.java +++ b/core/java/android/view/animation/AnimationSet.java @@ -263,35 +263,13 @@ public class AnimationSet extends Animation { 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); + region.inset(-1.0f, -1.0f); if (mFillBefore) { final int count = mAnimations.size(); @@ -400,8 +378,12 @@ public class AnimationSet extends Animation { long[] storedOffsets = mStoredOffsets; - if (storedOffsets == null || storedOffsets.length != count) { - storedOffsets = mStoredOffsets = new long[count]; + if (startOffsetSet) { + if (storedOffsets == null || storedOffsets.length != count) { + storedOffsets = mStoredOffsets = new long[count]; + } + } else if (storedOffsets != null) { + storedOffsets = mStoredOffsets = null; } for (int i = 0; i < count; i++) { @@ -446,7 +428,6 @@ public class AnimationSet extends Animation { final ArrayList* Includes error because we compare this to the result of * getDelta(getClosestTickeAngle(..), oldAngle) which ends up having some * rounding error. @@ -55,58 +83,93 @@ public class ZoomRing extends View { private static final int MAX_ABS_JUMP_DELTA_ANGLE = (2 * PI_INT_MULTIPLIED / 3) + RADIAN_INT_ERROR; - /** The cached X of our center. */ + /** The cached X of the zoom ring's center (in zoom ring coordinates). */ private int mCenterX; - /** The cached Y of our center. */ + /** The cached Y of the zoom ring's center (in zoom ring coordinates). */ private int mCenterY; /** The angle of the thumb (in int radians) */ private int mThumbAngle; + /** The cached width/2 of the zoom ring. */ private int mThumbHalfWidth; + /** The cached height/2 of the zoom ring. */ private int mThumbHalfHeight; + /** + * The bound for the thumb's movement when it is being dragged clockwise. + * Can be Integer.MIN_VALUE if there is no bound in this direction. + */ private int mThumbCwBound = Integer.MIN_VALUE; + /** + * The bound for the thumb's movement when it is being dragged + * counterclockwise. Can be Integer.MIN_VALUE if there is no bound in this + * direction. + */ private int mThumbCcwBound = Integer.MIN_VALUE; + + /** + * Whether to enforce the maximum absolute jump delta. See + * {@link #MAX_ABS_JUMP_DELTA_ANGLE}. + */ private boolean mEnforceMaxAbsJump = true; /** The inner radius of the track. */ - private int mBoundInnerRadiusSquared = 0; + private int mTrackInnerRadius; + /** Cached square of the inner radius of the track. */ + private int mTrackInnerRadiusSquared; /** The outer radius of the track. */ - private int mBoundOuterRadiusSquared = Integer.MAX_VALUE; + private int mTrackOuterRadius; + /** Cached square of the outer radius of the track. */ + private int mTrackOuterRadiusSquared; + /** The raw X of where the widget previously was located. */ private int mPreviousWidgetDragX; + /** The raw Y of where the widget previously was located. */ private int mPreviousWidgetDragY; + /** Whether the thumb should be visible. */ private boolean mThumbVisible = true; + + /** The drawable for the thumb. */ private Drawable mThumbDrawable; /** Shown beneath the thumb if we can still zoom in. */ - private Drawable mThumbPlusArrowDrawable; + private Drawable mZoomInArrowDrawable; /** Shown beneath the thumb if we can still zoom out. */ - private Drawable mThumbMinusArrowDrawable; + private Drawable mZoomOutArrowDrawable; + + /** @see #mThumbArrowsToDraw */ private static final int THUMB_ARROW_PLUS = 1 << 0; + /** @see #mThumbArrowsToDraw */ private static final int THUMB_ARROW_MINUS = 1 << 1; /** Bitwise-OR of {@link #THUMB_ARROW_MINUS} and {@link #THUMB_ARROW_PLUS} */ private int mThumbArrowsToDraw; + + /** The duration for the thumb arrows fading out */ private static final int THUMB_ARROWS_FADE_DURATION = 300; + /** The time when the fade out started. */ private long mThumbArrowsFadeStartTime; + /** The current alpha for the thumb arrows. */ private int mThumbArrowsAlpha = 255; - private static final int THUMB_PLUS_MINUS_DISTANCE = 69; - private static final int THUMB_PLUS_MINUS_OFFSET_ANGLE = TWO_PI_INT_MULTIPLIED / 11; + /** The distance from the center to the zoom arrow hints (usually plus and minus). */ + private int mZoomArrowHintDistance; + /** The offset angle from the thumb angle to draw the zoom arrow hints. */ + private int mZoomArrowHintOffsetAngle = TWO_PI_INT_MULTIPLIED / 11; /** Drawn (without rotation) on top of the arrow. */ - private Drawable mThumbPlusDrawable; + private Drawable mZoomInArrowHintDrawable; /** Drawn (without rotation) on top of the arrow. */ - private Drawable mThumbMinusDrawable; + private Drawable mZoomOutArrowHintDrawable; + /** Zoom ring is just chillin' */ private static final int MODE_IDLE = 0; - /** * User has his finger down somewhere on the ring (besides the thumb) and we * are waiting for him to move the slop amount before considering him in the * drag thumb state. */ private static final int MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP = 5; + /** User is dragging the thumb. */ private static final int MODE_DRAG_THUMB = 1; /** * User has his finger down, but we are waiting for him to pass the touch @@ -114,51 +177,65 @@ public class ZoomRing extends View { * show the movable hint. */ private static final int MODE_WAITING_FOR_MOVE_ZOOM_RING = 4; + /** User is moving the zoom ring. */ private static final int MODE_MOVE_ZOOM_RING = 2; + /** User is dragging the thumb via tap-drag. */ private static final int MODE_TAP_DRAG = 3; /** Ignore the touch interaction until the user touches the thumb again. */ private static final int MODE_IGNORE_UNTIL_TOUCHES_THUMB = 6; + /** The current mode of interaction. */ private int mMode; - /** Records the last mode the user was in. */ private int mPreviousMode; - + + /** The previous time of the up-touch on the center. */ private long mPreviousCenterUpTime; + /** The previous X of down-touch. */ private int mPreviousDownX; + /** The previous Y of down-touch. */ private int mPreviousDownY; - private int mWaitingForDragThumbDownAngle; + /** The angle where the user first grabbed the thumb. */ + private int mInitialGrabThumbAngle; + /** The callback. */ private OnZoomRingCallback mCallback; - private int mPreviousCallbackAngle; - private int mCallbackThreshold = Integer.MAX_VALUE; + /** The tick angle that we previously called back with. */ + private int mPreviousCallbackTickAngle; + /** The delta angle between ticks. A tick is a callback point. */ + private int mTickDelta = Integer.MAX_VALUE; /** If the user drags to within __% of a tick, snap to that tick. */ - private int mFuzzyCallbackThreshold = Integer.MAX_VALUE; + private int mFuzzyTickDelta = Integer.MAX_VALUE; - private boolean mResetThumbAutomatically = true; + /** The angle where the thumb is officially starting to be dragged. */ private int mThumbDragStartAngle; - private final int mTouchSlop; - + /** The drawable for the zoom trail. */ private Drawable mTrail; + /** The accumulated angle for the trail. */ private double mAcculumalatedTrailAngle; + /** The animation-step tracker for scrolling the thumb to a particular position. */ private Scroller mThumbScroller; + /** Whether to ever vibrate when passing a tick. */ private boolean mVibration = true; - private static final int MSG_THUMB_SCROLLER_TICK = 1; - private static final int MSG_THUMB_ARROWS_FADE_TICK = 2; + /** The drawable used to hint that this can pan its owner. */ + private Drawable mPanningArrowsDrawable; + + private static final int MSG_THUMB_SCROLLER_STEP = 1; + private static final int MSG_THUMB_ARROWS_FADE_STEP = 2; private Handler mHandler = new Handler() { @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_THUMB_SCROLLER_TICK: - onThumbScrollerTick(); + case MSG_THUMB_SCROLLER_STEP: + onThumbScrollerStep(); break; - case MSG_THUMB_ARROWS_FADE_TICK: - onThumbArrowsFadeTick(); + case MSG_THUMB_ARROWS_FADE_STEP: + onThumbArrowsFadeStep(); break; } } @@ -170,50 +247,64 @@ public class ZoomRing extends View { ViewConfiguration viewConfiguration = ViewConfiguration.get(context); mTouchSlop = viewConfiguration.getScaledTouchSlop(); - // TODO get drawables from style instead + TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ZoomRing, defStyle, 0); + mThumbDistance = (int) a.getDimension(R.styleable.ZoomRing_thumbDistance, 0); + setTrackRadii( + (int) a.getDimension(R.styleable.ZoomRing_trackInnerRadius, 0), + (int) a.getDimension(R.styleable.ZoomRing_trackOuterRadius, Integer.MAX_VALUE)); + mThumbDrawable = a.getDrawable(R.styleable.ZoomRing_thumbDrawable); + mZoomInArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowDrawable); + mZoomOutArrowDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowDrawable); + mZoomInArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomInArrowHintDrawable); + mZoomOutArrowHintDrawable = a.getDrawable(R.styleable.ZoomRing_zoomOutArrowHintDrawable); + mZoomArrowHintDistance = + (int) a.getDimension(R.styleable.ZoomRing_zoomArrowHintDistance, 0); + mZoomArrowHintOffsetAngle = + (int) (a.getInteger(R.styleable.ZoomRing_zoomArrowHintOffsetAngle, 0) + * TWO_PI_INT_MULTIPLIED / 360); + mPanningArrowsDrawable = a.getDrawable(R.styleable.ZoomRing_panningArrowsDrawable); + a.recycle(); + Resources res = context.getResources(); - mThumbDrawable = res.getDrawable(R.drawable.zoom_ring_thumb); - mThumbPlusArrowDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_plus_arrow_rotatable). - mutate(); - mThumbMinusArrowDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_minus_arrow_rotatable). - mutate(); - mThumbPlusDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_plus); - mThumbMinusDrawable = res.getDrawable(R.drawable.zoom_ring_thumb_minus); if (DRAW_TRAIL) { + // TODO get drawables from style instead mTrail = res.getDrawable(R.drawable.zoom_ring_trail).mutate(); } - // TODO: add padding to drawable - setBackgroundResource(R.drawable.zoom_ring_track); - // TODO get from style - setRingBounds(43, Integer.MAX_VALUE); - mThumbHalfHeight = mThumbDrawable.getIntrinsicHeight() / 2; mThumbHalfWidth = mThumbDrawable.getIntrinsicWidth() / 2; - setCallbackThreshold(PI_INT_MULTIPLIED / 6); + setTickDelta(PI_INT_MULTIPLIED / 6); } public ZoomRing(Context context, AttributeSet attrs) { - this(context, attrs, 0); + this(context, attrs, com.android.internal.R.attr.zoomRingStyle); } public ZoomRing(Context context) { this(context, null); } + public void setTrackDrawable(Drawable drawable) { + setBackgroundDrawable(drawable); + } + public void setCallback(OnZoomRingCallback callback) { mCallback = callback; } - // TODO: rename - public void setCallbackThreshold(int callbackThreshold) { - mCallbackThreshold = callbackThreshold; - mFuzzyCallbackThreshold = (int) (callbackThreshold * 0.65f); + /** + * Sets the distance between ticks. This will be used as a callback threshold. + * + * @param angle The angle between ticks. + */ + public void setTickDelta(int angle) { + mTickDelta = angle; + mFuzzyTickDelta = (int) (angle * 0.65f); } - public void setVibration(boolean vibrate) { - mVibration = vibrate; + public void setVibration(boolean vibration) { + mVibration = vibration; } public void setThumbVisible(boolean thumbVisible) { @@ -223,28 +314,42 @@ public class ZoomRing extends View { } } - // TODO: from XML too - public void setRingBounds(int innerRadius, int outerRadius) { - mBoundInnerRadiusSquared = innerRadius * innerRadius; - if (mBoundInnerRadiusSquared < innerRadius) { + public Drawable getPanningArrowsDrawable() { + return mPanningArrowsDrawable; + } + + public void setTrackRadii(int innerRadius, int outerRadius) { + mTrackInnerRadius = innerRadius; + mTrackOuterRadius = outerRadius; + + mTrackInnerRadiusSquared = innerRadius * innerRadius; + if (mTrackInnerRadiusSquared < innerRadius) { // Prevent overflow - mBoundInnerRadiusSquared = Integer.MAX_VALUE; + mTrackInnerRadiusSquared = Integer.MAX_VALUE; } - mBoundOuterRadiusSquared = outerRadius * outerRadius; - if (mBoundOuterRadiusSquared < outerRadius) { + mTrackOuterRadiusSquared = outerRadius * outerRadius; + if (mTrackOuterRadiusSquared < outerRadius) { // Prevent overflow - mBoundOuterRadiusSquared = Integer.MAX_VALUE; + mTrackOuterRadiusSquared = Integer.MAX_VALUE; } } + public int getTrackInnerRadius() { + return mTrackInnerRadius; + } + + public int getTrackOuterRadius() { + return mTrackOuterRadius; + } + public void setThumbClockwiseBound(int angle) { if (angle < 0) { mThumbCwBound = Integer.MIN_VALUE; } else { mThumbCwBound = getClosestTickAngle(angle); } - setEnforceMaxAbsJump(); + updateEnforceMaxAbsJump(); } public void setThumbCounterclockwiseBound(int angle) { @@ -253,14 +358,14 @@ public class ZoomRing extends View { } else { mThumbCcwBound = getClosestTickAngle(angle); } - setEnforceMaxAbsJump(); + updateEnforceMaxAbsJump(); } - private void setEnforceMaxAbsJump() { + private void updateEnforceMaxAbsJump() { // If there are bounds in both direction, there is no reason to restrict // the amount that a user can absolute jump to mEnforceMaxAbsJump = - mThumbCcwBound == Integer.MIN_VALUE || mThumbCwBound == Integer.MIN_VALUE; + mThumbCcwBound == Integer.MIN_VALUE || mThumbCwBound == Integer.MIN_VALUE; } public int getThumbAngle() { @@ -269,7 +374,7 @@ public class ZoomRing extends View { public void setThumbAngle(int angle) { angle = getValidAngle(angle); - mPreviousCallbackAngle = getClosestTickAngle(angle); + mPreviousCallbackTickAngle = getClosestTickAngle(angle); setThumbAngleAuto(angle, false, false); } @@ -299,9 +404,9 @@ public class ZoomRing extends View { mThumbAngle = angle; int unoffsetAngle = angle + mZeroAngle; int thumbCenterX = (int) (Math.cos(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) * - THUMB_DISTANCE) + mCenterX; + mThumbDistance) + mCenterX; int thumbCenterY = (int) (Math.sin(1f * unoffsetAngle / RADIAN_INT_MULTIPLIER) * - THUMB_DISTANCE) * -1 + mCenterY; + mThumbDistance) * -1 + mCenterY; mThumbDrawable.setBounds(thumbCenterX - mThumbHalfWidth, thumbCenterY - mThumbHalfHeight, @@ -356,7 +461,7 @@ public class ZoomRing extends View { duration = getAnimationDuration(deltaAngle); } mThumbScroller.startScroll(startAngle, 0, deltaAngle, 0, duration); - onThumbScrollerTick(); + onThumbScrollerStep(); } private int getAnimationDuration(int deltaAngle) { @@ -364,10 +469,10 @@ public class ZoomRing extends View { return 300 + deltaAngle * 300 / RADIAN_INT_MULTIPLIER; } - private void onThumbScrollerTick() { + private void onThumbScrollerStep() { if (!mThumbScroller.computeScrollOffset()) return; setThumbAngleInt(getThumbScrollerAngle()); - mHandler.sendEmptyMessage(MSG_THUMB_SCROLLER_TICK); + mHandler.sendEmptyMessage(MSG_THUMB_SCROLLER_STEP); } private int getThumbScrollerAngle() { @@ -375,16 +480,10 @@ public class ZoomRing extends View { } public void resetThumbAngle() { - if (mResetThumbAutomatically) { - mPreviousCallbackAngle = 0; - setThumbAngleInt(0); - } + mPreviousCallbackTickAngle = 0; + setThumbAngleInt(0); } - public void setResetThumbAutomatically(boolean resetThumbAutomatically) { - mResetThumbAutomatically = resetThumbAutomatically; - } - @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { setMeasuredDimension(resolveSize(getSuggestedMinimumWidth(), widthMeasureSpec), @@ -411,14 +510,12 @@ public class ZoomRing extends View { } // These drawables are the same size as the track - mThumbPlusArrowDrawable.setBounds(0, 0, right - left, bottom - top); - mThumbMinusArrowDrawable.setBounds(0, 0, right - left, bottom - top); + mZoomInArrowDrawable.setBounds(0, 0, right - left, bottom - top); + mZoomOutArrowDrawable.setBounds(0, 0, right - left, bottom - top); } @Override public boolean onTouchEvent(MotionEvent event) { -// Log.d(TAG, "History size: " + event.getHistorySize()); - return handleTouch(event.getAction(), event.getEventTime(), (int) event.getX(), (int) event.getY(), (int) event.getRawX(), (int) event.getRawY()); @@ -457,15 +554,10 @@ public class ZoomRing extends View { boolean isTouchingRing = mThumbVisible; int touchAngle = getAngle(localX, localY); -// printAngle("touchAngle", touchAngle); -// printAngle("mThumbAngle", mThumbAngle); -// printAngle("mPreviousCallbackAngle", mPreviousCallbackAngle); -// Log.d(TAG, ""); - int radiusSquared = localX * localX + localY * localY; - if (radiusSquared < mBoundInnerRadiusSquared || - radiusSquared > mBoundOuterRadiusSquared) { + if (radiusSquared < mTrackInnerRadiusSquared || + radiusSquared > mTrackOuterRadiusSquared) { // Out-of-bounds isTouchingThumb = false; isTouchingRing = false; @@ -486,7 +578,7 @@ public class ZoomRing extends View { if (!isTouchingRing && (time - mPreviousCenterUpTime <= DOUBLE_TAP_DISMISS_TIMEOUT)) { // Make sure the double-tap is in the center of the widget (and not on the ring) - mCallback.onZoomRingDismissed(true); + mCallback.onZoomRingDismissed(); onTouchUp(time, isTouchingRing); // Dismissing, so halt here @@ -557,7 +649,7 @@ public class ZoomRing extends View { } setMode(MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP); - mWaitingForDragThumbDownAngle = touchAngle; + mInitialGrabThumbAngle = touchAngle; boolean ccw = deltaThumbAndTick > 0; setThumbAngleAnimated(tickAngle, 0, ccw); @@ -577,9 +669,9 @@ public class ZoomRing extends View { } } else if (mMode == MODE_WAITING_FOR_DRAG_THUMB_AFTER_JUMP) { - int deltaDownAngle = getDelta(mWaitingForDragThumbDownAngle, touchAngle); + int deltaDownAngle = getDelta(mInitialGrabThumbAngle, touchAngle); if ((deltaDownAngle < -THUMB_DRAG_SLOP || deltaDownAngle > THUMB_DRAG_SLOP) && - isDeltaInBounds(mWaitingForDragThumbDownAngle, deltaDownAngle)) { + isDeltaInBounds(mInitialGrabThumbAngle, deltaDownAngle)) { setMode(MODE_DRAG_THUMB); // No need to call onThumbDragStarted, since that was done when they tapped-to-jump @@ -591,6 +683,8 @@ public class ZoomRing extends View { /* Make sure the user has moved the slop amount before going into that mode. */ setMode(MODE_MOVE_ZOOM_RING); mCallback.onZoomRingMovingStarted(); + // Move the zoom ring so it is under the finger where the user first touched + mCallback.onZoomRingMoved(x - mPreviousDownX, y - mPreviousDownY, rawX, rawY); } } else if (mMode == MODE_IGNORE_UNTIL_TOUCHES_THUMB) { if (isTouchingThumb) { @@ -629,7 +723,7 @@ public class ZoomRing extends View { if (mode == MODE_DRAG_THUMB || mode == MODE_TAP_DRAG) { // Animate back to a tick - setThumbAngleAnimated(mPreviousCallbackAngle, 0); + setThumbAngleAnimated(mPreviousCallbackTickAngle, 0); } } mCallback.onUserInteractionStopped(); @@ -741,9 +835,9 @@ public class ZoomRing extends View { boolean animateThumbToNewAngle = false; int totalDeltaAngle; - totalDeltaAngle = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw); - if (totalDeltaAngle >= mFuzzyCallbackThreshold - || totalDeltaAngle <= -mFuzzyCallbackThreshold) { + totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw); + if (totalDeltaAngle >= mFuzzyTickDelta + || totalDeltaAngle <= -mFuzzyTickDelta) { if (!useDirection) { // Set ccw to match the direction found by getDelta @@ -763,9 +857,9 @@ public class ZoomRing extends View { if (ccw && mThumbCcwBound != Integer.MIN_VALUE) { int deltaCcwBoundAndTouch = getDelta(mThumbCcwBound, touchAngle, useDirection, true); - if (deltaCcwBoundAndTouch >= mCallbackThreshold / 2) { + if (deltaCcwBoundAndTouch >= mTickDelta / 2) { // The touch has past a bound - int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle, + int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, true); if (deltaPreviousCbAndTouch >= deltaCcwBoundAndTouch) { // The bound is between the previous callback angle and the touch @@ -778,8 +872,8 @@ public class ZoomRing extends View { // See block above for general comments int deltaCwBoundAndTouch = getDelta(mThumbCwBound, touchAngle, useDirection, false); - if (deltaCwBoundAndTouch <= -mCallbackThreshold / 2) { - int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackAngle, + if (deltaCwBoundAndTouch <= -mTickDelta / 2) { + int deltaPreviousCbAndTouch = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, false); /* * Both of these will be negative since we got delta in @@ -795,7 +889,7 @@ public class ZoomRing extends View { } if (touchAngle != oldTouchAngle) { // We bounded the touch angle - totalDeltaAngle = getDelta(mPreviousCallbackAngle, touchAngle, useDirection, ccw); + totalDeltaAngle = getDelta(mPreviousCallbackTickAngle, touchAngle, useDirection, ccw); animateThumbToNewAngle = true; setMode(MODE_IGNORE_UNTIL_TOUCHES_THUMB); } @@ -819,7 +913,7 @@ public class ZoomRing extends View { * hit. If we do int division, we'll end up with one level lower * than the one he was going for. */ - int deltaLevels = Math.round((float) totalDeltaAngle / mCallbackThreshold); + int deltaLevels = Math.round((float) totalDeltaAngle / mTickDelta); if (deltaLevels != 0) { boolean canStillZoom = mCallback.onZoomRingThumbDragged( deltaLevels, mThumbDragStartAngle, touchAngle); @@ -833,8 +927,8 @@ public class ZoomRing extends View { } // Set the callback angle to the actual angle based on how many delta levels we gave - mPreviousCallbackAngle = getValidAngle( - mPreviousCallbackAngle + (deltaLevels * mCallbackThreshold)); + mPreviousCallbackTickAngle = getValidAngle( + mPreviousCallbackTickAngle + (deltaLevels * mTickDelta)); } } @@ -993,14 +1087,14 @@ public class ZoomRing extends View { } private int getClosestTickAngle(int angle) { - int smallerAngleDistance = angle % mCallbackThreshold; + int smallerAngleDistance = angle % mTickDelta; int smallerAngle = angle - smallerAngleDistance; - if (smallerAngleDistance < mCallbackThreshold / 2) { + if (smallerAngleDistance < mTickDelta / 2) { // Closer to the smaller angle return smallerAngle; } else { // Closer to the bigger angle (premodding) - return (smallerAngle + mCallbackThreshold) % TWO_PI_INT_MULTIPLIED; + return (smallerAngle + mTickDelta) % TWO_PI_INT_MULTIPLIED; } } @@ -1025,7 +1119,7 @@ public class ZoomRing extends View { super.onWindowFocusChanged(hasWindowFocus); if (!hasWindowFocus) { - mCallback.onZoomRingDismissed(true); + mCallback.onZoomRingDismissed(); } } @@ -1054,12 +1148,12 @@ public class ZoomRing extends View { mTrail.draw(canvas); } if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { - mThumbPlusArrowDrawable.draw(canvas); - mThumbPlusDrawable.draw(canvas); + mZoomInArrowDrawable.draw(canvas); + mZoomInArrowHintDrawable.draw(canvas); } if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { - mThumbMinusArrowDrawable.draw(canvas); - mThumbMinusDrawable.draw(canvas); + mZoomOutArrowDrawable.draw(canvas); + mZoomOutArrowHintDrawable.draw(canvas); } mThumbDrawable.draw(canvas); } @@ -1067,48 +1161,48 @@ public class ZoomRing extends View { private void setThumbArrowsAngle(int angle) { int level = -angle * 10000 / ZoomRing.TWO_PI_INT_MULTIPLIED; - mThumbPlusArrowDrawable.setLevel(level); - mThumbMinusArrowDrawable.setLevel(level); + mZoomInArrowDrawable.setLevel(level); + mZoomOutArrowDrawable.setLevel(level); // Assume it is a square - int halfSideLength = mThumbPlusDrawable.getIntrinsicHeight() / 2; + int halfSideLength = mZoomInArrowHintDrawable.getIntrinsicHeight() / 2; int unoffsetAngle = angle + mZeroAngle; - int plusCenterX = (int) (Math.cos(1f * (unoffsetAngle - THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) + mCenterX; - int plusCenterY = (int) (Math.sin(1f * (unoffsetAngle - THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) * -1 + mCenterY; - mThumbPlusDrawable.setBounds(plusCenterX - halfSideLength, + int plusCenterX = (int) (Math.cos(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle) + / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX; + int plusCenterY = (int) (Math.sin(1f * (unoffsetAngle - mZoomArrowHintOffsetAngle) + / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY; + mZoomInArrowHintDrawable.setBounds(plusCenterX - halfSideLength, plusCenterY - halfSideLength, plusCenterX + halfSideLength, plusCenterY + halfSideLength); - int minusCenterX = (int) (Math.cos(1f * (unoffsetAngle + THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) + mCenterX; - int minusCenterY = (int) (Math.sin(1f * (unoffsetAngle + THUMB_PLUS_MINUS_OFFSET_ANGLE) - / RADIAN_INT_MULTIPLIER) * THUMB_PLUS_MINUS_DISTANCE) * -1 + mCenterY; - mThumbMinusDrawable.setBounds(minusCenterX - halfSideLength, + int minusCenterX = (int) (Math.cos(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle) + / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) + mCenterX; + int minusCenterY = (int) (Math.sin(1f * (unoffsetAngle + mZoomArrowHintOffsetAngle) + / RADIAN_INT_MULTIPLIER) * mZoomArrowHintDistance) * -1 + mCenterY; + mZoomOutArrowHintDrawable.setBounds(minusCenterX - halfSideLength, minusCenterY - halfSideLength, minusCenterX + halfSideLength, minusCenterY + halfSideLength); } - public void setThumbArrowsVisible(boolean visible) { + void setThumbArrowsVisible(boolean visible) { if (visible) { mThumbArrowsAlpha = 255; - int callbackAngle = mPreviousCallbackAngle; + int callbackAngle = mPreviousCallbackTickAngle; if (callbackAngle < mThumbCwBound - RADIAN_INT_ERROR || callbackAngle > mThumbCwBound + RADIAN_INT_ERROR) { - mThumbPlusArrowDrawable.setAlpha(255); - mThumbPlusDrawable.setAlpha(255); + mZoomInArrowDrawable.setAlpha(255); + mZoomInArrowHintDrawable.setAlpha(255); mThumbArrowsToDraw |= THUMB_ARROW_PLUS; } else { mThumbArrowsToDraw &= ~THUMB_ARROW_PLUS; } if (callbackAngle < mThumbCcwBound - RADIAN_INT_ERROR || callbackAngle > mThumbCcwBound + RADIAN_INT_ERROR) { - mThumbMinusArrowDrawable.setAlpha(255); - mThumbMinusDrawable.setAlpha(255); + mZoomOutArrowDrawable.setAlpha(255); + mZoomOutArrowHintDrawable.setAlpha(255); mThumbArrowsToDraw |= THUMB_ARROW_MINUS; } else { mThumbArrowsToDraw &= ~THUMB_ARROW_MINUS; @@ -1117,11 +1211,11 @@ public class ZoomRing extends View { } else if (mThumbArrowsAlpha == 255) { // Only start fade if we're fully visible (otherwise another fade is happening already) mThumbArrowsFadeStartTime = SystemClock.elapsedRealtime(); - onThumbArrowsFadeTick(); + onThumbArrowsFadeStep(); } } - private void onThumbArrowsFadeTick() { + private void onThumbArrowsFadeStep() { if (mThumbArrowsAlpha <= 0) { mThumbArrowsToDraw = 0; return; @@ -1132,20 +1226,20 @@ public class ZoomRing extends View { / THUMB_ARROWS_FADE_DURATION)); if (mThumbArrowsAlpha < 0) mThumbArrowsAlpha = 0; if ((mThumbArrowsToDraw & THUMB_ARROW_PLUS) != 0) { - mThumbPlusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbPlusDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbPlusDrawable); - invalidateDrawable(mThumbPlusArrowDrawable); + mZoomInArrowDrawable.setAlpha(mThumbArrowsAlpha); + mZoomInArrowHintDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mZoomInArrowHintDrawable); + invalidateDrawable(mZoomInArrowDrawable); } if ((mThumbArrowsToDraw & THUMB_ARROW_MINUS) != 0) { - mThumbMinusArrowDrawable.setAlpha(mThumbArrowsAlpha); - mThumbMinusDrawable.setAlpha(mThumbArrowsAlpha); - invalidateDrawable(mThumbMinusDrawable); - invalidateDrawable(mThumbMinusArrowDrawable); + mZoomOutArrowDrawable.setAlpha(mThumbArrowsAlpha); + mZoomOutArrowHintDrawable.setAlpha(mThumbArrowsAlpha); + invalidateDrawable(mZoomOutArrowHintDrawable); + invalidateDrawable(mZoomOutArrowDrawable); } - if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_TICK)) { - mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_TICK); + if (!mHandler.hasMessages(MSG_THUMB_ARROWS_FADE_STEP)) { + mHandler.sendEmptyMessage(MSG_THUMB_ARROWS_FADE_STEP); } } @@ -1168,7 +1262,7 @@ public class ZoomRing extends View { boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle); void onZoomRingThumbDraggingStopped(); - void onZoomRingDismissed(boolean dismissImmediately); + void onZoomRingDismissed(); void onUserInteractionStarted(); void onUserInteractionStopped(); diff --git a/core/java/android/widget/ZoomRingController.java b/core/java/android/widget/ZoomRingController.java index 19f66a0c873c9..3bf3b227dba04 100644 --- a/core/java/android/widget/ZoomRingController.java +++ b/core/java/android/widget/ZoomRingController.java @@ -1,5 +1,5 @@ /* - * Copyright (C) 2008 The Android Open Source Project + * 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. @@ -30,7 +30,8 @@ import android.os.Handler; import android.os.Message; import android.os.SystemClock; import android.provider.Settings; -import android.util.Log; +import android.util.DisplayMetrics; +import android.view.GestureDetector; import android.view.Gravity; import android.view.KeyEvent; import android.view.LayoutInflater; @@ -44,11 +45,9 @@ import android.view.animation.Animation; import android.view.animation.AnimationUtils; import android.view.animation.DecelerateInterpolator; -// TODO: make sure no px values exist, only dip (scale if necessary from Viewconfiguration) - /** - * TODO: Docs - * + * A controller to simplify the use of the zoom ring widget. + *
* If you are using this with a custom View, please call * {@link #setVisible(boolean) setVisible(false)} from the * {@link View#onDetachedFromWindow}. @@ -58,13 +57,7 @@ import android.view.animation.DecelerateInterpolator; public class ZoomRingController implements ZoomRing.OnZoomRingCallback, View.OnTouchListener, View.OnKeyListener { - private static final int ZOOM_RING_RADIUS_INSET = 24; - - private static final int ZOOM_RING_RECENTERING_DURATION = 500; - - private static final String TAG = "ZoomRing"; - - public static final boolean USE_OLD_ZOOM = false; + // Temporary methods for different zoom types static int getZoomType(Context context) { return Settings.System.getInt(context.getContentResolver(), "zoom", 1); } @@ -75,19 +68,43 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, return getZoomType(context) == 1; } - private static final int ZOOM_CONTROLS_TIMEOUT = - (int) ViewConfiguration.getZoomControlsTimeout(); + /** The duration for the animation to re-center the zoom ring. */ + private static final int RECENTERING_DURATION = 500; - // TODO: move these to ViewConfiguration or re-use existing ones - // TODO: scale px values based on latest from ViewConfiguration - private static final int SECOND_TAP_TIMEOUT = 500; - private static final int ZOOM_RING_DISMISS_DELAY = SECOND_TAP_TIMEOUT / 2; - // TODO: view config? at least scaled - private static final int MAX_PAN_GAP = 20; - private static final int MAX_INITIATE_PAN_GAP = 10; - // TODO view config + /** The inactivity timeout for the zoom ring. */ + private static final int INACTIVITY_TIMEOUT = + (int) ViewConfiguration.getZoomControlsTimeout(); + + /** + * The delay when the user taps outside to dismiss the zoom ring. This is + * because the user can do a second-tap to recenter the owner view instead + * of dismissing the zoom ring. + */ + private static final int OUTSIDE_TAP_DISMISS_DELAY = + ViewConfiguration.getDoubleTapTimeout() / 2; + + /** + * When the zoom ring is on the edge, this is the delay before we actually + * start panning the owner. + * @see #mInitiatePanGap + */ private static final int INITIATE_PAN_DELAY = 300; + /** + * While already panning, if the zoom ring remains this close to an edge, + * the owner will continue to be panned. + */ + private int mPanGap; + + /** To begin a pan, the zoom ring must be this close to an edge. */ + private int mInitiatePanGap; + + /** Initialized from ViewConfiguration. */ + private int mScaledTouchSlop; + + /** + * The setting name that tracks whether we've shown the zoom ring toast. + */ private static final String SETTING_NAME_SHOWN_TOAST = "shown_zoom_ring_toast"; private Context mContext; @@ -137,25 +154,37 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * screen once (for the first tap down) instead of twice (for the first tap * down and then to grab the thumb). */ + /** The X where the tap-drag started. */ private int mTapDragStartX; + /** The Y where the tap-drag started. */ private int mTapDragStartY; + /** The controller is idle */ private static final int TOUCH_MODE_IDLE = 0; - private static final int TOUCH_MODE_WAITING_FOR_SECOND_TAP = 1; + /** + * In the middle of a second-tap interaction, waiting for either an up-touch + * or the user to start dragging to go into tap-drag mode. + */ private static final int TOUCH_MODE_WAITING_FOR_TAP_DRAG_MOVEMENT = 2; + /** In the middle of a tap-drag. */ private static final int TOUCH_MODE_FORWARDING_FOR_TAP_DRAG = 3; private int mTouchMode; + /** Whether the zoom ring is visible. */ private boolean mIsZoomRingVisible; private ZoomRing mZoomRing; + /** Cached width of the zoom ring. */ private int mZoomRingWidth; + /** Cached height of the zoom ring. */ private int mZoomRingHeight; /** Invokes panning of owner view if the zoom ring is touching an edge. */ private Panner mPanner; + /** The time when the zoom ring first touched the edge. */ private long mTouchingEdgeStartTime; - private boolean mPanningEnabledForThisInteraction; + /** Whether the user has already initiated the panning. */ + private boolean mPanningInitiated; /** * When the finger moves the zoom ring to an edge, this is the horizontal @@ -167,16 +196,21 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, /** Vertical accumulator, see {@link #mMovingZoomRingOobX} */ private int mMovingZoomRingOobY; + /** Arrows that hint that the zoom ring is movable. */ private ImageView mPanningArrows; + /** The animation shown when the panning arrows are being shown. */ private Animation mPanningArrowsEnterAnimation; + /** The animation shown when the panning arrows are being hidden. */ private Animation mPanningArrowsExitAnimation; + /** + * Temporary rectangle, only use from the UI thread (and ideally don't rely + * on it being unused across many method calls.) + */ private Rect mTempRect = new Rect(); private OnZoomListener mCallback; - private ViewConfiguration mViewConfig; - /** * When the zoom ring is centered on screen, this will be the x value used * for the container's layout params. @@ -217,6 +251,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, private IntentFilter mConfigurationChangedFilter = new IntentFilter(Intent.ACTION_CONFIGURATION_CHANGED); + /** Listens for configuration changes so we can make sure we're still in a reasonable state. */ private BroadcastReceiver mConfigurationChangedReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { @@ -228,7 +263,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, }; /** Keeps the scroller going (or starts it). */ - private static final int MSG_SCROLLER_TICK = 1; + private static final int MSG_SCROLLER_STEP = 1; /** When configuration changes, this is called after the UI thread is idle. */ private static final int MSG_POST_CONFIGURATION_CHANGED = 2; /** Used to delay the zoom ring dismissal. */ @@ -244,8 +279,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, @Override public void handleMessage(Message msg) { switch (msg.what) { - case MSG_SCROLLER_TICK: - onScrollerTick(); + case MSG_SCROLLER_STEP: + onScrollerStep(); break; case MSG_POST_CONFIGURATION_CHANGED: @@ -296,8 +331,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mContainerLayoutParams.width = LayoutParams.WRAP_CONTENT; mContainerLayoutParams.type = LayoutParams.TYPE_APPLICATION_PANEL; mContainerLayoutParams.format = PixelFormat.TRANSPARENT; - // TODO: make a new animation for this - mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_Dialog; + mContainerLayoutParams.windowAnimations = com.android.internal.R.style.Animation_ZoomRing; mContainer = new FrameLayout(context); mContainer.setLayoutParams(mContainerLayoutParams); @@ -308,13 +342,17 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mScroller = new Scroller(context, new DecelerateInterpolator()); - mViewConfig = ViewConfiguration.get(context); + ViewConfiguration vc = ViewConfiguration.get(context); + mScaledTouchSlop = vc.getScaledTouchSlop(); + + float density = context.getResources().getDisplayMetrics().density; + mPanGap = (int) (20 * density); + mInitiatePanGap = (int) (10 * density); } private void createPanningArrows() { - // TODO: style mPanningArrows = new ImageView(mContext); - mPanningArrows.setImageResource(com.android.internal.R.drawable.zoom_ring_arrows); + mPanningArrows.setImageDrawable(mZoomRing.getPanningArrowsDrawable()); mPanningArrows.setLayoutParams(new FrameLayout.LayoutParams( FrameLayout.LayoutParams.WRAP_CONTENT, FrameLayout.LayoutParams.WRAP_CONTENT, @@ -328,15 +366,16 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } /** - * Sets the angle (in radians) a user must travel in order for the client to - * get a callback. Once there is a callback, the accumulator resets. For - * example, if you set this to PI/6, it will give a callback every time the - * user moves PI/6 amount on the ring. - * - * @param callbackThreshold The angle for the callback threshold, in radians + * Sets the angle (in radians) between ticks. This is also the angle a user + * must move the thumb in order for the client to get a callback. Once there + * is a callback, the accumulator resets. For example, if you set this to + * PI/6, it will give a callback every time the user moves PI/6 amount on + * the ring. + * + * @param angle The angle for the callback threshold, in radians */ - public void setZoomCallbackThreshold(float callbackThreshold) { - mZoomRing.setCallbackThreshold((int) (callbackThreshold * ZoomRing.RADIAN_INT_MULTIPLIER)); + public void setTickDelta(float angle) { + mZoomRing.setTickDelta((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER)); } /** @@ -346,14 +385,23 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, * @hide Need a better way of doing this, but this one-off for browser so it * can have its final look for the usability study */ - public void setZoomRingTrack(int drawable) { + public void setTrackDrawable(int drawable) { mZoomRing.setBackgroundResource(drawable); } - + + /** + * Sets the callback for the zoom ring controller. + * + * @param callback The callback. + */ public void setCallback(OnZoomListener callback) { mCallback = callback; } + public void setVibration(boolean vibrate) { + mZoomRing.setVibration(vibrate); + } + public void setThumbAngle(float angle) { mZoomRing.setThumbAngle((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER)); } @@ -362,14 +410,6 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, mZoomRing.setThumbAngleAnimated((int) (angle * ZoomRing.RADIAN_INT_MULTIPLIER), 0); } - public void setResetThumbAutomatically(boolean resetThumbAutomatically) { - mZoomRing.setResetThumbAutomatically(resetThumbAutomatically); - } - - public void setVibration(boolean vibrate) { - mZoomRing.setVibration(vibrate); - } - public void setThumbVisible(boolean thumbVisible) { mZoomRing.setThumbVisible(thumbVisible); } @@ -407,7 +447,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, return; } - dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT); + dismissZoomRingDelayed(INACTIVITY_TIMEOUT); } else { mPanner.stop(); } @@ -429,12 +469,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, public void run() { refreshPositioningVariables(); resetZoomRing(); - - // TODO: remove this 'update' and just center zoom ring before the - // 'add', but need to make sure we have the width and height (which - // probably can only be retrieved after it's measured, which happens - // after it's added). - mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + refreshContainerLayout(); if (mCallback != null) { mCallback.onVisibilityChanged(true); @@ -479,19 +514,34 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback, } + private void refreshContainerLayout() { + if (mIsZoomRingVisible) { + mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams); + } + } + /** - * TODO: docs - * + * Returns the container of the zoom ring widget. The client can add views + * here to be shown alongside the zoom ring. See {@link #getZoomRingId()}. + *
* Notes: - * - Touch dispatching is different. Only direct children who are clickable are eligble for touch events. - * - Please ensure you set your View to INVISIBLE not GONE when hiding it. - * - * @return + *
+ * In most cases, the client can use a {@link GestureDetector} and forward events from
+ * {@link GestureDetector.OnDoubleTapListener#onDoubleTapEvent(MotionEvent)}.
+ *
* @param event The event belonging to the second tap.
* @return Whether the event was consumed.
*/
@@ -550,9 +603,9 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
case MotionEvent.ACTION_MOVE:
int x = (int) event.getX();
int y = (int) event.getY();
- if (Math.abs(x - mTapDragStartX) > mViewConfig.getScaledTouchSlop() ||
+ if (Math.abs(x - mTapDragStartX) > mScaledTouchSlop ||
Math.abs(y - mTapDragStartY) >
- mViewConfig.getScaledTouchSlop()) {
+ mScaledTouchSlop) {
mZoomRing.setTapDragMode(true, x, y);
mTouchMode = TOUCH_MODE_FORWARDING_FOR_TAP_DRAG;
setTouchTargetView(mZoomRing);
@@ -600,8 +653,8 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
int width = mContainer.getWidth();
int height = mContainer.getHeight();
mScroller.startScroll(lp.x, lp.y, mCenteredContainerX - lp.x,
- mCenteredContainerY - lp.y, ZOOM_RING_RECENTERING_DURATION);
- mHandler.sendEmptyMessage(MSG_SCROLLER_TICK);
+ mCenteredContainerY - lp.y, RECENTERING_DURATION);
+ mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
}
}
@@ -636,18 +689,22 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
mZoomRing.handleTouch(event.getAction(), event.getEventTime(), x, y, rawX, rawY);
}
+ /** @hide */
public void onZoomRingSetMovableHintVisible(boolean visible) {
setPanningArrowsVisible(visible);
}
+ /** @hide */
public void onUserInteractionStarted() {
mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
}
+ /** @hide */
public void onUserInteractionStopped() {
- dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
+ dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
}
+ /** @hide */
public void onZoomRingMovingStarted() {
mScroller.abortAnimation();
mTouchingEdgeStartTime = 0;
@@ -664,6 +721,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
mPanningArrows.setVisibility(visible ? View.VISIBLE : View.INVISIBLE);
}
+ /** @hide */
public boolean onZoomRingMoved(int deltaX, int deltaY, int rawX, int rawY) {
if (mMovingZoomRingOobX != 0) {
@@ -721,12 +779,12 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
ownerBounds.bottom - mZoomRingHeight : newZoomRingY;
lp.y = newZoomRingY - zoomRingTop;
- mWindowManager.updateViewLayout(mContainer, lp);
-
+ refreshContainerLayout();
+
// Check for pan
boolean horizontalPanning = true;
int leftGap = newZoomRingX - ownerBounds.left;
- if (leftGap < MAX_PAN_GAP) {
+ if (leftGap < mPanGap) {
if (leftGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
// Future moves in this direction should be accumulated in mMovingZoomRingOobX
mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
@@ -736,7 +794,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
} else {
int rightGap = ownerBounds.right - (lp.x + mZoomRingWidth + zoomRingLeft);
- if (rightGap < MAX_PAN_GAP) {
+ if (rightGap < mPanGap) {
if (rightGap == 0 && deltaX != 0 && mMovingZoomRingOobX == 0) {
mMovingZoomRingOobX = deltaX / Math.abs(deltaX);
}
@@ -750,7 +808,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
int topGap = newZoomRingY - ownerBounds.top;
- if (topGap < MAX_PAN_GAP) {
+ if (topGap < mPanGap) {
if (topGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
}
@@ -759,7 +817,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
} else {
int bottomGap = ownerBounds.bottom - (lp.y + mZoomRingHeight + zoomRingTop);
- if (bottomGap < MAX_PAN_GAP) {
+ if (bottomGap < mPanGap) {
if (bottomGap == 0 && deltaY != 0 && mMovingZoomRingOobY == 0) {
mMovingZoomRingOobY = deltaY / Math.abs(deltaY);
}
@@ -771,7 +829,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
if (!horizontalPanning) {
// Neither are panning, reset any timer to start pan mode
mTouchingEdgeStartTime = 0;
- mPanningEnabledForThisInteraction = false;
+ mPanningInitiated = false;
mPanner.stop();
}
}
@@ -781,13 +839,13 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
private boolean shouldPan(int gap) {
- if (mPanningEnabledForThisInteraction) return true;
+ if (mPanningInitiated) return true;
- if (gap < MAX_INITIATE_PAN_GAP) {
+ if (gap < mInitiatePanGap) {
long time = SystemClock.elapsedRealtime();
if (mTouchingEdgeStartTime != 0 &&
mTouchingEdgeStartTime + INITIATE_PAN_DELAY < time) {
- mPanningEnabledForThisInteraction = true;
+ mPanningInitiated = true;
return true;
} else if (mTouchingEdgeStartTime == 0) {
mTouchingEdgeStartTime = time;
@@ -800,6 +858,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
return false;
}
+ /** @hide */
public void onZoomRingMovingStopped() {
mPanner.stop();
setPanningArrowsVisible(false);
@@ -809,27 +868,25 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
private int getStrengthFromGap(int gap) {
- return gap > MAX_PAN_GAP ? 0 :
- (MAX_PAN_GAP - gap) * 100 / MAX_PAN_GAP;
+ return gap > mPanGap ? 0 :
+ (mPanGap - gap) * 100 / mPanGap;
}
+ /** @hide */
public void onZoomRingThumbDraggingStarted() {
if (mCallback != null) {
mCallback.onBeginDrag();
}
}
+ /** @hide */
public boolean onZoomRingThumbDragged(int numLevels, int startAngle, int curAngle) {
if (mCallback != null) {
int deltaZoomLevel = -numLevels;
- int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() +
- mZoomRingWidth / 2;
- int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() +
- mZoomRingHeight / 2;
return mCallback.onDragZoom(deltaZoomLevel,
- globalZoomCenterX - mOwnerViewBounds.left,
- globalZoomCenterY - mOwnerViewBounds.top,
+ getZoomRingCenterXInOwnerCoordinates(),
+ getZoomRingCenterYInOwnerCoordinates(),
(float) startAngle / ZoomRing.RADIAN_INT_MULTIPLIER,
(float) curAngle / ZoomRing.RADIAN_INT_MULTIPLIER);
}
@@ -837,24 +894,36 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
return false;
}
+ private int getZoomRingCenterXInOwnerCoordinates() {
+ int globalZoomCenterX = mContainerLayoutParams.x + mZoomRing.getLeft() +
+ mZoomRingWidth / 2;
+ return globalZoomCenterX - mOwnerViewBounds.left;
+ }
+
+ private int getZoomRingCenterYInOwnerCoordinates() {
+ int globalZoomCenterY = mContainerLayoutParams.y + mZoomRing.getTop() +
+ mZoomRingHeight / 2;
+ return globalZoomCenterY - mOwnerViewBounds.top;
+ }
+
+ /** @hide */
public void onZoomRingThumbDraggingStopped() {
if (mCallback != null) {
mCallback.onEndDrag();
}
}
- public void onZoomRingDismissed(boolean dismissImmediately) {
- if (dismissImmediately) {
- mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
- setVisible(false);
- } else {
- dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY);
- }
+ /** @hide */
+ public void onZoomRingDismissed() {
+ mHandler.removeMessages(MSG_DISMISS_ZOOM_RING);
+ setVisible(false);
}
+ /** @hide */
public void onRingDown(int tickAngle, int touchAngle) {
}
+ /** @hide */
public boolean onTouch(View v, MotionEvent event) {
if (sTutorialDialog != null && sTutorialDialog.isShowing() &&
SystemClock.elapsedRealtime() - sTutorialShowTime >= TUTORIAL_MIN_DISPLAY_TIME) {
@@ -904,8 +973,9 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
return retValue;
} else {
+// dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
if (action == MotionEvent.ACTION_DOWN) {
- dismissZoomRingDelayed(ZOOM_RING_DISMISS_DELAY);
+ dismissZoomRingDelayed(OUTSIDE_TAP_DISMISS_DELAY);
}
return false;
@@ -932,7 +1002,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
int containerCenterY = mContainerLayoutParams.y + mContainer.getHeight() / 2;
int distanceFromCenterX = rawX - containerCenterX;
int distanceFromCenterY = rawY - containerCenterY;
- int zoomRingRadius = mZoomRingWidth / 2 - ZOOM_RING_RADIUS_INSET;
+ int zoomRingRadius = mZoomRing.getTrackOuterRadius();
if (distanceFromCenterX * distanceFromCenterX +
distanceFromCenterY * distanceFromCenterY <=
zoomRingRadius * zoomRingRadius) {
@@ -960,7 +1030,11 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
return null;
}
- /** Steals key events from the owner view. */
+ /**
+ * Steals key events from the owner view.
+ *
+ * @hide
+ */
public boolean onKey(View v, int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
@@ -971,12 +1045,14 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
case KeyEvent.KEYCODE_DPAD_UP:
case KeyEvent.KEYCODE_DPAD_DOWN:
// Keep the zoom alive a little longer
- dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
+ dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
// They started zooming, hide the thumb arrows
mZoomRing.setThumbArrowsVisible(false);
if (mCallback != null && event.getAction() == KeyEvent.ACTION_DOWN) {
- mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP);
+ mCallback.onSimpleZoom(keyCode == KeyEvent.KEYCODE_DPAD_UP,
+ getZoomRingCenterXInOwnerCoordinates(),
+ getZoomRingCenterYInOwnerCoordinates());
}
return true;
@@ -985,18 +1061,18 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
return false;
}
- private void onScrollerTick() {
+ private void onScrollerStep() {
if (!mScroller.computeScrollOffset() || !mIsZoomRingVisible) return;
mContainerLayoutParams.x = mScroller.getCurrX();
mContainerLayoutParams.y = mScroller.getCurrY();
- mWindowManager.updateViewLayout(mContainer, mContainerLayoutParams);
+ refreshContainerLayout();
- mHandler.sendEmptyMessage(MSG_SCROLLER_TICK);
+ mHandler.sendEmptyMessage(MSG_SCROLLER_STEP);
}
private void onPostConfigurationChanged() {
- dismissZoomRingDelayed(ZOOM_CONTROLS_TIMEOUT);
+ dismissZoomRingDelayed(INACTIVITY_TIMEOUT);
refreshPositioningVariables();
ensureZoomRingIsCentered();
}
@@ -1056,6 +1132,7 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
sTutorialShowTime = SystemClock.elapsedRealtime();
}
+ /** @hide Should only be used by Android platform apps */
public static void finishZoomTutorial(Context context, boolean userNotified) {
if (sTutorialDialog == null) return;
@@ -1078,22 +1155,45 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
}
+ /** @hide Should only be used by Android platform apps */
public void finishZoomTutorial() {
finishZoomTutorial(mContext, true);
}
+ /**
+ * Sets the initial velocity of a pan.
+ *
+ * @param startVelocity The initial velocity to move the owner view, in
+ * pixels per second.
+ */
public void setPannerStartVelocity(float startVelocity) {
mPanner.mStartVelocity = startVelocity;
}
+ /**
+ * Sets the accelartion of the pan.
+ *
+ * @param acceleration The acceleration, in pixels per second squared.
+ */
public void setPannerAcceleration(float acceleration) {
mPanner.mAcceleration = acceleration;
}
+ /**
+ * Sets the maximum velocity of a pan.
+ *
+ * @param maxVelocity The max velocity to move the owner view, in pixels per
+ * second.
+ */
public void setPannerMaxVelocity(float maxVelocity) {
mPanner.mMaxVelocity = maxVelocity;
}
+ /**
+ * Sets the duration before acceleration will be applied.
+ *
+ * @param duration The duration, in milliseconds.
+ */
public void setPannerStartAcceleratingDuration(int duration) {
mPanner.mStartAcceleratingDuration = duration;
}
@@ -1201,16 +1301,83 @@ public class ZoomRingController implements ZoomRing.OnZoomRingCallback,
}
+ /**
+ * Interface used to inform the client of zoom events that the user
+ * triggers.
+ */
public interface OnZoomListener {
+ /**
+ * Called when the user begins dragging the thumb on the zoom ring.
+ */
void onBeginDrag();
+
+ /**
+ * Called when the user drags the thumb and passes a tick causing a
+ * zoom.
+ *
+ * @param deltaZoomLevel The number of levels to be zoomed. Positive to
+ * zoom in, negative to zoom out.
+ * @param centerX The point about which to zoom. The zoom should pin
+ * this point, leaving it at the same coordinate. This is
+ * relative to the owner view's upper-left.
+ * @param centerY The point about which to zoom. The zoom should pin
+ * this point, leaving it at the same coordinate. This is
+ * relative to the owner view's upper-left.
+ * @param startAngle The angle where the user started dragging the thumb.
+ * @param curAngle The current angle of the thumb.
+ * @return Whether the owner was zoomed.
+ */
boolean onDragZoom(int deltaZoomLevel, int centerX, int centerY, float startAngle,
float curAngle);
+
+ /**
+ * Called when the user releases the thumb.
+ */
void onEndDrag();
- void onSimpleZoom(boolean deltaZoomLevel);
+
+ /**
+ * Called when the user zooms via some other mechanism, for example
+ * arrow keys or a trackball.
+ *
+ * @param zoomIn Whether to zoom in (true) or out (false).
+ * @param centerX See {@link #onDragZoom(int, int, int, float, float)}.
+ * @param centerY See {@link #onDragZoom(int, int, int, float, float)}.
+ */
+ void onSimpleZoom(boolean zoomIn, int centerX, int centerY);
+
+ /**
+ * Called when the user begins moving the zoom ring in order to pan the
+ * owner.
+ */
void onBeginPan();
+
+ /**
+ * Called when the owner should pan as a result of the user moving the zoom ring.
+ *
+ * @param deltaX The amount to pan horizontally.
+ * @param deltaY The amount to pan vertically.
+ * @return Whether the owner was panned.
+ */
boolean onPan(int deltaX, int deltaY);
+
+ /**
+ * Called when the user releases the zoom ring.
+ */
void onEndPan();
+
+ /**
+ * Called when the client should center the owner on the given point.
+ *
+ * @param x The x to center on, relative to the owner view's upper-left.
+ * @param y The y to center on, relative to the owner view's upper-left.
+ */
void onCenter(int x, int y);
+
+ /**
+ * Called when the zoom ring's visibility changes.
+ *
+ * @param visible Whether the zoom ring is visible (true) or not (false).
+ */
void onVisibilityChanged(boolean visible);
}
}
diff --git a/core/java/com/android/internal/os/HandlerCaller.java b/core/java/com/android/internal/os/HandlerCaller.java
index bab1e21812ae2..932555d15b9cb 100644
--- a/core/java/com/android/internal/os/HandlerCaller.java
+++ b/core/java/com/android/internal/os/HandlerCaller.java
@@ -120,6 +120,10 @@ public class HandlerCaller {
return mH.obtainMessage(what, arg1, 0);
}
+ public Message obtainMessageII(int what, int arg1, int arg2) {
+ return mH.obtainMessage(what, arg1, arg2);
+ }
+
public Message obtainMessageIO(int what, int arg1, Object arg2) {
return mH.obtainMessage(what, arg1, 0, arg2);
}
diff --git a/core/java/com/android/internal/os/IResultReceiver.aidl b/core/java/com/android/internal/os/IResultReceiver.aidl
new file mode 100644
index 0000000000000..2b70f95a5cf2a
--- /dev/null
+++ b/core/java/com/android/internal/os/IResultReceiver.aidl
@@ -0,0 +1,25 @@
+/* //device/java/android/android/app/IActivityPendingResult.aidl
+**
+** 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.
+*/
+
+package com.android.internal.os;
+
+import android.os.Bundle;
+
+/** @hide */
+oneway interface IResultReceiver {
+ void send(int resultCode, in Bundle resultData);
+}
diff --git a/core/java/com/android/internal/view/IInputMethod.aidl b/core/java/com/android/internal/view/IInputMethod.aidl
index 9b004025ea9ae..8ff18ed69f846 100644
--- a/core/java/com/android/internal/view/IInputMethod.aidl
+++ b/core/java/com/android/internal/view/IInputMethod.aidl
@@ -18,6 +18,7 @@ package com.android.internal.view;
import android.graphics.Rect;
import android.os.IBinder;
+import android.os.ResultReceiver;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.inputmethod.EditorInfo;
@@ -48,7 +49,7 @@ oneway interface IInputMethod {
void revokeSession(IInputMethodSession session);
- void showSoftInput(int flags);
+ void showSoftInput(int flags, in ResultReceiver resultReceiver);
- void hideSoftInput();
+ void hideSoftInput(int flags, in ResultReceiver resultReceiver);
}
diff --git a/core/java/com/android/internal/view/IInputMethodManager.aidl b/core/java/com/android/internal/view/IInputMethodManager.aidl
index 1b1c7f78a7aff..9030a3e377c26 100644
--- a/core/java/com/android/internal/view/IInputMethodManager.aidl
+++ b/core/java/com/android/internal/view/IInputMethodManager.aidl
@@ -16,6 +16,7 @@
package com.android.internal.view;
+import android.os.ResultReceiver;
import android.view.inputmethod.InputMethodInfo;
import android.view.inputmethod.EditorInfo;
import com.android.internal.view.InputBindResult;
@@ -38,8 +39,10 @@ interface IInputMethodManager {
IInputContext inputContext, in EditorInfo attribute,
boolean initial, boolean needResult);
void finishInput(in IInputMethodClient client);
- void showSoftInput(in IInputMethodClient client, int flags);
- void hideSoftInput(in IInputMethodClient client, int flags);
+ boolean showSoftInput(in IInputMethodClient client, int flags,
+ in ResultReceiver resultReceiver);
+ boolean hideSoftInput(in IInputMethodClient client, int flags,
+ in ResultReceiver resultReceiver);
void windowGainedFocus(in IInputMethodClient client,
boolean viewHasFocus, boolean isTextEditor,
int softInputMode, boolean first, int windowFlags);
@@ -47,6 +50,7 @@ interface IInputMethodManager {
void showInputMethodPickerFromClient(in IInputMethodClient client);
void setInputMethod(in IBinder token, String id);
void hideMySoftInput(in IBinder token, int flags);
+ void showMySoftInput(in IBinder token, int flags);
void updateStatusIcon(in IBinder token, String packageName, int iconId);
boolean setInputMethodEnabled(String id, boolean enabled);
diff --git a/core/java/com/android/internal/view/IInputMethodSession.aidl b/core/java/com/android/internal/view/IInputMethodSession.aidl
index 8a4497692d6e6..a05ff14bcccd6 100644
--- a/core/java/com/android/internal/view/IInputMethodSession.aidl
+++ b/core/java/com/android/internal/view/IInputMethodSession.aidl
@@ -46,4 +46,6 @@ oneway interface IInputMethodSession {
void dispatchTrackballEvent(int seq, in MotionEvent event, IInputMethodCallback callback);
void appPrivateCommand(String action, in Bundle data);
+
+ void toggleSoftInput(int showFlags, int hideFlags);
}
diff --git a/core/java/com/android/internal/widget/EditStyledText.java b/core/java/com/android/internal/widget/EditStyledText.java
new file mode 100644
index 0000000000000..48b47806a0bb7
--- /dev/null
+++ b/core/java/com/android/internal/widget/EditStyledText.java
@@ -0,0 +1,653 @@
+/*
+ * 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.text.Editable;
+import android.text.Spannable;
+import android.text.style.AbsoluteSizeSpan;
+import android.text.style.ForegroundColorSpan;
+import android.util.AttributeSet;
+import android.util.Log;
+import android.view.MotionEvent;
+import android.widget.EditText;
+
+/**
+ * EditStyledText extends EditText for managing the flow and status
+ * to edit the styled text. This manages the states and flows of editing,
+ * supports inserting image, import/export HTML.
+ */
+public class EditStyledText extends EditText {
+
+ private static final String LOG_TAG = "EditStyledText";
+ private static final boolean DBG = true;
+
+ /**
+ * The modes of editing actions.
+ */
+ /** The mode that no editing action is done. */
+ public static final int MODE_NOTHING = 0;
+ /** The mode of copy. */
+ public static final int MODE_COPY = 1;
+ /** The mode of paste. */
+ public static final int MODE_PASTE = 2;
+ /** The mode of changing size. */
+ public static final int MODE_SIZE = 3;
+ /** The mode of changing color. */
+ public static final int MODE_COLOR = 4;
+ /** The mode of selection. */
+ public static final int MODE_SELECT = 5;
+
+ /**
+ * The state of selection.
+ */
+ /** The state that selection isn't started. */
+ public static final int STATE_SELECT_OFF = 0;
+ /** The state that selection is started. */
+ public static final int STATE_SELECT_ON = 1;
+ /** The state that selection is done, but not fixed. */
+ public static final int STATE_SELECTED = 2;
+ /** The state that selection is done and not fixed.*/
+ public static final int STATE_SELECT_FIX = 3;
+
+ /**
+ * The help message strings.
+ */
+ public static final int HINT_MSG_NULL = 0;
+ public static final int HINT_MSG_COPY_BUF_BLANK = 1;
+ public static final int HINT_MSG_SELECT_START = 2;
+ public static final int HINT_MSG_SELECT_END = 3;
+ public static final int HINT_MSG_PUSH_COMPETE = 4;
+
+
+ /**
+ * EditStyledTextInterface provides functions for notifying messages
+ * to calling class.
+ */
+ public interface EditStyledTextInterface {
+ public void notifyHintMsg(int msg_id);
+ }
+ private EditStyledTextInterface mESTInterface;
+
+ /**
+ * EditStyledTextEditorManager manages the flow and status of
+ * each function for editing styled text.
+ */
+ private EditStyledTextEditorManager mManager;
+
+ /**
+ * EditStyledText extends EditText for managing flow of each editing
+ * action.
+ */
+ public EditStyledText(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ init();
+ }
+
+ public EditStyledText(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ public EditStyledText(Context context) {
+ super(context);
+ init();
+ }
+
+ /**
+ * Set View objects used in EditStyledText.
+ * @param helptext The view shows help messages.
+ */
+ public void setParts(EditStyledTextInterface est_interface) {
+ mESTInterface = est_interface;
+ }
+
+ @Override
+ public boolean onTouchEvent(MotionEvent event) {
+ final boolean superResult = super.onTouchEvent(event);
+ if (event.getAction() == MotionEvent.ACTION_UP) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onTouchEvent");
+ }
+ mManager.onTouchScreen();
+ }
+ return superResult;
+ }
+
+ /**
+ * Start editing. This function have to be called before other
+ * editing actions.
+ */
+ public void onStartEdit() {
+ mManager.onStartEdit();
+ }
+
+ /**
+ * End editing.
+ */
+ public void onEndEdit() {
+ mManager.onEndEdit();
+ }
+
+ /**
+ * Start "Copy" action.
+ */
+ public void onStartCopy() {
+ mManager.onStartCopy();
+ }
+
+ /**
+ * Start "Paste" action.
+ */
+ public void onStartPaste() {
+ mManager.onStartPaste();
+ }
+
+ /**
+ * Start changing "Size" action.
+ */
+ public void onStartSize() {
+ mManager.onStartSize();
+ }
+
+ /**
+ * Start changing "Color" action.
+ */
+ public void onStartColor() {
+ mManager.onStartColor();
+ }
+
+ /**
+ * Start "Select" action.
+ */
+ public void onStartSelect() {
+ mManager.onStartSelect();
+ }
+
+ /**
+ * Start "SelectAll" action.
+ */
+ public void onStartSelectAll() {
+ mManager.onStartSelectAll();
+ }
+
+ /**
+ * Fix Selected Item.
+ */
+ public void fixSelectedItem() {
+ mManager.onFixSelectItem();
+ }
+
+ /**
+ * Set Size of the Item.
+ * @param size The size of the Item.
+ */
+ public void setItemSize(int size) {
+ mManager.setItemSize(size);
+ }
+
+ /**
+ * Set Color of the Item.
+ * @param color The color of the Item.
+ */
+ public void setItemColor(int color) {
+ mManager.setItemColor(color);
+ }
+
+ /**
+ * Check editing is started.
+ * @return Whether editing is started or not.
+ */
+ public boolean isEditting() {
+ return mManager.isEditting();
+ }
+
+ /**
+ * Get the mode of the action.
+ * @return The mode of the action.
+ */
+ public int getEditMode() {
+ return mManager.getEditMode();
+ }
+
+ /**
+ * Get the state of the selection.
+ * @return The state of the selection.
+ */
+ public int getSelectState() {
+ return mManager.getSelectState();
+ }
+
+ /**
+ * Initialize members.
+ */
+ private void init() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- init");
+ requestFocus();
+ }
+ mManager = new EditStyledTextEditorManager(this);
+ }
+
+ /**
+ * Notify hint messages what action is expected to calling class.
+ * @param msg
+ */
+ private void setHintMessage(int msg_id) {
+ if (mESTInterface != null) {
+ mESTInterface.notifyHintMsg(msg_id);
+ }
+ }
+
+ /**
+ * Object which manages the flow and status of editing actions.
+ */
+ private class EditStyledTextEditorManager {
+ private boolean mEditFlag = false;
+ private int mMode = 0;
+ private int mState = 0;
+ private int mCurStart = 0;
+ private int mCurEnd = 0;
+ private EditStyledText mEST;
+ private Editable mTextSelectBuffer;
+ private CharSequence mTextCopyBufer;
+
+ EditStyledTextEditorManager(EditStyledText est) {
+ mEST = est;
+ }
+
+ public void onStartEdit() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onEdit");
+ }
+ handleResetEdit();
+ }
+
+ public void onEndEdit() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickCancel");
+ }
+ handleCancel();
+ }
+
+ public void onStartCopy() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickCopy");
+ }
+ handleCopy();
+ }
+
+ public void onStartPaste() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickPaste");
+ }
+ handlePaste();
+ }
+
+ public void onStartSize() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickSize");
+ }
+ handleSize();
+ }
+
+ public void setItemSize(int size) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickSizeItem");
+ }
+ if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
+ changeSizeSelectedText(size);
+ handleResetEdit();
+ }
+ }
+
+ public void setItemColor(int color) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickColorItem");
+ }
+ if (mState == STATE_SELECTED || mState == STATE_SELECT_FIX) {
+ changeColorSelectedText(color);
+ handleResetEdit();
+ }
+ }
+
+ public void onStartColor() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickColor");
+ }
+ handleColor();
+ }
+
+ public void onStartSelect() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickSelect");
+ }
+ mMode = MODE_SELECT;
+ if (mState == STATE_SELECT_OFF) {
+ handleSelect();
+ } else {
+ offSelect();
+ handleSelect();
+ }
+ }
+
+ public void onStartSelectAll() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickSelectAll");
+ }
+ handleSelectAll();
+ }
+
+ public void onTouchScreen() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickView");
+ }
+ if (mState == STATE_SELECT_ON || mState == STATE_SELECTED) {
+ handleSelect();
+ }
+ }
+
+ public void onFixSelectItem() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onClickComplete");
+ }
+ handleComplete();
+ }
+
+ public boolean isEditting() {
+ return mEditFlag;
+ }
+
+ public int getEditMode() {
+ return mMode;
+ }
+
+ public int getSelectState() {
+ return mState;
+ }
+
+ private void handleCancel() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleCancel");
+ }
+ mMode = MODE_NOTHING;
+ mState = STATE_SELECT_OFF;
+ mEditFlag = false;
+ offSelect();
+ }
+
+ private void handleComplete() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleComplete");
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mState == STATE_SELECTED) {
+ mState = STATE_SELECT_FIX;
+ }
+ switch (mMode) {
+ case MODE_COPY:
+ handleCopy();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleCopy() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleCopy: " + mMode + "," + mState);
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
+ mMode = MODE_COPY;
+ if (mState == STATE_SELECTED) {
+ mState = STATE_SELECT_FIX;
+ storeSelectedText();
+ } else {
+ handleSelect();
+ }
+ } else if (mMode != MODE_COPY) {
+ handleCancel();
+ mMode = MODE_COPY;
+ handleCopy();
+ } else if (mState == STATE_SELECT_FIX) {
+ mEST.setHintMessage(HINT_MSG_NULL);
+ storeSelectedText();
+ handleResetEdit();
+ }
+ }
+
+ private void handlePaste() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handlePaste");
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mTextSelectBuffer != null && mTextCopyBufer.length() > 0) {
+ mTextSelectBuffer.insert(mEST.getSelectionStart(),
+ mTextCopyBufer);
+ } else {
+ mEST.setHintMessage(HINT_MSG_COPY_BUF_BLANK);
+ }
+ }
+
+ private void handleSize() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleSize: " + mMode + "," + mState);
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
+ mMode = MODE_SIZE;
+ if (mState == STATE_SELECTED) {
+ mState = STATE_SELECT_FIX;
+ } else {
+ handleSelect();
+ }
+ } else if (mMode != MODE_SIZE) {
+ handleCancel();
+ mMode = MODE_SIZE;
+ handleSize();
+ } else if (mState == STATE_SELECT_FIX) {
+ mEST.setHintMessage(HINT_MSG_NULL);
+ }
+ }
+
+ private void handleColor() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleColor");
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mMode == MODE_NOTHING || mMode == MODE_SELECT) {
+ mMode = MODE_COLOR;
+ if (mState == STATE_SELECTED) {
+ mState = STATE_SELECT_FIX;
+ } else {
+ handleSelect();
+ }
+ } else if (mMode != MODE_COLOR) {
+ handleCancel();
+ mMode = MODE_COLOR;
+ handleSize();
+ } else if (mState == STATE_SELECT_FIX) {
+ mEST.setHintMessage(HINT_MSG_NULL);
+ }
+ }
+
+ private void handleSelect() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleSelect" + mEditFlag + "," + mState);
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ if (mState == STATE_SELECT_OFF) {
+ if (isTextSelected()) {
+ Log.e(LOG_TAG, "Selection state is off, but selected");
+ }
+ setSelectStartPos();
+ mEST.setHintMessage(HINT_MSG_SELECT_END);
+ } else if (mState == STATE_SELECT_ON) {
+ if (isTextSelected()) {
+ Log.e(LOG_TAG, "Selection state now start, but selected");
+ }
+ setSelectEndPos();
+ mEST.setHintMessage(HINT_MSG_PUSH_COMPETE);
+ doNextHandle();
+ } else if (mState == STATE_SELECTED) {
+ if (!isTextSelected()) {
+ Log.e(LOG_TAG,
+ "Selection state is done, but not selected");
+ }
+ setSelectEndPos();
+ doNextHandle();
+ }
+ }
+
+ private void handleSelectAll() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- handleSelectAll");
+ }
+ if (!mEditFlag) {
+ return;
+ }
+ mEST.selectAll();
+ }
+
+ private void doNextHandle() {
+ switch (mMode) {
+ case MODE_COPY:
+ handleCopy();
+ break;
+ case MODE_PASTE:
+ handlePaste();
+ break;
+ case MODE_SIZE:
+ handleSize();
+ break;
+ case MODE_COLOR:
+ handleColor();
+ break;
+ default:
+ break;
+ }
+ }
+
+ private void handleResetEdit() {
+ handleCancel();
+ mEditFlag = true;
+ mEST.setHintMessage(HINT_MSG_SELECT_START);
+ }
+
+ // Methods of selection
+ private void onSelect() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- onSelect");
+ }
+ if (mCurStart >= 0 && mCurStart <= mEST.getText().length()
+ && mCurEnd >= 0 && mCurEnd <= mEST.getText().length()) {
+ mEST.setSelection(mCurStart, mCurEnd);
+ mState = STATE_SELECTED;
+ } else {
+ Log.e(LOG_TAG,
+ "Select is on, but cursor positions are illigal.:"
+ + mEST.getText().length() + "," + mCurStart
+ + "," + mCurEnd);
+ }
+ }
+
+ private void offSelect() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- offSelect");
+ }
+ int currpos = mEST.getSelectionStart();
+ mEST.setSelection(currpos, currpos);
+ mState = STATE_SELECT_OFF;
+ }
+
+ private void setSelectStartPos() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- setSelectStartPos");
+ }
+ mCurStart = mEST.getSelectionStart();
+ mState = STATE_SELECT_ON;
+ }
+
+ private void setSelectEndPos() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- setSelectEndPos:"
+ + mEST.getSelectionStart());
+ }
+ int curpos = mEST.getSelectionStart();
+ if (curpos < mCurStart) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- setSelectEndPos: swap is done.");
+ }
+ mCurEnd = mCurStart;
+ mCurStart = curpos;
+ } else {
+ mCurEnd = curpos;
+ }
+ onSelect();
+ }
+
+ private boolean isTextSelected() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- isTextSelected:" + mCurStart + ","
+ + mCurEnd);
+ }
+ return (mCurStart != mCurEnd)
+ && (mState == STATE_SELECTED ||
+ mState == STATE_SELECT_FIX);
+ }
+
+ private void storeSelectedText() {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- storeSelectedText");
+ }
+ mTextSelectBuffer = mEST.getText();
+ mTextCopyBufer = mTextSelectBuffer.subSequence(mCurStart, mCurEnd);
+ }
+
+ private void changeSizeSelectedText(int size) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- changeSizeSelectedText:" + size + ","
+ + mCurStart + "," + mCurEnd);
+ }
+ mEST.getText().setSpan(new AbsoluteSizeSpan(size), mCurStart,
+ mCurEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+
+ private void changeColorSelectedText(int color) {
+ if (DBG) {
+ Log.d(LOG_TAG, "--- changeCollorSelectedText:" + color + ","
+ + mCurStart + "," + mCurEnd);
+ }
+ mEST.getText().setSpan(new ForegroundColorSpan(color), mCurStart,
+ mCurEnd, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
+ }
+ }
+
+}
diff --git a/core/jni/android/graphics/Typeface.cpp b/core/jni/android/graphics/Typeface.cpp
index 32954cef63f13..e9514317b8f3c 100644
--- a/core/jni/android/graphics/Typeface.cpp
+++ b/core/jni/android/graphics/Typeface.cpp
@@ -32,11 +32,11 @@ static SkTypeface* Typeface_create(JNIEnv* env, jobject, jstring name,
SkTypeface* face;
if (NULL == name) {
- face = SkTypeface::Create(NULL, (SkTypeface::Style)style);
+ face = SkTypeface::CreateFromName(NULL, (SkTypeface::Style)style);
}
else {
AutoJavaStringToUTF8 str(env, name);
- face = SkTypeface::Create(str.c_str(), style);
+ face = SkTypeface::CreateFromName(str.c_str(), style);
}
return face;
}
@@ -50,7 +50,7 @@ static void Typeface_unref(JNIEnv* env, jobject obj, SkTypeface* face) {
}
static int Typeface_getStyle(JNIEnv* env, jobject obj, SkTypeface* face) {
- return face->getStyle();
+ return face->style();
}
class AssetStream : public SkStream {
diff --git a/core/jni/android_text_format_Time.cpp b/core/jni/android_text_format_Time.cpp
index 8e41ec76a8014..923e1aa05cc83 100644
--- a/core/jni/android_text_format_Time.cpp
+++ b/core/jni/android_text_format_Time.cpp
@@ -52,6 +52,9 @@ static jfieldID g_dateTimeFormatField = 0;
static jfieldID g_amField = 0;
static jfieldID g_pmField = 0;
static jfieldID g_dateCommandField = 0;
+static jfieldID g_localeField = 0;
+
+static jclass g_timeClass = NULL;
static inline bool java2time(JNIEnv* env, Time* t, jobject o)
{
@@ -183,57 +186,102 @@ static jstring android_text_format_Time_format2445(JNIEnv* env, jobject This)
static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
jstring formatObject)
{
- Time t;
- struct strftime_locale locale;
- jclass timeClass = env->FindClass("android/text/format/Time");
- jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
- jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
- jobjectArray ja;
+ // We only teardown and setup our 'locale' struct and other state
+ // when the Java-side locale changed. This is safe to do here
+ // without locking because we're always called from Java code
+ // synchronized on the class instance.
+ static jobject js_locale_previous = NULL;
+ static struct strftime_locale locale;
+ static jstring js_mon[12], js_month[12], js_wday[7], js_weekday[7];
+ static jstring js_X_fmt, js_x_fmt, js_c_fmt, js_am, js_pm, js_date_fmt;
+ Time t;
if (!java2time(env, &t, This)) return env->NewStringUTF("");
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
- for (int i = 0; i < 12; i++) {
- js_mon[i] = (jstring) env->GetObjectArrayElement(ja, i);
- locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
+ jclass timeClass = g_timeClass;
+ jobject js_locale = (jobject) env->GetStaticObjectField(timeClass, g_localeField);
+ if (js_locale_previous != js_locale) {
+ if (js_locale_previous != NULL) {
+ // Free the old one.
+ for (int i = 0; i < 12; i++) {
+ env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
+ env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
+ env->DeleteGlobalRef(js_mon[i]);
+ env->DeleteGlobalRef(js_month[i]);
+ }
+
+ for (int i = 0; i < 7; i++) {
+ env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
+ env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
+ env->DeleteGlobalRef(js_wday[i]);
+ env->DeleteGlobalRef(js_weekday[i]);
+ }
+
+ env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
+ env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
+ env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
+ env->ReleaseStringUTFChars(js_am, locale.am);
+ env->ReleaseStringUTFChars(js_pm, locale.pm);
+ env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
+ env->DeleteGlobalRef(js_X_fmt);
+ env->DeleteGlobalRef(js_x_fmt);
+ env->DeleteGlobalRef(js_c_fmt);
+ env->DeleteGlobalRef(js_am);
+ env->DeleteGlobalRef(js_pm);
+ env->DeleteGlobalRef(js_date_fmt);
+ }
+ js_locale_previous = js_locale;
+
+ jobjectArray ja;
+ ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortMonthsField);
+ for (int i = 0; i < 12; i++) {
+ js_mon[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
+ locale.mon[i] = env->GetStringUTFChars(js_mon[i], NULL);
+ }
+
+ ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
+ for (int i = 0; i < 12; i++) {
+ js_month[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
+ locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
+ }
+
+ ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
+ for (int i = 0; i < 7; i++) {
+ js_wday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
+ locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
+ }
+
+ ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
+ for (int i = 0; i < 7; i++) {
+ js_weekday[i] = (jstring) env->NewGlobalRef(env->GetObjectArrayElement(ja, i));
+ locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
+ }
+
+ js_X_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_timeOnlyFormatField));
+ locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
+
+ js_x_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_dateOnlyFormatField));
+ locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
+
+ js_c_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_dateTimeFormatField));
+ locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
+
+ js_am = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_amField));
+ locale.am = env->GetStringUTFChars(js_am, NULL);
+
+ js_pm = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_pmField));
+ locale.pm = env->GetStringUTFChars(js_pm, NULL);
+
+ js_date_fmt = (jstring) env->NewGlobalRef(env->GetStaticObjectField(
+ timeClass, g_dateCommandField));
+ locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
}
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longMonthsField);
- for (int i = 0; i < 12; i++) {
- js_month[i] = (jstring) env->GetObjectArrayElement(ja, i);
- locale.month[i] = env->GetStringUTFChars(js_month[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_shortWeekdaysField);
- for (int i = 0; i < 7; i++) {
- js_wday[i] = (jstring) env->GetObjectArrayElement(ja, i);
- locale.wday[i] = env->GetStringUTFChars(js_wday[i], NULL);
- }
-
- ja = (jobjectArray) env->GetStaticObjectField(timeClass, g_longWeekdaysField);
- for (int i = 0; i < 7; i++) {
- js_weekday[i] = (jstring) env->GetObjectArrayElement(ja, i);
- locale.weekday[i] = env->GetStringUTFChars(js_weekday[i], NULL);
- }
-
- js_X_fmt = (jstring) env->GetStaticObjectField(timeClass, g_timeOnlyFormatField);
- locale.X_fmt = env->GetStringUTFChars(js_X_fmt, NULL);
-
- js_x_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateOnlyFormatField);
- locale.x_fmt = env->GetStringUTFChars(js_x_fmt, NULL);
-
- js_c_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateTimeFormatField);
- locale.c_fmt = env->GetStringUTFChars(js_c_fmt, NULL);
-
- js_am = (jstring) env->GetStaticObjectField(timeClass, g_amField);
- locale.am = env->GetStringUTFChars(js_am, NULL);
-
- js_pm = (jstring) env->GetStaticObjectField(timeClass, g_pmField);
- locale.pm = env->GetStringUTFChars(js_pm, NULL);
-
- js_date_fmt = (jstring) env->GetStaticObjectField(timeClass, g_dateCommandField);
- locale.date_fmt = env->GetStringUTFChars(js_date_fmt, NULL);
-
ACQUIRE_TIMEZONE(This, t)
const char* format = env->GetStringUTFChars(formatObject, NULL);
@@ -243,23 +291,6 @@ static jstring android_text_format_Time_format(JNIEnv* env, jobject This,
env->ReleaseStringUTFChars(formatObject, format);
RELEASE_TIMEZONE(This, t)
- for (int i = 0; i < 12; i++) {
- env->ReleaseStringUTFChars(js_mon[i], locale.mon[i]);
- env->ReleaseStringUTFChars(js_month[i], locale.month[i]);
- }
-
- for (int i = 0; i < 7; i++) {
- env->ReleaseStringUTFChars(js_wday[i], locale.wday[i]);
- env->ReleaseStringUTFChars(js_weekday[i], locale.weekday[i]);
- }
-
- env->ReleaseStringUTFChars(js_X_fmt, locale.X_fmt);
- env->ReleaseStringUTFChars(js_x_fmt, locale.x_fmt);
- env->ReleaseStringUTFChars(js_c_fmt, locale.c_fmt);
- env->ReleaseStringUTFChars(js_am, locale.am);
- env->ReleaseStringUTFChars(js_pm, locale.pm);
- env->ReleaseStringUTFChars(js_date_fmt, locale.date_fmt);
-
return env->NewStringUTF(r.string());
}
@@ -307,7 +338,6 @@ static void android_text_format_Time_set(JNIEnv* env, jobject This, jlong millis
{
env->SetBooleanField(This, g_allDayField, JNI_FALSE);
Time t;
- if (!java2time(env, &t, This)) return;
ACQUIRE_TIMEZONE(This, t)
t.set(millis);
@@ -592,6 +622,8 @@ int register_android_text_format_Time(JNIEnv* env)
{
jclass timeClass = env->FindClass("android/text/format/Time");
+ g_timeClass = (jclass) env->NewGlobalRef(timeClass);
+
g_allDayField = env->GetFieldID(timeClass, "allDay", "Z");
g_secField = env->GetFieldID(timeClass, "second", "I");
g_minField = env->GetFieldID(timeClass, "minute", "I");
@@ -615,9 +647,9 @@ int register_android_text_format_Time(JNIEnv* env)
g_amField = env->GetStaticFieldID(timeClass, "sAm", "Ljava/lang/String;");
g_pmField = env->GetStaticFieldID(timeClass, "sPm", "Ljava/lang/String;");
g_dateCommandField = env->GetStaticFieldID(timeClass, "sDateCommand", "Ljava/lang/String;");
+ g_localeField = env->GetStaticFieldID(timeClass, "sLocale", "Ljava/util/Locale;");
return AndroidRuntime::registerNativeMethods(env, "android/text/format/Time", gMethods, NELEM(gMethods));
}
}; // namespace android
-
diff --git a/core/res/assets/webkit/nullplugin.png b/core/res/assets/webkit/nullPlugin.png
similarity index 100%
rename from core/res/assets/webkit/nullplugin.png
rename to core/res/assets/webkit/nullPlugin.png
diff --git a/core/res/res/anim/zoom_ring_enter.xml b/core/res/res/anim/zoom_ring_enter.xml
new file mode 100644
index 0000000000000..13d89b2f229b2
--- /dev/null
+++ b/core/res/res/anim/zoom_ring_enter.xml
@@ -0,0 +1,29 @@
+
+
+
+
* It can be defined in an XML file with the <level-list> element.
- * Each Drawable level is defined in a nested <item>
+ * Each Drawable level is defined in a nested <item>. For example:
*
+ * <level-list xmlns:android="http://schemas.android.com/apk/res/android"> + * <item android:maxLevel="0" android:drawable="@drawable/ic_wifi_signal_1" /> + * <item android:maxLevel="1" android:drawable="@drawable/ic_wifi_signal_2" /> + * <item android:maxLevel="2" android:drawable="@drawable/ic_wifi_signal_3" /> + * <item android:maxLevel="3" android:drawable="@drawable/ic_wifi_signal_4" /> + * </level-list> + *+ *
With this XML saved into the res/drawable/ folder of the project, it can be referenced as + * the drawable for an {@link android.widget.ImageView}. The default image is the first in the list. + * It can then be changed to one of the other levels with + * {@link android.widget.ImageView#setImageLevel(int)}.
* @attr ref android.R.styleable#LevelListDrawableItem_minLevel * @attr ref android.R.styleable#LevelListDrawableItem_maxLevel * @attr ref android.R.styleable#LevelListDrawableItem_drawable diff --git a/include/ui/ICameraService.h b/include/ui/ICameraService.h index dfd89230017d5..c652c5169a0a4 100644 --- a/include/ui/ICameraService.h +++ b/include/ui/ICameraService.h @@ -28,7 +28,7 @@ namespace android { class ICameraService : public IInterface { -protected: +public: enum { CONNECT = IBinder::FIRST_CALL_TRANSACTION, }; diff --git a/include/utils/threads.h b/include/utils/threads.h index 7dca810043e98..8d8d46a054d8e 100644 --- a/include/utils/threads.h +++ b/include/utils/threads.h @@ -248,41 +248,6 @@ private: }; -/* - * Read/write lock. The resource can have multiple readers or one writer, - * but can't be read and written at the same time. - * - * The same thread should not call a lock function while it already has - * a lock. (Should be okay for multiple readers.) - */ -class ReadWriteLock { -public: - ReadWriteLock() - : mNumReaders(0), mNumWriters(0) - {} - ~ReadWriteLock() {} - - void lockForRead(); - bool tryLockForRead(); - void unlockForRead(); - - void lockForWrite(); - bool tryLockForWrite(); - void unlockForWrite(); - -private: - int mNumReaders; - int mNumWriters; - - Mutex mLock; - Condition mReadWaiter; - Condition mWriteWaiter; -#if defined(PRINT_RENDER_TIMES) - DurationTimer mDebugTimer; -#endif -}; - - /* * This is our spiffy thread object! */ diff --git a/libs/utils/CallStack.cpp b/libs/utils/CallStack.cpp index 26fb22abc01dc..2fdaa71186b38 100644 --- a/libs/utils/CallStack.cpp +++ b/libs/utils/CallStack.cpp @@ -120,13 +120,18 @@ class MapInfo { char name[]; }; - const char *map_to_name(uint64_t pc, const char* def) { + const char *map_to_name(uint64_t pc, const char* def, uint64_t* start) { mapinfo* mi = getMapInfoList(); while(mi) { - if ((pc >= mi->start) && (pc < mi->end)) + if ((pc >= mi->start) && (pc < mi->end)) { + if (start) + *start = mi->start; return mi->name; + } mi = mi->next; } + if (start) + *start = 0; return def; } @@ -183,8 +188,15 @@ public: } } - static const char *mapAddressToName(const void* pc, const char* def) { - return sMapInfo.map_to_name((uint64_t)pc, def); + static const char *mapAddressToName(const void* pc, const char* def, + void const** start) + { + uint64_t s; + char const* name = sMapInfo.map_to_name(uint64_t(uintptr_t(pc)), def, &s); + if (start) { + *start = (void*)s; + } + return name; } }; @@ -297,8 +309,9 @@ String8 CallStack::toStringSingleLevel(const char* prefix, int32_t level) const res.append(name); res.append(tmp2); } else { - name = MapInfo::mapAddressToName(ip, "