1d05336248c5600cde35ad564840532f59b4c085cChiao Cheng/*
2d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * Copyright (C) 2012 The Android Open Source Project
3d05336248c5600cde35ad564840532f59b4c085cChiao Cheng *
4d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * Licensed under the Apache License, Version 2.0 (the "License");
5d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * you may not use this file except in compliance with the License.
6d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * You may obtain a copy of the License at
7d05336248c5600cde35ad564840532f59b4c085cChiao Cheng *
8d05336248c5600cde35ad564840532f59b4c085cChiao Cheng *      http://www.apache.org/licenses/LICENSE-2.0
9d05336248c5600cde35ad564840532f59b4c085cChiao Cheng *
10d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * Unless required by applicable law or agreed to in writing, software
11d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * distributed under the License is distributed on an "AS IS" BASIS,
12d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * See the License for the specific language governing permissions and
14d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * limitations under the License.
15d05336248c5600cde35ad564840532f59b4c085cChiao Cheng */
16d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
17d05336248c5600cde35ad564840532f59b4c085cChiao Chengpackage com.android.incallui.widget.multiwaveview;
18d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
19d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.Animator;
20d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.Animator.AnimatorListener;
21d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.AnimatorListenerAdapter;
22d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.TimeInterpolator;
23d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.ValueAnimator;
24d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.animation.ValueAnimator.AnimatorUpdateListener;
25d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.ComponentName;
26d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.Context;
27d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.pm.PackageManager;
28d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.pm.PackageManager.NameNotFoundException;
29d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.res.Resources;
30d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.content.res.TypedArray;
31d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.graphics.Canvas;
32d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.graphics.drawable.Drawable;
33d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.os.Bundle;
34d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.os.Vibrator;
35d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.text.TextUtils;
36d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.util.AttributeSet;
37d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.util.Log;
38d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.util.TypedValue;
39d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.view.Gravity;
40d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.view.MotionEvent;
41d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.view.View;
42d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport android.view.accessibility.AccessibilityManager;
43d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
44d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport com.android.incallui.R;
45d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
46d05336248c5600cde35ad564840532f59b4c085cChiao Chengimport java.util.ArrayList;
47d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
48d05336248c5600cde35ad564840532f59b4c085cChiao Cheng/**
49d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * This is a copy of com.android.internal.widget.multiwaveview.GlowPadView with minor changes
50d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * to remove dependencies on private api's.
51dc8cda5a71ee4ee644561fed0767c6d0b3a3447dSantos Cordon *
52b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen * Incoporated the scaling functionality.
53d05336248c5600cde35ad564840532f59b4c085cChiao Cheng *
54d05336248c5600cde35ad564840532f59b4c085cChiao Cheng * A re-usable widget containing a center, outer ring and wave animation.
55d05336248c5600cde35ad564840532f59b4c085cChiao Cheng */
56d05336248c5600cde35ad564840532f59b4c085cChiao Chengpublic class GlowPadView extends View {
57d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final String TAG = "GlowPadView";
58d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final boolean DEBUG = false;
59d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
60d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    // Wave state machine
61d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_IDLE = 0;
62d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_START = 1;
63d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_FIRST_TOUCH = 2;
64d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_TRACKING = 3;
65d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_SNAP = 4;
66d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int STATE_FINISH = 5;
67d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
68d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    // Animation properties.
69d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
70d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
71d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public interface OnTriggerListener {
72d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int NO_HANDLE = 0;
73d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int CENTER_HANDLE = 1;
74d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onGrabbed(View v, int handle);
75d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onReleased(View v, int handle);
76d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onTrigger(View v, int target);
77d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onGrabbedStateChange(View v, int handle);
78d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onFinishFinalAnimation();
79d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
80d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
81d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    // Tuneable parameters for animation
82d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int WAVE_ANIMATION_DURATION = 1350;
83d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int RETURN_TO_HOME_DELAY = 1200;
84d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int RETURN_TO_HOME_DURATION = 200;
85d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int HIDE_ANIMATION_DELAY = 200;
86d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int HIDE_ANIMATION_DURATION = 200;
87d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int SHOW_ANIMATION_DURATION = 200;
88d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int SHOW_ANIMATION_DELAY = 50;
89d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
90d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int REVEAL_GLOW_DELAY = 0;
91d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final int REVEAL_GLOW_DURATION = 0;
92d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
93d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
94d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float TARGET_SCALE_EXPANDED = 1.0f;
95d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float TARGET_SCALE_COLLAPSED = 0.8f;
96d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float RING_SCALE_EXPANDED = 1.0f;
97d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private static final float RING_SCALE_COLLAPSED = 0.5f;
98d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
99d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
100d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimationBundle mWaveAnimations = new AnimationBundle();
101d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimationBundle mTargetAnimations = new AnimationBundle();
102d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimationBundle mGlowAnimations = new AnimationBundle();
103d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private ArrayList<String> mTargetDescriptions;
104d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private ArrayList<String> mDirectionDescriptions;
105d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private OnTriggerListener mOnTriggerListener;
106d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private TargetDrawable mHandleDrawable;
107d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private TargetDrawable mOuterRing;
108d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private Vibrator mVibrator;
109d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
110d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mFeedbackCount = 3;
111d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mVibrationDuration = 0;
112d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mGrabbedState;
113d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mActiveTarget = -1;
114d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mGlowRadius;
115d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mWaveCenterX;
116d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mWaveCenterY;
117d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mMaxTargetHeight;
118d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mMaxTargetWidth;
119b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    private float mRingScaleFactor = 1f;
120b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    private boolean mAllowScaling;
121d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
122d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mOuterRadius = 0.0f;
123d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mSnapMargin = 0.0f;
124d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean mDragging;
125d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mNewTargetResources;
126d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
127d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private class AnimationBundle extends ArrayList<Tweener> {
128d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        private static final long serialVersionUID = 0xA84D78726F127468L;
129d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        private boolean mSuspended;
130d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
131d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void start() {
132d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mSuspended) return; // ignore attempts to start animations
133d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int count = size();
134d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            for (int i = 0; i < count; i++) {
135d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Tweener anim = get(i);
136d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                anim.animator.start();
137d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
138d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
139d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
140d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void cancel() {
141d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int count = size();
142d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            for (int i = 0; i < count; i++) {
143d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Tweener anim = get(i);
144d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                anim.animator.cancel();
145d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
146d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            clear();
147d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
148d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
149d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void stop() {
150d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int count = size();
151d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            for (int i = 0; i < count; i++) {
152d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Tweener anim = get(i);
153d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                anim.animator.end();
154d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
155d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            clear();
156d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
157d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
158d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void setSuspended(boolean suspend) {
159d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mSuspended = suspend;
160d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
161d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    };
162d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
163d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
164d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onAnimationEnd(Animator animator) {
165d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
166d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            dispatchOnFinishFinalAnimation();
167d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
168d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    };
169d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
170d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
171d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onAnimationEnd(Animator animator) {
172d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            ping();
173d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
174d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            dispatchOnFinishFinalAnimation();
175d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
176d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    };
177d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
178d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
179d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onAnimationUpdate(ValueAnimator animation) {
180d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            invalidate();
181d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
182d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    };
183d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
184d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean mAnimatingTargets;
185d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
186d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        public void onAnimationEnd(Animator animator) {
187d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mNewTargetResources != 0) {
188d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                internalSetTargetResources(mNewTargetResources);
189d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mNewTargetResources = 0;
190d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                hideTargets(false, false);
191d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
192d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mAnimatingTargets = false;
193d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
194d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    };
195d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mTargetResourceId;
196d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mTargetDescriptionsResourceId;
197d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mDirectionDescriptionsResourceId;
198d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean mAlwaysTrackFinger;
199d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mHorizontalInset;
200d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mVerticalInset;
201d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mGravity = Gravity.TOP;
202d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean mInitialLayout = true;
203d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private Tweener mBackgroundAnimator;
204d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private PointCloud mPointCloud;
205d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float mInnerRadius;
206d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int mPointerId;
207d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
208d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public GlowPadView(Context context) {
209d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        this(context, null);
210d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
211d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
212d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public GlowPadView(Context context, AttributeSet attrs) {
213d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        super(context, attrs);
214d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Resources res = context.getResources();
215d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
216d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
217d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
218d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
219d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
220d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
221d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mVibrationDuration);
222d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
223d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mFeedbackCount);
224b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
225d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
226efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee        setHandleDrawable(handle != null ? handle.resourceId : R.drawable.ic_incall_audio_handle);
227d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOuterRing = new TargetDrawable(res,
228d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                getResourceId(a, R.styleable.GlowPadView_outerRingDrawable), 1);
229d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
230d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
231d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
232d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
233d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Drawable pointDrawable = pointId != 0 ? res.getDrawable(pointId) : null;
234d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
235d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
236d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedValue outValue = new TypedValue();
237d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
238d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Read array of target drawables
239d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
240d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            internalSetTargetResources(outValue.resourceId);
241d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
242d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
243d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            throw new IllegalStateException("Must specify at least one target drawable");
244d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
245d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
246d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Read array of target descriptions
247d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
248d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int resourceId = outValue.resourceId;
249d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (resourceId == 0) {
250d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                throw new IllegalStateException("Must specify target descriptions");
251d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
252d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            setTargetDescriptionsResourceId(resourceId);
253d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
254d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
255d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Read array of direction descriptions
256d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
257d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int resourceId = outValue.resourceId;
258d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (resourceId == 0) {
259d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                throw new IllegalStateException("Must specify direction descriptions");
260d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
261d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            setDirectionDescriptionsResourceId(resourceId);
262d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
263d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
264d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Use gravity attribute from LinearLayout
265d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        //a = context.obtainStyledAttributes(attrs, R.styleable.LinearLayout);
266d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGravity = a.getInt(R.styleable.GlowPadView_android_gravity, Gravity.TOP);
267d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        a.recycle();
268d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
269d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
270d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        setVibrateEnabled(mVibrationDuration > 0);
271d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
272d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        assignDefaultsIfNeeded();
273d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
274d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud = new PointCloud(pointDrawable);
275d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
276d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.glowManager.setRadius(mGlowRadius);
277d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
278d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
279d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int getResourceId(TypedArray a, int id) {
280d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedValue tv = a.peekValue(id);
281d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return tv == null ? 0 : tv.resourceId;
282d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
283d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
284d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void dump() {
285d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "Outer Radius = " + mOuterRadius);
286d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "SnapMargin = " + mSnapMargin);
287d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
288d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
289d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "GlowRadius = " + mGlowRadius);
290d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
291d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
292d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
293d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
294d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void suspendAnimations() {
295d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.setSuspended(true);
296d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.setSuspended(true);
297d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.setSuspended(true);
298d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
299d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
300d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void resumeAnimations() {
301d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.setSuspended(false);
302d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.setSuspended(false);
303d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.setSuspended(false);
304d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.start();
305d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.start();
306d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.start();
307d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
308d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
309d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
310d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    protected int getSuggestedMinimumWidth() {
311d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // View should be large enough to contain the background + handle and
312d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // target drawable on either edge.
313d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
314d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
315d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
316d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
317d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    protected int getSuggestedMinimumHeight() {
318d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // View should be large enough to contain the unlock ring + target and
319d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // target drawable on either edge
320d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
321d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
322d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
323b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    /**
324b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     * This gets the suggested width accounting for the ring's scale factor.
325b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     */
326b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    protected int getScaledSuggestedMinimumWidth() {
327b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
328b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                + mMaxTargetWidth);
329b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    }
330b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
331b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    /**
332b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     * This gets the suggested height accounting for the ring's scale factor.
333b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     */
334b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    protected int getScaledSuggestedMinimumHeight() {
335b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
336b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                + mMaxTargetHeight);
337b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    }
338b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
339d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private int resolveMeasured(int measureSpec, int desired)
340d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    {
341d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int result = 0;
342d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int specSize = MeasureSpec.getSize(measureSpec);
343d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switch (MeasureSpec.getMode(measureSpec)) {
344d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MeasureSpec.UNSPECIFIED:
345d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                result = desired;
346d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
347d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MeasureSpec.AT_MOST:
348d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                result = Math.min(specSize, desired);
349d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
350d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MeasureSpec.EXACTLY:
351d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            default:
352d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                result = specSize;
353d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
354d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return result;
355d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
356d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
357d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
358d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
359d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int minimumWidth = getSuggestedMinimumWidth();
360d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int minimumHeight = getSuggestedMinimumHeight();
361d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
362d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
363b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
364b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
365b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                computedWidth, computedHeight);
366b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
367b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        int scaledWidth = getScaledSuggestedMinimumWidth();
368b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        int scaledHeight = getScaledSuggestedMinimumHeight();
369b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
370b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
371d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        setMeasuredDimension(computedWidth, computedHeight);
372d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
373d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
374d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void switchToState(int state, float x, float y) {
375d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switch (state) {
376d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_IDLE:
377d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                deactivateTargets();
378d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                hideGlow(0, 0, 0.0f, null);
379d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                startBackgroundAnimation(0, 0.0f);
380d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
381d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHandleDrawable.setAlpha(1.0f);
382d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
383d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
384d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_START:
385d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                startBackgroundAnimation(0, 0.0f);
386d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
387d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
388d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_FIRST_TOUCH:
389d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHandleDrawable.setAlpha(0.0f);
390d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                deactivateTargets();
391d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                showTargets(true);
392d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
393d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
394d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
395d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                final AccessibilityManager accessibilityManager =
396d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    (AccessibilityManager) getContext().getSystemService(
397d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                            Context.ACCESSIBILITY_SERVICE);
398d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (accessibilityManager.isEnabled()) {
399d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    announceTargets();
400d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
401d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
402d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
403d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_TRACKING:
404d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHandleDrawable.setAlpha(0.0f);
405d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
406d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
407d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
408d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_SNAP:
409d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                // TODO: Add transition states (see list_selector_background_transition.xml)
410d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHandleDrawable.setAlpha(0.0f);
411d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
412d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
413d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
414d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case STATE_FINISH:
415d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                doFinish();
416d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
417d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
418d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
419d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
420d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void showGlow(int duration, int delay, float finalAlpha,
421d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            AnimatorListener finishListener) {
422d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.cancel();
423d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
424d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "ease", Ease.Cubic.easeIn,
425d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "delay", delay,
426d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "alpha", finalAlpha,
427d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onUpdate", mUpdateListener,
428d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onComplete", finishListener));
429d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.start();
430d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
431d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
432d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void hideGlow(int duration, int delay, float finalAlpha,
433d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            AnimatorListener finishListener) {
434d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.cancel();
435d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
436d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "ease", Ease.Quart.easeOut,
437d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "delay", delay,
438d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "alpha", finalAlpha,
439d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "x", 0.0f,
440d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "y", 0.0f,
441d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onUpdate", mUpdateListener,
442d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onComplete", finishListener));
443d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.start();
444d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
445d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
446d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void deactivateTargets() {
447d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int count = mTargetDrawables.size();
448d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < count; i++) {
449d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = mTargetDrawables.get(i);
450d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            target.setState(TargetDrawable.STATE_INACTIVE);
451d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
452d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mActiveTarget = -1;
453d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
454d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
455d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
456d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Dispatches a trigger event to listener. Ignored if a listener is not set.
457d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param whichTarget the target that was triggered.
458d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
459d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void dispatchTriggerEvent(int whichTarget) {
460d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        vibrate();
461d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mOnTriggerListener != null) {
462d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mOnTriggerListener.onTrigger(this, whichTarget);
463d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
464d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
465d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
466d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void dispatchOnFinishFinalAnimation() {
467d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mOnTriggerListener != null) {
468d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mOnTriggerListener.onFinishFinalAnimation();
469d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
470d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
471d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
472d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void doFinish() {
473d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int activeTarget = mActiveTarget;
474d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final boolean targetHit =  activeTarget != -1;
475d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
476d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (targetHit) {
477d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
478d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
479d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            highlightSelected(activeTarget);
480d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
481d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Inform listener of any active targets.  Typically only one will be active.
482d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
483d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            dispatchTriggerEvent(activeTarget);
484d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (!mAlwaysTrackFinger) {
485d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                // Force ring and targets to finish animation to final expanded state
486d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mTargetAnimations.stop();
487d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
488d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
489d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Animate handle back to the center based on current state.
490d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
491d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            hideTargets(true, false);
492d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
493d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
494d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        setGrabbedState(OnTriggerListener.NO_HANDLE);
495d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
496d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
497d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void highlightSelected(int activeTarget) {
498d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Highlight the given target and fade others
499d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
500d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        hideUnselected(activeTarget);
501d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
502d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
503d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void hideUnselected(int active) {
504d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < mTargetDrawables.size(); i++) {
505d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (i != active) {
506d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mTargetDrawables.get(i).setAlpha(0.0f);
507d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
508d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
509d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
510d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
511d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void hideTargets(boolean animate, boolean expanded) {
512d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.cancel();
513d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Note: these animations should complete at the same time so that we can swap out
514d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // the target assets asynchronously from the setTargetResources() call.
515d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mAnimatingTargets = animate;
516d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
517d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
518d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
519d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final float targetScale = expanded ?
520d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
521d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int length = mTargetDrawables.size();
522d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final TimeInterpolator interpolator = Ease.Cubic.easeOut;
523d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < length; i++) {
524d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = mTargetDrawables.get(i);
525d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            target.setState(TargetDrawable.STATE_INACTIVE);
526d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mTargetAnimations.add(Tweener.to(target, duration,
527d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "ease", interpolator,
528d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "alpha", 0.0f,
529d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "scaleX", targetScale,
530d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "scaleY", targetScale,
531d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "delay", delay,
532d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "onUpdate", mUpdateListener));
533d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
534d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
535b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float ringScaleTarget = expanded ?
536d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
537b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        ringScaleTarget *= mRingScaleFactor;
538d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
539d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "ease", interpolator,
540d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "alpha", 0.0f,
541d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "scaleX", ringScaleTarget,
542d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "scaleY", ringScaleTarget,
543d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "delay", delay,
544d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onUpdate", mUpdateListener,
545d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onComplete", mTargetUpdateListener));
546d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
547d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.start();
548d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
549d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
550d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void showTargets(boolean animate) {
551d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.stop();
552d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mAnimatingTargets = animate;
553d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
554d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
555d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int length = mTargetDrawables.size();
556d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < length; i++) {
557d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = mTargetDrawables.get(i);
558d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            target.setState(TargetDrawable.STATE_INACTIVE);
559d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mTargetAnimations.add(Tweener.to(target, duration,
560d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "ease", Ease.Cubic.easeOut,
561d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "alpha", 1.0f,
562d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "scaleX", 1.0f,
563d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "scaleY", 1.0f,
564d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "delay", delay,
565d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "onUpdate", mUpdateListener));
566d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
567b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
568d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
569d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "ease", Ease.Cubic.easeOut,
570d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "alpha", 1.0f,
571b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                "scaleX", ringScale,
572b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                "scaleY", ringScale,
573d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "delay", delay,
574d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onUpdate", mUpdateListener,
575d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onComplete", mTargetUpdateListener));
576d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
577d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.start();
578d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
579d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
580d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void vibrate() {
581d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mVibrator != null) {
582d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mVibrator.vibrate(mVibrationDuration);
583d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
584d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
585d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
586d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
587d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Resources res = getContext().getResources();
588d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedArray array = res.obtainTypedArray(resourceId);
589d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int count = array.length();
590d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
591d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < count; i++) {
592d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TypedValue value = array.peekValue(i);
593d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0, 3);
594d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            drawables.add(target);
595d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
596d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        array.recycle();
597d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return drawables;
598d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
599d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
600d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void internalSetTargetResources(int resourceId) {
601d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
602d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetDrawables = targets;
603d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetResourceId = resourceId;
604d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
605d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int maxWidth = mHandleDrawable.getWidth();
606d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int maxHeight = mHandleDrawable.getHeight();
607d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int count = targets.size();
608d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < count; i++) {
609d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = targets.get(i);
610d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            maxWidth = Math.max(maxWidth, target.getWidth());
611d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            maxHeight = Math.max(maxHeight, target.getHeight());
612d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
613d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
614d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mMaxTargetWidth = maxWidth;
615d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mMaxTargetHeight = maxHeight;
616d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            requestLayout(); // required to resize layout and call updateTargetPositions()
617d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
618d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updateTargetPositions(mWaveCenterX, mWaveCenterY);
619d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
620d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
621d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
622d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
623d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Loads an array of drawables from the given resourceId.
624d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
625d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param resourceId
626d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
627d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setTargetResources(int resourceId) {
628d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mAnimatingTargets) {
629d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // postpone this change until we return to the initial state
630d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mNewTargetResources = resourceId;
631d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
632d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            internalSetTargetResources(resourceId);
633d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
634d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
635d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
636d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public int getTargetResourceId() {
637d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return mTargetResourceId;
638d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
639d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
640d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
641efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee     * Sets teh handle drawable to the drawable specified by the resource ID.
642efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee     * @param resourceId
643efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee     */
644efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee    public void setHandleDrawable(int resourceId) {
645efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee        mHandleDrawable = new TargetDrawable(getResources(), resourceId, 2);
646efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee        mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
647efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee    }
648efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee
649efe6a5d64851519eea0fe9d349b7b8f904193a36Andrew Lee    /**
650d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Sets the resource id specifying the target descriptions for accessibility.
651d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
652d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param resourceId The resource id.
653d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
654d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setTargetDescriptionsResourceId(int resourceId) {
655d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetDescriptionsResourceId = resourceId;
656d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mTargetDescriptions != null) {
657d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mTargetDescriptions.clear();
658d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
659d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
660d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
661d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
662d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Gets the resource id specifying the target descriptions for accessibility.
663d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
664d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @return The resource id.
665d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
666d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public int getTargetDescriptionsResourceId() {
667d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return mTargetDescriptionsResourceId;
668d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
669d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
670d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
671d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Sets the resource id specifying the target direction descriptions for accessibility.
672d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
673d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param resourceId The resource id.
674d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
675d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setDirectionDescriptionsResourceId(int resourceId) {
676d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mDirectionDescriptionsResourceId = resourceId;
677d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mDirectionDescriptions != null) {
678d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mDirectionDescriptions.clear();
679d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
680d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
681d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
682d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
683d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Gets the resource id specifying the target direction descriptions.
684d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
685d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @return The resource id.
686d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
687d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public int getDirectionDescriptionsResourceId() {
688d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return mDirectionDescriptionsResourceId;
689d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
690d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
691d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
692d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Enable or disable vibrate on touch.
693d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
694d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param enabled
695d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
696d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setVibrateEnabled(boolean enabled) {
697d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (enabled && mVibrator == null) {
698d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
699d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
700d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mVibrator = null;
701d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
702d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
703d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
704d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
705d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Starts wave animation.
706d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
707d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
708d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void ping() {
709d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mFeedbackCount > 0) {
710d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            boolean doWaveAnimation = true;
711d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final AnimationBundle waveAnimations = mWaveAnimations;
712d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
713d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Don't do a wave if there's already one in progress
714d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
715d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                long t = waveAnimations.get(0).animator.getCurrentPlayTime();
716d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (t < WAVE_ANIMATION_DURATION/2) {
717d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    doWaveAnimation = false;
718d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
719d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
720d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
721d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (doWaveAnimation) {
722d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                startWaveAnimation();
723d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
724d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
725d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
726d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
727d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void stopAndHideWaveAnimation() {
728d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.cancel();
729d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.waveManager.setAlpha(0.0f);
730d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
731d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
732d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void startWaveAnimation() {
733d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.cancel();
734d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.waveManager.setAlpha(1.0f);
735d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
736d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
737d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "ease", Ease.Quad.easeOut,
738d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "delay", 0,
739d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "radius", 2.0f * mOuterRadius,
740d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onUpdate", mUpdateListener,
741d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                "onComplete",
742d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                new AnimatorListenerAdapter() {
743d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    public void onAnimationEnd(Animator animator) {
744d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        mPointCloud.waveManager.setRadius(0.0f);
745d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        mPointCloud.waveManager.setAlpha(0.0f);
746d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    }
747d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }));
748d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveAnimations.start();
749d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
750d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
751d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
752d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Resets the widget to default state and cancels all animation. If animate is 'true', will
753d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * animate objects into place. Otherwise, objects will snap back to place.
754d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     *
755d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param animate
756d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
757d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void reset(boolean animate) {
758d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mGlowAnimations.stop();
759d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mTargetAnimations.stop();
760d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        startBackgroundAnimation(0, 0.0f);
761d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        stopAndHideWaveAnimation();
762d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        hideTargets(animate, false);
763d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        hideGlow(0, 0, 0.0f, null);
764d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        Tweener.reset();
765d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
766d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
767d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void startBackgroundAnimation(int duration, float alpha) {
768d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final Drawable background = getBackground();
769d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mAlwaysTrackFinger && background != null) {
770d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mBackgroundAnimator != null) {
771d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mBackgroundAnimator.animator.cancel();
772d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
773d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mBackgroundAnimator = Tweener.to(background, duration,
774d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "ease", Ease.Cubic.easeIn,
775d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "alpha", (int)(255.0f * alpha),
776d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    "delay", SHOW_ANIMATION_DELAY);
777d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mBackgroundAnimator.animator.start();
778d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
779d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
780d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
781d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
782d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public boolean onTouchEvent(MotionEvent event) {
783d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int action = event.getActionMasked();
784d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        boolean handled = false;
785d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switch (action) {
786d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_POINTER_DOWN:
787d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_DOWN:
788d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (DEBUG) Log.v(TAG, "*** DOWN ***");
789d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleDown(event);
790d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleMove(event);
791d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handled = true;
792d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
793d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
794d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_MOVE:
795d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (DEBUG) Log.v(TAG, "*** MOVE ***");
796d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleMove(event);
797d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handled = true;
798d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
799d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
800d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_POINTER_UP:
801d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_UP:
802d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (DEBUG) Log.v(TAG, "*** UP ***");
803d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleMove(event);
804d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleUp(event);
805d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handled = true;
806d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
807d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
808d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case MotionEvent.ACTION_CANCEL:
809d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (DEBUG) Log.v(TAG, "*** CANCEL ***");
810d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleMove(event);
811d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handleCancel(event);
812d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                handled = true;
813d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
814d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
815d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        invalidate();
816d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return handled ? true : super.onTouchEvent(event);
817d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
818d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
819d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void updateGlowPosition(float x, float y) {
820b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float dx = x - mOuterRing.getX();
821b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float dy = y - mOuterRing.getY();
822b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        dx *= 1f / mRingScaleFactor;
823b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        dy *= 1f / mRingScaleFactor;
824b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
825b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
826d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
827d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
828d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void handleDown(MotionEvent event) {
829d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int actionIndex = event.getActionIndex();
830d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float eventX = event.getX(actionIndex);
831d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float eventY = event.getY(actionIndex);
832d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switchToState(STATE_START, eventX, eventY);
833d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (!trySwitchToFirstTouchState(eventX, eventY)) {
834d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mDragging = false;
835d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
836d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mPointerId = event.getPointerId(actionIndex);
837d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updateGlowPosition(eventX, eventY);
838d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
839d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
840d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
841d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void handleUp(MotionEvent event) {
842d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
843d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int actionIndex = event.getActionIndex();
844d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (event.getPointerId(actionIndex) == mPointerId) {
845d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
846d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
847d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
848d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
849d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void handleCancel(MotionEvent event) {
850d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
851d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
852d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // We should drop the active target here but it interferes with
853d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // moving off the screen in the direction of the navigation bar. At some point we may
854d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // want to revisit how we handle this. For now we'll allow a canceled event to
855d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // activate the current target.
856d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
857d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // mActiveTarget = -1; // Drop the active target if canceled.
858d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
859d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int actionIndex = event.findPointerIndex(mPointerId);
860d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        actionIndex = actionIndex == -1 ? 0 : actionIndex;
861d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
862d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
863d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
864d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void handleMove(MotionEvent event) {
865d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int activeTarget = -1;
866d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int historySize = event.getHistorySize();
867d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        ArrayList<TargetDrawable> targets = mTargetDrawables;
868d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int ntargets = targets.size();
869d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float x = 0.0f;
870d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float y = 0.0f;
871d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        int actionIndex = event.findPointerIndex(mPointerId);
872d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
873d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (actionIndex == -1) {
874d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            return;  // no data for this pointer
875d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
876d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
877d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int k = 0; k < historySize + 1; k++) {
878d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
879d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    : event.getX(actionIndex);
880d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
881d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    :event.getY(actionIndex);
882d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // tx and ty are relative to wave center
883d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float tx = eventX - mWaveCenterX;
884d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float ty = eventY - mWaveCenterY;
885d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float touchRadius = (float) Math.sqrt(dist2(tx, ty));
886d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
887d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float limitX = tx * scale;
888d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            float limitY = ty * scale;
889d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            double angleRad = Math.atan2(-ty, tx);
890d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
891d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (!mDragging) {
892d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                trySwitchToFirstTouchState(eventX, eventY);
893d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
894d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
895d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mDragging) {
896d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                // For multiple targets, snap to the one that matches
897b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
898d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                final float snapDistance2 = snapRadius * snapRadius;
899d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                // Find first target in range
900d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                for (int i = 0; i < ntargets; i++) {
901d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    TargetDrawable target = targets.get(i);
902d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
903d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    double targetMinRad = (i - 0.5) * 2 * Math.PI / ntargets;
904d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    double targetMaxRad = (i + 0.5) * 2 * Math.PI / ntargets;
905d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    if (target.isEnabled()) {
906d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        boolean angleMatches =
907d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                            (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
908d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                            (angleRad + 2 * Math.PI > targetMinRad &&
909d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                             angleRad + 2 * Math.PI <= targetMaxRad);
910d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
911d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                            activeTarget = i;
912d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        }
913d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    }
914d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
915d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
916d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            x = limitX;
917d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            y = limitY;
918d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
919d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
920d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (!mDragging) {
921d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            return;
922d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
923d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
924d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (activeTarget != -1) {
925d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_SNAP, x,y);
926d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updateGlowPosition(x, y);
927d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
928d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_TRACKING, x, y);
929d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updateGlowPosition(x, y);
930d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
931d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
932d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mActiveTarget != activeTarget) {
933d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Defocus the old target
934d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mActiveTarget != -1) {
935d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                TargetDrawable target = targets.get(mActiveTarget);
936d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                target.setState(TargetDrawable.STATE_INACTIVE);
937d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
938d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Focus the new target
939d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (activeTarget != -1) {
940d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                TargetDrawable target = targets.get(activeTarget);
941d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                target.setState(TargetDrawable.STATE_FOCUSED);
942d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                final AccessibilityManager accessibilityManager =
943d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        (AccessibilityManager) getContext().getSystemService(
944d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                                Context.ACCESSIBILITY_SERVICE);
945d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (accessibilityManager.isEnabled()) {
946d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    String targetContentDescription = getTargetDescription(activeTarget);
947d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    announceForAccessibility(targetContentDescription);
948d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
949d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
950d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
951d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mActiveTarget = activeTarget;
952d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
953d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
954d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
955d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public boolean onHoverEvent(MotionEvent event) {
956d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final AccessibilityManager accessibilityManager =
957d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                (AccessibilityManager) getContext().getSystemService(
958d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        Context.ACCESSIBILITY_SERVICE);
959d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (accessibilityManager.isTouchExplorationEnabled()) {
960d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final int action = event.getAction();
961d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switch (action) {
962d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                case MotionEvent.ACTION_HOVER_ENTER:
963d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    event.setAction(MotionEvent.ACTION_DOWN);
964d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    break;
965d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                case MotionEvent.ACTION_HOVER_MOVE:
966d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    event.setAction(MotionEvent.ACTION_MOVE);
967d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    break;
968d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                case MotionEvent.ACTION_HOVER_EXIT:
969d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    event.setAction(MotionEvent.ACTION_UP);
970d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    break;
971d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
972d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            onTouchEvent(event);
973d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            event.setAction(action);
974d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
975d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        super.onHoverEvent(event);
976d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return true;
977d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
978d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
979d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
980d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Sets the current grabbed state, and dispatches a grabbed state change
981d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * event to our listener.
982d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
983d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void setGrabbedState(int newState) {
984d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (newState != mGrabbedState) {
985d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (newState != OnTriggerListener.NO_HANDLE) {
986d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                vibrate();
987d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
988d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mGrabbedState = newState;
989d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mOnTriggerListener != null) {
990d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (newState == OnTriggerListener.NO_HANDLE) {
991d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
992d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                } else {
993d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
994d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
995d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mOnTriggerListener.onGrabbedStateChange(this, newState);
996d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
997d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
998d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
999d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1000d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean trySwitchToFirstTouchState(float x, float y) {
1001d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final float tx = x - mWaveCenterX;
1002d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final float ty = y - mWaveCenterY;
1003d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
1004d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (DEBUG) Log.v(TAG, "** Handle HIT");
1005d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            switchToState(STATE_FIRST_TOUCH, x, y);
1006d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            updateGlowPosition(tx, ty);
1007d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mDragging = true;
1008d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            return true;
1009d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1010d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return false;
1011d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1012d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1013d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void assignDefaultsIfNeeded() {
1014d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mOuterRadius == 0.0f) {
1015d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
1016d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1017d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mSnapMargin == 0.0f) {
1018d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
1019d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
1020d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1021d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mInnerRadius == 0.0f) {
1022d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
1023d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1024d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1025d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1026d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void computeInsets(int dx, int dy) {
1027d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int layoutDirection = getLayoutDirection();
1028d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
1029d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1030d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1031d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.LEFT:
1032d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHorizontalInset = 0;
1033d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1034d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.RIGHT:
1035d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHorizontalInset = dx;
1036d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1037d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.CENTER_HORIZONTAL:
1038d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            default:
1039d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mHorizontalInset = dx / 2;
1040d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1041d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1042d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1043d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.TOP:
1044d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mVerticalInset = 0;
1045d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1046d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.BOTTOM:
1047d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mVerticalInset = dy;
1048d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1049d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            case Gravity.CENTER_VERTICAL:
1050d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            default:
1051d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                mVerticalInset = dy / 2;
1052d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break;
1053d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1054d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1055d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1056b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    /**
1057b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     * Given the desired width and height of the ring and the allocated width and height, compute
1058b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     * how much we need to scale the ring.
1059b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen     */
1060b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    private float computeScaleFactor(int desiredWidth, int desiredHeight,
1061b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            int actualWidth, int actualHeight) {
1062b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1063b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        // Return unity if scaling is not allowed.
1064b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        if (!mAllowScaling) return 1f;
1065b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1066b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        final int layoutDirection = getLayoutDirection();
1067b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
1068b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1069b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float scaleX = 1f;
1070b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        float scaleY = 1f;
1071b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1072b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        // We use the gravity as a cue for whether we want to scale on a particular axis.
1073b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
1074b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        // we only scale to fit vertically if we're not pinned to the top or bottom. In these
1075b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        // cases, we want the ring to hang off the side or top/bottom, respectively.
1076b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1077b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.LEFT:
1078b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.RIGHT:
1079b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                break;
1080b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.CENTER_HORIZONTAL:
1081b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            default:
1082b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                if (desiredWidth > actualWidth) {
1083b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                    scaleX = (1f * actualWidth - mMaxTargetWidth) /
1084b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                            (desiredWidth - mMaxTargetWidth);
1085b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                }
1086b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                break;
1087b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        }
1088b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1089b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.TOP:
1090b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.BOTTOM:
1091b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                break;
1092b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            case Gravity.CENTER_VERTICAL:
1093b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            default:
1094b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                if (desiredHeight > actualHeight) {
1095b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                    scaleY = (1f * actualHeight - mMaxTargetHeight) /
1096b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                            (desiredHeight - mMaxTargetHeight);
1097b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                }
1098b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen                break;
1099b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        }
1100b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        return Math.min(scaleX, scaleY);
1101b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    }
1102b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1103b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    private float getRingWidth() {
1104b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
1105b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    }
1106b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1107b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    private float getRingHeight() {
1108b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
1109b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen    }
1110b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1111d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
1112d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1113d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        super.onLayout(changed, left, top, right, bottom);
1114d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int width = right - left;
1115d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int height = bottom - top;
1116d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1117d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Target placement width/height. This puts the targets on the greater of the ring
1118d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // width or the specified outer radius.
1119b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        final float placementWidth = getRingWidth();
1120b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        final float placementHeight = getRingHeight();
1121d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float newWaveCenterX = mHorizontalInset
1122dc8cda5a71ee4ee644561fed0767c6d0b3a3447dSantos Cordon                + (mMaxTargetWidth + placementWidth) / 2;
1123d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        float newWaveCenterY = mVerticalInset
1124dc8cda5a71ee4ee644561fed0767c6d0b3a3447dSantos Cordon                + (mMaxTargetHeight + placementHeight) / 2;
1125d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1126d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mInitialLayout) {
1127d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            stopAndHideWaveAnimation();
1128d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            hideTargets(false, false);
1129d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mInitialLayout = false;
1130d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1131d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1132d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOuterRing.setPositionX(newWaveCenterX);
1133d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOuterRing.setPositionY(newWaveCenterY);
1134d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1135b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen        mPointCloud.setScale(mRingScaleFactor);
1136b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen
1137d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mHandleDrawable.setPositionX(newWaveCenterX);
1138d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mHandleDrawable.setPositionY(newWaveCenterY);
1139d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1140d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        updateTargetPositions(newWaveCenterX, newWaveCenterY);
1141d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
1142d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        updateGlowPosition(newWaveCenterX, newWaveCenterY);
1143d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1144d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveCenterX = newWaveCenterX;
1145d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mWaveCenterY = newWaveCenterY;
1146d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1147d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (DEBUG) dump();
1148d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1149d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1150d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void updateTargetPositions(float centerX, float centerY) {
1151d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        // Reposition the target drawables if the view changed.
1152d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        ArrayList<TargetDrawable> targets = mTargetDrawables;
1153d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int size = targets.size();
1154d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final float alpha = (float) (-2.0f * Math.PI / size);
1155d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < size; i++) {
1156d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final TargetDrawable targetIcon = targets.get(i);
1157d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final float angle = alpha * i;
1158d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            targetIcon.setPositionX(centerX);
1159d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            targetIcon.setPositionY(centerY);
1160b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            targetIcon.setX(getRingWidth() / 2 * (float) Math.cos(angle));
1161b0899adba59d1c5cdd2db1363f49803a24546883Christine Chen            targetIcon.setY(getRingHeight() / 2 * (float) Math.sin(angle));
1162d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1163d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1164d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1165d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void updatePointCloudPosition(float centerX, float centerY) {
1166d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.setCenter(centerX, centerY);
1167d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1168d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1169d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    @Override
1170d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    protected void onDraw(Canvas canvas) {
1171d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mPointCloud.draw(canvas);
1172d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOuterRing.draw(canvas);
1173d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int ntargets = mTargetDrawables.size();
1174d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < ntargets; i++) {
1175d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            TargetDrawable target = mTargetDrawables.get(i);
1176d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (target != null) {
1177d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                target.draw(canvas);
1178d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1179d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1180d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mHandleDrawable.draw(canvas);
1181d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1182d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1183d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setOnTriggerListener(OnTriggerListener listener) {
1184d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        mOnTriggerListener = listener;
1185d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1186d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1187d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float square(float d) {
1188d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return d * d;
1189d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1190d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1191d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float dist2(float dx, float dy) {
1192d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return dx*dx + dy*dy;
1193d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1194d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1195d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private float getScaledGlowRadiusSquared() {
1196d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final float scaledTapRadius;
1197d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final AccessibilityManager accessibilityManager =
1198d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                (AccessibilityManager) getContext().getSystemService(
1199d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        Context.ACCESSIBILITY_SERVICE);
1200d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (accessibilityManager.isEnabled()) {
1201d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
1202d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        } else {
1203d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            scaledTapRadius = mGlowRadius;
1204d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1205d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return square(scaledTapRadius);
1206d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1207d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1208d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private void announceTargets() {
1209d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        StringBuilder utterance = new StringBuilder();
1210d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int targetCount = mTargetDrawables.size();
1211d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < targetCount; i++) {
1212d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            String targetDescription = getTargetDescription(i);
1213d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            String directionDescription = getDirectionDescription(i);
1214d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (!TextUtils.isEmpty(targetDescription)
1215d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    && !TextUtils.isEmpty(directionDescription)) {
1216d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                String text = String.format(directionDescription, targetDescription);
1217d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                utterance.append(text);
1218d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1219d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1220d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (utterance.length() > 0) {
1221d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            announceForAccessibility(utterance.toString());
1222d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1223d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1224d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1225d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private String getTargetDescription(int index) {
1226d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
1227d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
1228d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mTargetDrawables.size() != mTargetDescriptions.size()) {
1229d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Log.w(TAG, "The number of target drawables must be"
1230d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        + " equal to the number of target descriptions.");
1231d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                return null;
1232d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1233d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1234d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return mTargetDescriptions.get(index);
1235d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1236d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1237d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private String getDirectionDescription(int index) {
1238d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
1239d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
1240d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
1241d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Log.w(TAG, "The number of target drawables must be"
1242d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        + " equal to the number of direction descriptions.");
1243d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                return null;
1244d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1245d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1246d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return mDirectionDescriptions.get(index);
1247d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1248d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1249d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private ArrayList<String> loadDescriptions(int resourceId) {
1250d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
1251d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int count = array.length();
1252d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
1253d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < count; i++) {
1254d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            String contentDescription = array.getString(i);
1255d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            targetContentDescriptions.add(contentDescription);
1256d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1257d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        array.recycle();
1258d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return targetContentDescriptions;
1259d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1260d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1261d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public int getResourceIdForTarget(int index) {
1262d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final TargetDrawable drawable = mTargetDrawables.get(index);
1263d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return drawable == null ? 0 : drawable.getResourceId();
1264d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1265d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1266d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public void setEnableTarget(int resourceId, boolean enabled) {
1267d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < mTargetDrawables.size(); i++) {
1268d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final TargetDrawable target = mTargetDrawables.get(i);
1269d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (target.getResourceId() == resourceId) {
1270d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                target.setEnabled(enabled);
1271d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                break; // should never be more than one match
1272d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1273d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1274d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1275d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1276d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
1277d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Gets the position of a target in the array that matches the given resource.
1278d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param resourceId
1279d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @return the index or -1 if not found
1280d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
1281d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public int getTargetPosition(int resourceId) {
1282d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < mTargetDrawables.size(); i++) {
1283d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final TargetDrawable target = mTargetDrawables.get(i);
1284d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (target.getResourceId() == resourceId) {
1285d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                return i; // should never be more than one match
1286d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1287d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1288d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return -1;
1289d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1290d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1291d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    private boolean replaceTargetDrawables(Resources res, int existingResourceId,
1292d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            int newResourceId) {
1293d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (existingResourceId == 0 || newResourceId == 0) {
1294d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            return false;
1295d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1296d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1297d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        boolean result = false;
1298d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final ArrayList<TargetDrawable> drawables = mTargetDrawables;
1299d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        final int size = drawables.size();
1300d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        for (int i = 0; i < size; i++) {
1301d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            final TargetDrawable target = drawables.get(i);
1302d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            if (target != null && target.getResourceId() == existingResourceId) {
1303d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                target.setDrawable(res, newResourceId);
1304d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                result = true;
1305d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1306d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1307d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1308d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (result) {
1309d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            requestLayout(); // in case any given drawable's size changes
1310d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1311d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1312d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return result;
1313d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1314d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1315d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    /**
1316d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * Searches the given package for a resource to use to replace the Drawable on the
1317d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * target with the given resource id
1318d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param component of the .apk that contains the resource
1319d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param name of the metadata in the .apk
1320d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @param existingResId the resource id of the target to search for
1321d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     * @return true if found in the given package and replaced at least one target Drawables
1322d05336248c5600cde35ad564840532f59b4c085cChiao Cheng     */
1323d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
1324d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                int existingResId) {
1325d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (existingResId == 0) return false;
1326d05336248c5600cde35ad564840532f59b4c085cChiao Cheng
1327d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        boolean replaced = false;
1328d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (component != null) {
1329d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            try {
1330d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                PackageManager packageManager = getContext().getPackageManager();
1331d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                // Look for the search icon specified in the activity meta-data
1332d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Bundle metaData = packageManager.getActivityInfo(
1333d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        component, PackageManager.GET_META_DATA).metaData;
1334d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                if (metaData != null) {
1335d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    int iconResId = metaData.getInt(name);
1336d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    if (iconResId != 0) {
1337d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        Resources res = packageManager.getResourcesForActivity(component);
1338d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        replaced = replaceTargetDrawables(res, existingResId, iconResId);
1339d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                    }
1340d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                }
1341d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            } catch (NameNotFoundException e) {
1342d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Log.w(TAG, "Failed to swap drawable; "
1343d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        + component.flattenToShortString() + " not found", e);
1344d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            } catch (Resources.NotFoundException nfe) {
1345d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                Log.w(TAG, "Failed to swap drawable from "
1346d05336248c5600cde35ad564840532f59b4c085cChiao Cheng                        + component.flattenToShortString(), nfe);
1347d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            }
1348d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1349d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        if (!replaced) {
1350d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            // Restore the original drawable
1351d05336248c5600cde35ad564840532f59b4c085cChiao Cheng            replaceTargetDrawables(getContext().getResources(), existingResId, existingResId);
1352d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        }
1353d05336248c5600cde35ad564840532f59b4c085cChiao Cheng        return replaced;
1354d05336248c5600cde35ad564840532f59b4c085cChiao Cheng    }
1355d05336248c5600cde35ad564840532f59b4c085cChiao Cheng}
1356