package org.holoeverywhere.widget;
import org.holoeverywhere.R;
import org.holoeverywhere.internal._View;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Rect;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.util.AttributeSet;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.view.accessibility.AccessibilityEvent;
import android.view.accessibility.AccessibilityNodeInfo;
public abstract class AbsSeekBar extends ProgressBar {
private static final int NO_ALPHA = 0xFF;
private float mDisabledAlpha;
private boolean mIsDragging;
boolean mIsUserSeekable = true;
private int mKeyProgressIncrement = 1;
private int mScaledTouchSlop;
private Drawable mThumb;
private int mThumbOffset;
private float mTouchDownX;
float mTouchProgressOffset;
public AbsSeekBar(Context context) {
super(context);
}
public AbsSeekBar(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AbsSeekBar(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
TypedArray a = context.obtainStyledAttributes(attrs,
R.styleable.SeekBar, defStyle, 0);
Drawable thumb = a.getDrawable(R.styleable.SeekBar_android_thumb);
setThumb(thumb);
int thumbOffset = a.getDimensionPixelOffset(
R.styleable.SeekBar_android_thumbOffset, getThumbOffset());
setThumbOffset(thumbOffset);
mDisabledAlpha = a.getFloat(R.styleable.SeekBar_android_disabledAlpha,
0.5f);
a.recycle();
mScaledTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop();
}
private void attemptClaimDrag() {
if (getParent() != null) {
getParent().requestDisallowInterceptTouchEvent(true);
}
}
@Override
protected void drawableStateChanged() {
super.drawableStateChanged();
Drawable progressDrawable = getProgressDrawable();
if (progressDrawable != null) {
progressDrawable.setAlpha(isEnabled() ? AbsSeekBar.NO_ALPHA
: (int) (AbsSeekBar.NO_ALPHA * mDisabledAlpha));
}
if (mThumb != null && mThumb.isStateful()) {
int[] state = getDrawableState();
mThumb.setState(state);
}
}
public int getKeyProgressIncrement() {
return mKeyProgressIncrement;
}
public Drawable getThumb() {
return mThumb;
}
public int getThumbOffset() {
return mThumbOffset;
}
public boolean isInScrollingContainer() {
return false;
}
@Override
@SuppressLint("NewApi")
public void jumpDrawablesToCurrentState() {
super.jumpDrawablesToCurrentState();
if (mThumb != null) {
mThumb.jumpToCurrentState();
}
}
@Override
protected synchronized void onDraw(Canvas canvas) {
super.onDraw(canvas);
if (mThumb != null) {
canvas.save();
canvas.translate(getPaddingLeft() - mThumbOffset, getPaddingTop());
mThumb.draw(canvas);
canvas.restore();
}
}
@Override
@SuppressLint("NewApi")
public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
super.onInitializeAccessibilityEvent(event);
event.setClassName(AbsSeekBar.class.getName());
}
@Override
@SuppressLint("NewApi")
public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
super.onInitializeAccessibilityNodeInfo(info);
info.setClassName(AbsSeekBar.class.getName());
if (isEnabled()) {
final int progress = getProgress();
if (progress > 0) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD);
}
if (progress < getMax()) {
info.addAction(AccessibilityNodeInfo.ACTION_SCROLL_FORWARD);
}
}
}
void onKeyChange() {
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (isEnabled()) {
int progress = getProgress();
switch (keyCode) {
case KeyEvent.KEYCODE_DPAD_LEFT:
if (progress <= 0) {
break;
}
setProgress(progress - mKeyProgressIncrement, true);
onKeyChange();
return true;
case KeyEvent.KEYCODE_DPAD_RIGHT:
if (progress >= getMax()) {
break;
}
setProgress(progress + mKeyProgressIncrement, true);
onKeyChange();
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@Override
protected synchronized void onMeasure(int widthMeasureSpec,
int heightMeasureSpec) {
Drawable d = getCurrentDrawable();
int thumbHeight = mThumb == null ? 0 : mThumb.getIntrinsicHeight();
int dw = 0;
int dh = 0;
if (d != null) {
dw = Math
.max(mMinWidth, Math.min(mMaxWidth, d.getIntrinsicWidth()));
dh = Math.max(mMinHeight,
Math.min(mMaxHeight, d.getIntrinsicHeight()));
dh = Math.max(thumbHeight, dh);
}
dw += getPaddingLeft() + getPaddingRight();
dh += getPaddingTop() + getPaddingBottom();
setMeasuredDimension(
_View.supportResolveSizeAndState(dw, widthMeasureSpec, 0),
_View.supportResolveSizeAndState(dh, heightMeasureSpec, 0));
}
@Override
public void onProgressRefresh(float scale, boolean fromUser) {
super.onProgressRefresh(scale, fromUser);
Drawable thumb = mThumb;
if (thumb != null) {
setThumbPos(getWidth(), thumb, scale, Integer.MIN_VALUE);
invalidate();
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
updateThumbPos(w, h);
}
void onStartTrackingTouch() {
mIsDragging = true;
}
void onStopTrackingTouch() {
mIsDragging = false;
}
@Override
public boolean onTouchEvent(MotionEvent event) {
if (!mIsUserSeekable || !isEnabled()) {
return false;
}
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
if (isInScrollingContainer()) {
mTouchDownX = event.getX();
} else {
setPressed(true);
if (mThumb != null) {
invalidate(mThumb.getBounds());
}
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
break;
case MotionEvent.ACTION_MOVE:
if (mIsDragging) {
trackTouchEvent(event);
} else {
final float x = event.getX();
if (Math.abs(x - mTouchDownX) > mScaledTouchSlop) {
setPressed(true);
if (mThumb != null) {
invalidate(mThumb.getBounds());
}
onStartTrackingTouch();
trackTouchEvent(event);
attemptClaimDrag();
}
}
break;
case MotionEvent.ACTION_UP:
if (mIsDragging) {
trackTouchEvent(event);
onStopTrackingTouch();
setPressed(false);
} else {
onStartTrackingTouch();
trackTouchEvent(event);
onStopTrackingTouch();
}
invalidate();
break;
case MotionEvent.ACTION_CANCEL:
if (mIsDragging) {
onStopTrackingTouch();
setPressed(false);
}
invalidate();
break;
}
return true;
}
@Override
public boolean performAccessibilityAction(int action, Bundle arguments) {
if (super.performAccessibilityAction(action, arguments)) {
return true;
}
if (!isEnabled()) {
return false;
}
final int progress = getProgress();
final int increment = Math.max(1, Math.round((float) getMax() / 5));
switch (action) {
case AccessibilityNodeInfo.ACTION_SCROLL_BACKWARD: {
if (progress <= 0) {
return false;
}
setProgress(progress - increment, true);
onKeyChange();
return true;
}
case AccessibilityNodeInfo.ACTION_SCROLL_FORWARD: {
if (progress >= getMax()) {
return false;
}
setProgress(progress + increment, true);
onKeyChange();
return true;
}
}
return false;
}
public void setKeyProgressIncrement(int increment) {
mKeyProgressIncrement = increment < 0 ? -increment : increment;
}
@Override
public synchronized void setMax(int max) {
super.setMax(max);
if (mKeyProgressIncrement == 0 || getMax() / mKeyProgressIncrement > 20) {
setKeyProgressIncrement(Math.max(1,
Math.round((float) getMax() / 20)));
}
}
public void setThumb(Drawable thumb) {
boolean needUpdate;
if (mThumb != null && thumb != mThumb) {
mThumb.setCallback(null);
needUpdate = true;
} else {
needUpdate = false;
}
if (thumb != null) {
thumb.setCallback(this);
mThumbOffset = thumb.getIntrinsicWidth() / 2;
if (needUpdate
&& (thumb.getIntrinsicWidth() != mThumb.getIntrinsicWidth() || thumb
.getIntrinsicHeight() != mThumb
.getIntrinsicHeight())) {
requestLayout();
}
}
mThumb = thumb;
invalidate();
if (needUpdate) {
updateThumbPos(getWidth(), getHeight());
if (thumb != null && thumb.isStateful()) {
int[] state = getDrawableState();
thumb.setState(state);
}
}
}
public void setThumbOffset(int thumbOffset) {
mThumbOffset = thumbOffset;
invalidate();
}
private void setThumbPos(int w, Drawable thumb, float scale, int gap) {
int available = w - getPaddingLeft() - getPaddingRight();
int thumbWidth = thumb.getIntrinsicWidth();
int thumbHeight = thumb.getIntrinsicHeight();
available -= thumbWidth;
available += mThumbOffset * 2;
int thumbPos = (int) (scale * available);
int topBound, bottomBound;
if (gap == Integer.MIN_VALUE) {
Rect oldBounds = thumb.getBounds();
topBound = oldBounds.top;
bottomBound = oldBounds.bottom;
} else {
topBound = gap;
bottomBound = gap + thumbHeight;
}
thumb.setBounds(thumbPos, topBound, thumbPos + thumbWidth, bottomBound);
}
private void trackTouchEvent(MotionEvent event) {
final int width = getWidth();
final int available = width - getPaddingLeft() - getPaddingRight();
int x = (int) event.getX();
float scale;
float progress = 0;
if (x < getPaddingLeft()) {
scale = 0.0f;
} else if (x > width - getPaddingRight()) {
scale = 1.0f;
} else {
scale = (float) (x - getPaddingLeft()) / (float) available;
progress = mTouchProgressOffset;
}
final int max = getMax();
progress += scale * max;
setProgress((int) progress, true);
}
private void updateThumbPos(int w, int h) {
Drawable d = getCurrentDrawable();
Drawable thumb = mThumb;
int thumbHeight = thumb == null ? 0 : thumb.getIntrinsicHeight();
int trackHeight = Math.min(mMaxHeight, h - getPaddingTop()
- getPaddingBottom());
int max = getMax();
float scale = max > 0 ? (float) getProgress() / (float) max : 0;
if (thumbHeight > trackHeight) {
if (thumb != null) {
setThumbPos(w, thumb, scale, 0);
}
int gapForCenteringTrack = (thumbHeight - trackHeight) / 2;
if (d != null) {
d.setBounds(0, gapForCenteringTrack, w - getPaddingRight()
- getPaddingLeft(), h - getPaddingBottom()
- gapForCenteringTrack - getPaddingTop());
}
} else {
if (d != null) {
d.setBounds(0, 0, w - getPaddingRight() - getPaddingLeft(), h
- getPaddingBottom() - getPaddingTop());
}
int gap = (trackHeight - thumbHeight) / 2;
if (thumb != null) {
setThumbPos(w, thumb, scale, gap);
}
}
}
@Override
protected boolean verifyDrawable(Drawable who) {
return who == mThumb || super.verifyDrawable(who);
}
}