WaveView.java revision 3fa8a454f61c772036f5f38661d1a077fd3d8388
13060421045d4d9e411797f91bb509824b03e33fbJim Miller/*
23060421045d4d9e411797f91bb509824b03e33fbJim Miller * Copyright (C) 2010 The Android Open Source Project
33060421045d4d9e411797f91bb509824b03e33fbJim Miller *
43060421045d4d9e411797f91bb509824b03e33fbJim Miller * Licensed under the Apache License, Version 2.0 (the "License");
53060421045d4d9e411797f91bb509824b03e33fbJim Miller * you may not use this file except in compliance with the License.
63060421045d4d9e411797f91bb509824b03e33fbJim Miller * You may obtain a copy of the License at
73060421045d4d9e411797f91bb509824b03e33fbJim Miller *
83060421045d4d9e411797f91bb509824b03e33fbJim Miller *      http://www.apache.org/licenses/LICENSE-2.0
93060421045d4d9e411797f91bb509824b03e33fbJim Miller *
103060421045d4d9e411797f91bb509824b03e33fbJim Miller * Unless required by applicable law or agreed to in writing, software
113060421045d4d9e411797f91bb509824b03e33fbJim Miller * distributed under the License is distributed on an "AS IS" BASIS,
123060421045d4d9e411797f91bb509824b03e33fbJim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
133060421045d4d9e411797f91bb509824b03e33fbJim Miller * See the License for the specific language governing permissions and
143060421045d4d9e411797f91bb509824b03e33fbJim Miller * limitations under the License.
153060421045d4d9e411797f91bb509824b03e33fbJim Miller */
163060421045d4d9e411797f91bb509824b03e33fbJim Miller
173060421045d4d9e411797f91bb509824b03e33fbJim Millerpackage com.android.internal.widget;
183060421045d4d9e411797f91bb509824b03e33fbJim Miller
193060421045d4d9e411797f91bb509824b03e33fbJim Millerimport java.util.ArrayList;
203060421045d4d9e411797f91bb509824b03e33fbJim Miller
213060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.animation.ValueAnimator;
223060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.content.Context;
233060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.content.res.Resources;
243060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.graphics.Bitmap;
253060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.graphics.BitmapFactory;
263060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.graphics.Canvas;
273060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.graphics.drawable.BitmapDrawable;
283060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.os.Vibrator;
293060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.util.AttributeSet;
303060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.util.Log;
313060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.view.MotionEvent;
323060421045d4d9e411797f91bb509824b03e33fbJim Millerimport android.view.View;
333060421045d4d9e411797f91bb509824b03e33fbJim Miller
343060421045d4d9e411797f91bb509824b03e33fbJim Millerimport com.android.internal.R;
353060421045d4d9e411797f91bb509824b03e33fbJim Miller
363060421045d4d9e411797f91bb509824b03e33fbJim Miller/**
373060421045d4d9e411797f91bb509824b03e33fbJim Miller * A special widget containing a center and outer ring. Moving the center ring to the outer ring
383060421045d4d9e411797f91bb509824b03e33fbJim Miller * causes an event that can be caught by implementing OnTriggerListener.
393060421045d4d9e411797f91bb509824b03e33fbJim Miller */
403060421045d4d9e411797f91bb509824b03e33fbJim Millerpublic class WaveView extends View implements ValueAnimator.AnimatorUpdateListener {
413060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final String TAG = "WaveView";
423060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final boolean DBG = false;
433fa8a454f61c772036f5f38661d1a077fd3d8388Jim Miller    private static final int WAVE_COUNT = 20; // default wave count
443060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long VIBRATE_SHORT = 20;  // msec
453060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long VIBRATE_LONG = 20;  // msec
463060421045d4d9e411797f91bb509824b03e33fbJim Miller
473060421045d4d9e411797f91bb509824b03e33fbJim Miller    // Lock state machine states
483060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_RESET_LOCK = 0;
493060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_READY = 1;
503060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_START_ATTEMPT = 2;
513060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_ATTEMPTING = 3;
523060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_UNLOCK_ATTEMPT = 4;
533060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final int STATE_UNLOCK_SUCCESS = 5;
543060421045d4d9e411797f91bb509824b03e33fbJim Miller
553060421045d4d9e411797f91bb509824b03e33fbJim Miller    // Animation properties.
560c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller    private static final long DURATION = 300; // duration of transitional animations
570c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller    private static final long FINAL_DURATION = 200; // duration of final animations when unlocking
580c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller    private static final long RING_DELAY = 1300; // when to start fading animated rings
590c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller    private static final long FINAL_DELAY = 200; // delay for unlock success animation
603060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long SHORT_DELAY = 100; // for starting one animation after another.
613060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long WAVE_DURATION = 2000; // amount of time for way to expand/decay
623060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long RESET_TIMEOUT = 3000; // elapsed time of inactivity before we reset
633060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long DELAY_INCREMENT = 15; // increment per wave while tracking motion
643060421045d4d9e411797f91bb509824b03e33fbJim Miller    private static final long DELAY_INCREMENT2 = 12; // increment per wave while not tracking
653262a51a91655659741b3b02071ccaed258fc01fJim Miller    private static final long WAVE_DELAY = WAVE_DURATION / WAVE_COUNT; // initial propagation delay
663060421045d4d9e411797f91bb509824b03e33fbJim Miller
673060421045d4d9e411797f91bb509824b03e33fbJim Miller    private Vibrator mVibrator;
683060421045d4d9e411797f91bb509824b03e33fbJim Miller    private OnTriggerListener mOnTriggerListener;
693060421045d4d9e411797f91bb509824b03e33fbJim Miller    private ArrayList<DrawableHolder> mDrawables = new ArrayList<DrawableHolder>(3);
703060421045d4d9e411797f91bb509824b03e33fbJim Miller    private ArrayList<DrawableHolder> mLightWaves = new ArrayList<DrawableHolder>(WAVE_COUNT);
713060421045d4d9e411797f91bb509824b03e33fbJim Miller    private boolean mFingerDown = false;
723060421045d4d9e411797f91bb509824b03e33fbJim Miller    private float mRingRadius = 182.0f; // Radius of bitmap ring. Used to snap halo to it
733060421045d4d9e411797f91bb509824b03e33fbJim Miller    private int mSnapRadius = 136; // minimum threshold for drag unlock
743060421045d4d9e411797f91bb509824b03e33fbJim Miller    private int mWaveCount = WAVE_COUNT;  // number of waves
753262a51a91655659741b3b02071ccaed258fc01fJim Miller    private long mWaveTimerDelay = WAVE_DELAY;
763060421045d4d9e411797f91bb509824b03e33fbJim Miller    private int mCurrentWave = 0;
773060421045d4d9e411797f91bb509824b03e33fbJim Miller    private float mLockCenterX; // center of widget as dictated by widget size
783060421045d4d9e411797f91bb509824b03e33fbJim Miller    private float mLockCenterY;
793060421045d4d9e411797f91bb509824b03e33fbJim Miller    private float mMouseX; // current mouse position as of last touch event
803060421045d4d9e411797f91bb509824b03e33fbJim Miller    private float mMouseY;
813060421045d4d9e411797f91bb509824b03e33fbJim Miller    private DrawableHolder mUnlockRing;
823060421045d4d9e411797f91bb509824b03e33fbJim Miller    private DrawableHolder mUnlockDefault;
833060421045d4d9e411797f91bb509824b03e33fbJim Miller    private DrawableHolder mUnlockHalo;
843060421045d4d9e411797f91bb509824b03e33fbJim Miller    private int mLockState = STATE_RESET_LOCK;
853060421045d4d9e411797f91bb509824b03e33fbJim Miller    private int mGrabbedState = OnTriggerListener.NO_HANDLE;
868996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller    private boolean mWavesRunning;
878996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller    private boolean mFinishWaves;
883060421045d4d9e411797f91bb509824b03e33fbJim Miller
893060421045d4d9e411797f91bb509824b03e33fbJim Miller    public WaveView(Context context) {
903060421045d4d9e411797f91bb509824b03e33fbJim Miller        this(context, null);
913060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
923060421045d4d9e411797f91bb509824b03e33fbJim Miller
933060421045d4d9e411797f91bb509824b03e33fbJim Miller    public WaveView(Context context, AttributeSet attrs) {
943060421045d4d9e411797f91bb509824b03e33fbJim Miller        super(context, attrs);
953060421045d4d9e411797f91bb509824b03e33fbJim Miller
963060421045d4d9e411797f91bb509824b03e33fbJim Miller        // TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.WaveView);
973060421045d4d9e411797f91bb509824b03e33fbJim Miller        // mOrientation = a.getInt(R.styleable.WaveView_orientation, HORIZONTAL);
983060421045d4d9e411797f91bb509824b03e33fbJim Miller        // a.recycle();
993060421045d4d9e411797f91bb509824b03e33fbJim Miller
1003060421045d4d9e411797f91bb509824b03e33fbJim Miller        initDrawables();
1013060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1023060421045d4d9e411797f91bb509824b03e33fbJim Miller
1033060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
1043060421045d4d9e411797f91bb509824b03e33fbJim Miller    protected void onSizeChanged(int w, int h, int oldw, int oldh) {
1053060421045d4d9e411797f91bb509824b03e33fbJim Miller        mLockCenterX = 0.5f * w;
1063060421045d4d9e411797f91bb509824b03e33fbJim Miller        mLockCenterY = 0.5f * h;
1073060421045d4d9e411797f91bb509824b03e33fbJim Miller        super.onSizeChanged(w, h, oldw, oldh);
1083060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1093060421045d4d9e411797f91bb509824b03e33fbJim Miller
1103060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
1113060421045d4d9e411797f91bb509824b03e33fbJim Miller    protected int getSuggestedMinimumWidth() {
1123060421045d4d9e411797f91bb509824b03e33fbJim Miller        // View should be large enough to contain the unlock ring + halo
1133060421045d4d9e411797f91bb509824b03e33fbJim Miller        return mUnlockRing.getWidth() + mUnlockHalo.getWidth();
1143060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1153060421045d4d9e411797f91bb509824b03e33fbJim Miller
1163060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
1173060421045d4d9e411797f91bb509824b03e33fbJim Miller    protected int getSuggestedMinimumHeight() {
1183060421045d4d9e411797f91bb509824b03e33fbJim Miller        // View should be large enough to contain the unlock ring + halo
1193060421045d4d9e411797f91bb509824b03e33fbJim Miller        return mUnlockRing.getHeight() + mUnlockHalo.getHeight();
1203060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1213060421045d4d9e411797f91bb509824b03e33fbJim Miller
1223060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
1233060421045d4d9e411797f91bb509824b03e33fbJim Miller    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1243060421045d4d9e411797f91bb509824b03e33fbJim Miller        int widthSpecMode = MeasureSpec.getMode(widthMeasureSpec);
1253060421045d4d9e411797f91bb509824b03e33fbJim Miller        int heightSpecMode = MeasureSpec.getMode(heightMeasureSpec);
1263060421045d4d9e411797f91bb509824b03e33fbJim Miller        int widthSpecSize =  MeasureSpec.getSize(widthMeasureSpec);
1273060421045d4d9e411797f91bb509824b03e33fbJim Miller        int heightSpecSize =  MeasureSpec.getSize(heightMeasureSpec);
1283060421045d4d9e411797f91bb509824b03e33fbJim Miller        int width;
1293060421045d4d9e411797f91bb509824b03e33fbJim Miller        int height;
1303060421045d4d9e411797f91bb509824b03e33fbJim Miller
1313060421045d4d9e411797f91bb509824b03e33fbJim Miller        if (widthSpecMode == MeasureSpec.AT_MOST) {
1323060421045d4d9e411797f91bb509824b03e33fbJim Miller            width = Math.min(widthSpecSize, getSuggestedMinimumWidth());
1333060421045d4d9e411797f91bb509824b03e33fbJim Miller        } else if (widthSpecMode == MeasureSpec.EXACTLY) {
1343060421045d4d9e411797f91bb509824b03e33fbJim Miller            width = widthSpecSize;
1353060421045d4d9e411797f91bb509824b03e33fbJim Miller        } else {
1363060421045d4d9e411797f91bb509824b03e33fbJim Miller            width = getSuggestedMinimumWidth();
1373060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
1383060421045d4d9e411797f91bb509824b03e33fbJim Miller
1393060421045d4d9e411797f91bb509824b03e33fbJim Miller        if (heightSpecMode == MeasureSpec.AT_MOST) {
1403060421045d4d9e411797f91bb509824b03e33fbJim Miller            height = Math.min(heightSpecSize, getSuggestedMinimumWidth());
1413060421045d4d9e411797f91bb509824b03e33fbJim Miller        } else if (heightSpecMode == MeasureSpec.EXACTLY) {
1423060421045d4d9e411797f91bb509824b03e33fbJim Miller            height = heightSpecSize;
1433060421045d4d9e411797f91bb509824b03e33fbJim Miller        } else {
1443060421045d4d9e411797f91bb509824b03e33fbJim Miller            height = getSuggestedMinimumHeight();
1453060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
1463060421045d4d9e411797f91bb509824b03e33fbJim Miller
1473060421045d4d9e411797f91bb509824b03e33fbJim Miller        setMeasuredDimension(width, height);
1483060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1493060421045d4d9e411797f91bb509824b03e33fbJim Miller
1503060421045d4d9e411797f91bb509824b03e33fbJim Miller    private void initDrawables() {
151c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing = new DrawableHolder(createDrawable(R.drawable.unlock_ring));
152c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing.setX(mLockCenterX);
153c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing.setY(mLockCenterY);
154c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing.setScaleX(0.1f);
155c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing.setScaleY(0.1f);
156c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockRing.setAlpha(0.0f);
1573060421045d4d9e411797f91bb509824b03e33fbJim Miller        mDrawables.add(mUnlockRing);
1583060421045d4d9e411797f91bb509824b03e33fbJim Miller
159c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault = new DrawableHolder(createDrawable(R.drawable.unlock_default));
160c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault.setX(mLockCenterX);
161c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault.setY(mLockCenterY);
162c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault.setScaleX(0.1f);
163c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault.setScaleY(0.1f);
164c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockDefault.setAlpha(0.0f);
1653060421045d4d9e411797f91bb509824b03e33fbJim Miller        mDrawables.add(mUnlockDefault);
1663060421045d4d9e411797f91bb509824b03e33fbJim Miller
167c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo = new DrawableHolder(createDrawable(R.drawable.unlock_halo));
168c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo.setX(mLockCenterX);
169c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo.setY(mLockCenterY);
170c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo.setScaleX(0.1f);
171c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo.setScaleY(0.1f);
172c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller        mUnlockHalo.setAlpha(0.0f);
1733060421045d4d9e411797f91bb509824b03e33fbJim Miller        mDrawables.add(mUnlockHalo);
1743060421045d4d9e411797f91bb509824b03e33fbJim Miller
1753060421045d4d9e411797f91bb509824b03e33fbJim Miller        BitmapDrawable wave = createDrawable(R.drawable.unlock_wave);
1763060421045d4d9e411797f91bb509824b03e33fbJim Miller        for (int i = 0; i < mWaveCount; i++) {
1773060421045d4d9e411797f91bb509824b03e33fbJim Miller            DrawableHolder holder = new DrawableHolder(wave);
1783060421045d4d9e411797f91bb509824b03e33fbJim Miller            mLightWaves.add(holder);
1793060421045d4d9e411797f91bb509824b03e33fbJim Miller            holder.setAlpha(0.0f);
1803060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
1813060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
1823060421045d4d9e411797f91bb509824b03e33fbJim Miller
1833060421045d4d9e411797f91bb509824b03e33fbJim Miller    private void waveUpdateFrame(float mouseX, float mouseY, boolean fingerDown) {
1843060421045d4d9e411797f91bb509824b03e33fbJim Miller        double distX = mouseX - mLockCenterX;
1853060421045d4d9e411797f91bb509824b03e33fbJim Miller        double distY = mouseY - mLockCenterY;
1863060421045d4d9e411797f91bb509824b03e33fbJim Miller        int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
1873060421045d4d9e411797f91bb509824b03e33fbJim Miller        double touchA = Math.atan2(distX, distY);
1883060421045d4d9e411797f91bb509824b03e33fbJim Miller        float ringX = (float) (mLockCenterX + mRingRadius * Math.sin(touchA));
1893060421045d4d9e411797f91bb509824b03e33fbJim Miller        float ringY = (float) (mLockCenterY + mRingRadius * Math.cos(touchA));
1903060421045d4d9e411797f91bb509824b03e33fbJim Miller
1913060421045d4d9e411797f91bb509824b03e33fbJim Miller        switch (mLockState) {
1923060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_RESET_LOCK:
1933060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "State RESET_LOCK");
1943262a51a91655659741b3b02071ccaed258fc01fJim Miller                mWaveTimerDelay = WAVE_DELAY;
1953060421045d4d9e411797f91bb509824b03e33fbJim Miller                for (int i = 0; i < mLightWaves.size(); i++) {
1963060421045d4d9e411797f91bb509824b03e33fbJim Miller                    DrawableHolder holder = mLightWaves.get(i);
1973060421045d4d9e411797f91bb509824b03e33fbJim Miller                    holder.addAnimTo(300, 0, "alpha", 0.0f, false);
1983060421045d4d9e411797f91bb509824b03e33fbJim Miller                }
1993060421045d4d9e411797f91bb509824b03e33fbJim Miller                for (int i = 0; i < mLightWaves.size(); i++) {
2003060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mLightWaves.get(i).startAnimations(this);
2013060421045d4d9e411797f91bb509824b03e33fbJim Miller                }
2023060421045d4d9e411797f91bb509824b03e33fbJim Miller
2033060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "x", mLockCenterX, true);
2043060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "y", mLockCenterY, true);
2053060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 0.1f, true);
2063060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 0.1f, true);
2073060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 0.0f, true);
2083060421045d4d9e411797f91bb509824b03e33fbJim Miller
2093060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("x");
2103060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("y");
2113060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("scaleX");
2123060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("scaleY");
2133060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("alpha");
214c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setX(mLockCenterX);
215c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setY(mLockCenterY);
216c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setScaleX(0.1f);
217c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setScaleY(0.1f);
218c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setAlpha(0.0f);
2193060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
2203060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
2213060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
2223060421045d4d9e411797f91bb509824b03e33fbJim Miller
2233060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.removeAnimationFor("x");
2243060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.removeAnimationFor("y");
2253060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.removeAnimationFor("scaleX");
2263060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.removeAnimationFor("scaleY");
2273060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.removeAnimationFor("alpha");
228c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockHalo.setX(mLockCenterX);
229c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockHalo.setY(mLockCenterY);
230c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockHalo.setScaleX(0.1f);
231c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockHalo.setScaleY(0.1f);
232c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockHalo.setAlpha(0.0f);
2333060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "x", mLockCenterX, true);
2343060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "y", mLockCenterY, true);
2353060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, true);
2363060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, true);
2373060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockHalo.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, true);
2383060421045d4d9e411797f91bb509824b03e33fbJim Miller
2393060421045d4d9e411797f91bb509824b03e33fbJim Miller                removeCallbacks(mLockTimerActions);
2403060421045d4d9e411797f91bb509824b03e33fbJim Miller
2413060421045d4d9e411797f91bb509824b03e33fbJim Miller                mLockState = STATE_READY;
2423060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
2433060421045d4d9e411797f91bb509824b03e33fbJim Miller
2443060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_READY:
2453060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "State READY");
2468996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                mWaveTimerDelay = WAVE_DELAY;
2473060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
2483060421045d4d9e411797f91bb509824b03e33fbJim Miller
2493060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_START_ATTEMPT:
2503060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "State START_ATTEMPT");
2513060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("x");
2523060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("y");
2533060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("scaleX");
2543060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("scaleY");
2553060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.removeAnimationFor("alpha");
256c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setX(mLockCenterX + 182);
257c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setY(mLockCenterY);
258c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setScaleX(0.1f);
259c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setScaleY(0.1f);
260c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                mUnlockDefault.setAlpha(0.0f);
2613060421045d4d9e411797f91bb509824b03e33fbJim Miller
2623060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleX", 1.0f, false);
2633060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "scaleY", 1.0f, false);
2643060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockDefault.addAnimTo(DURATION, SHORT_DELAY, "alpha", 1.0f, false);
2653060421045d4d9e411797f91bb509824b03e33fbJim Miller
2663060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "scaleX", 1.0f, true);
2673060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "scaleY", 1.0f, true);
2683060421045d4d9e411797f91bb509824b03e33fbJim Miller                mUnlockRing.addAnimTo(DURATION, 0, "alpha", 1.0f, true);
2693060421045d4d9e411797f91bb509824b03e33fbJim Miller
2703060421045d4d9e411797f91bb509824b03e33fbJim Miller                mLockState = STATE_ATTEMPTING;
2713060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
2723060421045d4d9e411797f91bb509824b03e33fbJim Miller
2733060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_ATTEMPTING:
274ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                if (DBG) Log.v(TAG, "State ATTEMPTING (fingerDown = " + fingerDown + ")");
2753060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (dragDistance > mSnapRadius) {
2768996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                    mFinishWaves = true; // don't start any more waves.
2773060421045d4d9e411797f91bb509824b03e33fbJim Miller                    if (fingerDown) {
2783060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mUnlockHalo.addAnimTo(0, 0, "x", ringX, true);
2793060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mUnlockHalo.addAnimTo(0, 0, "y", ringY, true);
2803060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
2813060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
2823060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
2833060421045d4d9e411797f91bb509824b03e33fbJim Miller                    }  else {
284ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                        if (DBG) Log.v(TAG, "up detected, moving to STATE_UNLOCK_ATTEMPT");
2853060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mLockState = STATE_UNLOCK_ATTEMPT;
2863060421045d4d9e411797f91bb509824b03e33fbJim Miller                    }
2873060421045d4d9e411797f91bb509824b03e33fbJim Miller                } else {
2888996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                    // If waves have stopped, we need to kick them off again...
2898996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                    if (!mWavesRunning) {
2908996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                        mWavesRunning = true;
2918996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                        mFinishWaves = false;
2928996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                        // mWaveTimerDelay = WAVE_DELAY;
2938996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                        postDelayed(mAddWaveAction, mWaveTimerDelay);
2948996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                    }
2953060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockHalo.addAnimTo(0, 0, "x", mouseX, true);
2963060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockHalo.addAnimTo(0, 0, "y", mouseY, true);
2973060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockHalo.addAnimTo(0, 0, "scaleX", 1.0f, true);
2983060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockHalo.addAnimTo(0, 0, "scaleY", 1.0f, true);
2993060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockHalo.addAnimTo(0, 0, "alpha", 1.0f, true);
3003060421045d4d9e411797f91bb509824b03e33fbJim Miller                }
3013060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
3023060421045d4d9e411797f91bb509824b03e33fbJim Miller
3033060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_UNLOCK_ATTEMPT:
3043060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "State UNLOCK_ATTEMPT");
3053060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (dragDistance > mSnapRadius) {
3063060421045d4d9e411797f91bb509824b03e33fbJim Miller                    for (int n = 0; n < mLightWaves.size(); n++) {
3073060421045d4d9e411797f91bb509824b03e33fbJim Miller                        DrawableHolder wave = mLightWaves.get(n);
3083060421045d4d9e411797f91bb509824b03e33fbJim Miller                        long delay = 1000L*(6 + n - mCurrentWave)/10L;
3090c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                        wave.addAnimTo(FINAL_DURATION, delay, "x", ringX, true);
3100c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                        wave.addAnimTo(FINAL_DURATION, delay, "y", ringY, true);
3110c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                        wave.addAnimTo(FINAL_DURATION, delay, "scaleX", 0.1f, true);
3120c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                        wave.addAnimTo(FINAL_DURATION, delay, "scaleY", 0.1f, true);
3130c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                        wave.addAnimTo(FINAL_DURATION, delay, "alpha", 0.0f, true);
3143060421045d4d9e411797f91bb509824b03e33fbJim Miller                    }
3153060421045d4d9e411797f91bb509824b03e33fbJim Miller                    for (int i = 0; i < mLightWaves.size(); i++) {
3163060421045d4d9e411797f91bb509824b03e33fbJim Miller                        mLightWaves.get(i).startAnimations(this);
3173060421045d4d9e411797f91bb509824b03e33fbJim Miller                    }
3183060421045d4d9e411797f91bb509824b03e33fbJim Miller
3190c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
3200c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
3210c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleX", 0.1f, false);
3220c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "scaleY", 0.1f, false);
3230c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, 0, "alpha", 0.0f, false);
3243060421045d4d9e411797f91bb509824b03e33fbJim Miller
3250c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockRing.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
3263060421045d4d9e411797f91bb509824b03e33fbJim Miller
3273060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockDefault.removeAnimationFor("x");
3283060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockDefault.removeAnimationFor("y");
3293060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockDefault.removeAnimationFor("scaleX");
3303060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockDefault.removeAnimationFor("scaleY");
3313060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mUnlockDefault.removeAnimationFor("alpha");
332c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                    mUnlockDefault.setX(ringX);
333c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                    mUnlockDefault.setY(ringY);
334c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                    mUnlockDefault.setScaleX(0.1f);
335c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                    mUnlockDefault.setScaleY(0.1f);
336c002fab06d5f6d3c83bb2b89c8ee9a14f6580af3Jim Miller                    mUnlockDefault.setAlpha(0.0f);
3373060421045d4d9e411797f91bb509824b03e33fbJim Miller
3380c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "x", ringX, true);
3390c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "y", ringY, true);
3400c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleX", 1.0f, true);
3410c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "scaleY", 1.0f, true);
3420c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, 0, "alpha", 1.0f, true);
3433060421045d4d9e411797f91bb509824b03e33fbJim Miller
3440c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
3450c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
3460c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockDefault.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
3473060421045d4d9e411797f91bb509824b03e33fbJim Miller
3480c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "x", ringX, false);
3490c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockHalo.addAnimTo(FINAL_DURATION, 0, "y", ringY, false);
3503060421045d4d9e411797f91bb509824b03e33fbJim Miller
3510c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleX", 3.0f, false);
3520c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "scaleY", 3.0f, false);
3530c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                    mUnlockHalo.addAnimTo(FINAL_DURATION, FINAL_DELAY, "alpha", 0.0f, false);
3543060421045d4d9e411797f91bb509824b03e33fbJim Miller
3553060421045d4d9e411797f91bb509824b03e33fbJim Miller                    removeCallbacks(mLockTimerActions);
3563060421045d4d9e411797f91bb509824b03e33fbJim Miller
3573060421045d4d9e411797f91bb509824b03e33fbJim Miller                    postDelayed(mLockTimerActions, RESET_TIMEOUT);
3583060421045d4d9e411797f91bb509824b03e33fbJim Miller
3593060421045d4d9e411797f91bb509824b03e33fbJim Miller                    dispatchTriggerEvent(OnTriggerListener.CENTER_HANDLE);
3603060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mLockState = STATE_UNLOCK_SUCCESS;
3613060421045d4d9e411797f91bb509824b03e33fbJim Miller                } else {
3623060421045d4d9e411797f91bb509824b03e33fbJim Miller                    mLockState = STATE_RESET_LOCK;
3633060421045d4d9e411797f91bb509824b03e33fbJim Miller                }
3643060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
3653060421045d4d9e411797f91bb509824b03e33fbJim Miller
3663060421045d4d9e411797f91bb509824b03e33fbJim Miller            case STATE_UNLOCK_SUCCESS:
3673060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "State UNLOCK_SUCCESS");
3683060421045d4d9e411797f91bb509824b03e33fbJim Miller                removeCallbacks(mAddWaveAction);
3693060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
3703060421045d4d9e411797f91bb509824b03e33fbJim Miller
3713060421045d4d9e411797f91bb509824b03e33fbJim Miller            default:
3723060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "Unknown state " + mLockState);
3733060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
3743060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
3753060421045d4d9e411797f91bb509824b03e33fbJim Miller        mUnlockDefault.startAnimations(this);
3763060421045d4d9e411797f91bb509824b03e33fbJim Miller        mUnlockHalo.startAnimations(this);
3773060421045d4d9e411797f91bb509824b03e33fbJim Miller        mUnlockRing.startAnimations(this);
3783060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
3793060421045d4d9e411797f91bb509824b03e33fbJim Miller
3803060421045d4d9e411797f91bb509824b03e33fbJim Miller    BitmapDrawable createDrawable(int resId) {
3813060421045d4d9e411797f91bb509824b03e33fbJim Miller        Resources res = getResources();
3823060421045d4d9e411797f91bb509824b03e33fbJim Miller        Bitmap bitmap = BitmapFactory.decodeResource(res, resId);
3833060421045d4d9e411797f91bb509824b03e33fbJim Miller        return new BitmapDrawable(res, bitmap);
3843060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
3853060421045d4d9e411797f91bb509824b03e33fbJim Miller
3863060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
3873060421045d4d9e411797f91bb509824b03e33fbJim Miller    protected void onDraw(Canvas canvas) {
3883060421045d4d9e411797f91bb509824b03e33fbJim Miller        waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
3893060421045d4d9e411797f91bb509824b03e33fbJim Miller        for (int i = 0; i < mDrawables.size(); ++i) {
3903060421045d4d9e411797f91bb509824b03e33fbJim Miller            mDrawables.get(i).draw(canvas);
3913060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
3923060421045d4d9e411797f91bb509824b03e33fbJim Miller        for (int i = 0; i < mLightWaves.size(); ++i) {
3933060421045d4d9e411797f91bb509824b03e33fbJim Miller            mLightWaves.get(i).draw(canvas);
3943060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
3953060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
3963060421045d4d9e411797f91bb509824b03e33fbJim Miller
3973060421045d4d9e411797f91bb509824b03e33fbJim Miller    private final Runnable mLockTimerActions = new Runnable() {
3983060421045d4d9e411797f91bb509824b03e33fbJim Miller        public void run() {
3993060421045d4d9e411797f91bb509824b03e33fbJim Miller            if (DBG) Log.v(TAG, "LockTimerActions");
4003060421045d4d9e411797f91bb509824b03e33fbJim Miller            // reset lock after inactivity
4013060421045d4d9e411797f91bb509824b03e33fbJim Miller            if (mLockState == STATE_ATTEMPTING) {
402607d320c9d18a57350bef72cc74017366ffd04ecJim Miller                if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK");
4033060421045d4d9e411797f91bb509824b03e33fbJim Miller                mLockState = STATE_RESET_LOCK;
4043060421045d4d9e411797f91bb509824b03e33fbJim Miller            }
4053060421045d4d9e411797f91bb509824b03e33fbJim Miller            // for prototype, reset after successful unlock
4063060421045d4d9e411797f91bb509824b03e33fbJim Miller            if (mLockState == STATE_UNLOCK_SUCCESS) {
407607d320c9d18a57350bef72cc74017366ffd04ecJim Miller                if (DBG) Log.v(TAG, "Timer resets to STATE_RESET_LOCK after success");
4083060421045d4d9e411797f91bb509824b03e33fbJim Miller                mLockState = STATE_RESET_LOCK;
4093060421045d4d9e411797f91bb509824b03e33fbJim Miller            }
4103060421045d4d9e411797f91bb509824b03e33fbJim Miller            invalidate();
4113060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
4123060421045d4d9e411797f91bb509824b03e33fbJim Miller    };
4133060421045d4d9e411797f91bb509824b03e33fbJim Miller
4143060421045d4d9e411797f91bb509824b03e33fbJim Miller    private final Runnable mAddWaveAction = new Runnable() {
4153060421045d4d9e411797f91bb509824b03e33fbJim Miller        public void run() {
4163060421045d4d9e411797f91bb509824b03e33fbJim Miller            double distX = mMouseX - mLockCenterX;
4173060421045d4d9e411797f91bb509824b03e33fbJim Miller            double distY = mMouseY - mLockCenterY;
4183060421045d4d9e411797f91bb509824b03e33fbJim Miller            int dragDistance = (int) Math.ceil(Math.hypot(distX, distY));
4193060421045d4d9e411797f91bb509824b03e33fbJim Miller            if (mLockState == STATE_ATTEMPTING && dragDistance < mSnapRadius
4203262a51a91655659741b3b02071ccaed258fc01fJim Miller                    && mWaveTimerDelay >= WAVE_DELAY) {
4213060421045d4d9e411797f91bb509824b03e33fbJim Miller                mWaveTimerDelay = Math.min(WAVE_DURATION, mWaveTimerDelay + DELAY_INCREMENT);
4223060421045d4d9e411797f91bb509824b03e33fbJim Miller
4233060421045d4d9e411797f91bb509824b03e33fbJim Miller                DrawableHolder wave = mLightWaves.get(mCurrentWave);
4243060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.setAlpha(0.0f);
4253060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.setScaleX(0.2f);
4263060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.setScaleY(0.2f);
4273060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.setX(mMouseX);
4283060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.setY(mMouseY);
4293060421045d4d9e411797f91bb509824b03e33fbJim Miller
4303060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.addAnimTo(WAVE_DURATION, 0, "x", mLockCenterX, true);
4313060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.addAnimTo(WAVE_DURATION, 0, "y", mLockCenterY, true);
4323060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.addAnimTo(WAVE_DURATION*2/3, 0, "alpha", 1.0f, true);
4333060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.addAnimTo(WAVE_DURATION, 0, "scaleX", 1.0f, true);
4343060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.addAnimTo(WAVE_DURATION, 0, "scaleY", 1.0f, true);
4353060421045d4d9e411797f91bb509824b03e33fbJim Miller
4360c91a8ee9321da4dee17f0aeffbc20b0c5bb0644Jim Miller                wave.addAnimTo(1000, RING_DELAY, "alpha", 0.0f, false);
4373060421045d4d9e411797f91bb509824b03e33fbJim Miller                wave.startAnimations(WaveView.this);
4383060421045d4d9e411797f91bb509824b03e33fbJim Miller
4393060421045d4d9e411797f91bb509824b03e33fbJim Miller                mCurrentWave = (mCurrentWave+1) % mWaveCount;
4403060421045d4d9e411797f91bb509824b03e33fbJim Miller                if (DBG) Log.v(TAG, "WaveTimerDelay: start new wave in " + mWaveTimerDelay);
4413060421045d4d9e411797f91bb509824b03e33fbJim Miller            } else {
4423060421045d4d9e411797f91bb509824b03e33fbJim Miller                mWaveTimerDelay += DELAY_INCREMENT2;
4433060421045d4d9e411797f91bb509824b03e33fbJim Miller            }
4448996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller            if (mFinishWaves) {
4458996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                // sentinel used to restart the waves after they've stopped
4468996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                mWavesRunning = false;
4478996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller            } else {
4488996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller                postDelayed(mAddWaveAction, mWaveTimerDelay);
4498996cf071abf5e6a6458da258a1d59bcaafc3c2cJim Miller            }
4503060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
4513060421045d4d9e411797f91bb509824b03e33fbJim Miller    };
4523060421045d4d9e411797f91bb509824b03e33fbJim Miller
4533060421045d4d9e411797f91bb509824b03e33fbJim Miller    @Override
4543060421045d4d9e411797f91bb509824b03e33fbJim Miller    public boolean onTouchEvent(MotionEvent event) {
4553060421045d4d9e411797f91bb509824b03e33fbJim Miller        final int action = event.getAction();
4563060421045d4d9e411797f91bb509824b03e33fbJim Miller        mMouseX = event.getX();
4573060421045d4d9e411797f91bb509824b03e33fbJim Miller        mMouseY = event.getY();
4583060421045d4d9e411797f91bb509824b03e33fbJim Miller        boolean handled = false;
4593060421045d4d9e411797f91bb509824b03e33fbJim Miller        switch (action) {
4603060421045d4d9e411797f91bb509824b03e33fbJim Miller            case MotionEvent.ACTION_DOWN:
4613060421045d4d9e411797f91bb509824b03e33fbJim Miller                removeCallbacks(mLockTimerActions);
4623060421045d4d9e411797f91bb509824b03e33fbJim Miller                mFingerDown = true;
4633060421045d4d9e411797f91bb509824b03e33fbJim Miller                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
4643060421045d4d9e411797f91bb509824b03e33fbJim Miller                {
4653060421045d4d9e411797f91bb509824b03e33fbJim Miller                    float x = mMouseX - mUnlockHalo.getX();
4663060421045d4d9e411797f91bb509824b03e33fbJim Miller                    float y = mMouseY - mUnlockHalo.getY();
4673060421045d4d9e411797f91bb509824b03e33fbJim Miller                    float dist = (float) Math.hypot(x, y);
4683060421045d4d9e411797f91bb509824b03e33fbJim Miller                    if (dist < mUnlockHalo.getWidth()*0.5f) {
4693060421045d4d9e411797f91bb509824b03e33fbJim Miller                        if (mLockState == STATE_READY) {
4703060421045d4d9e411797f91bb509824b03e33fbJim Miller                            mLockState = STATE_START_ATTEMPT;
4713060421045d4d9e411797f91bb509824b03e33fbJim Miller                        }
4723060421045d4d9e411797f91bb509824b03e33fbJim Miller                    }
4733060421045d4d9e411797f91bb509824b03e33fbJim Miller                }
4743060421045d4d9e411797f91bb509824b03e33fbJim Miller                handled = true;
4753060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
4763060421045d4d9e411797f91bb509824b03e33fbJim Miller
4773060421045d4d9e411797f91bb509824b03e33fbJim Miller            case MotionEvent.ACTION_MOVE:
4783060421045d4d9e411797f91bb509824b03e33fbJim Miller                handled = true;
4793060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
4803060421045d4d9e411797f91bb509824b03e33fbJim Miller
4813060421045d4d9e411797f91bb509824b03e33fbJim Miller            case MotionEvent.ACTION_UP:
482ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                if (DBG) Log.v(TAG, "ACTION_UP");
4833060421045d4d9e411797f91bb509824b03e33fbJim Miller                mFingerDown = false;
4843060421045d4d9e411797f91bb509824b03e33fbJim Miller                postDelayed(mLockTimerActions, RESET_TIMEOUT);
4853060421045d4d9e411797f91bb509824b03e33fbJim Miller                setGrabbedState(OnTriggerListener.NO_HANDLE);
486ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                // Normally the state machine is driven by user interaction causing redraws.
487ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                // However, when there's no more user interaction and no running animations,
488ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                // the state machine stops advancing because onDraw() never gets called.
489ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                // The following ensures we advance to the next state in this case,
490ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                // either STATE_UNLOCK_ATTEMPT or STATE_RESET_LOCK.
491ec6ac82a295b80a39f12ff7951ec254ba45809fdJim Miller                waveUpdateFrame(mMouseX, mMouseY, mFingerDown);
4923060421045d4d9e411797f91bb509824b03e33fbJim Miller                handled = true;
4933060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
4943060421045d4d9e411797f91bb509824b03e33fbJim Miller
4953060421045d4d9e411797f91bb509824b03e33fbJim Miller            case MotionEvent.ACTION_CANCEL:
4963060421045d4d9e411797f91bb509824b03e33fbJim Miller                mFingerDown = false;
4973060421045d4d9e411797f91bb509824b03e33fbJim Miller                handled = true;
4983060421045d4d9e411797f91bb509824b03e33fbJim Miller                break;
4993060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
5003060421045d4d9e411797f91bb509824b03e33fbJim Miller        invalidate();
5013060421045d4d9e411797f91bb509824b03e33fbJim Miller        return handled ? true : super.onTouchEvent(event);
5023060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5033060421045d4d9e411797f91bb509824b03e33fbJim Miller
5043060421045d4d9e411797f91bb509824b03e33fbJim Miller    /**
5053060421045d4d9e411797f91bb509824b03e33fbJim Miller     * Triggers haptic feedback.
5063060421045d4d9e411797f91bb509824b03e33fbJim Miller     */
5073060421045d4d9e411797f91bb509824b03e33fbJim Miller    private synchronized void vibrate(long duration) {
5083060421045d4d9e411797f91bb509824b03e33fbJim Miller        if (mVibrator == null) {
5093060421045d4d9e411797f91bb509824b03e33fbJim Miller            mVibrator = (android.os.Vibrator)
5103060421045d4d9e411797f91bb509824b03e33fbJim Miller                    getContext().getSystemService(Context.VIBRATOR_SERVICE);
5113060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
5123060421045d4d9e411797f91bb509824b03e33fbJim Miller        mVibrator.vibrate(duration);
5133060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5143060421045d4d9e411797f91bb509824b03e33fbJim Miller
5153060421045d4d9e411797f91bb509824b03e33fbJim Miller    /**
5163060421045d4d9e411797f91bb509824b03e33fbJim Miller     * Registers a callback to be invoked when the user triggers an event.
5173060421045d4d9e411797f91bb509824b03e33fbJim Miller     *
5183060421045d4d9e411797f91bb509824b03e33fbJim Miller     * @param listener the OnDialTriggerListener to attach to this view
5193060421045d4d9e411797f91bb509824b03e33fbJim Miller     */
5203060421045d4d9e411797f91bb509824b03e33fbJim Miller    public void setOnTriggerListener(OnTriggerListener listener) {
5213060421045d4d9e411797f91bb509824b03e33fbJim Miller        mOnTriggerListener = listener;
5223060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5233060421045d4d9e411797f91bb509824b03e33fbJim Miller
5243060421045d4d9e411797f91bb509824b03e33fbJim Miller    /**
5253060421045d4d9e411797f91bb509824b03e33fbJim Miller     * Dispatches a trigger event to listener. Ignored if a listener is not set.
5263060421045d4d9e411797f91bb509824b03e33fbJim Miller     * @param whichHandle the handle that triggered the event.
5273060421045d4d9e411797f91bb509824b03e33fbJim Miller     */
5283060421045d4d9e411797f91bb509824b03e33fbJim Miller    private void dispatchTriggerEvent(int whichHandle) {
5293060421045d4d9e411797f91bb509824b03e33fbJim Miller        vibrate(VIBRATE_LONG);
5303060421045d4d9e411797f91bb509824b03e33fbJim Miller        if (mOnTriggerListener != null) {
5313060421045d4d9e411797f91bb509824b03e33fbJim Miller            mOnTriggerListener.onTrigger(this, whichHandle);
5323060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
5333060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5343060421045d4d9e411797f91bb509824b03e33fbJim Miller
5353060421045d4d9e411797f91bb509824b03e33fbJim Miller    /**
5363060421045d4d9e411797f91bb509824b03e33fbJim Miller     * Sets the current grabbed state, and dispatches a grabbed state change
5373060421045d4d9e411797f91bb509824b03e33fbJim Miller     * event to our listener.
5383060421045d4d9e411797f91bb509824b03e33fbJim Miller     */
5393060421045d4d9e411797f91bb509824b03e33fbJim Miller    private void setGrabbedState(int newState) {
5403060421045d4d9e411797f91bb509824b03e33fbJim Miller        if (newState != mGrabbedState) {
5413060421045d4d9e411797f91bb509824b03e33fbJim Miller            mGrabbedState = newState;
5423060421045d4d9e411797f91bb509824b03e33fbJim Miller            if (mOnTriggerListener != null) {
5433060421045d4d9e411797f91bb509824b03e33fbJim Miller                mOnTriggerListener.onGrabbedStateChange(this, mGrabbedState);
5443060421045d4d9e411797f91bb509824b03e33fbJim Miller            }
5453060421045d4d9e411797f91bb509824b03e33fbJim Miller        }
5463060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5473060421045d4d9e411797f91bb509824b03e33fbJim Miller
5483060421045d4d9e411797f91bb509824b03e33fbJim Miller    public interface OnTriggerListener {
5493060421045d4d9e411797f91bb509824b03e33fbJim Miller        /**
5503060421045d4d9e411797f91bb509824b03e33fbJim Miller         * Sent when the user releases the handle.
5513060421045d4d9e411797f91bb509824b03e33fbJim Miller         */
5523060421045d4d9e411797f91bb509824b03e33fbJim Miller        public static final int NO_HANDLE = 0;
5533060421045d4d9e411797f91bb509824b03e33fbJim Miller
5543060421045d4d9e411797f91bb509824b03e33fbJim Miller        /**
5553060421045d4d9e411797f91bb509824b03e33fbJim Miller         * Sent when the user grabs the center handle
5563060421045d4d9e411797f91bb509824b03e33fbJim Miller         */
5573060421045d4d9e411797f91bb509824b03e33fbJim Miller        public static final int CENTER_HANDLE = 10;
5583060421045d4d9e411797f91bb509824b03e33fbJim Miller
5593060421045d4d9e411797f91bb509824b03e33fbJim Miller        /**
5603060421045d4d9e411797f91bb509824b03e33fbJim Miller         * Called when the user drags the center ring beyond a threshold.
5613060421045d4d9e411797f91bb509824b03e33fbJim Miller         */
5623060421045d4d9e411797f91bb509824b03e33fbJim Miller        void onTrigger(View v, int whichHandle);
5633060421045d4d9e411797f91bb509824b03e33fbJim Miller
5643060421045d4d9e411797f91bb509824b03e33fbJim Miller        /**
5653060421045d4d9e411797f91bb509824b03e33fbJim Miller         * Called when the "grabbed state" changes (i.e. when the user either grabs or releases
5663060421045d4d9e411797f91bb509824b03e33fbJim Miller         * one of the handles.)
5673060421045d4d9e411797f91bb509824b03e33fbJim Miller         *
5683060421045d4d9e411797f91bb509824b03e33fbJim Miller         * @param v the view that was triggered
5693060421045d4d9e411797f91bb509824b03e33fbJim Miller         * @param grabbedState the new state: {@link #NO_HANDLE}, {@link #CENTER_HANDLE},
5703060421045d4d9e411797f91bb509824b03e33fbJim Miller         */
5713060421045d4d9e411797f91bb509824b03e33fbJim Miller        void onGrabbedStateChange(View v, int grabbedState);
5723060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5733060421045d4d9e411797f91bb509824b03e33fbJim Miller
5743060421045d4d9e411797f91bb509824b03e33fbJim Miller    public void onAnimationUpdate(ValueAnimator animation) {
5753060421045d4d9e411797f91bb509824b03e33fbJim Miller        invalidate();
5763060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5773060421045d4d9e411797f91bb509824b03e33fbJim Miller
5783060421045d4d9e411797f91bb509824b03e33fbJim Miller    public void reset() {
579607d320c9d18a57350bef72cc74017366ffd04ecJim Miller        if (DBG) Log.v(TAG, "reset() : resets state to STATE_RESET_LOCK");
5803060421045d4d9e411797f91bb509824b03e33fbJim Miller        mLockState = STATE_RESET_LOCK;
581607d320c9d18a57350bef72cc74017366ffd04ecJim Miller        invalidate();
5823060421045d4d9e411797f91bb509824b03e33fbJim Miller    }
5833060421045d4d9e411797f91bb509824b03e33fbJim Miller}
584