/*
* Copyright (C) 2010 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.talent.allshare.widget;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Bitmap.Config;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.PorterDuff.Mode;
import android.graphics.PorterDuffXfermode;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.os.Handler;
import android.os.Message;
import android.os.SystemClock;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.ViewConfiguration;
import android.widget.CompoundButton;
import com.youplayer.player.R;
public class IOSSwitch extends CompoundButton{
private static final boolean DBG = false;
private static final String TAG = "IOSSwitch";
private static final int MSG_ANIMATE = 1000;
private static final int MSG_ANIMATE_REVEAL = 1001;
private static final int MSG_ANIMATE_END = 1002;
private int mSwitcherStyle = 0;
private BitmapDrawable mDrawable;
private Bitmap mBitmap;
private float mOffset;
//54(Handle left width) + 40(handle width) + 54(handle right width) = 148(bitmap width) * 27(bitmap height)
private int mDisplayWidth = 0; //116
private int mExtra = 0;
private int mWidth;
private int mHeight;
private boolean mOn = false;
private Paint mPaint = new Paint();
private H mHandler = new H();
private OnSwitchChangeListener mOnSwitchChangeListener;
static final int ANIM_FRAME_DURATION = (1000/60);
static final int CLICK_FAULT_TOLERANCE = 2;
private int mTouchSlop;
private boolean mDragging = false;
private boolean mDraggStatue = false;
boolean mAnimating = false;
long mCurAnimationTime;
long mAnimLastTime;
float mAnimVel = 200.0f;
float mAnimAccel = 1000.0f;
private float[] mTouchDown = {0.0f,0.0f};
private float[] mDragTrack = {0.0f,0.0f};
public IOSSwitch(Context context) {
this(context, null);
}
public IOSSwitch(Context context, AttributeSet attrs) {
this(context, attrs, 0);
}
public IOSSwitch(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
ViewConfiguration config = ViewConfiguration.get(context);
mTouchSlop = config.getScaledTouchSlop();
init();
}
@Override
public boolean onTouchEvent(MotionEvent ev) {
float[] touchPonit={0.0f, 0.0f};
float distanceX = 0.0f;
float distanceY = 0.0f;
final int action = ev.getActionMasked();
Log.d(TAG, "onTouchEvent():: action = "+action +" mAnimating="+mAnimating +" isEnabled="+IOSSwitch.this.isEnabled());
if(mAnimating){
return true;
}
switch (action) {
case MotionEvent.ACTION_DOWN: {
mDragging = false;
mDraggStatue = false;
mTouchDown[0] = ev.getX();
mTouchDown[1] = ev.getY();
return true;
}
case MotionEvent.ACTION_MOVE: {
touchPonit[0] = ev.getX();
touchPonit[1] = ev.getY();
if(mDragging){
distanceX = touchPonit[0]-mDragTrack[0];
distanceY = touchPonit[1]-mDragTrack[1];
}else{
distanceX = touchPonit[0]-mTouchDown[0];
distanceY = touchPonit[1]-mTouchDown[1];
if(Math.abs(distanceX)>mTouchSlop){
mDraggStatue = true;
}
mDragTrack[0] = ev.getX();
mDragTrack[1] = ev.getY();
}
if(Math.abs(distanceX)>mTouchSlop){
mDragging = true;
distanceX = touchPonit[0]-mTouchDown[0];
}
if(mDragging && !this.mOn && 0<distanceX){
mOffset = mExtra - distanceX + 0.5f;
}else if(mDragging && this.mOn && 0>distanceX){
mOffset = 0.5f - distanceX;
}
mDragging = false;
invalidate();
return true;
}
case MotionEvent.ACTION_UP:
case MotionEvent.ACTION_CANCEL: {
touchPonit[0] = ev.getX();
touchPonit[1] = ev.getY();
distanceX = touchPonit[0]-mTouchDown[0];
distanceY = touchPonit[1]-mTouchDown[1];
if(!mDraggStatue){
performFling(!this.mOn);
}else{
if(mOffset >= mExtra/2 && mOffset<mExtra){
performFling(false);
}else if(mOffset<mExtra/2 && mOffset>0){
performFling(true);
}else{
mOn = (mOffset == 0);
if (mOnSwitchChangeListener != null) {
mOnSwitchChangeListener.onSwitchChanged(IOSSwitch.this, mOn);
}
IOSSwitch.this.setChecked(mOn);
}
}
return true;
}
}
return false;
}
public void setOn(boolean on) {
if (on) {
mOffset = 0;
Log.d(TAG, "setOn::mOffset = 0");
} else {
mOffset = mExtra;
}
invalidate();
mOn = on;
}
public void setSwitchStyle(int iStyle){
mSwitcherStyle = iStyle;
setDrawable();
}
private void setDrawable(){
switch(mSwitcherStyle){
case 1:
mDrawable = (BitmapDrawable)getResources().getDrawable(R.drawable.switch_handler_normal);
break;
case 2:
mDrawable = (BitmapDrawable)getResources().getDrawable(R.drawable.switch_handler_warning);
break;
default:
mDrawable = (BitmapDrawable)getResources().getDrawable(R.drawable.switch_handler_normal);
break;
}
mBitmap = mDrawable.getBitmap();
mDrawable = null;
}
private void init() {
setDrawable();
mWidth = mBitmap.getWidth();
mHeight = mBitmap.getHeight();
mDisplayWidth= (mWidth+mHeight)/2; //(SWITCHER_DISPLAYWIDTH*mWidth)/SWITCHER_WIDTH ;
mExtra = mWidth - mDisplayWidth; //148 - 94 = 54
mOffset = mExtra;
Log.d(TAG, "init::mOffset = " +mOffset);
}
@Override
public void setChecked(boolean checked) {
// TODO Auto-generated method stub
super.setChecked(checked);
if(IOSSwitch.this.isEnabled() && !mAnimating){
setOn(isChecked());
}
}
@Override
protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
int width = MeasureSpec.getSize(widthMeasureSpec);
int height = MeasureSpec.getSize(heightMeasureSpec);
if (DBG) Log.d(TAG, "onMeasure mode:"+widthSpecMode+" w:"+width+" h:"+height);
setMeasuredDimension(mDisplayWidth, mHeight);
}
//two conditions: one is touch switch ok, the other is animation switch ok
boolean isSwitchOk(){
if(this.mAnimating){
if((mOffset == mExtra)||(mOffset == 0)){
return true;
}
/*
if (mOn && (mOffset == mExtra)) {
return true;
}
if ((!mOn) && (mOffset == 0)) {
return true;
}*/
}
return false;
}
@Override
protected void onDraw(Canvas canvas) {
Log.d(TAG, "init::mOffset = " +mOffset);
if (mOffset < 0){
mOffset = 0;
}else if (mOffset > mExtra){
mOffset = mExtra;
}else if(!mAnimating && !mDraggStatue){
mOffset = mOffset>=mExtra?mExtra:0;
}
Rect srcRect = new Rect((int)mOffset, 0, (int)mOffset+mDisplayWidth, mHeight);
Rect dstRect = new Rect(0, 0, mDisplayWidth, mHeight);
//Log.e("AppleSwitcher", "bitmap drawn");
// canvas.drawBitmap(mBitmap, srcRect, dstRect, mPaint);
//get rounder bitmap start
Bitmap Roundedoutput = Bitmap.createBitmap(mDisplayWidth,mHeight, Config.ARGB_8888);
Canvas canvas1 = new Canvas(Roundedoutput);
int color = 0xff424242;
Paint paint = new Paint();
RectF rectF = new RectF(dstRect);
float roundPx = mHeight/2;//(ROUND_RADIUS*mWidth)/SWITCHER_WIDTH; //21
paint.setAntiAlias(true);
canvas1.drawARGB(0, 0, 0, 0);
paint.setColor(color);
canvas1.drawRoundRect(rectF, roundPx, roundPx, paint);
paint.setXfermode(new PorterDuffXfermode(Mode.SRC_IN));
canvas1.drawBitmap(mBitmap, srcRect, dstRect, paint);
//get rounder bitmap end
canvas.drawBitmap(Roundedoutput, dstRect, dstRect, mPaint);
if (isSwitchOk()) {
Log.d(TAG, "onDraw() mOn=" +mOn +" mOffset=" +mOffset);
mHandler.sendEmptyMessage(MSG_ANIMATE_END);
}
}
private void performFling(boolean on) {
Log.d(TAG, "mAnimating = " +mAnimating);
if( true == mAnimating){
//Log.e(TAG, "mAnimating is true, not performfling");
return;
}
long now = SystemClock.uptimeMillis();
mCurAnimationTime = now + ANIM_FRAME_DURATION;
mAnimLastTime = now;
mAnimating = true;
mAnimVel = 200.0f;
mHandler.removeMessages(MSG_ANIMATE);
mHandler.removeMessages(MSG_ANIMATE_REVEAL);
Log.d(TAG, "sendMessageAtTime on = " +on);
mHandler.sendMessageAtTime(mHandler.obtainMessage(on ? MSG_ANIMATE : MSG_ANIMATE_REVEAL), mCurAnimationTime);
}
private void decrementAnim() {
long now = SystemClock.uptimeMillis();
float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
final float x = mOffset;
final float v = mAnimVel; // px/s
final float a = mAnimAccel; // px/s/s
mOffset = x - (v*t) - (0.5f*a*t*t); // px
mAnimVel = v + (a*t); // px/s
mAnimLastTime = now; // ms
}
private void incrementAnim() {
long now = SystemClock.uptimeMillis();
float t = ((float)(now - mAnimLastTime)) / 1000; // ms -> s
final float x = mOffset;
final float v = mAnimVel; // px/s
final float a = mAnimAccel; // px/s/s
mOffset = x + (v*t) + (0.5f*a*t*t); // px
mAnimVel = v + (a*t); // px/s
mAnimLastTime = now; // ms
}
private void doAnimation(boolean on) {
if (on) incrementAnim();
else decrementAnim();
if (mAnimating) {
if (DBG) Log.d(TAG, "doAnimation mOffset:"+mOffset+" mExtra:"+mExtra);
if((mOffset < mExtra) && (mOffset > 0)) {
mCurAnimationTime += ANIM_FRAME_DURATION;
mHandler.sendMessageAtTime(mHandler.obtainMessage(on ? MSG_ANIMATE_REVEAL : MSG_ANIMATE), mCurAnimationTime);
}
invalidate();
}
}
private class H extends Handler {
public void handleMessage(Message m) {
if (m.what == MSG_ANIMATE_REVEAL) {
doAnimation(true);
return;
}
if (m.what == MSG_ANIMATE) {
doAnimation(false);
return;
}
if(m.what == MSG_ANIMATE_END){
mAnimating = false;
mOn = (mOffset == 0);
if (mOnSwitchChangeListener != null) {
mOnSwitchChangeListener.onSwitchChanged(IOSSwitch.this, mOn);
}
IOSSwitch.this.setChecked(mOn);
return;
}
}
}
/**
* Register a callback to be invoked when the checked state of this button
* changes.
*
* @param listener the callback to call on checked state change
*/
public void setOnSwitchChangeListener(OnSwitchChangeListener listener) {
mOnSwitchChangeListener = listener;
}
/**
* Interface definition for a callback to be invoked when the checked state
* of a compound button changed.
*/
public static interface OnSwitchChangeListener {
/**
* Called when the checked state of a compound button has changed.
*
* @param switcher The compound button view whose state has changed.
* @param isChecked The new checked state of buttonView.
*/
void onSwitchChanged(IOSSwitch switcher, boolean isChecked);
}
}