| /* |
| * 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.camera; |
| |
| import android.content.Context; |
| import android.graphics.Canvas; |
| import android.graphics.drawable.Drawable; |
| import android.util.AttributeSet; |
| import android.view.MotionEvent; |
| import android.view.View; |
| import android.view.animation.AnimationUtils; |
| import android.widget.ImageView; |
| |
| public class Switcher extends ImageView implements View.OnTouchListener { |
| |
| @SuppressWarnings("unused") |
| private static final String TAG = "Switcher"; |
| |
| public interface OnSwitchListener { |
| // Returns true if the listener agrees that the switch can be changed. |
| public boolean onSwitchChanged(Switcher source, boolean onOff); |
| } |
| |
| private static final int ANIMATION_SPEED = 200; |
| private static final long NO_ANIMATION = -1; |
| |
| private boolean mSwitch = false; |
| private int mPosition = 0; |
| private long mAnimationStartTime = 0; |
| private int mAnimationStartPosition; |
| private OnSwitchListener mListener; |
| |
| public Switcher(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| |
| public void setSwitch(boolean onOff) { |
| if (mSwitch == onOff) return; |
| mSwitch = onOff; |
| invalidate(); |
| } |
| |
| // Try to change the switch position. (The client can veto it.) |
| private void tryToSetSwitch(boolean onOff) { |
| try { |
| if (mSwitch == onOff) return; |
| |
| if (mListener != null) { |
| if (!mListener.onSwitchChanged(this, onOff)) { |
| return; |
| } |
| } |
| |
| mSwitch = onOff; |
| } finally { |
| startParkingAnimation(); |
| } |
| } |
| |
| public void setOnSwitchListener(OnSwitchListener listener) { |
| mListener = listener; |
| } |
| |
| @Override |
| public boolean onTouchEvent(MotionEvent event) { |
| if (!isEnabled()) return false; |
| |
| final int available = getHeight() - mPaddingTop - mPaddingBottom |
| - getDrawable().getIntrinsicHeight(); |
| |
| switch (event.getAction()) { |
| case MotionEvent.ACTION_DOWN: |
| mAnimationStartTime = NO_ANIMATION; |
| setPressed(true); |
| trackTouchEvent(event); |
| break; |
| |
| case MotionEvent.ACTION_MOVE: |
| trackTouchEvent(event); |
| break; |
| |
| case MotionEvent.ACTION_UP: |
| trackTouchEvent(event); |
| tryToSetSwitch(mPosition >= available / 2); |
| setPressed(false); |
| break; |
| |
| case MotionEvent.ACTION_CANCEL: |
| tryToSetSwitch(mSwitch); |
| setPressed(false); |
| break; |
| } |
| return true; |
| } |
| |
| private void startParkingAnimation() { |
| mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); |
| mAnimationStartPosition = mPosition; |
| } |
| |
| private void trackTouchEvent(MotionEvent event) { |
| Drawable drawable = getDrawable(); |
| int drawableHeight = drawable.getIntrinsicHeight(); |
| final int height = getHeight(); |
| final int available = height - mPaddingTop - mPaddingBottom |
| - drawableHeight; |
| int x = (int) event.getY(); |
| mPosition = x - mPaddingTop - drawableHeight / 2; |
| if (mPosition < 0) mPosition = 0; |
| if (mPosition > available) mPosition = available; |
| invalidate(); |
| } |
| |
| @Override |
| protected void onDraw(Canvas canvas) { |
| |
| Drawable drawable = getDrawable(); |
| int drawableHeight = drawable.getIntrinsicHeight(); |
| int drawableWidth = drawable.getIntrinsicWidth(); |
| |
| if (drawableWidth == 0 || drawableHeight == 0) { |
| return; // nothing to draw (empty bounds) |
| } |
| |
| if (mAnimationStartTime != NO_ANIMATION) { |
| final int available = getHeight() - mPaddingTop - mPaddingBottom |
| - drawableHeight; |
| long time = AnimationUtils.currentAnimationTimeMillis(); |
| int deltaTime = (int)(time - mAnimationStartTime); |
| mPosition = mAnimationStartPosition + |
| ANIMATION_SPEED * (mSwitch ? deltaTime : -deltaTime) / 1000; |
| if (mPosition < 0) mPosition = 0; |
| if (mPosition > available) mPosition = available; |
| boolean done = (mPosition == (mSwitch ? available : 0)); |
| if (!done) { |
| invalidate(); |
| } else { |
| mAnimationStartTime = NO_ANIMATION; |
| } |
| } |
| |
| int offsetTop = mPaddingTop + mPosition; |
| int offsetLeft = (getWidth() |
| - drawableWidth - mPaddingLeft - mPaddingRight) / 2; |
| int saveCount = canvas.getSaveCount(); |
| canvas.save(); |
| canvas.translate(offsetLeft, offsetTop); |
| drawable.draw(canvas); |
| canvas.restoreToCount(saveCount); |
| } |
| |
| // Consume the touch events for the specified view. |
| public void addTouchView(View v) { |
| v.setOnTouchListener(this); |
| } |
| |
| // This implements View.OnTouchListener so we intercept the touch events |
| // and pass them to ourselves. |
| public boolean onTouch(View v, MotionEvent event) { |
| onTouchEvent(event); |
| return true; |
| } |
| } |