1955a016922ea49f154d190b054a202559b41a4d3Jim Miller/*
2955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Copyright (C) 2012 The Android Open Source Project
3955a016922ea49f154d190b054a202559b41a4d3Jim Miller *
4955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Licensed under the Apache License, Version 2.0 (the "License");
5955a016922ea49f154d190b054a202559b41a4d3Jim Miller * you may not use this file except in compliance with the License.
6955a016922ea49f154d190b054a202559b41a4d3Jim Miller * You may obtain a copy of the License at
7955a016922ea49f154d190b054a202559b41a4d3Jim Miller *
8955a016922ea49f154d190b054a202559b41a4d3Jim Miller *      http://www.apache.org/licenses/LICENSE-2.0
9955a016922ea49f154d190b054a202559b41a4d3Jim Miller *
10955a016922ea49f154d190b054a202559b41a4d3Jim Miller * Unless required by applicable law or agreed to in writing, software
11955a016922ea49f154d190b054a202559b41a4d3Jim Miller * distributed under the License is distributed on an "AS IS" BASIS,
12955a016922ea49f154d190b054a202559b41a4d3Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13955a016922ea49f154d190b054a202559b41a4d3Jim Miller * See the License for the specific language governing permissions and
14955a016922ea49f154d190b054a202559b41a4d3Jim Miller * limitations under the License.
15955a016922ea49f154d190b054a202559b41a4d3Jim Miller */
16955a016922ea49f154d190b054a202559b41a4d3Jim Miller
17955a016922ea49f154d190b054a202559b41a4d3Jim Millerpackage com.android.internal.widget.multiwaveview;
18955a016922ea49f154d190b054a202559b41a4d3Jim Miller
19955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.Animator;
20955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.Animator.AnimatorListener;
21955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.AnimatorListenerAdapter;
22955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.TimeInterpolator;
23955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.ValueAnimator;
24955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.animation.ValueAnimator.AnimatorUpdateListener;
25955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.ComponentName;
26955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.Context;
27955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.pm.PackageManager;
28955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.pm.PackageManager.NameNotFoundException;
29955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.res.Resources;
30955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.content.res.TypedArray;
31955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.Canvas;
32955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.graphics.drawable.Drawable;
33955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.os.Bundle;
34723a725e790d269f32980116e775d3d7f0037865Jeff Sharkeyimport android.os.UserHandle;
35955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.os.Vibrator;
36723a725e790d269f32980116e775d3d7f0037865Jeff Sharkeyimport android.provider.Settings;
37955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.text.TextUtils;
38955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.util.AttributeSet;
39955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.util.Log;
40955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.util.TypedValue;
41955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.view.Gravity;
42955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.view.MotionEvent;
43955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.view.View;
44955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport android.view.accessibility.AccessibilityManager;
45955a016922ea49f154d190b054a202559b41a4d3Jim Miller
46955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport com.android.internal.R;
47955a016922ea49f154d190b054a202559b41a4d3Jim Miller
48955a016922ea49f154d190b054a202559b41a4d3Jim Millerimport java.util.ArrayList;
49955a016922ea49f154d190b054a202559b41a4d3Jim Miller
50955a016922ea49f154d190b054a202559b41a4d3Jim Miller/**
51955a016922ea49f154d190b054a202559b41a4d3Jim Miller * A re-usable widget containing a center, outer ring and wave animation.
52955a016922ea49f154d190b054a202559b41a4d3Jim Miller */
53955a016922ea49f154d190b054a202559b41a4d3Jim Millerpublic class GlowPadView extends View {
54955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final String TAG = "GlowPadView";
55955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final boolean DEBUG = false;
56955a016922ea49f154d190b054a202559b41a4d3Jim Miller
57955a016922ea49f154d190b054a202559b41a4d3Jim Miller    // Wave state machine
58955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_IDLE = 0;
59955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_START = 1;
60955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_FIRST_TOUCH = 2;
61955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_TRACKING = 3;
62955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_SNAP = 4;
63955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int STATE_FINISH = 5;
64955a016922ea49f154d190b054a202559b41a4d3Jim Miller
65955a016922ea49f154d190b054a202559b41a4d3Jim Miller    // Animation properties.
66955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it
67955a016922ea49f154d190b054a202559b41a4d3Jim Miller
68955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public interface OnTriggerListener {
69955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int NO_HANDLE = 0;
70955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int CENTER_HANDLE = 1;
71955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onGrabbed(View v, int handle);
72955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onReleased(View v, int handle);
73955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onTrigger(View v, int target);
74955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onGrabbedStateChange(View v, int handle);
75955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onFinishFinalAnimation();
76955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
77955a016922ea49f154d190b054a202559b41a4d3Jim Miller
78955a016922ea49f154d190b054a202559b41a4d3Jim Miller    // Tuneable parameters for animation
795892e2ec253465a46b346fc813a21b412ae85e2eJim Miller    private static final int WAVE_ANIMATION_DURATION = 1350;
80955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int RETURN_TO_HOME_DELAY = 1200;
81955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int RETURN_TO_HOME_DURATION = 200;
82955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int HIDE_ANIMATION_DELAY = 200;
83955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int HIDE_ANIMATION_DURATION = 200;
84955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int SHOW_ANIMATION_DURATION = 200;
85955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int SHOW_ANIMATION_DELAY = 50;
86955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int INITIAL_SHOW_HANDLE_DURATION = 200;
87955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int REVEAL_GLOW_DELAY = 0;
88955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final int REVEAL_GLOW_DURATION = 0;
89955a016922ea49f154d190b054a202559b41a4d3Jim Miller
90955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f;
91955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float TARGET_SCALE_EXPANDED = 1.0f;
92955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float TARGET_SCALE_COLLAPSED = 0.8f;
93955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float RING_SCALE_EXPANDED = 1.0f;
94955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private static final float RING_SCALE_COLLAPSED = 0.5f;
95955a016922ea49f154d190b054a202559b41a4d3Jim Miller
96955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>();
97955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimationBundle mWaveAnimations = new AnimationBundle();
98955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimationBundle mTargetAnimations = new AnimationBundle();
99955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimationBundle mGlowAnimations = new AnimationBundle();
100955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private ArrayList<String> mTargetDescriptions;
101955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private ArrayList<String> mDirectionDescriptions;
102955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private OnTriggerListener mOnTriggerListener;
103955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private TargetDrawable mHandleDrawable;
104955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private TargetDrawable mOuterRing;
105955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private Vibrator mVibrator;
106955a016922ea49f154d190b054a202559b41a4d3Jim Miller
107955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mFeedbackCount = 3;
108955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mVibrationDuration = 0;
109955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mGrabbedState;
110955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mActiveTarget = -1;
111955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mGlowRadius;
112955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mWaveCenterX;
113955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mWaveCenterY;
114955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mMaxTargetHeight;
115955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mMaxTargetWidth;
116e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    private float mRingScaleFactor = 1f;
117f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen    private boolean mAllowScaling;
118955a016922ea49f154d190b054a202559b41a4d3Jim Miller
119955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mOuterRadius = 0.0f;
120955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mSnapMargin = 0.0f;
121f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private float mFirstItemOffset = 0.0f;
122f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private boolean mMagneticTargets = false;
123955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean mDragging;
124955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mNewTargetResources;
125955a016922ea49f154d190b054a202559b41a4d3Jim Miller
126955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private class AnimationBundle extends ArrayList<Tweener> {
127955a016922ea49f154d190b054a202559b41a4d3Jim Miller        private static final long serialVersionUID = 0xA84D78726F127468L;
128955a016922ea49f154d190b054a202559b41a4d3Jim Miller        private boolean mSuspended;
129955a016922ea49f154d190b054a202559b41a4d3Jim Miller
130955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void start() {
131955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mSuspended) return; // ignore attempts to start animations
132955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int count = size();
133955a016922ea49f154d190b054a202559b41a4d3Jim Miller            for (int i = 0; i < count; i++) {
134955a016922ea49f154d190b054a202559b41a4d3Jim Miller                Tweener anim = get(i);
135955a016922ea49f154d190b054a202559b41a4d3Jim Miller                anim.animator.start();
136955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
137955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
138955a016922ea49f154d190b054a202559b41a4d3Jim Miller
139955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void cancel() {
140955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int count = size();
141955a016922ea49f154d190b054a202559b41a4d3Jim Miller            for (int i = 0; i < count; i++) {
142955a016922ea49f154d190b054a202559b41a4d3Jim Miller                Tweener anim = get(i);
143955a016922ea49f154d190b054a202559b41a4d3Jim Miller                anim.animator.cancel();
144955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
145955a016922ea49f154d190b054a202559b41a4d3Jim Miller            clear();
146955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
147955a016922ea49f154d190b054a202559b41a4d3Jim Miller
148955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void stop() {
149955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int count = size();
150955a016922ea49f154d190b054a202559b41a4d3Jim Miller            for (int i = 0; i < count; i++) {
151955a016922ea49f154d190b054a202559b41a4d3Jim Miller                Tweener anim = get(i);
152955a016922ea49f154d190b054a202559b41a4d3Jim Miller                anim.animator.end();
153955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
154955a016922ea49f154d190b054a202559b41a4d3Jim Miller            clear();
155955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
156955a016922ea49f154d190b054a202559b41a4d3Jim Miller
157955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void setSuspended(boolean suspend) {
158955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mSuspended = suspend;
159955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
160955a016922ea49f154d190b054a202559b41a4d3Jim Miller    };
161955a016922ea49f154d190b054a202559b41a4d3Jim Miller
162955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimatorListener mResetListener = new AnimatorListenerAdapter() {
163955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onAnimationEnd(Animator animator) {
164955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
165955a016922ea49f154d190b054a202559b41a4d3Jim Miller            dispatchOnFinishFinalAnimation();
166955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
167955a016922ea49f154d190b054a202559b41a4d3Jim Miller    };
168955a016922ea49f154d190b054a202559b41a4d3Jim Miller
169955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() {
170955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onAnimationEnd(Animator animator) {
171955a016922ea49f154d190b054a202559b41a4d3Jim Miller            ping();
172955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY);
173955a016922ea49f154d190b054a202559b41a4d3Jim Miller            dispatchOnFinishFinalAnimation();
174955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
175955a016922ea49f154d190b054a202559b41a4d3Jim Miller    };
176955a016922ea49f154d190b054a202559b41a4d3Jim Miller
177955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() {
178955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onAnimationUpdate(ValueAnimator animation) {
179955a016922ea49f154d190b054a202559b41a4d3Jim Miller            invalidate();
180955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
181955a016922ea49f154d190b054a202559b41a4d3Jim Miller    };
182955a016922ea49f154d190b054a202559b41a4d3Jim Miller
183955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean mAnimatingTargets;
184955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() {
185955a016922ea49f154d190b054a202559b41a4d3Jim Miller        public void onAnimationEnd(Animator animator) {
186955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mNewTargetResources != 0) {
187955a016922ea49f154d190b054a202559b41a4d3Jim Miller                internalSetTargetResources(mNewTargetResources);
188955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mNewTargetResources = 0;
189955a016922ea49f154d190b054a202559b41a4d3Jim Miller                hideTargets(false, false);
190955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
191955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mAnimatingTargets = false;
192955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
193955a016922ea49f154d190b054a202559b41a4d3Jim Miller    };
194955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mTargetResourceId;
195955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mTargetDescriptionsResourceId;
196955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mDirectionDescriptionsResourceId;
197955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean mAlwaysTrackFinger;
198955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mHorizontalInset;
199955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mVerticalInset;
200955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int mGravity = Gravity.TOP;
201955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean mInitialLayout = true;
202955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private Tweener mBackgroundAnimator;
203955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private PointCloud mPointCloud;
204955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float mInnerRadius;
205b499884ba1fa9577363524d1f28100627926d3b6Jim Miller    private int mPointerId;
206955a016922ea49f154d190b054a202559b41a4d3Jim Miller
207955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public GlowPadView(Context context) {
208955a016922ea49f154d190b054a202559b41a4d3Jim Miller        this(context, null);
209955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
210955a016922ea49f154d190b054a202559b41a4d3Jim Miller
211955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public GlowPadView(Context context, AttributeSet attrs) {
212955a016922ea49f154d190b054a202559b41a4d3Jim Miller        super(context, attrs);
213955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Resources res = context.getResources();
214955a016922ea49f154d190b054a202559b41a4d3Jim Miller
215955a016922ea49f154d190b054a202559b41a4d3Jim Miller        TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.GlowPadView);
216955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mInnerRadius = a.getDimension(R.styleable.GlowPadView_innerRadius, mInnerRadius);
217955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOuterRadius = a.getDimension(R.styleable.GlowPadView_outerRadius, mOuterRadius);
218955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mSnapMargin = a.getDimension(R.styleable.GlowPadView_snapMargin, mSnapMargin);
219f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        mFirstItemOffset = (float) Math.toRadians(
220f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                a.getFloat(R.styleable.GlowPadView_firstItemOffset,
221f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                        (float) Math.toDegrees(mFirstItemOffset)));
222955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mVibrationDuration = a.getInt(R.styleable.GlowPadView_vibrationDuration,
223955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mVibrationDuration);
224955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
225955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mFeedbackCount);
226f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
227a592d224732a06c3776a41df37a52bac4c64a654Jim Miller        TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
228a592d224732a06c3776a41df37a52bac4c64a654Jim Miller        mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0);
229955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
230955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOuterRing = new TargetDrawable(res,
231955a016922ea49f154d190b054a202559b41a4d3Jim Miller                getResourceId(a, R.styleable.GlowPadView_outerRingDrawable));
232955a016922ea49f154d190b054a202559b41a4d3Jim Miller
233955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mAlwaysTrackFinger = a.getBoolean(R.styleable.GlowPadView_alwaysTrackFinger, false);
234f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        mMagneticTargets = a.getBoolean(R.styleable.GlowPadView_magneticTargets, mMagneticTargets);
235955a016922ea49f154d190b054a202559b41a4d3Jim Miller
236955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int pointId = getResourceId(a, R.styleable.GlowPadView_pointDrawable);
237955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Drawable pointDrawable = pointId != 0 ? res.getDrawable(pointId) : null;
238955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowRadius = a.getDimension(R.styleable.GlowPadView_glowRadius, 0.0f);
239955a016922ea49f154d190b054a202559b41a4d3Jim Miller
240955a016922ea49f154d190b054a202559b41a4d3Jim Miller        TypedValue outValue = new TypedValue();
241955a016922ea49f154d190b054a202559b41a4d3Jim Miller
242955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Read array of target drawables
243955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (a.getValue(R.styleable.GlowPadView_targetDrawables, outValue)) {
244955a016922ea49f154d190b054a202559b41a4d3Jim Miller            internalSetTargetResources(outValue.resourceId);
245955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
246955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mTargetDrawables == null || mTargetDrawables.size() == 0) {
247955a016922ea49f154d190b054a202559b41a4d3Jim Miller            throw new IllegalStateException("Must specify at least one target drawable");
248955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
249955a016922ea49f154d190b054a202559b41a4d3Jim Miller
250955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Read array of target descriptions
251955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (a.getValue(R.styleable.GlowPadView_targetDescriptions, outValue)) {
252955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int resourceId = outValue.resourceId;
253955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (resourceId == 0) {
254955a016922ea49f154d190b054a202559b41a4d3Jim Miller                throw new IllegalStateException("Must specify target descriptions");
255955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
256955a016922ea49f154d190b054a202559b41a4d3Jim Miller            setTargetDescriptionsResourceId(resourceId);
257955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
258955a016922ea49f154d190b054a202559b41a4d3Jim Miller
259955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Read array of direction descriptions
260955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (a.getValue(R.styleable.GlowPadView_directionDescriptions, outValue)) {
261955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int resourceId = outValue.resourceId;
262955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (resourceId == 0) {
263955a016922ea49f154d190b054a202559b41a4d3Jim Miller                throw new IllegalStateException("Must specify direction descriptions");
264955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
265955a016922ea49f154d190b054a202559b41a4d3Jim Miller            setDirectionDescriptionsResourceId(resourceId);
266955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
267955a016922ea49f154d190b054a202559b41a4d3Jim Miller
268962159addd6705d11bdd4c0fa3b83c0b7d95b28aAdam Powell        mGravity = a.getInt(R.styleable.GlowPadView_gravity, Gravity.TOP);
269955a016922ea49f154d190b054a202559b41a4d3Jim Miller
270955a016922ea49f154d190b054a202559b41a4d3Jim Miller        a.recycle();
271955a016922ea49f154d190b054a202559b41a4d3Jim Miller
272955a016922ea49f154d190b054a202559b41a4d3Jim Miller        setVibrateEnabled(mVibrationDuration > 0);
273955a016922ea49f154d190b054a202559b41a4d3Jim Miller
274955a016922ea49f154d190b054a202559b41a4d3Jim Miller        assignDefaultsIfNeeded();
275955a016922ea49f154d190b054a202559b41a4d3Jim Miller
276955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud = new PointCloud(pointDrawable);
277955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.makePointCloud(mInnerRadius, mOuterRadius);
278955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.glowManager.setRadius(mGlowRadius);
279955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
280955a016922ea49f154d190b054a202559b41a4d3Jim Miller
281955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int getResourceId(TypedArray a, int id) {
282955a016922ea49f154d190b054a202559b41a4d3Jim Miller        TypedValue tv = a.peekValue(id);
283955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return tv == null ? 0 : tv.resourceId;
284955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
285955a016922ea49f154d190b054a202559b41a4d3Jim Miller
286955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void dump() {
287955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "Outer Radius = " + mOuterRadius);
288955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "SnapMargin = " + mSnapMargin);
289955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "FeedbackCount = " + mFeedbackCount);
290955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "VibrationDuration = " + mVibrationDuration);
291955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "GlowRadius = " + mGlowRadius);
292955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "WaveCenterX = " + mWaveCenterX);
293955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Log.v(TAG, "WaveCenterY = " + mWaveCenterY);
294955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
295955a016922ea49f154d190b054a202559b41a4d3Jim Miller
296955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void suspendAnimations() {
297955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.setSuspended(true);
298955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.setSuspended(true);
299955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.setSuspended(true);
300955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
301955a016922ea49f154d190b054a202559b41a4d3Jim Miller
302955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void resumeAnimations() {
303955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.setSuspended(false);
304955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.setSuspended(false);
305955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.setSuspended(false);
306955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.start();
307955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.start();
308955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.start();
309955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
310955a016922ea49f154d190b054a202559b41a4d3Jim Miller
311955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
312955a016922ea49f154d190b054a202559b41a4d3Jim Miller    protected int getSuggestedMinimumWidth() {
313955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // View should be large enough to contain the background + handle and
314955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // target drawable on either edge.
315955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth);
316955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
317955a016922ea49f154d190b054a202559b41a4d3Jim Miller
318955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
319955a016922ea49f154d190b054a202559b41a4d3Jim Miller    protected int getSuggestedMinimumHeight() {
320955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // View should be large enough to contain the unlock ring + target and
321955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // target drawable on either edge
322955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
323955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
324955a016922ea49f154d190b054a202559b41a4d3Jim Miller
325e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    /**
326e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     * This gets the suggested width accounting for the ring's scale factor.
327e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     */
328e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    protected int getScaledSuggestedMinimumWidth() {
329e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
330e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                + mMaxTargetWidth);
331e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
332e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
333e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    /**
334e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     * This gets the suggested height accounting for the ring's scale factor.
335e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     */
336e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    protected int getScaledSuggestedMinimumHeight() {
337e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
338e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                + mMaxTargetHeight);
339e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
340e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
341955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private int resolveMeasured(int measureSpec, int desired)
342955a016922ea49f154d190b054a202559b41a4d3Jim Miller    {
343955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int result = 0;
344955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int specSize = MeasureSpec.getSize(measureSpec);
345955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switch (MeasureSpec.getMode(measureSpec)) {
346955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MeasureSpec.UNSPECIFIED:
347955a016922ea49f154d190b054a202559b41a4d3Jim Miller                result = desired;
348955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
349955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MeasureSpec.AT_MOST:
350955a016922ea49f154d190b054a202559b41a4d3Jim Miller                result = Math.min(specSize, desired);
351955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
352955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MeasureSpec.EXACTLY:
353955a016922ea49f154d190b054a202559b41a4d3Jim Miller            default:
354955a016922ea49f154d190b054a202559b41a4d3Jim Miller                result = specSize;
355955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
356955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return result;
357955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
358955a016922ea49f154d190b054a202559b41a4d3Jim Miller
359955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void switchToState(int state, float x, float y) {
360955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switch (state) {
361955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_IDLE:
362955a016922ea49f154d190b054a202559b41a4d3Jim Miller                deactivateTargets();
363955a016922ea49f154d190b054a202559b41a4d3Jim Miller                hideGlow(0, 0, 0.0f, null);
364955a016922ea49f154d190b054a202559b41a4d3Jim Miller                startBackgroundAnimation(0, 0.0f);
365955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
366955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHandleDrawable.setAlpha(1.0f);
367955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
368955a016922ea49f154d190b054a202559b41a4d3Jim Miller
369955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_START:
370955a016922ea49f154d190b054a202559b41a4d3Jim Miller                startBackgroundAnimation(0, 0.0f);
371955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
372955a016922ea49f154d190b054a202559b41a4d3Jim Miller
373955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_FIRST_TOUCH:
374955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHandleDrawable.setAlpha(0.0f);
375955a016922ea49f154d190b054a202559b41a4d3Jim Miller                deactivateTargets();
376955a016922ea49f154d190b054a202559b41a4d3Jim Miller                showTargets(true);
377955a016922ea49f154d190b054a202559b41a4d3Jim Miller                startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f);
378955a016922ea49f154d190b054a202559b41a4d3Jim Miller                setGrabbedState(OnTriggerListener.CENTER_HANDLE);
379955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
380955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    announceTargets();
381955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
382955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
383955a016922ea49f154d190b054a202559b41a4d3Jim Miller
384955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_TRACKING:
385955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHandleDrawable.setAlpha(0.0f);
386955a016922ea49f154d190b054a202559b41a4d3Jim Miller                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 1.0f, null);
387955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
388955a016922ea49f154d190b054a202559b41a4d3Jim Miller
389955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_SNAP:
390955a016922ea49f154d190b054a202559b41a4d3Jim Miller                // TODO: Add transition states (see list_selector_background_transition.xml)
391955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHandleDrawable.setAlpha(0.0f);
392955a016922ea49f154d190b054a202559b41a4d3Jim Miller                showGlow(REVEAL_GLOW_DURATION , REVEAL_GLOW_DELAY, 0.0f, null);
393955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
394955a016922ea49f154d190b054a202559b41a4d3Jim Miller
395955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case STATE_FINISH:
396955a016922ea49f154d190b054a202559b41a4d3Jim Miller                doFinish();
397955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
398955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
399955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
400955a016922ea49f154d190b054a202559b41a4d3Jim Miller
401955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void showGlow(int duration, int delay, float finalAlpha,
402955a016922ea49f154d190b054a202559b41a4d3Jim Miller            AnimatorListener finishListener) {
403955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.cancel();
404955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
405955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "ease", Ease.Cubic.easeIn,
406955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "delay", delay,
407955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "alpha", finalAlpha,
408955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onUpdate", mUpdateListener,
409955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onComplete", finishListener));
410955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.start();
411955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
412955a016922ea49f154d190b054a202559b41a4d3Jim Miller
413955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void hideGlow(int duration, int delay, float finalAlpha,
414955a016922ea49f154d190b054a202559b41a4d3Jim Miller            AnimatorListener finishListener) {
415955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.cancel();
416955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.add(Tweener.to(mPointCloud.glowManager, duration,
417955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "ease", Ease.Quart.easeOut,
418955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "delay", delay,
419955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "alpha", finalAlpha,
420955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "x", 0.0f,
421955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "y", 0.0f,
422955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onUpdate", mUpdateListener,
423955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onComplete", finishListener));
424955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.start();
425955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
426955a016922ea49f154d190b054a202559b41a4d3Jim Miller
427955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void deactivateTargets() {
428955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int count = mTargetDrawables.size();
429955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < count; i++) {
430955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = mTargetDrawables.get(i);
431955a016922ea49f154d190b054a202559b41a4d3Jim Miller            target.setState(TargetDrawable.STATE_INACTIVE);
432955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
433955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mActiveTarget = -1;
434955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
435955a016922ea49f154d190b054a202559b41a4d3Jim Miller
436955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
437955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Dispatches a trigger event to listener. Ignored if a listener is not set.
438955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param whichTarget the target that was triggered.
439955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
440955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void dispatchTriggerEvent(int whichTarget) {
441955a016922ea49f154d190b054a202559b41a4d3Jim Miller        vibrate();
442955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mOnTriggerListener != null) {
443955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mOnTriggerListener.onTrigger(this, whichTarget);
444955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
445955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
446955a016922ea49f154d190b054a202559b41a4d3Jim Miller
447955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void dispatchOnFinishFinalAnimation() {
448955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mOnTriggerListener != null) {
449955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mOnTriggerListener.onFinishFinalAnimation();
450955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
451955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
452955a016922ea49f154d190b054a202559b41a4d3Jim Miller
453955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void doFinish() {
454955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int activeTarget = mActiveTarget;
455955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final boolean targetHit =  activeTarget != -1;
456955a016922ea49f154d190b054a202559b41a4d3Jim Miller
457955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (targetHit) {
458955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit);
459955a016922ea49f154d190b054a202559b41a4d3Jim Miller
460955a016922ea49f154d190b054a202559b41a4d3Jim Miller            highlightSelected(activeTarget);
461955a016922ea49f154d190b054a202559b41a4d3Jim Miller
462955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // Inform listener of any active targets.  Typically only one will be active.
463955a016922ea49f154d190b054a202559b41a4d3Jim Miller            hideGlow(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener);
464955a016922ea49f154d190b054a202559b41a4d3Jim Miller            dispatchTriggerEvent(activeTarget);
465955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (!mAlwaysTrackFinger) {
466955a016922ea49f154d190b054a202559b41a4d3Jim Miller                // Force ring and targets to finish animation to final expanded state
467955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mTargetAnimations.stop();
468955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
469955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
470955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // Animate handle back to the center based on current state.
471955a016922ea49f154d190b054a202559b41a4d3Jim Miller            hideGlow(HIDE_ANIMATION_DURATION, 0, 0.0f, mResetListenerWithPing);
472955a016922ea49f154d190b054a202559b41a4d3Jim Miller            hideTargets(true, false);
473955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
474955a016922ea49f154d190b054a202559b41a4d3Jim Miller
475955a016922ea49f154d190b054a202559b41a4d3Jim Miller        setGrabbedState(OnTriggerListener.NO_HANDLE);
476955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
477955a016922ea49f154d190b054a202559b41a4d3Jim Miller
478955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void highlightSelected(int activeTarget) {
479955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Highlight the given target and fade others
480955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE);
481955a016922ea49f154d190b054a202559b41a4d3Jim Miller        hideUnselected(activeTarget);
482955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
483955a016922ea49f154d190b054a202559b41a4d3Jim Miller
484955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void hideUnselected(int active) {
485955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < mTargetDrawables.size(); i++) {
486955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (i != active) {
487955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mTargetDrawables.get(i).setAlpha(0.0f);
488955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
489955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
490955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
491955a016922ea49f154d190b054a202559b41a4d3Jim Miller
492955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void hideTargets(boolean animate, boolean expanded) {
493955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.cancel();
494955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Note: these animations should complete at the same time so that we can swap out
495955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // the target assets asynchronously from the setTargetResources() call.
496955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mAnimatingTargets = animate;
497955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int duration = animate ? HIDE_ANIMATION_DURATION : 0;
498955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int delay = animate ? HIDE_ANIMATION_DELAY : 0;
499955a016922ea49f154d190b054a202559b41a4d3Jim Miller
500a7da8afe6dc866786acab8b06524d0079caa3fd7Jim Miller        final float targetScale = expanded ?
5015892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED;
502955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int length = mTargetDrawables.size();
503955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final TimeInterpolator interpolator = Ease.Cubic.easeOut;
504955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < length; i++) {
505955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = mTargetDrawables.get(i);
506955a016922ea49f154d190b054a202559b41a4d3Jim Miller            target.setState(TargetDrawable.STATE_INACTIVE);
507955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mTargetAnimations.add(Tweener.to(target, duration,
508955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "ease", interpolator,
509955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "alpha", 0.0f,
510955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "scaleX", targetScale,
511955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "scaleY", targetScale,
512955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "delay", delay,
513955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "onUpdate", mUpdateListener));
514955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
515955a016922ea49f154d190b054a202559b41a4d3Jim Miller
516e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        float ringScaleTarget = expanded ?
5175892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
518e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        ringScaleTarget *= mRingScaleFactor;
519955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
520955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "ease", interpolator,
521955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "alpha", 0.0f,
522955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "scaleX", ringScaleTarget,
523955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "scaleY", ringScaleTarget,
524955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "delay", delay,
525955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onUpdate", mUpdateListener,
526955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onComplete", mTargetUpdateListener));
527955a016922ea49f154d190b054a202559b41a4d3Jim Miller
528955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.start();
529955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
530955a016922ea49f154d190b054a202559b41a4d3Jim Miller
531955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void showTargets(boolean animate) {
532955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.stop();
533955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mAnimatingTargets = animate;
534955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int delay = animate ? SHOW_ANIMATION_DELAY : 0;
535955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int duration = animate ? SHOW_ANIMATION_DURATION : 0;
536955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int length = mTargetDrawables.size();
537955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < length; i++) {
538955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = mTargetDrawables.get(i);
539955a016922ea49f154d190b054a202559b41a4d3Jim Miller            target.setState(TargetDrawable.STATE_INACTIVE);
540955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mTargetAnimations.add(Tweener.to(target, duration,
541955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "ease", Ease.Cubic.easeOut,
542955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "alpha", 1.0f,
543955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "scaleX", 1.0f,
544955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "scaleY", 1.0f,
545955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "delay", delay,
546955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "onUpdate", mUpdateListener));
547955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
548e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
549e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
550955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.add(Tweener.to(mOuterRing, duration,
551955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "ease", Ease.Cubic.easeOut,
552955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "alpha", 1.0f,
553e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                "scaleX", ringScale,
554e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                "scaleY", ringScale,
555955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "delay", delay,
556955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onUpdate", mUpdateListener,
557955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onComplete", mTargetUpdateListener));
558955a016922ea49f154d190b054a202559b41a4d3Jim Miller
559955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.start();
560955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
561955a016922ea49f154d190b054a202559b41a4d3Jim Miller
562955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void vibrate() {
563723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey        final boolean hapticEnabled = Settings.System.getIntForUser(
564723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey                mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1,
565723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey                UserHandle.USER_CURRENT) != 0;
566723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey        if (mVibrator != null && hapticEnabled) {
567955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mVibrator.vibrate(mVibrationDuration);
568955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
569955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
570955a016922ea49f154d190b054a202559b41a4d3Jim Miller
571955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) {
572955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Resources res = getContext().getResources();
573955a016922ea49f154d190b054a202559b41a4d3Jim Miller        TypedArray array = res.obtainTypedArray(resourceId);
574955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int count = array.length();
575955a016922ea49f154d190b054a202559b41a4d3Jim Miller        ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count);
576955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < count; i++) {
577955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TypedValue value = array.peekValue(i);
578955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0);
579955a016922ea49f154d190b054a202559b41a4d3Jim Miller            drawables.add(target);
580955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
581955a016922ea49f154d190b054a202559b41a4d3Jim Miller        array.recycle();
582955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return drawables;
583955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
584955a016922ea49f154d190b054a202559b41a4d3Jim Miller
585955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void internalSetTargetResources(int resourceId) {
586955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final ArrayList<TargetDrawable> targets = loadDrawableArray(resourceId);
587955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetDrawables = targets;
588955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetResourceId = resourceId;
589955a016922ea49f154d190b054a202559b41a4d3Jim Miller
590955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int maxWidth = mHandleDrawable.getWidth();
591955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int maxHeight = mHandleDrawable.getHeight();
592955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int count = targets.size();
593955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < count; i++) {
594955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = targets.get(i);
595955a016922ea49f154d190b054a202559b41a4d3Jim Miller            maxWidth = Math.max(maxWidth, target.getWidth());
596955a016922ea49f154d190b054a202559b41a4d3Jim Miller            maxHeight = Math.max(maxHeight, target.getHeight());
597955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
598955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) {
599955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mMaxTargetWidth = maxWidth;
600955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mMaxTargetHeight = maxHeight;
601955a016922ea49f154d190b054a202559b41a4d3Jim Miller            requestLayout(); // required to resize layout and call updateTargetPositions()
602955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
603955a016922ea49f154d190b054a202559b41a4d3Jim Miller            updateTargetPositions(mWaveCenterX, mWaveCenterY);
604955a016922ea49f154d190b054a202559b41a4d3Jim Miller            updatePointCloudPosition(mWaveCenterX, mWaveCenterY);
605955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
606955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
607955a016922ea49f154d190b054a202559b41a4d3Jim Miller
608955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
609955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Loads an array of drawables from the given resourceId.
610955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
611955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param resourceId
612955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
613955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setTargetResources(int resourceId) {
614955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mAnimatingTargets) {
615955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // postpone this change until we return to the initial state
616955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mNewTargetResources = resourceId;
617955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
618955a016922ea49f154d190b054a202559b41a4d3Jim Miller            internalSetTargetResources(resourceId);
619955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
620955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
621955a016922ea49f154d190b054a202559b41a4d3Jim Miller
622955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public int getTargetResourceId() {
623955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return mTargetResourceId;
624955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
625955a016922ea49f154d190b054a202559b41a4d3Jim Miller
626955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
627955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Sets the resource id specifying the target descriptions for accessibility.
628955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
629955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param resourceId The resource id.
630955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
631955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setTargetDescriptionsResourceId(int resourceId) {
632955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetDescriptionsResourceId = resourceId;
633955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mTargetDescriptions != null) {
634955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mTargetDescriptions.clear();
635955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
636955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
637955a016922ea49f154d190b054a202559b41a4d3Jim Miller
638955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
639955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Gets the resource id specifying the target descriptions for accessibility.
640955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
641955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @return The resource id.
642955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
643955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public int getTargetDescriptionsResourceId() {
644955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return mTargetDescriptionsResourceId;
645955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
646955a016922ea49f154d190b054a202559b41a4d3Jim Miller
647955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
648955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Sets the resource id specifying the target direction descriptions for accessibility.
649955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
650955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param resourceId The resource id.
651955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
652955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setDirectionDescriptionsResourceId(int resourceId) {
653955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mDirectionDescriptionsResourceId = resourceId;
654955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mDirectionDescriptions != null) {
655955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mDirectionDescriptions.clear();
656955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
657955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
658955a016922ea49f154d190b054a202559b41a4d3Jim Miller
659955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
660955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Gets the resource id specifying the target direction descriptions.
661955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
662955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @return The resource id.
663955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
664955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public int getDirectionDescriptionsResourceId() {
665955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return mDirectionDescriptionsResourceId;
666955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
667955a016922ea49f154d190b054a202559b41a4d3Jim Miller
668955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
669955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Enable or disable vibrate on touch.
670955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
671955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param enabled
672955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
673955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setVibrateEnabled(boolean enabled) {
674955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (enabled && mVibrator == null) {
675955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE);
676955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
677955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mVibrator = null;
678955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
679955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
680955a016922ea49f154d190b054a202559b41a4d3Jim Miller
681955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
682955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Starts wave animation.
683955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
684955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
685955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void ping() {
686955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mFeedbackCount > 0) {
6875892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            boolean doWaveAnimation = true;
6885892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            final AnimationBundle waveAnimations = mWaveAnimations;
6895892e2ec253465a46b346fc813a21b412ae85e2eJim Miller
6905892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            // Don't do a wave if there's already one in progress
6915892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            if (waveAnimations.size() > 0 && waveAnimations.get(0).animator.isRunning()) {
6925892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                long t = waveAnimations.get(0).animator.getCurrentPlayTime();
6935892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                if (t < WAVE_ANIMATION_DURATION/2) {
6945892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                    doWaveAnimation = false;
6955892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                }
6965892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            }
6975892e2ec253465a46b346fc813a21b412ae85e2eJim Miller
6985892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            if (doWaveAnimation) {
6995892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                startWaveAnimation();
7005892e2ec253465a46b346fc813a21b412ae85e2eJim Miller            }
701955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
702955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
703955a016922ea49f154d190b054a202559b41a4d3Jim Miller
704955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void stopAndHideWaveAnimation() {
705955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.cancel();
706955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.waveManager.setAlpha(0.0f);
707955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
708955a016922ea49f154d190b054a202559b41a4d3Jim Miller
709955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void startWaveAnimation() {
710955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.cancel();
711955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.waveManager.setAlpha(1.0f);
712955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.waveManager.setRadius(mHandleDrawable.getWidth()/2.0f);
713955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.add(Tweener.to(mPointCloud.waveManager, WAVE_ANIMATION_DURATION,
7145892e2ec253465a46b346fc813a21b412ae85e2eJim Miller                "ease", Ease.Quad.easeOut,
715955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "delay", 0,
716955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "radius", 2.0f * mOuterRadius,
717955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onUpdate", mUpdateListener,
718955a016922ea49f154d190b054a202559b41a4d3Jim Miller                "onComplete",
719955a016922ea49f154d190b054a202559b41a4d3Jim Miller                new AnimatorListenerAdapter() {
720955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    public void onAnimationEnd(Animator animator) {
721955a016922ea49f154d190b054a202559b41a4d3Jim Miller                        mPointCloud.waveManager.setRadius(0.0f);
722955a016922ea49f154d190b054a202559b41a4d3Jim Miller                        mPointCloud.waveManager.setAlpha(0.0f);
723955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    }
724955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }));
725955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveAnimations.start();
726955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
727955a016922ea49f154d190b054a202559b41a4d3Jim Miller
728955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
729955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Resets the widget to default state and cancels all animation. If animate is 'true', will
730955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * animate objects into place. Otherwise, objects will snap back to place.
731955a016922ea49f154d190b054a202559b41a4d3Jim Miller     *
732955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param animate
733955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
734955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void reset(boolean animate) {
735955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mGlowAnimations.stop();
736955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mTargetAnimations.stop();
737955a016922ea49f154d190b054a202559b41a4d3Jim Miller        startBackgroundAnimation(0, 0.0f);
738955a016922ea49f154d190b054a202559b41a4d3Jim Miller        stopAndHideWaveAnimation();
739955a016922ea49f154d190b054a202559b41a4d3Jim Miller        hideTargets(animate, false);
740a592d224732a06c3776a41df37a52bac4c64a654Jim Miller        hideGlow(0, 0, 0.0f, null);
741955a016922ea49f154d190b054a202559b41a4d3Jim Miller        Tweener.reset();
742955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
743955a016922ea49f154d190b054a202559b41a4d3Jim Miller
744955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void startBackgroundAnimation(int duration, float alpha) {
745955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final Drawable background = getBackground();
746955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mAlwaysTrackFinger && background != null) {
747955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mBackgroundAnimator != null) {
748955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mBackgroundAnimator.animator.cancel();
749955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
750955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mBackgroundAnimator = Tweener.to(background, duration,
751955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "ease", Ease.Cubic.easeIn,
752955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "alpha", (int)(255.0f * alpha),
753955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    "delay", SHOW_ANIMATION_DELAY);
754955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mBackgroundAnimator.animator.start();
755955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
756955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
757955a016922ea49f154d190b054a202559b41a4d3Jim Miller
758955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
759955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public boolean onTouchEvent(MotionEvent event) {
760b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        final int action = event.getActionMasked();
761955a016922ea49f154d190b054a202559b41a4d3Jim Miller        boolean handled = false;
762955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switch (action) {
763b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            case MotionEvent.ACTION_POINTER_DOWN:
764955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MotionEvent.ACTION_DOWN:
765955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (DEBUG) Log.v(TAG, "*** DOWN ***");
766955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleDown(event);
767955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleMove(event);
768955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handled = true;
769955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
770955a016922ea49f154d190b054a202559b41a4d3Jim Miller
771955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MotionEvent.ACTION_MOVE:
772955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (DEBUG) Log.v(TAG, "*** MOVE ***");
773955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleMove(event);
774955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handled = true;
775955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
776955a016922ea49f154d190b054a202559b41a4d3Jim Miller
777b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            case MotionEvent.ACTION_POINTER_UP:
778955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MotionEvent.ACTION_UP:
779955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (DEBUG) Log.v(TAG, "*** UP ***");
780955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleMove(event);
781955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleUp(event);
782955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handled = true;
783955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
784955a016922ea49f154d190b054a202559b41a4d3Jim Miller
785955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case MotionEvent.ACTION_CANCEL:
786955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (DEBUG) Log.v(TAG, "*** CANCEL ***");
787955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleMove(event);
788955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handleCancel(event);
789955a016922ea49f154d190b054a202559b41a4d3Jim Miller                handled = true;
790955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
791b499884ba1fa9577363524d1f28100627926d3b6Jim Miller
792955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
793955a016922ea49f154d190b054a202559b41a4d3Jim Miller        invalidate();
794955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return handled ? true : super.onTouchEvent(event);
795955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
796955a016922ea49f154d190b054a202559b41a4d3Jim Miller
797955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void updateGlowPosition(float x, float y) {
798f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        float dx = x - mOuterRing.getX();
799f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        float dy = y - mOuterRing.getY();
800f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        dx *= 1f / mRingScaleFactor;
801f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        dy *= 1f / mRingScaleFactor;
802f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
803f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
804955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
805955a016922ea49f154d190b054a202559b41a4d3Jim Miller
806955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void handleDown(MotionEvent event) {
807b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        int actionIndex = event.getActionIndex();
808b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        float eventX = event.getX(actionIndex);
809b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        float eventY = event.getY(actionIndex);
810955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switchToState(STATE_START, eventX, eventY);
811955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (!trySwitchToFirstTouchState(eventX, eventY)) {
812955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mDragging = false;
813955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
814b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            mPointerId = event.getPointerId(actionIndex);
815955a016922ea49f154d190b054a202559b41a4d3Jim Miller            updateGlowPosition(eventX, eventY);
816955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
817955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
818955a016922ea49f154d190b054a202559b41a4d3Jim Miller
819955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void handleUp(MotionEvent event) {
820955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE");
821b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        int actionIndex = event.getActionIndex();
822b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        if (event.getPointerId(actionIndex) == mPointerId) {
823b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
824b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        }
825955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
826955a016922ea49f154d190b054a202559b41a4d3Jim Miller
827955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void handleCancel(MotionEvent event) {
828955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL");
829955a016922ea49f154d190b054a202559b41a4d3Jim Miller
830245b453733d0b611960844d939e0013f285a5a9aJim Miller        // Drop the active target if canceled.
831245b453733d0b611960844d939e0013f285a5a9aJim Miller        mActiveTarget = -1;
832955a016922ea49f154d190b054a202559b41a4d3Jim Miller
833b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        int actionIndex = event.findPointerIndex(mPointerId);
834b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        actionIndex = actionIndex == -1 ? 0 : actionIndex;
835b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        switchToState(STATE_FINISH, event.getX(actionIndex), event.getY(actionIndex));
836955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
837955a016922ea49f154d190b054a202559b41a4d3Jim Miller
838955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void handleMove(MotionEvent event) {
839955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int activeTarget = -1;
840955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int historySize = event.getHistorySize();
841955a016922ea49f154d190b054a202559b41a4d3Jim Miller        ArrayList<TargetDrawable> targets = mTargetDrawables;
842955a016922ea49f154d190b054a202559b41a4d3Jim Miller        int ntargets = targets.size();
843955a016922ea49f154d190b054a202559b41a4d3Jim Miller        float x = 0.0f;
844955a016922ea49f154d190b054a202559b41a4d3Jim Miller        float y = 0.0f;
845f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        float activeAngle = 0.0f;
846b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        int actionIndex = event.findPointerIndex(mPointerId);
847b499884ba1fa9577363524d1f28100627926d3b6Jim Miller
848b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        if (actionIndex == -1) {
849b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            return;  // no data for this pointer
850b499884ba1fa9577363524d1f28100627926d3b6Jim Miller        }
851b499884ba1fa9577363524d1f28100627926d3b6Jim Miller
852955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int k = 0; k < historySize + 1; k++) {
853b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            float eventX = k < historySize ? event.getHistoricalX(actionIndex, k)
854b499884ba1fa9577363524d1f28100627926d3b6Jim Miller                    : event.getX(actionIndex);
855b499884ba1fa9577363524d1f28100627926d3b6Jim Miller            float eventY = k < historySize ? event.getHistoricalY(actionIndex, k)
856b499884ba1fa9577363524d1f28100627926d3b6Jim Miller                    : event.getY(actionIndex);
857955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // tx and ty are relative to wave center
858955a016922ea49f154d190b054a202559b41a4d3Jim Miller            float tx = eventX - mWaveCenterX;
859955a016922ea49f154d190b054a202559b41a4d3Jim Miller            float ty = eventY - mWaveCenterY;
860955a016922ea49f154d190b054a202559b41a4d3Jim Miller            float touchRadius = (float) Math.sqrt(dist2(tx, ty));
861955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f;
862955a016922ea49f154d190b054a202559b41a4d3Jim Miller            float limitX = tx * scale;
863955a016922ea49f154d190b054a202559b41a4d3Jim Miller            float limitY = ty * scale;
86453f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka            double angleRad = Math.atan2(-ty, tx);
865955a016922ea49f154d190b054a202559b41a4d3Jim Miller
866955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (!mDragging) {
867955a016922ea49f154d190b054a202559b41a4d3Jim Miller                trySwitchToFirstTouchState(eventX, eventY);
868955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
869955a016922ea49f154d190b054a202559b41a4d3Jim Miller
870955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mDragging) {
87153f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                // For multiple targets, snap to the one that matches
872f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen                final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
87353f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                final float snapDistance2 = snapRadius * snapRadius;
87453f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                // Find first target in range
87553f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                for (int i = 0; i < ntargets; i++) {
87653f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                    TargetDrawable target = targets.get(i);
87753f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka
878f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                    double targetMinRad = mFirstItemOffset + (i - 0.5) * 2 * Math.PI / ntargets;
879f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                    double targetMaxRad = mFirstItemOffset + (i + 0.5) * 2 * Math.PI / ntargets;
88053f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                    if (target.isEnabled()) {
88153f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                        boolean angleMatches =
88253f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                            (angleRad > targetMinRad && angleRad <= targetMaxRad) ||
88353f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                            (angleRad + 2 * Math.PI > targetMinRad &&
884f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                             angleRad + 2 * Math.PI <= targetMaxRad) ||
885f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                            (angleRad - 2 * Math.PI > targetMinRad &&
886f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                             angleRad - 2 * Math.PI <= targetMaxRad);
88753f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka                        if (angleMatches && (dist2(tx, ty) > snapDistance2)) {
888955a016922ea49f154d190b054a202559b41a4d3Jim Miller                            activeTarget = i;
889f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                            activeAngle = (float) -angleRad;
890955a016922ea49f154d190b054a202559b41a4d3Jim Miller                        }
891955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    }
892955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
893955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
894955a016922ea49f154d190b054a202559b41a4d3Jim Miller            x = limitX;
895955a016922ea49f154d190b054a202559b41a4d3Jim Miller            y = limitY;
896955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
897955a016922ea49f154d190b054a202559b41a4d3Jim Miller
898955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (!mDragging) {
899955a016922ea49f154d190b054a202559b41a4d3Jim Miller            return;
900955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
901955a016922ea49f154d190b054a202559b41a4d3Jim Miller
902955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (activeTarget != -1) {
903955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switchToState(STATE_SNAP, x,y);
90453f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka            updateGlowPosition(x, y);
905955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
906955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switchToState(STATE_TRACKING, x, y);
907955a016922ea49f154d190b054a202559b41a4d3Jim Miller            updateGlowPosition(x, y);
908955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
909955a016922ea49f154d190b054a202559b41a4d3Jim Miller
910955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mActiveTarget != activeTarget) {
911955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // Defocus the old target
912955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mActiveTarget != -1) {
913955a016922ea49f154d190b054a202559b41a4d3Jim Miller                TargetDrawable target = targets.get(mActiveTarget);
914955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
915955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    target.setState(TargetDrawable.STATE_INACTIVE);
916955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
917f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                if (mMagneticTargets) {
918f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                    updateTargetPosition(mActiveTarget, mWaveCenterX, mWaveCenterY);
919f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                }
920955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
921955a016922ea49f154d190b054a202559b41a4d3Jim Miller            // Focus the new target
922955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (activeTarget != -1) {
923955a016922ea49f154d190b054a202559b41a4d3Jim Miller                TargetDrawable target = targets.get(activeTarget);
924955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (target.hasState(TargetDrawable.STATE_FOCUSED)) {
925955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    target.setState(TargetDrawable.STATE_FOCUSED);
926955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
927f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                if (mMagneticTargets) {
928f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                    updateTargetPosition(activeTarget, mWaveCenterX, mWaveCenterY, activeAngle);
929f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                }
930955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (AccessibilityManager.getInstance(mContext).isEnabled()) {
931955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    String targetContentDescription = getTargetDescription(activeTarget);
93278bfb9829561876137c62ca1fff13760bfc77472alanv                    announceForAccessibility(targetContentDescription);
933955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
934955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
935955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
936955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mActiveTarget = activeTarget;
937955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
938955a016922ea49f154d190b054a202559b41a4d3Jim Miller
939955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
940955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public boolean onHoverEvent(MotionEvent event) {
941955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) {
942955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final int action = event.getAction();
943955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switch (action) {
944955a016922ea49f154d190b054a202559b41a4d3Jim Miller                case MotionEvent.ACTION_HOVER_ENTER:
945955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    event.setAction(MotionEvent.ACTION_DOWN);
946955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    break;
947955a016922ea49f154d190b054a202559b41a4d3Jim Miller                case MotionEvent.ACTION_HOVER_MOVE:
948955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    event.setAction(MotionEvent.ACTION_MOVE);
949955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    break;
950955a016922ea49f154d190b054a202559b41a4d3Jim Miller                case MotionEvent.ACTION_HOVER_EXIT:
951955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    event.setAction(MotionEvent.ACTION_UP);
952955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    break;
953955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
954955a016922ea49f154d190b054a202559b41a4d3Jim Miller            onTouchEvent(event);
955955a016922ea49f154d190b054a202559b41a4d3Jim Miller            event.setAction(action);
956955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
9577ce0c13c0e5a1413ecde1a5c36510d22979f9012Svetoslav Ganov        super.onHoverEvent(event);
9587ce0c13c0e5a1413ecde1a5c36510d22979f9012Svetoslav Ganov        return true;
959955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
960955a016922ea49f154d190b054a202559b41a4d3Jim Miller
961955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
962955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Sets the current grabbed state, and dispatches a grabbed state change
963955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * event to our listener.
964955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
965955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void setGrabbedState(int newState) {
966955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (newState != mGrabbedState) {
967955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (newState != OnTriggerListener.NO_HANDLE) {
968955a016922ea49f154d190b054a202559b41a4d3Jim Miller                vibrate();
969955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
970955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mGrabbedState = newState;
971955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mOnTriggerListener != null) {
972955a016922ea49f154d190b054a202559b41a4d3Jim Miller                if (newState == OnTriggerListener.NO_HANDLE) {
973955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE);
974955a016922ea49f154d190b054a202559b41a4d3Jim Miller                } else {
975955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE);
976955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
977955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mOnTriggerListener.onGrabbedStateChange(this, newState);
978955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
979955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
980955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
981955a016922ea49f154d190b054a202559b41a4d3Jim Miller
982955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean trySwitchToFirstTouchState(float x, float y) {
983955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final float tx = x - mWaveCenterX;
984955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final float ty = y - mWaveCenterY;
985955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledGlowRadiusSquared()) {
986955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (DEBUG) Log.v(TAG, "** Handle HIT");
987955a016922ea49f154d190b054a202559b41a4d3Jim Miller            switchToState(STATE_FIRST_TOUCH, x, y);
988955a016922ea49f154d190b054a202559b41a4d3Jim Miller            updateGlowPosition(tx, ty);
989955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mDragging = true;
990955a016922ea49f154d190b054a202559b41a4d3Jim Miller            return true;
991955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
992955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return false;
993955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
994955a016922ea49f154d190b054a202559b41a4d3Jim Miller
995955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void assignDefaultsIfNeeded() {
996955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mOuterRadius == 0.0f) {
997955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f;
998955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
999955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mSnapMargin == 0.0f) {
1000955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
1001955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics());
1002955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1003955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mInnerRadius == 0.0f) {
1004955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mInnerRadius = mHandleDrawable.getWidth() / 10.0f;
1005955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1006955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1007955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1008955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void computeInsets(int dx, int dy) {
1009e56ffdc7b31b0937628609cc3bbaa15879023569Fabrice Di Meglio        final int layoutDirection = getLayoutDirection();
1010955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
1011955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1012955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1013955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.LEFT:
1014955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHorizontalInset = 0;
1015955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1016955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.RIGHT:
1017955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHorizontalInset = dx;
1018955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1019955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.CENTER_HORIZONTAL:
1020955a016922ea49f154d190b054a202559b41a4d3Jim Miller            default:
1021955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mHorizontalInset = dx / 2;
1022955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1023955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1024955a016922ea49f154d190b054a202559b41a4d3Jim Miller        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1025955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.TOP:
1026955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mVerticalInset = 0;
1027955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1028955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.BOTTOM:
1029955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mVerticalInset = dy;
1030955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1031955a016922ea49f154d190b054a202559b41a4d3Jim Miller            case Gravity.CENTER_VERTICAL:
1032955a016922ea49f154d190b054a202559b41a4d3Jim Miller            default:
1033955a016922ea49f154d190b054a202559b41a4d3Jim Miller                mVerticalInset = dy / 2;
1034955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break;
1035955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1036955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1037955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1038e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    /**
1039e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     * Given the desired width and height of the ring and the allocated width and height, compute
1040e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     * how much we need to scale the ring.
1041e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen     */
1042e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    private float computeScaleFactor(int desiredWidth, int desiredHeight,
1043e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            int actualWidth, int actualHeight) {
1044f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen
1045f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        // Return unity if scaling is not allowed.
1046f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen        if (!mAllowScaling) return 1f;
1047f988bdfbaebe8ee94f6459f3c8d964467f2c62c9Adam Cohen
1048e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final int layoutDirection = getLayoutDirection();
1049e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
1050e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1051e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        float scaleX = 1f;
1052e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        float scaleY = 1f;
1053e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1054e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        // We use the gravity as a cue for whether we want to scale on a particular axis.
1055e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
1056e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        // we only scale to fit vertically if we're not pinned to the top or bottom. In these
1057e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        // cases, we want the ring to hang off the side or top/bottom, respectively.
1058e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
1059e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.LEFT:
1060e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.RIGHT:
1061e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                break;
1062e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.CENTER_HORIZONTAL:
1063e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            default:
1064e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                if (desiredWidth > actualWidth) {
1065e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                    scaleX = (1f * actualWidth - mMaxTargetWidth) /
1066e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                            (desiredWidth - mMaxTargetWidth);
1067e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                }
1068e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                break;
1069e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        }
1070e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
1071e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.TOP:
1072e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.BOTTOM:
1073e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                break;
1074e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            case Gravity.CENTER_VERTICAL:
1075e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            default:
1076e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                if (desiredHeight > actualHeight) {
1077e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                    scaleY = (1f * actualHeight - mMaxTargetHeight) /
1078e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                            (desiredHeight - mMaxTargetHeight);
1079e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                }
1080e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                break;
1081e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        }
1082e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        return Math.min(scaleX, scaleY);
1083e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
1084e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1085e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    @Override
1086e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
1087e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final int minimumWidth = getSuggestedMinimumWidth();
1088e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final int minimumHeight = getSuggestedMinimumHeight();
1089e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
1090e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
1091e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1092e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
1093e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen                computedWidth, computedHeight);
1094e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1095e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        int scaledWidth = getScaledSuggestedMinimumWidth();
1096e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        int scaledHeight = getScaledSuggestedMinimumHeight();
1097e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1098e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
1099e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        setMeasuredDimension(computedWidth, computedHeight);
1100e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
1101e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1102e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    private float getRingWidth() {
1103e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
1104e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
1105e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1106e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    private float getRingHeight() {
1107e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
1108e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen    }
1109e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1110955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
1111955a016922ea49f154d190b054a202559b41a4d3Jim Miller    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
1112955a016922ea49f154d190b054a202559b41a4d3Jim Miller        super.onLayout(changed, left, top, right, bottom);
1113955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int width = right - left;
1114955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int height = bottom - top;
1115955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1116955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // Target placement width/height. This puts the targets on the greater of the ring
1117955a016922ea49f154d190b054a202559b41a4d3Jim Miller        // width or the specified outer radius.
1118e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final float placementWidth = getRingWidth();
1119e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final float placementHeight = getRingHeight();
1120955a016922ea49f154d190b054a202559b41a4d3Jim Miller        float newWaveCenterX = mHorizontalInset
1121955a016922ea49f154d190b054a202559b41a4d3Jim Miller                + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
1122955a016922ea49f154d190b054a202559b41a4d3Jim Miller        float newWaveCenterY = mVerticalInset
1123955a016922ea49f154d190b054a202559b41a4d3Jim Miller                + Math.max(height, + mMaxTargetHeight + placementHeight) / 2;
1124955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1125955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mInitialLayout) {
1126955a016922ea49f154d190b054a202559b41a4d3Jim Miller            stopAndHideWaveAnimation();
1127955a016922ea49f154d190b054a202559b41a4d3Jim Miller            hideTargets(false, false);
1128955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mInitialLayout = false;
1129955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1130955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1131955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOuterRing.setPositionX(newWaveCenterX);
1132955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOuterRing.setPositionY(newWaveCenterY);
1133955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1134e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        mPointCloud.setScale(mRingScaleFactor);
1135e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen
1136955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mHandleDrawable.setPositionX(newWaveCenterX);
1137955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mHandleDrawable.setPositionY(newWaveCenterY);
1138955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1139955a016922ea49f154d190b054a202559b41a4d3Jim Miller        updateTargetPositions(newWaveCenterX, newWaveCenterY);
1140955a016922ea49f154d190b054a202559b41a4d3Jim Miller        updatePointCloudPosition(newWaveCenterX, newWaveCenterY);
1141955a016922ea49f154d190b054a202559b41a4d3Jim Miller        updateGlowPosition(newWaveCenterX, newWaveCenterY);
1142955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1143955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveCenterX = newWaveCenterX;
1144955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mWaveCenterY = newWaveCenterY;
1145955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1146955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (DEBUG) dump();
1147955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1148955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1149f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private void updateTargetPosition(int i, float centerX, float centerY) {
1150f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        final float angle = getAngle(getSliceAngle(), i);
1151f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        updateTargetPosition(i, centerX, centerY, angle);
1152f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    }
1153f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren
1154f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private void updateTargetPosition(int i, float centerX, float centerY, float angle) {
1155e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final float placementRadiusX = getRingWidth() / 2;
1156e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen        final float placementRadiusY = getRingHeight() / 2;
1157f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        if (i >= 0) {
1158f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren            ArrayList<TargetDrawable> targets = mTargetDrawables;
1159955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final TargetDrawable targetIcon = targets.get(i);
1160955a016922ea49f154d190b054a202559b41a4d3Jim Miller            targetIcon.setPositionX(centerX);
1161955a016922ea49f154d190b054a202559b41a4d3Jim Miller            targetIcon.setPositionY(centerY);
1162e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            targetIcon.setX(placementRadiusX * (float) Math.cos(angle));
1163e41dd0f195d77329bdf686af1dcbfd92bdbb5373Adam Cohen            targetIcon.setY(placementRadiusY * (float) Math.sin(angle));
1164955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1165955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1166955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1167f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private void updateTargetPositions(float centerX, float centerY) {
1168f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        updateTargetPositions(centerX, centerY, false);
1169f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    }
1170f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren
1171f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private void updateTargetPositions(float centerX, float centerY, boolean skipActive) {
1172f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        final int size = mTargetDrawables.size();
1173f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        final float alpha = getSliceAngle();
1174f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        // Reposition the target drawables if the view changed.
1175f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        for (int i = 0; i < size; i++) {
1176f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren            if (!skipActive || i != mActiveTarget) {
1177f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren                updateTargetPosition(i, centerX, centerY, getAngle(alpha, i));
1178f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren            }
1179f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        }
1180f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    }
1181f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren
1182f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private float getAngle(float alpha, int i) {
1183f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        return mFirstItemOffset + alpha * i;
1184f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    }
1185f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren
1186f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    private float getSliceAngle() {
1187f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren        return (float) (-2.0f * Math.PI / mTargetDrawables.size());
1188f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren    }
1189f0ee5b813597e97123d518b227c555c0ea0c0f29Chris Wren
1190955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void updatePointCloudPosition(float centerX, float centerY) {
1191955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.setCenter(centerX, centerY);
1192955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1193955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1194955a016922ea49f154d190b054a202559b41a4d3Jim Miller    @Override
1195955a016922ea49f154d190b054a202559b41a4d3Jim Miller    protected void onDraw(Canvas canvas) {
1196955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mPointCloud.draw(canvas);
1197955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOuterRing.draw(canvas);
1198955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int ntargets = mTargetDrawables.size();
1199955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < ntargets; i++) {
1200955a016922ea49f154d190b054a202559b41a4d3Jim Miller            TargetDrawable target = mTargetDrawables.get(i);
1201955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (target != null) {
1202955a016922ea49f154d190b054a202559b41a4d3Jim Miller                target.draw(canvas);
1203955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1204955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1205955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mHandleDrawable.draw(canvas);
1206955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1207955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1208955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setOnTriggerListener(OnTriggerListener listener) {
1209955a016922ea49f154d190b054a202559b41a4d3Jim Miller        mOnTriggerListener = listener;
1210955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1211955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1212955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float square(float d) {
1213955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return d * d;
1214955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1215955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1216955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float dist2(float dx, float dy) {
1217955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return dx*dx + dy*dy;
1218955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1219955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1220955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private float getScaledGlowRadiusSquared() {
1221955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final float scaledTapRadius;
1222955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (AccessibilityManager.getInstance(mContext).isEnabled()) {
1223955a016922ea49f154d190b054a202559b41a4d3Jim Miller            scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mGlowRadius;
1224955a016922ea49f154d190b054a202559b41a4d3Jim Miller        } else {
1225955a016922ea49f154d190b054a202559b41a4d3Jim Miller            scaledTapRadius = mGlowRadius;
1226955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1227955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return square(scaledTapRadius);
1228955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1229955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1230955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private void announceTargets() {
1231955a016922ea49f154d190b054a202559b41a4d3Jim Miller        StringBuilder utterance = new StringBuilder();
1232955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int targetCount = mTargetDrawables.size();
1233955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < targetCount; i++) {
1234955a016922ea49f154d190b054a202559b41a4d3Jim Miller            String targetDescription = getTargetDescription(i);
1235955a016922ea49f154d190b054a202559b41a4d3Jim Miller            String directionDescription = getDirectionDescription(i);
1236955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (!TextUtils.isEmpty(targetDescription)
1237955a016922ea49f154d190b054a202559b41a4d3Jim Miller                    && !TextUtils.isEmpty(directionDescription)) {
1238955a016922ea49f154d190b054a202559b41a4d3Jim Miller                String text = String.format(directionDescription, targetDescription);
1239955a016922ea49f154d190b054a202559b41a4d3Jim Miller                utterance.append(text);
1240955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1241955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
124278bfb9829561876137c62ca1fff13760bfc77472alanv        if (utterance.length() > 0) {
124378bfb9829561876137c62ca1fff13760bfc77472alanv            announceForAccessibility(utterance.toString());
124478bfb9829561876137c62ca1fff13760bfc77472alanv        }
1245955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1246955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1247955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private String getTargetDescription(int index) {
1248955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) {
1249955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId);
1250955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mTargetDrawables.size() != mTargetDescriptions.size()) {
1251955a016922ea49f154d190b054a202559b41a4d3Jim Miller                Log.w(TAG, "The number of target drawables must be"
1252955a016922ea49f154d190b054a202559b41a4d3Jim Miller                        + " equal to the number of target descriptions.");
1253955a016922ea49f154d190b054a202559b41a4d3Jim Miller                return null;
1254955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1255955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1256955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return mTargetDescriptions.get(index);
1257955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1258955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1259955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private String getDirectionDescription(int index) {
1260955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) {
1261955a016922ea49f154d190b054a202559b41a4d3Jim Miller            mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId);
1262955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (mTargetDrawables.size() != mDirectionDescriptions.size()) {
1263955a016922ea49f154d190b054a202559b41a4d3Jim Miller                Log.w(TAG, "The number of target drawables must be"
1264955a016922ea49f154d190b054a202559b41a4d3Jim Miller                        + " equal to the number of direction descriptions.");
1265955a016922ea49f154d190b054a202559b41a4d3Jim Miller                return null;
1266955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1267955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1268955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return mDirectionDescriptions.get(index);
1269955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1270955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1271955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private ArrayList<String> loadDescriptions(int resourceId) {
1272955a016922ea49f154d190b054a202559b41a4d3Jim Miller        TypedArray array = getContext().getResources().obtainTypedArray(resourceId);
1273955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int count = array.length();
1274955a016922ea49f154d190b054a202559b41a4d3Jim Miller        ArrayList<String> targetContentDescriptions = new ArrayList<String>(count);
1275955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < count; i++) {
1276955a016922ea49f154d190b054a202559b41a4d3Jim Miller            String contentDescription = array.getString(i);
1277955a016922ea49f154d190b054a202559b41a4d3Jim Miller            targetContentDescriptions.add(contentDescription);
1278955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1279955a016922ea49f154d190b054a202559b41a4d3Jim Miller        array.recycle();
1280955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return targetContentDescriptions;
1281955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1282955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1283955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public int getResourceIdForTarget(int index) {
1284955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final TargetDrawable drawable = mTargetDrawables.get(index);
1285955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return drawable == null ? 0 : drawable.getResourceId();
1286955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1287955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1288955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public void setEnableTarget(int resourceId, boolean enabled) {
1289955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < mTargetDrawables.size(); i++) {
1290955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final TargetDrawable target = mTargetDrawables.get(i);
1291955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (target.getResourceId() == resourceId) {
1292955a016922ea49f154d190b054a202559b41a4d3Jim Miller                target.setEnabled(enabled);
1293955a016922ea49f154d190b054a202559b41a4d3Jim Miller                break; // should never be more than one match
1294955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1295955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1296955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1297955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1298955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
1299955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Gets the position of a target in the array that matches the given resource.
1300955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param resourceId
1301955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @return the index or -1 if not found
1302955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
1303955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public int getTargetPosition(int resourceId) {
1304955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < mTargetDrawables.size(); i++) {
1305955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final TargetDrawable target = mTargetDrawables.get(i);
1306955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (target.getResourceId() == resourceId) {
1307955a016922ea49f154d190b054a202559b41a4d3Jim Miller                return i; // should never be more than one match
1308955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1309955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1310955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return -1;
1311955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1312955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1313955a016922ea49f154d190b054a202559b41a4d3Jim Miller    private boolean replaceTargetDrawables(Resources res, int existingResourceId,
1314955a016922ea49f154d190b054a202559b41a4d3Jim Miller            int newResourceId) {
1315955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (existingResourceId == 0 || newResourceId == 0) {
1316955a016922ea49f154d190b054a202559b41a4d3Jim Miller            return false;
1317955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1318955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1319955a016922ea49f154d190b054a202559b41a4d3Jim Miller        boolean result = false;
1320955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final ArrayList<TargetDrawable> drawables = mTargetDrawables;
1321955a016922ea49f154d190b054a202559b41a4d3Jim Miller        final int size = drawables.size();
1322955a016922ea49f154d190b054a202559b41a4d3Jim Miller        for (int i = 0; i < size; i++) {
1323955a016922ea49f154d190b054a202559b41a4d3Jim Miller            final TargetDrawable target = drawables.get(i);
1324955a016922ea49f154d190b054a202559b41a4d3Jim Miller            if (target != null && target.getResourceId() == existingResourceId) {
1325955a016922ea49f154d190b054a202559b41a4d3Jim Miller                target.setDrawable(res, newResourceId);
1326955a016922ea49f154d190b054a202559b41a4d3Jim Miller                result = true;
1327955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1328955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1329955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1330955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (result) {
1331955a016922ea49f154d190b054a202559b41a4d3Jim Miller            requestLayout(); // in case any given drawable's size changes
1332955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
1333955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1334955a016922ea49f154d190b054a202559b41a4d3Jim Miller        return result;
1335955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1336955a016922ea49f154d190b054a202559b41a4d3Jim Miller
1337955a016922ea49f154d190b054a202559b41a4d3Jim Miller    /**
1338955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * Searches the given package for a resource to use to replace the Drawable on the
1339955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * target with the given resource id
1340955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param component of the .apk that contains the resource
1341955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param name of the metadata in the .apk
1342955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @param existingResId the resource id of the target to search for
1343955a016922ea49f154d190b054a202559b41a4d3Jim Miller     * @return true if found in the given package and replaced at least one target Drawables
1344955a016922ea49f154d190b054a202559b41a4d3Jim Miller     */
1345955a016922ea49f154d190b054a202559b41a4d3Jim Miller    public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name,
1346955a016922ea49f154d190b054a202559b41a4d3Jim Miller                int existingResId) {
1347955a016922ea49f154d190b054a202559b41a4d3Jim Miller        if (existingResId == 0) return false;
1348955a016922ea49f154d190b054a202559b41a4d3Jim Miller
134945308b1b3b1582d048845df2ee5301241e52a5cfJim Miller        boolean replaced = false;
135045308b1b3b1582d048845df2ee5301241e52a5cfJim Miller        if (component != null) {
135145308b1b3b1582d048845df2ee5301241e52a5cfJim Miller            try {
135245308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                PackageManager packageManager = mContext.getPackageManager();
135345308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                // Look for the search icon specified in the activity meta-data
135445308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                Bundle metaData = packageManager.getActivityInfo(
135545308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                        component, PackageManager.GET_META_DATA).metaData;
135645308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                if (metaData != null) {
135745308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                    int iconResId = metaData.getInt(name);
135845308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                    if (iconResId != 0) {
135945308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                        Resources res = packageManager.getResourcesForActivity(component);
136045308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                        replaced = replaceTargetDrawables(res, existingResId, iconResId);
136145308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                    }
1362955a016922ea49f154d190b054a202559b41a4d3Jim Miller                }
136345308b1b3b1582d048845df2ee5301241e52a5cfJim Miller            } catch (NameNotFoundException e) {
136445308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                Log.w(TAG, "Failed to swap drawable; "
136545308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                        + component.flattenToShortString() + " not found", e);
136645308b1b3b1582d048845df2ee5301241e52a5cfJim Miller            } catch (Resources.NotFoundException nfe) {
136745308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                Log.w(TAG, "Failed to swap drawable from "
136845308b1b3b1582d048845df2ee5301241e52a5cfJim Miller                        + component.flattenToShortString(), nfe);
1369955a016922ea49f154d190b054a202559b41a4d3Jim Miller            }
1370955a016922ea49f154d190b054a202559b41a4d3Jim Miller        }
137145308b1b3b1582d048845df2ee5301241e52a5cfJim Miller        if (!replaced) {
137245308b1b3b1582d048845df2ee5301241e52a5cfJim Miller            // Restore the original drawable
137345308b1b3b1582d048845df2ee5301241e52a5cfJim Miller            replaceTargetDrawables(mContext.getResources(), existingResId, existingResId);
137445308b1b3b1582d048845df2ee5301241e52a5cfJim Miller        }
137545308b1b3b1582d048845df2ee5301241e52a5cfJim Miller        return replaced;
1376955a016922ea49f154d190b054a202559b41a4d3Jim Miller    }
1377955a016922ea49f154d190b054a202559b41a4d3Jim Miller}
1378