1 /* 2 * Copyright (C) 2009 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.camera; 18 19 import android.content.Context; 20 import android.graphics.Canvas; 21 import android.graphics.drawable.Drawable; 22 import android.util.AttributeSet; 23 import android.view.MotionEvent; 24 import android.view.View; 25 import android.view.animation.AnimationUtils; 26 import android.widget.ImageView; 27 28 public class Switcher extends ImageView implements View.OnTouchListener { 29 30 @SuppressWarnings("unused") 31 private static final String TAG = "Switcher"; 32 33 public interface OnSwitchListener { 34 // Returns true if the listener agrees that the switch can be changed. onSwitchChanged(Switcher source, boolean onOff)35 public boolean onSwitchChanged(Switcher source, boolean onOff); 36 } 37 38 private static final int ANIMATION_SPEED = 200; 39 private static final long NO_ANIMATION = -1; 40 41 private boolean mSwitch = false; 42 private int mPosition = 0; 43 private long mAnimationStartTime = 0; 44 private OnSwitchListener mListener; 45 Switcher(Context context, AttributeSet attrs)46 public Switcher(Context context, AttributeSet attrs) { 47 super(context, attrs); 48 } 49 setSwitch(boolean onOff)50 public void setSwitch(boolean onOff) { 51 if (mSwitch == onOff) return; 52 mSwitch = onOff; 53 invalidate(); 54 } 55 56 // Try to change the switch position. (The client can veto it.) tryToSetSwitch(boolean onOff)57 private void tryToSetSwitch(boolean onOff) { 58 try { 59 if (mSwitch == onOff) return; 60 61 if (mListener != null) { 62 if (!mListener.onSwitchChanged(this, onOff)) { 63 return; 64 } 65 } 66 67 mSwitch = onOff; 68 } finally { 69 startParkingAnimation(); 70 } 71 } 72 setOnSwitchListener(OnSwitchListener listener)73 public void setOnSwitchListener(OnSwitchListener listener) { 74 mListener = listener; 75 } 76 77 @Override onTouchEvent(MotionEvent event)78 public boolean onTouchEvent(MotionEvent event) { 79 if (!isEnabled()) return false; 80 81 final int available = getHeight() - mPaddingTop - mPaddingBottom 82 - getDrawable().getIntrinsicHeight(); 83 84 switch (event.getAction()) { 85 case MotionEvent.ACTION_DOWN: 86 mAnimationStartTime = NO_ANIMATION; 87 setPressed(true); 88 trackTouchEvent(event); 89 break; 90 91 case MotionEvent.ACTION_MOVE: 92 trackTouchEvent(event); 93 break; 94 95 case MotionEvent.ACTION_UP: 96 trackTouchEvent(event); 97 tryToSetSwitch(mPosition >= available / 2); 98 setPressed(false); 99 break; 100 101 case MotionEvent.ACTION_CANCEL: 102 tryToSetSwitch(mSwitch); 103 setPressed(false); 104 break; 105 } 106 return true; 107 } 108 startParkingAnimation()109 private void startParkingAnimation() { 110 mAnimationStartTime = AnimationUtils.currentAnimationTimeMillis(); 111 } 112 trackTouchEvent(MotionEvent event)113 private void trackTouchEvent(MotionEvent event) { 114 Drawable drawable = getDrawable(); 115 int drawableHeight = drawable.getIntrinsicHeight(); 116 final int height = getHeight(); 117 final int available = height - mPaddingTop - mPaddingBottom 118 - drawableHeight; 119 int x = (int) event.getY(); 120 mPosition = x - mPaddingTop - drawableHeight / 2; 121 if (mPosition < 0) mPosition = 0; 122 if (mPosition > available) mPosition = available; 123 invalidate(); 124 } 125 126 @Override onDraw(Canvas canvas)127 protected void onDraw(Canvas canvas) { 128 129 Drawable drawable = getDrawable(); 130 int drawableHeight = drawable.getIntrinsicHeight(); 131 int drawableWidth = drawable.getIntrinsicWidth(); 132 133 if (drawableWidth == 0 || drawableHeight == 0) { 134 return; // nothing to draw (empty bounds) 135 } 136 137 if (mAnimationStartTime != NO_ANIMATION) { 138 final int available = getHeight() - mPaddingTop - mPaddingBottom 139 - drawableHeight; 140 long time = AnimationUtils.currentAnimationTimeMillis(); 141 long deltaTime = time - mAnimationStartTime; 142 mPosition += ANIMATION_SPEED 143 * (mSwitch ? deltaTime : -deltaTime) / 1000; 144 mAnimationStartTime = time; 145 if (mPosition < 0) mPosition = 0; 146 if (mPosition > available) mPosition = available; 147 if (mPosition != 0 && mPosition != available) { 148 postInvalidate(); 149 } else { 150 mAnimationStartTime = NO_ANIMATION; 151 } 152 } 153 154 int offsetTop = mPaddingTop + mPosition; 155 int offsetLeft = (getWidth() 156 - drawableWidth - mPaddingLeft - mPaddingRight) / 2; 157 int saveCount = canvas.getSaveCount(); 158 canvas.save(); 159 canvas.translate(offsetLeft, offsetTop); 160 drawable.draw(canvas); 161 canvas.restoreToCount(saveCount); 162 } 163 164 // Consume the touch events for the specified view. addTouchView(View v)165 public void addTouchView(View v) { 166 v.setOnTouchListener(this); 167 } 168 169 // This implements View.OnTouchListener so we intercept the touch events 170 // and pass them to ourselves. onTouch(View v, MotionEvent event)171 public boolean onTouch(View v, MotionEvent event) { 172 onTouchEvent(event); 173 return true; 174 } 175 } 176