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