1b505074e8273887fbcd1e933738a42e770085fb8Jim Miller/* 2b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Copyright (C) 2011 The Android Open Source Project 3b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 4b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Licensed under the Apache License, Version 2.0 (the "License"); 5b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * you may not use this file except in compliance with the License. 6b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * You may obtain a copy of the License at 7b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 8b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * http://www.apache.org/licenses/LICENSE-2.0 9b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 10b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Unless required by applicable law or agreed to in writing, software 11b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * distributed under the License is distributed on an "AS IS" BASIS, 12b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * See the License for the specific language governing permissions and 14b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * limitations under the License. 15b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 16b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 17b505074e8273887fbcd1e933738a42e770085fb8Jim Millerpackage com.android.internal.widget.multiwaveview; 18b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 19b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.animation.Animator; 20b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.animation.Animator.AnimatorListener; 211c8d207201150c29ac92c424e1320c715a64b5bcJim Millerimport android.animation.AnimatorListenerAdapter; 22b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.animation.TimeInterpolator; 23b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.animation.ValueAnimator; 24b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.animation.ValueAnimator.AnimatorUpdateListener; 253294b6b09b2f52cb44005720051c32c9c851fc9fJim Millerimport android.content.ComponentName; 26b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.content.Context; 273294b6b09b2f52cb44005720051c32c9c851fc9fJim Millerimport android.content.pm.PackageManager; 283294b6b09b2f52cb44005720051c32c9c851fc9fJim Millerimport android.content.pm.PackageManager.NameNotFoundException; 29b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.content.res.Resources; 30b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.content.res.TypedArray; 31b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.graphics.Canvas; 32b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.graphics.RectF; 33a073e570789e5b49e8339af44516444b13db4428Jim Millerimport android.graphics.drawable.Drawable; 343294b6b09b2f52cb44005720051c32c9c851fc9fJim Millerimport android.os.Bundle; 35723a725e790d269f32980116e775d3d7f0037865Jeff Sharkeyimport android.os.UserHandle; 36b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.os.Vibrator; 37723a725e790d269f32980116e775d3d7f0037865Jeff Sharkeyimport android.provider.Settings; 38f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganovimport android.text.TextUtils; 39b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.util.AttributeSet; 40b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.util.Log; 41b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.util.TypedValue; 42354619c1cc1b4668c81c5368b2256335cc9e8538Jim Millerimport android.view.Gravity; 43b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.view.MotionEvent; 44b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport android.view.View; 45f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganovimport android.view.accessibility.AccessibilityEvent; 46f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganovimport android.view.accessibility.AccessibilityManager; 47b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 48b505074e8273887fbcd1e933738a42e770085fb8Jim Millerimport com.android.internal.R; 49b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 50f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganovimport java.util.ArrayList; 51f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 52b505074e8273887fbcd1e933738a42e770085fb8Jim Miller/** 53b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * A special widget containing a center and outer ring. Moving the center ring to the outer ring 54b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * causes an event that can be caught by implementing OnTriggerListener. 55b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 561c8d207201150c29ac92c424e1320c715a64b5bcJim Millerpublic class MultiWaveView extends View { 57b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private static final String TAG = "MultiWaveView"; 58e20414f37a9aa0c5add34253033a2847905687d7Jim Miller private static final boolean DEBUG = false; 59b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 60b505074e8273887fbcd1e933738a42e770085fb8Jim Miller // Wave state machine 61b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private static final int STATE_IDLE = 0; 62a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int STATE_START = 1; 63a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int STATE_FIRST_TOUCH = 2; 64a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int STATE_TRACKING = 3; 65a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int STATE_SNAP = 4; 66a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int STATE_FINISH = 5; 67b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 68b505074e8273887fbcd1e933738a42e770085fb8Jim Miller // Animation properties. 69b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private static final float SNAP_MARGIN_DEFAULT = 20.0f; // distance to ring before we snap to it 70b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 71b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public interface OnTriggerListener { 72b505074e8273887fbcd1e933738a42e770085fb8Jim Miller int NO_HANDLE = 0; 73b505074e8273887fbcd1e933738a42e770085fb8Jim Miller int CENTER_HANDLE = 1; 74b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void onGrabbed(View v, int handle); 75b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void onReleased(View v, int handle); 76b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void onTrigger(View v, int target); 77b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void onGrabbedStateChange(View v, int handle); 78998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller public void onFinishFinalAnimation(); 79b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 80b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 8110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // Tuneable parameters for animation 821c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private static final int CHEVRON_INCREMENTAL_DELAY = 160; 83ce6eb1f5f2b4dae3f4b487f6da579149f42a8b4bJim Miller private static final int CHEVRON_ANIMATION_DURATION = 850; 841c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private static final int RETURN_TO_HOME_DELAY = 1200; 85a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int RETURN_TO_HOME_DURATION = 200; 861c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private static final int HIDE_ANIMATION_DELAY = 200; 8710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private static final int HIDE_ANIMATION_DURATION = 200; 8810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private static final int SHOW_ANIMATION_DURATION = 200; 89998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller private static final int SHOW_ANIMATION_DELAY = 50; 90a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final int INITIAL_SHOW_HANDLE_DURATION = 200; 91a073e570789e5b49e8339af44516444b13db4428Jim Miller 9266b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov private static final float TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED = 1.3f; 93a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final float TARGET_SCALE_EXPANDED = 1.0f; 94a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final float TARGET_SCALE_COLLAPSED = 0.8f; 95a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final float RING_SCALE_EXPANDED = 1.0f; 96a073e570789e5b49e8339af44516444b13db4428Jim Miller private static final float RING_SCALE_COLLAPSED = 0.5f; 9710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 981c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private TimeInterpolator mChevronAnimationInterpolator = Ease.Quad.easeOut; 99b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 100b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private ArrayList<TargetDrawable> mTargetDrawables = new ArrayList<TargetDrawable>(); 101b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private ArrayList<TargetDrawable> mChevronDrawables = new ArrayList<TargetDrawable>(); 10220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private AnimationBundle mChevronAnimations = new AnimationBundle(); 10320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private AnimationBundle mTargetAnimations = new AnimationBundle(); 10420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private AnimationBundle mHandleAnimations = new AnimationBundle(); 105f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private ArrayList<String> mTargetDescriptions; 106f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private ArrayList<String> mDirectionDescriptions; 107b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private OnTriggerListener mOnTriggerListener; 108b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private TargetDrawable mHandleDrawable; 109b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private TargetDrawable mOuterRing; 110b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private Vibrator mVibrator; 111b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 112b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private int mFeedbackCount = 3; 113b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private int mVibrationDuration = 0; 114b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private int mGrabbedState; 115b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private int mActiveTarget = -1; 116b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float mTapRadius; 117b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float mWaveCenterX; 118b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float mWaveCenterY; 119354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller private int mMaxTargetHeight; 120354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller private int mMaxTargetWidth; 121354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller 122b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float mOuterRadius = 0.0f; 123b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float mSnapMargin = 0.0f; 124b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private boolean mDragging; 1251c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private int mNewTargetResources; 126b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 12720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private class AnimationBundle extends ArrayList<Tweener> { 12820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private static final long serialVersionUID = 0xA84D78726F127468L; 12920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private boolean mSuspended; 13020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 13120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller public void start() { 13220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller if (mSuspended) return; // ignore attempts to start animations 13320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int count = size(); 13420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < count; i++) { 13520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller Tweener anim = get(i); 13620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller anim.animator.start(); 13720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 13820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 13920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 140998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller public void cancel() { 141998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller final int count = size(); 142998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller for (int i = 0; i < count; i++) { 143998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller Tweener anim = get(i); 144998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller anim.animator.cancel(); 145998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller } 146998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller clear(); 147998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller } 148998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller 14920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller public void stop() { 15020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int count = size(); 15120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < count; i++) { 15220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller Tweener anim = get(i); 15320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller anim.animator.end(); 15420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 15520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller clear(); 15620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 15720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 15820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller public void setSuspended(boolean suspend) { 15920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mSuspended = suspend; 16020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 16120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller }; 16220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 1631c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private AnimatorListener mResetListener = new AnimatorListenerAdapter() { 1641c8d207201150c29ac92c424e1320c715a64b5bcJim Miller public void onAnimationEnd(Animator animator) { 165b505074e8273887fbcd1e933738a42e770085fb8Jim Miller switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); 166998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller dispatchOnFinishFinalAnimation(); 167b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1681c8d207201150c29ac92c424e1320c715a64b5bcJim Miller }; 1691c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 1708dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely private AnimatorListener mResetListenerWithPing = new AnimatorListenerAdapter() { 1718dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely public void onAnimationEnd(Animator animator) { 1728dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely ping(); 1738dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely switchToState(STATE_IDLE, mWaveCenterX, mWaveCenterY); 174998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller dispatchOnFinishFinalAnimation(); 1758dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely } 1768dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely }; 1778dcd571b89580ada6b557fc5e72010fa9696cf4eBrandon Keely 1781c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private AnimatorUpdateListener mUpdateListener = new AnimatorUpdateListener() { 1791c8d207201150c29ac92c424e1320c715a64b5bcJim Miller public void onAnimationUpdate(ValueAnimator animation) { 1801c8d207201150c29ac92c424e1320c715a64b5bcJim Miller invalidateGlobalRegion(mHandleDrawable); 1811c8d207201150c29ac92c424e1320c715a64b5bcJim Miller invalidate(); 1821c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 1831c8d207201150c29ac92c424e1320c715a64b5bcJim Miller }; 1841c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 1851c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private boolean mAnimatingTargets; 1861c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private AnimatorListener mTargetUpdateListener = new AnimatorListenerAdapter() { 1871c8d207201150c29ac92c424e1320c715a64b5bcJim Miller public void onAnimationEnd(Animator animator) { 1881c8d207201150c29ac92c424e1320c715a64b5bcJim Miller if (mNewTargetResources != 0) { 1891c8d207201150c29ac92c424e1320c715a64b5bcJim Miller internalSetTargetResources(mNewTargetResources); 1901c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mNewTargetResources = 0; 191a073e570789e5b49e8339af44516444b13db4428Jim Miller hideTargets(false, false); 1921c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 1931c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mAnimatingTargets = false; 1941c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 195b505074e8273887fbcd1e933738a42e770085fb8Jim Miller }; 196be8d1cf1ac9fc514fb0cc2e8ef4a85beb0197fa0Jim Miller private int mTargetResourceId; 197f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private int mTargetDescriptionsResourceId; 198f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private int mDirectionDescriptionsResourceId; 199e898ac59db04d8ab0762180ca8ec7cea1347aa09Jim Miller private boolean mAlwaysTrackFinger; 200354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller private int mHorizontalInset; 201354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller private int mVerticalInset; 202354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller private int mGravity = Gravity.TOP; 20310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private boolean mInitialLayout = true; 204a073e570789e5b49e8339af44516444b13db4428Jim Miller private Tweener mBackgroundAnimator; 205b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 206b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public MultiWaveView(Context context) { 207b505074e8273887fbcd1e933738a42e770085fb8Jim Miller this(context, null); 208b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 209b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 210b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public MultiWaveView(Context context, AttributeSet attrs) { 211b505074e8273887fbcd1e933738a42e770085fb8Jim Miller super(context, attrs); 212b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Resources res = context.getResources(); 213b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 214b505074e8273887fbcd1e933738a42e770085fb8Jim Miller TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MultiWaveView); 215b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mOuterRadius = a.getDimension(R.styleable.MultiWaveView_outerRadius, mOuterRadius); 216b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mSnapMargin = a.getDimension(R.styleable.MultiWaveView_snapMargin, mSnapMargin); 217b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mVibrationDuration = a.getInt(R.styleable.MultiWaveView_vibrationDuration, 218b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mVibrationDuration); 219b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mFeedbackCount = a.getInt(R.styleable.MultiWaveView_feedbackCount, 220b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mFeedbackCount); 221b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mHandleDrawable = new TargetDrawable(res, 222b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller a.peekValue(R.styleable.MultiWaveView_handleDrawable).resourceId); 223b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mTapRadius = mHandleDrawable.getWidth()/2; 224b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller mOuterRing = new TargetDrawable(res, 225b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller a.peekValue(R.styleable.MultiWaveView_waveDrawable).resourceId); 226e898ac59db04d8ab0762180ca8ec7cea1347aa09Jim Miller mAlwaysTrackFinger = a.getBoolean(R.styleable.MultiWaveView_alwaysTrackFinger, false); 227b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 2284c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller // Read array of chevron drawables 2294c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller TypedValue outValue = new TypedValue(); 2304c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller if (a.getValue(R.styleable.MultiWaveView_chevronDrawables, outValue)) { 2314c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller ArrayList<TargetDrawable> chevrons = loadDrawableArray(outValue.resourceId); 2324c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller for (int i = 0; i < chevrons.size(); i++) { 2334c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final TargetDrawable chevron = chevrons.get(i); 2344c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller for (int k = 0; k < mFeedbackCount; k++) { 2354c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller mChevronDrawables.add(chevron == null ? null : new TargetDrawable(chevron)); 2364c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller } 2374c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller } 2384c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller } 2394c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller 240b505074e8273887fbcd1e933738a42e770085fb8Jim Miller // Read array of target drawables 241b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (a.getValue(R.styleable.MultiWaveView_targetDrawables, outValue)) { 2421c8d207201150c29ac92c424e1320c715a64b5bcJim Miller internalSetTargetResources(outValue.resourceId); 243b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 244b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (mTargetDrawables == null || mTargetDrawables.size() == 0) { 245b505074e8273887fbcd1e933738a42e770085fb8Jim Miller throw new IllegalStateException("Must specify at least one target drawable"); 246b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 247b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 248f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov // Read array of target descriptions 249f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (a.getValue(R.styleable.MultiWaveView_targetDescriptions, outValue)) { 250f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov final int resourceId = outValue.resourceId; 251f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (resourceId == 0) { 252f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov throw new IllegalStateException("Must specify target descriptions"); 253f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 254f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov setTargetDescriptionsResourceId(resourceId); 255f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 256f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 257f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov // Read array of direction descriptions 258f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (a.getValue(R.styleable.MultiWaveView_directionDescriptions, outValue)) { 259f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov final int resourceId = outValue.resourceId; 260f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (resourceId == 0) { 261f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov throw new IllegalStateException("Must specify direction descriptions"); 262f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 263f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov setDirectionDescriptionsResourceId(resourceId); 264f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 265f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 266f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov a.recycle(); 267955a016922ea49f154d190b054a202559b41a4d3Jim Miller 268955a016922ea49f154d190b054a202559b41a4d3Jim Miller // Use gravity attribute from LinearLayout 269955a016922ea49f154d190b054a202559b41a4d3Jim Miller a = context.obtainStyledAttributes(attrs, android.R.styleable.LinearLayout); 270955a016922ea49f154d190b054a202559b41a4d3Jim Miller mGravity = a.getInt(android.R.styleable.LinearLayout_gravity, Gravity.TOP); 271955a016922ea49f154d190b054a202559b41a4d3Jim Miller a.recycle(); 272955a016922ea49f154d190b054a202559b41a4d3Jim Miller 273b505074e8273887fbcd1e933738a42e770085fb8Jim Miller setVibrateEnabled(mVibrationDuration > 0); 27420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller assignDefaultsIfNeeded(); 275b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 276b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 277b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void dump() { 278b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "Outer Radius = " + mOuterRadius); 279b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "SnapMargin = " + mSnapMargin); 280b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "FeedbackCount = " + mFeedbackCount); 281b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "VibrationDuration = " + mVibrationDuration); 282b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "TapRadius = " + mTapRadius); 283b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "WaveCenterX = " + mWaveCenterX); 284b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Log.v(TAG, "WaveCenterY = " + mWaveCenterY); 285b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 286b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 28720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller public void suspendAnimations() { 28820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mChevronAnimations.setSuspended(true); 28920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.setSuspended(true); 29020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mHandleAnimations.setSuspended(true); 29120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 29220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 29320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller public void resumeAnimations() { 29420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mChevronAnimations.setSuspended(false); 29520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.setSuspended(false); 29620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mHandleAnimations.setSuspended(false); 29720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mChevronAnimations.start(); 29820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.start(); 29920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mHandleAnimations.start(); 30020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 30120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 302b505074e8273887fbcd1e933738a42e770085fb8Jim Miller @Override 303b505074e8273887fbcd1e933738a42e770085fb8Jim Miller protected int getSuggestedMinimumWidth() { 304354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller // View should be large enough to contain the background + handle and 305354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller // target drawable on either edge. 306c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller return (int) (Math.max(mOuterRing.getWidth(), 2 * mOuterRadius) + mMaxTargetWidth); 307b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 308b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 309b505074e8273887fbcd1e933738a42e770085fb8Jim Miller @Override 310b505074e8273887fbcd1e933738a42e770085fb8Jim Miller protected int getSuggestedMinimumHeight() { 311354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller // View should be large enough to contain the unlock ring + target and 312354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller // target drawable on either edge 313c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight); 3146b05d58018c2806459c121e507c005639b74aee9Jim Miller } 3156b05d58018c2806459c121e507c005639b74aee9Jim Miller 3166b05d58018c2806459c121e507c005639b74aee9Jim Miller private int resolveMeasured(int measureSpec, int desired) 3176b05d58018c2806459c121e507c005639b74aee9Jim Miller { 3186b05d58018c2806459c121e507c005639b74aee9Jim Miller int result = 0; 3196b05d58018c2806459c121e507c005639b74aee9Jim Miller int specSize = MeasureSpec.getSize(measureSpec); 3206b05d58018c2806459c121e507c005639b74aee9Jim Miller switch (MeasureSpec.getMode(measureSpec)) { 3216b05d58018c2806459c121e507c005639b74aee9Jim Miller case MeasureSpec.UNSPECIFIED: 3226b05d58018c2806459c121e507c005639b74aee9Jim Miller result = desired; 3236b05d58018c2806459c121e507c005639b74aee9Jim Miller break; 3246b05d58018c2806459c121e507c005639b74aee9Jim Miller case MeasureSpec.AT_MOST: 3256b05d58018c2806459c121e507c005639b74aee9Jim Miller result = Math.min(specSize, desired); 3266b05d58018c2806459c121e507c005639b74aee9Jim Miller break; 3276b05d58018c2806459c121e507c005639b74aee9Jim Miller case MeasureSpec.EXACTLY: 3286b05d58018c2806459c121e507c005639b74aee9Jim Miller default: 3296b05d58018c2806459c121e507c005639b74aee9Jim Miller result = specSize; 3306b05d58018c2806459c121e507c005639b74aee9Jim Miller } 3316b05d58018c2806459c121e507c005639b74aee9Jim Miller return result; 3326b05d58018c2806459c121e507c005639b74aee9Jim Miller } 3336b05d58018c2806459c121e507c005639b74aee9Jim Miller 3346b05d58018c2806459c121e507c005639b74aee9Jim Miller @Override 3356b05d58018c2806459c121e507c005639b74aee9Jim Miller protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 3366b05d58018c2806459c121e507c005639b74aee9Jim Miller final int minimumWidth = getSuggestedMinimumWidth(); 3376b05d58018c2806459c121e507c005639b74aee9Jim Miller final int minimumHeight = getSuggestedMinimumHeight(); 338354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth); 339354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight); 34010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller computeInsets((computedWidth - minimumWidth), (computedHeight - minimumHeight)); 341354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller setMeasuredDimension(computedWidth, computedHeight); 342b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 343b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 344b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void switchToState(int state, float x, float y) { 345b505074e8273887fbcd1e933738a42e770085fb8Jim Miller switch (state) { 346b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case STATE_IDLE: 347b505074e8273887fbcd1e933738a42e770085fb8Jim Miller deactivateTargets(); 348a073e570789e5b49e8339af44516444b13db4428Jim Miller hideTargets(true, false); 349a073e570789e5b49e8339af44516444b13db4428Jim Miller startBackgroundAnimation(0, 0.0f); 350b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE); 351b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 352b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 353a073e570789e5b49e8339af44516444b13db4428Jim Miller case STATE_START: 354a073e570789e5b49e8339af44516444b13db4428Jim Miller deactivateHandle(0, 0, 1.0f, null); 355a073e570789e5b49e8339af44516444b13db4428Jim Miller startBackgroundAnimation(0, 0.0f); 356a073e570789e5b49e8339af44516444b13db4428Jim Miller break; 357a073e570789e5b49e8339af44516444b13db4428Jim Miller 358b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case STATE_FIRST_TOUCH: 359b505074e8273887fbcd1e933738a42e770085fb8Jim Miller deactivateTargets(); 3601c8d207201150c29ac92c424e1320c715a64b5bcJim Miller showTargets(true); 361a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleDrawable.setState(TargetDrawable.STATE_ACTIVE); 362a073e570789e5b49e8339af44516444b13db4428Jim Miller startBackgroundAnimation(INITIAL_SHOW_HANDLE_DURATION, 1.0f); 363b505074e8273887fbcd1e933738a42e770085fb8Jim Miller setGrabbedState(OnTriggerListener.CENTER_HANDLE); 364f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (AccessibilityManager.getInstance(mContext).isEnabled()) { 365f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov announceTargets(); 366f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 367b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 368b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 369b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case STATE_TRACKING: 370b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 371b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 372b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case STATE_SNAP: 373b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 374b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 375b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case STATE_FINISH: 376b505074e8273887fbcd1e933738a42e770085fb8Jim Miller doFinish(); 377b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 378b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 379b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 380b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 381a073e570789e5b49e8339af44516444b13db4428Jim Miller private void activateHandle(int duration, int delay, float finalAlpha, 382a073e570789e5b49e8339af44516444b13db4428Jim Miller AnimatorListener finishListener) { 383a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.cancel(); 384a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.add(Tweener.to(mHandleDrawable, duration, 385a073e570789e5b49e8339af44516444b13db4428Jim Miller "ease", Ease.Cubic.easeIn, 386a073e570789e5b49e8339af44516444b13db4428Jim Miller "delay", delay, 387a073e570789e5b49e8339af44516444b13db4428Jim Miller "alpha", finalAlpha, 388a073e570789e5b49e8339af44516444b13db4428Jim Miller "onUpdate", mUpdateListener, 389a073e570789e5b49e8339af44516444b13db4428Jim Miller "onComplete", finishListener)); 390a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.start(); 391a073e570789e5b49e8339af44516444b13db4428Jim Miller } 392a073e570789e5b49e8339af44516444b13db4428Jim Miller 393a073e570789e5b49e8339af44516444b13db4428Jim Miller private void deactivateHandle(int duration, int delay, float finalAlpha, 394a073e570789e5b49e8339af44516444b13db4428Jim Miller AnimatorListener finishListener) { 395a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.cancel(); 396a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.add(Tweener.to(mHandleDrawable, duration, 397a073e570789e5b49e8339af44516444b13db4428Jim Miller "ease", Ease.Quart.easeOut, 398a073e570789e5b49e8339af44516444b13db4428Jim Miller "delay", delay, 399a073e570789e5b49e8339af44516444b13db4428Jim Miller "alpha", finalAlpha, 400a073e570789e5b49e8339af44516444b13db4428Jim Miller "x", 0, 401a073e570789e5b49e8339af44516444b13db4428Jim Miller "y", 0, 402a073e570789e5b49e8339af44516444b13db4428Jim Miller "onUpdate", mUpdateListener, 403a073e570789e5b49e8339af44516444b13db4428Jim Miller "onComplete", finishListener)); 4042a7b17bb2604823e140fb867fe87836dd039611aJim Miller mHandleAnimations.start(); 405998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller } 406998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller 407b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 408b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Animation used to attract user's attention to the target button. 409bf591ff682f14db7ba7b3554897e9cdcf245da59Jim Miller * Assumes mChevronDrawables is an a list with an even number of chevrons filled with 410bf591ff682f14db7ba7b3554897e9cdcf245da59Jim Miller * mFeedbackCount items in the order: left, right, top, bottom. 411b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 412b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void startChevronAnimation() { 4134c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float chevronStartDistance = mHandleDrawable.getWidth() * 0.8f; 4144c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float chevronStopDistance = mOuterRadius * 0.9f / 2.0f; 415ce6eb1f5f2b4dae3f4b487f6da579149f42a8b4bJim Miller final float startScale = 0.5f; 416ce6eb1f5f2b4dae3f4b487f6da579149f42a8b4bJim Miller final float endScale = 2.0f; 4174c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final int directionCount = mFeedbackCount > 0 ? mChevronDrawables.size()/mFeedbackCount : 0; 4184c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller 41920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mChevronAnimations.stop(); 42020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 4214c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller // Add an animation for all chevron drawables. There are mFeedbackCount drawables 4224c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller // in each direction and directionCount directions. 4234c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller for (int direction = 0; direction < directionCount; direction++) { 4244c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller double angle = 2.0 * Math.PI * direction / directionCount; 4254c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float sx = (float) Math.cos(angle); 4264c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float sy = 0.0f - (float) Math.sin(angle); 4274c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float[] xrange = new float[] 4284c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller {sx * chevronStartDistance, sx * chevronStopDistance}; 4294c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final float[] yrange = new float[] 4304c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller {sy * chevronStartDistance, sy * chevronStopDistance}; 43170832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller for (int count = 0; count < mFeedbackCount; count++) { 43270832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller int delay = count * CHEVRON_INCREMENTAL_DELAY; 43370832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller final TargetDrawable icon = mChevronDrawables.get(direction*mFeedbackCount + count); 43470832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller if (icon == null) { 43570832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller continue; 43670832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller } 437b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mChevronAnimations.add(Tweener.to(icon, CHEVRON_ANIMATION_DURATION, 438b505074e8273887fbcd1e933738a42e770085fb8Jim Miller "ease", mChevronAnimationInterpolator, 439b505074e8273887fbcd1e933738a42e770085fb8Jim Miller "delay", delay, 4404c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller "x", xrange, 4414c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller "y", yrange, 44270832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller "alpha", new float[] {1.0f, 0.0f}, 443ce6eb1f5f2b4dae3f4b487f6da579149f42a8b4bJim Miller "scaleX", new float[] {startScale, endScale}, 444ce6eb1f5f2b4dae3f4b487f6da579149f42a8b4bJim Miller "scaleY", new float[] {startScale, endScale}, 4451c8d207201150c29ac92c424e1320c715a64b5bcJim Miller "onUpdate", mUpdateListener)); 446b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 447b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 44820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mChevronAnimations.start(); 449b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 450b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 451b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void deactivateTargets() { 45220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int count = mTargetDrawables.size(); 45320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < count; i++) { 45420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable target = mTargetDrawables.get(i); 455b505074e8273887fbcd1e933738a42e770085fb8Jim Miller target.setState(TargetDrawable.STATE_INACTIVE); 456b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 457b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mActiveTarget = -1; 458b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 459b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 460b505074e8273887fbcd1e933738a42e770085fb8Jim Miller void invalidateGlobalRegion(TargetDrawable drawable) { 461b505074e8273887fbcd1e933738a42e770085fb8Jim Miller int width = drawable.getWidth(); 462b505074e8273887fbcd1e933738a42e770085fb8Jim Miller int height = drawable.getHeight(); 463b505074e8273887fbcd1e933738a42e770085fb8Jim Miller RectF childBounds = new RectF(0, 0, width, height); 464b505074e8273887fbcd1e933738a42e770085fb8Jim Miller childBounds.offset(drawable.getX() - width/2, drawable.getY() - height/2); 465b505074e8273887fbcd1e933738a42e770085fb8Jim Miller View view = this; 466b505074e8273887fbcd1e933738a42e770085fb8Jim Miller while (view.getParent() != null && view.getParent() instanceof View) { 467b505074e8273887fbcd1e933738a42e770085fb8Jim Miller view = (View) view.getParent(); 468b505074e8273887fbcd1e933738a42e770085fb8Jim Miller view.getMatrix().mapRect(childBounds); 469b505074e8273887fbcd1e933738a42e770085fb8Jim Miller view.invalidate((int) Math.floor(childBounds.left), 470b505074e8273887fbcd1e933738a42e770085fb8Jim Miller (int) Math.floor(childBounds.top), 471b505074e8273887fbcd1e933738a42e770085fb8Jim Miller (int) Math.ceil(childBounds.right), 472b505074e8273887fbcd1e933738a42e770085fb8Jim Miller (int) Math.ceil(childBounds.bottom)); 473b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 474b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 475b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 476b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 477b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Dispatches a trigger event to listener. Ignored if a listener is not set. 478960892c0afa7f2b91236928e29e3987ed35b2077Jim Miller * @param whichTarget the target that was triggered. 479b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 480960892c0afa7f2b91236928e29e3987ed35b2077Jim Miller private void dispatchTriggerEvent(int whichTarget) { 481b505074e8273887fbcd1e933738a42e770085fb8Jim Miller vibrate(); 482b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (mOnTriggerListener != null) { 483960892c0afa7f2b91236928e29e3987ed35b2077Jim Miller mOnTriggerListener.onTrigger(this, whichTarget); 484f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 485f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 486f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 487998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller private void dispatchOnFinishFinalAnimation() { 488998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller if (mOnTriggerListener != null) { 489998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller mOnTriggerListener.onFinishFinalAnimation(); 490998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller } 491998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller } 492998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller 493b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void doFinish() { 494b505074e8273887fbcd1e933738a42e770085fb8Jim Miller final int activeTarget = mActiveTarget; 495a073e570789e5b49e8339af44516444b13db4428Jim Miller final boolean targetHit = activeTarget != -1; 496b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 497b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (targetHit) { 498a073e570789e5b49e8339af44516444b13db4428Jim Miller if (DEBUG) Log.v(TAG, "Finish with target hit = " + targetHit); 499a073e570789e5b49e8339af44516444b13db4428Jim Miller 500a073e570789e5b49e8339af44516444b13db4428Jim Miller highlightSelected(activeTarget); 5011c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 5021c8d207201150c29ac92c424e1320c715a64b5bcJim Miller // Inform listener of any active targets. Typically only one will be active. 503a073e570789e5b49e8339af44516444b13db4428Jim Miller deactivateHandle(RETURN_TO_HOME_DURATION, RETURN_TO_HOME_DELAY, 0.0f, mResetListener); 504960892c0afa7f2b91236928e29e3987ed35b2077Jim Miller dispatchTriggerEvent(activeTarget); 5059a720f5eb6c67b581df22f4ecb498cebb459babeJim Miller if (!mAlwaysTrackFinger) { 5069a720f5eb6c67b581df22f4ecb498cebb459babeJim Miller // Force ring and targets to finish animation to final expanded state 5079a720f5eb6c67b581df22f4ecb498cebb459babeJim Miller mTargetAnimations.stop(); 5089a720f5eb6c67b581df22f4ecb498cebb459babeJim Miller } 509a073e570789e5b49e8339af44516444b13db4428Jim Miller } else { 510a073e570789e5b49e8339af44516444b13db4428Jim Miller // Animate handle back to the center based on current state. 511a073e570789e5b49e8339af44516444b13db4428Jim Miller deactivateHandle(HIDE_ANIMATION_DURATION, HIDE_ANIMATION_DELAY, 1.0f, 512a073e570789e5b49e8339af44516444b13db4428Jim Miller mResetListenerWithPing); 513a073e570789e5b49e8339af44516444b13db4428Jim Miller hideTargets(true, false); 514b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 5151c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 5161c8d207201150c29ac92c424e1320c715a64b5bcJim Miller setGrabbedState(OnTriggerListener.NO_HANDLE); 5171c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 5181c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 519a073e570789e5b49e8339af44516444b13db4428Jim Miller private void highlightSelected(int activeTarget) { 520a073e570789e5b49e8339af44516444b13db4428Jim Miller // Highlight the given target and fade others 521a073e570789e5b49e8339af44516444b13db4428Jim Miller mTargetDrawables.get(activeTarget).setState(TargetDrawable.STATE_ACTIVE); 522a073e570789e5b49e8339af44516444b13db4428Jim Miller hideUnselected(activeTarget); 523a073e570789e5b49e8339af44516444b13db4428Jim Miller } 524a073e570789e5b49e8339af44516444b13db4428Jim Miller 5251c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private void hideUnselected(int active) { 5261c8d207201150c29ac92c424e1320c715a64b5bcJim Miller for (int i = 0; i < mTargetDrawables.size(); i++) { 5271c8d207201150c29ac92c424e1320c715a64b5bcJim Miller if (i != active) { 5281c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mTargetDrawables.get(i).setAlpha(0.0f); 5291c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 5301c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 531b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 532b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 533a073e570789e5b49e8339af44516444b13db4428Jim Miller private void hideTargets(boolean animate, boolean expanded) { 534998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller mTargetAnimations.cancel(); 5351c8d207201150c29ac92c424e1320c715a64b5bcJim Miller // Note: these animations should complete at the same time so that we can swap out 5361c8d207201150c29ac92c424e1320c715a64b5bcJim Miller // the target assets asynchronously from the setTargetResources() call. 5371c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mAnimatingTargets = animate; 53810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final int duration = animate ? HIDE_ANIMATION_DURATION : 0; 53910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final int delay = animate ? HIDE_ANIMATION_DELAY : 0; 540998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller 541a073e570789e5b49e8339af44516444b13db4428Jim Miller final float targetScale = expanded ? TARGET_SCALE_EXPANDED : TARGET_SCALE_COLLAPSED; 54210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final int length = mTargetDrawables.size(); 54310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller for (int i = 0; i < length; i++) { 54410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller TargetDrawable target = mTargetDrawables.get(i); 54510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setState(TargetDrawable.STATE_INACTIVE); 54610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mTargetAnimations.add(Tweener.to(target, duration, 54710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "ease", Ease.Cubic.easeOut, 548b505074e8273887fbcd1e933738a42e770085fb8Jim Miller "alpha", 0.0f, 549998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller "scaleX", targetScale, 550998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller "scaleY", targetScale, 55110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "delay", delay, 55210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onUpdate", mUpdateListener)); 553b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 55410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 555a073e570789e5b49e8339af44516444b13db4428Jim Miller final float ringScaleTarget = expanded ? RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED; 55610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mTargetAnimations.add(Tweener.to(mOuterRing, duration, 55710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "ease", Ease.Cubic.easeOut, 55810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "alpha", 0.0f, 55910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleX", ringScaleTarget, 56010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleY", ringScaleTarget, 56110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "delay", delay, 56210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onUpdate", mUpdateListener, 56310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onComplete", mTargetUpdateListener)); 56420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 56520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.start(); 566b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 567b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 5681c8d207201150c29ac92c424e1320c715a64b5bcJim Miller private void showTargets(boolean animate) { 56920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.stop(); 5701c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mAnimatingTargets = animate; 57110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final int delay = animate ? SHOW_ANIMATION_DELAY : 0; 572998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller final int duration = animate ? SHOW_ANIMATION_DURATION : 0; 57310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final int length = mTargetDrawables.size(); 57410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller for (int i = 0; i < length; i++) { 57510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller TargetDrawable target = mTargetDrawables.get(i); 57610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setState(TargetDrawable.STATE_INACTIVE); 577998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller mTargetAnimations.add(Tweener.to(target, duration, 57810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "ease", Ease.Cubic.easeOut, 579b505074e8273887fbcd1e933738a42e770085fb8Jim Miller "alpha", 1.0f, 58010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleX", 1.0f, 58110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleY", 1.0f, 58210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "delay", delay, 58310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onUpdate", mUpdateListener)); 58410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 585998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller mTargetAnimations.add(Tweener.to(mOuterRing, duration, 58610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "ease", Ease.Cubic.easeOut, 58710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "alpha", 1.0f, 58810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleX", 1.0f, 58910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "scaleY", 1.0f, 59010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "delay", delay, 59110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onUpdate", mUpdateListener, 59210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller "onComplete", mTargetUpdateListener)); 59320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 59420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mTargetAnimations.start(); 595b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 596b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 597b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void vibrate() { 598723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey final boolean hapticEnabled = Settings.System.getIntForUser( 599723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey mContext.getContentResolver(), Settings.System.HAPTIC_FEEDBACK_ENABLED, 1, 600723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey UserHandle.USER_CURRENT) != 0; 601723a725e790d269f32980116e775d3d7f0037865Jeff Sharkey if (mVibrator != null && hapticEnabled) { 602b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mVibrator.vibrate(mVibrationDuration); 603b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 604b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 605b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 6064c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller private ArrayList<TargetDrawable> loadDrawableArray(int resourceId) { 607b505074e8273887fbcd1e933738a42e770085fb8Jim Miller Resources res = getContext().getResources(); 608b505074e8273887fbcd1e933738a42e770085fb8Jim Miller TypedArray array = res.obtainTypedArray(resourceId); 6094c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final int count = array.length(); 6104c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller ArrayList<TargetDrawable> drawables = new ArrayList<TargetDrawable>(count); 6114c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller for (int i = 0; i < count; i++) { 6124c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller TypedValue value = array.peekValue(i); 6134c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller TargetDrawable target = new TargetDrawable(res, value != null ? value.resourceId : 0); 6144c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller drawables.add(target); 6154c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller } 6164c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller array.recycle(); 6174c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller return drawables; 6184c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller } 6194c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller 6204c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller private void internalSetTargetResources(int resourceId) { 6214c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller mTargetDrawables = loadDrawableArray(resourceId); 6224c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller mTargetResourceId = resourceId; 6234c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller final int count = mTargetDrawables.size(); 624354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller int maxWidth = mHandleDrawable.getWidth(); 625354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller int maxHeight = mHandleDrawable.getHeight(); 626b505074e8273887fbcd1e933738a42e770085fb8Jim Miller for (int i = 0; i < count; i++) { 6274c351d62e7a09bcc29e7d0329bcdd947a302cf40Jim Miller TargetDrawable target = mTargetDrawables.get(i); 628354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller maxWidth = Math.max(maxWidth, target.getWidth()); 629354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller maxHeight = Math.max(maxHeight, target.getHeight()); 630354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } 631354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (mMaxTargetWidth != maxWidth || mMaxTargetHeight != maxHeight) { 632354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mMaxTargetWidth = maxWidth; 633354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mMaxTargetHeight = maxHeight; 634354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller requestLayout(); // required to resize layout and call updateTargetPositions() 635354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } else { 63610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller updateTargetPositions(mWaveCenterX, mWaveCenterY); 63710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller updateChevronPositions(mWaveCenterX, mWaveCenterY); 638b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 6391c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 6401c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 6411c8d207201150c29ac92c424e1320c715a64b5bcJim Miller /** 6421c8d207201150c29ac92c424e1320c715a64b5bcJim Miller * Loads an array of drawables from the given resourceId. 6431c8d207201150c29ac92c424e1320c715a64b5bcJim Miller * 6441c8d207201150c29ac92c424e1320c715a64b5bcJim Miller * @param resourceId 6451c8d207201150c29ac92c424e1320c715a64b5bcJim Miller */ 6461c8d207201150c29ac92c424e1320c715a64b5bcJim Miller public void setTargetResources(int resourceId) { 6471c8d207201150c29ac92c424e1320c715a64b5bcJim Miller if (mAnimatingTargets) { 6481c8d207201150c29ac92c424e1320c715a64b5bcJim Miller // postpone this change until we return to the initial state 6491c8d207201150c29ac92c424e1320c715a64b5bcJim Miller mNewTargetResources = resourceId; 6501c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } else { 6511c8d207201150c29ac92c424e1320c715a64b5bcJim Miller internalSetTargetResources(resourceId); 6521c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 653b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 654b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 655be8d1cf1ac9fc514fb0cc2e8ef4a85beb0197fa0Jim Miller public int getTargetResourceId() { 656be8d1cf1ac9fc514fb0cc2e8ef4a85beb0197fa0Jim Miller return mTargetResourceId; 657be8d1cf1ac9fc514fb0cc2e8ef4a85beb0197fa0Jim Miller } 658be8d1cf1ac9fc514fb0cc2e8ef4a85beb0197fa0Jim Miller 659b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 660f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * Sets the resource id specifying the target descriptions for accessibility. 661f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * 662f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * @param resourceId The resource id. 663f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov */ 664f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov public void setTargetDescriptionsResourceId(int resourceId) { 665f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mTargetDescriptionsResourceId = resourceId; 666f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mTargetDescriptions != null) { 667f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mTargetDescriptions.clear(); 668f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 669f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 670f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 671f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov /** 672f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * Gets the resource id specifying the target descriptions for accessibility. 673f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * 674f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * @return The resource id. 675f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov */ 676f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov public int getTargetDescriptionsResourceId() { 677f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return mTargetDescriptionsResourceId; 678f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 679f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 680f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov /** 681f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * Sets the resource id specifying the target direction descriptions for accessibility. 682f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * 683f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * @param resourceId The resource id. 684f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov */ 685f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov public void setDirectionDescriptionsResourceId(int resourceId) { 686f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mDirectionDescriptionsResourceId = resourceId; 687f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mDirectionDescriptions != null) { 688f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mDirectionDescriptions.clear(); 689f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 690f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 691f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 692f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov /** 693f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * Gets the resource id specifying the target direction descriptions. 694f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * 695f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov * @return The resource id. 696f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov */ 697f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov public int getDirectionDescriptionsResourceId() { 698f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return mDirectionDescriptionsResourceId; 699f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 700f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 701f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov /** 702b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Enable or disable vibrate on touch. 703b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 704b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * @param enabled 705b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 706b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void setVibrateEnabled(boolean enabled) { 707b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (enabled && mVibrator == null) { 708b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mVibrator = (Vibrator) getContext().getSystemService(Context.VIBRATOR_SERVICE); 709b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } else { 710b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mVibrator = null; 711b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 712b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 713b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 714b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 715b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Starts chevron animation. Example use case: show chevron animation whenever the phone rings 716b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * or the user touches the screen. 717b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 718b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 719b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void ping() { 720b505074e8273887fbcd1e933738a42e770085fb8Jim Miller startChevronAnimation(); 721b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 722b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 723b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 724b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Resets the widget to default state and cancels all animation. If animate is 'true', will 725b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * animate objects into place. Otherwise, objects will snap back to place. 726b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * 727b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * @param animate 728b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 729b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void reset(boolean animate) { 730a073e570789e5b49e8339af44516444b13db4428Jim Miller mChevronAnimations.stop(); 731a073e570789e5b49e8339af44516444b13db4428Jim Miller mHandleAnimations.stop(); 732998bb765290066822ff8a3b378dadd71deb0ce73Jim Miller mTargetAnimations.stop(); 733a073e570789e5b49e8339af44516444b13db4428Jim Miller startBackgroundAnimation(0, 0.0f); 734b505074e8273887fbcd1e933738a42e770085fb8Jim Miller hideChevrons(); 735a073e570789e5b49e8339af44516444b13db4428Jim Miller hideTargets(animate, false); 736a073e570789e5b49e8339af44516444b13db4428Jim Miller deactivateHandle(0, 0, 1.0f, null); 737bf591ff682f14db7ba7b3554897e9cdcf245da59Jim Miller Tweener.reset(); 738b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 739b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 740a073e570789e5b49e8339af44516444b13db4428Jim Miller private void startBackgroundAnimation(int duration, float alpha) { 741a073e570789e5b49e8339af44516444b13db4428Jim Miller Drawable background = getBackground(); 742a073e570789e5b49e8339af44516444b13db4428Jim Miller if (mAlwaysTrackFinger && background != null) { 743a073e570789e5b49e8339af44516444b13db4428Jim Miller if (mBackgroundAnimator != null) { 744a073e570789e5b49e8339af44516444b13db4428Jim Miller mBackgroundAnimator.animator.end(); 745a073e570789e5b49e8339af44516444b13db4428Jim Miller } 746a073e570789e5b49e8339af44516444b13db4428Jim Miller mBackgroundAnimator = Tweener.to(background, duration, 747a073e570789e5b49e8339af44516444b13db4428Jim Miller "ease", Ease.Cubic.easeIn, 748a073e570789e5b49e8339af44516444b13db4428Jim Miller "alpha", new int[] {0, (int)(255.0f * alpha)}, 749a073e570789e5b49e8339af44516444b13db4428Jim Miller "delay", SHOW_ANIMATION_DELAY); 750a073e570789e5b49e8339af44516444b13db4428Jim Miller mBackgroundAnimator.animator.start(); 751a073e570789e5b49e8339af44516444b13db4428Jim Miller } 752a073e570789e5b49e8339af44516444b13db4428Jim Miller } 753a073e570789e5b49e8339af44516444b13db4428Jim Miller 754b505074e8273887fbcd1e933738a42e770085fb8Jim Miller @Override 755b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public boolean onTouchEvent(MotionEvent event) { 756b505074e8273887fbcd1e933738a42e770085fb8Jim Miller final int action = event.getAction(); 757b505074e8273887fbcd1e933738a42e770085fb8Jim Miller boolean handled = false; 758b505074e8273887fbcd1e933738a42e770085fb8Jim Miller switch (action) { 759b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case MotionEvent.ACTION_DOWN: 760354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (DEBUG) Log.v(TAG, "*** DOWN ***"); 761b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller handleDown(event); 762b505074e8273887fbcd1e933738a42e770085fb8Jim Miller handled = true; 763b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 764b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 765b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case MotionEvent.ACTION_MOVE: 766354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (DEBUG) Log.v(TAG, "*** MOVE ***"); 767b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller handleMove(event); 768b505074e8273887fbcd1e933738a42e770085fb8Jim Miller handled = true; 769b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 770b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 771b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case MotionEvent.ACTION_UP: 772354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (DEBUG) Log.v(TAG, "*** UP ***"); 773b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller handleMove(event); 774b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller handleUp(event); 775b505074e8273887fbcd1e933738a42e770085fb8Jim Miller handled = true; 776b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 777b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 778b505074e8273887fbcd1e933738a42e770085fb8Jim Miller case MotionEvent.ACTION_CANCEL: 779354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (DEBUG) Log.v(TAG, "*** CANCEL ***"); 78010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller handleMove(event); 781bf032650e3938f264091764a606a06a0d34bd15aJim Miller handleCancel(event); 782b505074e8273887fbcd1e933738a42e770085fb8Jim Miller handled = true; 783b505074e8273887fbcd1e933738a42e770085fb8Jim Miller break; 784b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 785b505074e8273887fbcd1e933738a42e770085fb8Jim Miller invalidate(); 786b505074e8273887fbcd1e933738a42e770085fb8Jim Miller return handled ? true : super.onTouchEvent(event); 787b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 788b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 78970832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller private void moveHandleTo(float x, float y, boolean animate) { 79070832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller mHandleDrawable.setX(x); 79170832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller mHandleDrawable.setY(y); 79270832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller } 79370832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller 794b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller private void handleDown(MotionEvent event) { 795a073e570789e5b49e8339af44516444b13db4428Jim Miller float eventX = event.getX(); 796a073e570789e5b49e8339af44516444b13db4428Jim Miller float eventY = event.getY(); 797a073e570789e5b49e8339af44516444b13db4428Jim Miller switchToState(STATE_START, eventX, eventY); 798a073e570789e5b49e8339af44516444b13db4428Jim Miller if (!trySwitchToFirstTouchState(eventX, eventY)) { 799b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mDragging = false; 800b505074e8273887fbcd1e933738a42e770085fb8Jim Miller ping(); 801b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 802b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 803b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 804b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller private void handleUp(MotionEvent event) { 805b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (DEBUG && mDragging) Log.v(TAG, "** Handle RELEASE"); 806b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller switchToState(STATE_FINISH, event.getX(), event.getY()); 807bf032650e3938f264091764a606a06a0d34bd15aJim Miller } 808b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller 809bf032650e3938f264091764a606a06a0d34bd15aJim Miller private void handleCancel(MotionEvent event) { 810bf032650e3938f264091764a606a06a0d34bd15aJim Miller if (DEBUG && mDragging) Log.v(TAG, "** Handle CANCEL"); 81110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 81210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // We should drop the active target here but it interferes with 81310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // moving off the screen in the direction of the navigation bar. At some point we may 81410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // want to revisit how we handle this. For now we'll allow a canceled event to 81510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // activate the current target. 81610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 81710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // mActiveTarget = -1; // Drop the active target if canceled. 81810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 819bf032650e3938f264091764a606a06a0d34bd15aJim Miller switchToState(STATE_FINISH, event.getX(), event.getY()); 820b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 821b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 822b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller private void handleMove(MotionEvent event) { 823b505074e8273887fbcd1e933738a42e770085fb8Jim Miller int activeTarget = -1; 824b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller final int historySize = event.getHistorySize(); 82520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller ArrayList<TargetDrawable> targets = mTargetDrawables; 82620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller int ntargets = targets.size(); 82710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float x = 0.0f; 82810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float y = 0.0f; 829b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller for (int k = 0; k < historySize + 1; k++) { 83010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float eventX = k < historySize ? event.getHistoricalX(k) : event.getX(); 83110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float eventY = k < historySize ? event.getHistoricalY(k) : event.getY(); 83210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // tx and ty are relative to wave center 83310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float tx = eventX - mWaveCenterX; 83410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float ty = eventY - mWaveCenterY; 835b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller float touchRadius = (float) Math.sqrt(dist2(tx, ty)); 836b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller final float scale = touchRadius > mOuterRadius ? mOuterRadius / touchRadius : 1.0f; 83710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float limitX = tx * scale; 83810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller float limitY = ty * scale; 83953f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka double angleRad = Math.atan2(-ty, tx); 840b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller 84120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller if (!mDragging) { 84220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller trySwitchToFirstTouchState(eventX, eventY); 843a073e570789e5b49e8339af44516444b13db4428Jim Miller } 844a073e570789e5b49e8339af44516444b13db4428Jim Miller 845a073e570789e5b49e8339af44516444b13db4428Jim Miller if (mDragging) { 84653f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka // For multiple targets, snap to the one that matches 84753f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka final float snapRadius = mOuterRadius - mSnapMargin; 84853f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka final float snapDistance2 = snapRadius * snapRadius; 84953f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka // Find first target in range 85053f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka for (int i = 0; i < ntargets; i++) { 85153f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka TargetDrawable target = targets.get(i); 85253f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka 85353f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka double targetMinRad = (i - 0.5) * 2 * Math.PI / ntargets; 85453f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka double targetMaxRad = (i + 0.5) * 2 * Math.PI / ntargets; 85553f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka if (target.isEnabled()) { 85653f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka boolean angleMatches = 85753f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka (angleRad > targetMinRad && angleRad <= targetMaxRad) || 85853f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka (angleRad + 2 * Math.PI > targetMinRad && 85953f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka angleRad + 2 * Math.PI <= targetMaxRad); 86053f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka if (angleMatches && (dist2(tx, ty) > snapDistance2)) { 86120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller activeTarget = i; 86220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 863b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller } 864b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller } 865b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 86610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller x = limitX; 86710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller y = limitY; 86810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 86910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 87020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller if (!mDragging) { 87120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller return; 87220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller } 87320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 87410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (activeTarget != -1) { 87510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller switchToState(STATE_SNAP, x,y); 87653f109bf4923e111e796014e6701a14e5bfa5d1aMichael Jurka moveHandleTo(x, y, false); 87710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } else { 87810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller switchToState(STATE_TRACKING, x, y); 87910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller moveHandleTo(x, y, false); 880b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 881b35e372bf4080f3b1940a371f1e39fb5dccde990Jim Miller 882b505074e8273887fbcd1e933738a42e770085fb8Jim Miller // Draw handle outside parent's bounds 883b505074e8273887fbcd1e933738a42e770085fb8Jim Miller invalidateGlobalRegion(mHandleDrawable); 884b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 88510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (mActiveTarget != activeTarget) { 88610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // Defocus the old target 88710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (mActiveTarget != -1) { 88820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable target = targets.get(mActiveTarget); 88910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (target.hasState(TargetDrawable.STATE_FOCUSED)) { 89010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setState(TargetDrawable.STATE_INACTIVE); 89110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 89210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 89310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller // Focus the new target 89410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (activeTarget != -1) { 89520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable target = targets.get(activeTarget); 89610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (target.hasState(TargetDrawable.STATE_FOCUSED)) { 89710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setState(TargetDrawable.STATE_FOCUSED); 89810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 89910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (AccessibilityManager.getInstance(mContext).isEnabled()) { 90010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller String targetContentDescription = getTargetDescription(activeTarget); 90110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller announceText(targetContentDescription); 90210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 903a073e570789e5b49e8339af44516444b13db4428Jim Miller activateHandle(0, 0, 0.0f, null); 904a073e570789e5b49e8339af44516444b13db4428Jim Miller } else { 905a073e570789e5b49e8339af44516444b13db4428Jim Miller activateHandle(0, 0, 1.0f, null); 906f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 907b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 908b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mActiveTarget = activeTarget; 909b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 910b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 9112a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov @Override 9122a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov public boolean onHoverEvent(MotionEvent event) { 9132a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov if (AccessibilityManager.getInstance(mContext).isTouchExplorationEnabled()) { 9142a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov final int action = event.getAction(); 9152a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov switch (action) { 9162a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov case MotionEvent.ACTION_HOVER_ENTER: 9172a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov event.setAction(MotionEvent.ACTION_DOWN); 9182a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov break; 9192a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov case MotionEvent.ACTION_HOVER_MOVE: 9202a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov event.setAction(MotionEvent.ACTION_MOVE); 9212a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov break; 9222a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov case MotionEvent.ACTION_HOVER_EXIT: 9232a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov event.setAction(MotionEvent.ACTION_UP); 9242a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov break; 9252a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov } 9262a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov onTouchEvent(event); 9272a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov event.setAction(action); 9282a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov } 9292a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov return super.onHoverEvent(event); 9302a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov } 9312a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov 932b505074e8273887fbcd1e933738a42e770085fb8Jim Miller /** 933b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * Sets the current grabbed state, and dispatches a grabbed state change 934b505074e8273887fbcd1e933738a42e770085fb8Jim Miller * event to our listener. 935b505074e8273887fbcd1e933738a42e770085fb8Jim Miller */ 936b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void setGrabbedState(int newState) { 937b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (newState != mGrabbedState) { 938b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (newState != OnTriggerListener.NO_HANDLE) { 939b505074e8273887fbcd1e933738a42e770085fb8Jim Miller vibrate(); 940b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 941b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mGrabbedState = newState; 942b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (mOnTriggerListener != null) { 943354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller if (newState == OnTriggerListener.NO_HANDLE) { 944354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mOnTriggerListener.onReleased(this, OnTriggerListener.CENTER_HANDLE); 945354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } else { 946354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mOnTriggerListener.onGrabbed(this, OnTriggerListener.CENTER_HANDLE); 947354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } 948960892c0afa7f2b91236928e29e3987ed35b2077Jim Miller mOnTriggerListener.onGrabbedStateChange(this, newState); 949b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 950b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 951b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 952b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 95320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private boolean trySwitchToFirstTouchState(float x, float y) { 95410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final float tx = x - mWaveCenterX; 95510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller final float ty = y - mWaveCenterY; 95610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (mAlwaysTrackFinger || dist2(tx,ty) <= getScaledTapRadiusSquared()) { 9572a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov if (DEBUG) Log.v(TAG, "** Handle HIT"); 9582a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov switchToState(STATE_FIRST_TOUCH, x, y); 95910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller moveHandleTo(tx, ty, false); 9602a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov mDragging = true; 9612a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov return true; 9622a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov } 9632a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov return false; 9642a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov } 9652a671ac905e97d108e53e11856b01356f9248cc8Svetoslav Ganov 96620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller private void assignDefaultsIfNeeded() { 967b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (mOuterRadius == 0.0f) { 96820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller mOuterRadius = Math.max(mOuterRing.getWidth(), mOuterRing.getHeight())/2.0f; 969b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 970b505074e8273887fbcd1e933738a42e770085fb8Jim Miller if (mSnapMargin == 0.0f) { 971b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mSnapMargin = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 972b505074e8273887fbcd1e933738a42e770085fb8Jim Miller SNAP_MARGIN_DEFAULT, getContext().getResources().getDisplayMetrics()); 973b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 9741c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 9751c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 97610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private void computeInsets(int dx, int dy) { 977e56ffdc7b31b0937628609cc3bbaa15879023569Fabrice Di Meglio final int layoutDirection = getLayoutDirection(); 978354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection); 979354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller 980354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) { 981354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.LEFT: 982354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mHorizontalInset = 0; 983354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 984354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.RIGHT: 985354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mHorizontalInset = dx; 986354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 987354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.CENTER_HORIZONTAL: 988354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller default: 989354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mHorizontalInset = dx / 2; 990354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 991354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } 992354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) { 993354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.TOP: 994354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mVerticalInset = 0; 995354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 996354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.BOTTOM: 997354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mVerticalInset = dy; 998354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 999354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller case Gravity.CENTER_VERTICAL: 1000354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller default: 1001354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller mVerticalInset = dy / 2; 1002354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller break; 1003354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } 1004354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller } 1005354619c1cc1b4668c81c5368b2256335cc9e8538Jim Miller 10061c8d207201150c29ac92c424e1320c715a64b5bcJim Miller @Override 10071c8d207201150c29ac92c424e1320c715a64b5bcJim Miller protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 10081c8d207201150c29ac92c424e1320c715a64b5bcJim Miller super.onLayout(changed, left, top, right, bottom); 10091c8d207201150c29ac92c424e1320c715a64b5bcJim Miller final int width = right - left; 10101c8d207201150c29ac92c424e1320c715a64b5bcJim Miller final int height = bottom - top; 101120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller 1012c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller // Target placement width/height. This puts the targets on the greater of the ring 1013c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller // width or the specified outer radius. 1014c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller final float placementWidth = Math.max(mOuterRing.getWidth(), 2 * mOuterRadius); 1015c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller final float placementHeight = Math.max(mOuterRing.getHeight(), 2 * mOuterRadius); 1016955a016922ea49f154d190b054a202559b41a4d3Jim Miller float newWaveCenterX = mHorizontalInset 1017c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller + Math.max(width, mMaxTargetWidth + placementWidth) / 2; 1018955a016922ea49f154d190b054a202559b41a4d3Jim Miller float newWaveCenterY = mVerticalInset 1019c6e523ea9bc15f18c9cbf04b05e8d2c90298453fJim Miller + Math.max(height, + mMaxTargetHeight + placementHeight) / 2; 10201c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 102110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (mInitialLayout) { 102210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller hideChevrons(); 1023a073e570789e5b49e8339af44516444b13db4428Jim Miller hideTargets(false, false); 102410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller moveHandleTo(0, 0, false); 102510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mInitialLayout = false; 10261c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 102710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 102810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mOuterRing.setPositionX(newWaveCenterX); 102910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mOuterRing.setPositionY(newWaveCenterY); 103010c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 103110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mHandleDrawable.setPositionX(newWaveCenterX); 103210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mHandleDrawable.setPositionY(newWaveCenterY); 103310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 103410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller updateTargetPositions(newWaveCenterX, newWaveCenterY); 103510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller updateChevronPositions(newWaveCenterX, newWaveCenterY); 103610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 103710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mWaveCenterX = newWaveCenterX; 103810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller mWaveCenterY = newWaveCenterY; 103910c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 10401c8d207201150c29ac92c424e1320c715a64b5bcJim Miller if (DEBUG) dump(); 10411c8d207201150c29ac92c424e1320c715a64b5bcJim Miller } 10421c8d207201150c29ac92c424e1320c715a64b5bcJim Miller 104310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private void updateTargetPositions(float centerX, float centerY) { 10441c8d207201150c29ac92c424e1320c715a64b5bcJim Miller // Reposition the target drawables if the view changed. 104520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller ArrayList<TargetDrawable> targets = mTargetDrawables; 104620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int size = targets.size(); 104720830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final float alpha = (float) (-2.0f * Math.PI / size); 104820830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < size; i++) { 104920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final TargetDrawable targetIcon = targets.get(i); 105020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final float angle = alpha * i; 105110c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller targetIcon.setPositionX(centerX); 105210c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller targetIcon.setPositionY(centerY); 105310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller targetIcon.setX(mOuterRadius * (float) Math.cos(angle)); 105410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller targetIcon.setY(mOuterRadius * (float) Math.sin(angle)); 105510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 105610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 105710c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller 105810c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller private void updateChevronPositions(float centerX, float centerY) { 105920830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller ArrayList<TargetDrawable> chevrons = mChevronDrawables; 106020830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int size = chevrons.size(); 106120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < size; i++) { 106220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable target = chevrons.get(i); 106310c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller if (target != null) { 106410c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setPositionX(centerX); 106510c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller target.setPositionY(centerY); 106610c66afbd05e91143ea9bd109d3ce578e53dab14Jim Miller } 1067b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1068b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1069b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 1070b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private void hideChevrons() { 107120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller ArrayList<TargetDrawable> chevrons = mChevronDrawables; 107220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int size = chevrons.size(); 107320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < size; i++) { 107420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable chevron = chevrons.get(i); 107570832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller if (chevron != null) { 107670832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller chevron.setAlpha(0.0f); 107770832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller } 1078b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1079b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1080b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 1081b505074e8273887fbcd1e933738a42e770085fb8Jim Miller @Override 1082b505074e8273887fbcd1e933738a42e770085fb8Jim Miller protected void onDraw(Canvas canvas) { 1083b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mOuterRing.draw(canvas); 108420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int ntargets = mTargetDrawables.size(); 108520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < ntargets; i++) { 108620830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable target = mTargetDrawables.get(i); 108770832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller if (target != null) { 108870832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller target.draw(canvas); 108970832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller } 1090b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 109120830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller final int nchevrons = mChevronDrawables.size(); 109220830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller for (int i = 0; i < nchevrons; i++) { 109320830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller TargetDrawable chevron = mChevronDrawables.get(i); 109420830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller if (chevron != null) { 109520830421fe223bf2a8a69a67a6d26b0b5beb5baaJim Miller chevron.draw(canvas); 109670832a3d77d90f09fb7ba27612c9cbec6a92abe6Jim Miller } 1097b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1098b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mHandleDrawable.draw(canvas); 1099b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1100b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 1101b505074e8273887fbcd1e933738a42e770085fb8Jim Miller public void setOnTriggerListener(OnTriggerListener listener) { 1102b505074e8273887fbcd1e933738a42e770085fb8Jim Miller mOnTriggerListener = listener; 1103b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1104b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 1105b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float square(float d) { 1106b505074e8273887fbcd1e933738a42e770085fb8Jim Miller return d * d; 1107b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1108b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 1109b505074e8273887fbcd1e933738a42e770085fb8Jim Miller private float dist2(float dx, float dy) { 1110b505074e8273887fbcd1e933738a42e770085fb8Jim Miller return dx*dx + dy*dy; 1111b505074e8273887fbcd1e933738a42e770085fb8Jim Miller } 1112b505074e8273887fbcd1e933738a42e770085fb8Jim Miller 111366b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov private float getScaledTapRadiusSquared() { 111466b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov final float scaledTapRadius; 111566b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov if (AccessibilityManager.getInstance(mContext).isEnabled()) { 111666b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov scaledTapRadius = TAP_RADIUS_SCALE_ACCESSIBILITY_ENABLED * mTapRadius; 111766b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov } else { 111866b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov scaledTapRadius = mTapRadius; 111966b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov } 112066b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov return square(scaledTapRadius); 112166b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov } 112266b191dad09a7552d1579a4a1d57a84e27a9ddb1Svetoslav Ganov 1123f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private void announceTargets() { 1124f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov StringBuilder utterance = new StringBuilder(); 1125f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov final int targetCount = mTargetDrawables.size(); 1126f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov for (int i = 0; i < targetCount; i++) { 1127f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov String targetDescription = getTargetDescription(i); 1128f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov String directionDescription = getDirectionDescription(i); 1129f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (!TextUtils.isEmpty(targetDescription) 1130f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov && !TextUtils.isEmpty(directionDescription)) { 113157df88bc94f9aa06450e158b5a59ec29e3fe8874Svetoslav Ganov String text = String.format(directionDescription, targetDescription); 113257df88bc94f9aa06450e158b5a59ec29e3fe8874Svetoslav Ganov utterance.append(text); 113357df88bc94f9aa06450e158b5a59ec29e3fe8874Svetoslav Ganov } 113457df88bc94f9aa06450e158b5a59ec29e3fe8874Svetoslav Ganov if (utterance.length() > 0) { 113557df88bc94f9aa06450e158b5a59ec29e3fe8874Svetoslav Ganov announceText(utterance.toString()); 1136f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1137f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1138f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1139f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 1140f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private void announceText(String text) { 1141f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov setContentDescription(text); 1142f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_FOCUSED); 1143f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov setContentDescription(null); 1144f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1145f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 1146f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private String getTargetDescription(int index) { 1147f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mTargetDescriptions == null || mTargetDescriptions.isEmpty()) { 1148f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mTargetDescriptions = loadDescriptions(mTargetDescriptionsResourceId); 1149f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mTargetDrawables.size() != mTargetDescriptions.size()) { 1150f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov Log.w(TAG, "The number of target drawables must be" 1151f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov + " euqal to the number of target descriptions."); 1152f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return null; 1153f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1154f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1155f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return mTargetDescriptions.get(index); 1156f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1157f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 1158f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private String getDirectionDescription(int index) { 1159f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mDirectionDescriptions == null || mDirectionDescriptions.isEmpty()) { 1160f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov mDirectionDescriptions = loadDescriptions(mDirectionDescriptionsResourceId); 1161f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov if (mTargetDrawables.size() != mDirectionDescriptions.size()) { 1162f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov Log.w(TAG, "The number of target drawables must be" 1163f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov + " euqal to the number of direction descriptions."); 1164f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return null; 1165f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1166f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1167f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return mDirectionDescriptions.get(index); 1168f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1169f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov 1170f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov private ArrayList<String> loadDescriptions(int resourceId) { 1171f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov TypedArray array = getContext().getResources().obtainTypedArray(resourceId); 1172f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov final int count = array.length(); 1173f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov ArrayList<String> targetContentDescriptions = new ArrayList<String>(count); 1174f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov for (int i = 0; i < count; i++) { 1175f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov String contentDescription = array.getString(i); 1176f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov targetContentDescriptions.add(contentDescription); 1177f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1178f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov array.recycle(); 1179f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov return targetContentDescriptions; 1180f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov } 1181b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller 1182b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller public int getResourceIdForTarget(int index) { 1183b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller final TargetDrawable drawable = mTargetDrawables.get(index); 1184b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller return drawable == null ? 0 : drawable.getResourceId(); 1185b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1186b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller 1187b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller public void setEnableTarget(int resourceId, boolean enabled) { 1188b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller for (int i = 0; i < mTargetDrawables.size(); i++) { 1189b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller final TargetDrawable target = mTargetDrawables.get(i); 1190b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller if (target.getResourceId() == resourceId) { 1191b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller target.setEnabled(enabled); 1192b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller break; // should never be more than one match 1193b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1194b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1195b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1196b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller 1197b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller /** 1198b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller * Gets the position of a target in the array that matches the given resource. 1199b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller * @param resourceId 1200b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller * @return the index or -1 if not found 1201b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller */ 1202b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller public int getTargetPosition(int resourceId) { 1203b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller for (int i = 0; i < mTargetDrawables.size(); i++) { 1204b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller final TargetDrawable target = mTargetDrawables.get(i); 1205b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller if (target.getResourceId() == resourceId) { 1206b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller return i; // should never be more than one match 1207b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1208b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 1209b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller return -1; 1210b030476d193a423f6c1baf3053f66fc768c925e0Jim Miller } 12113294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12123294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller private boolean replaceTargetDrawables(Resources res, int existingResourceId, 12133294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller int newResourceId) { 12143294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (existingResourceId == 0 || newResourceId == 0) { 12153294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller return false; 12163294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12173294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12183294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller boolean result = false; 12193294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller final ArrayList<TargetDrawable> drawables = mTargetDrawables; 12203294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller final int size = drawables.size(); 12213294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller for (int i = 0; i < size; i++) { 12223294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller final TargetDrawable target = drawables.get(i); 12233294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (target != null && target.getResourceId() == existingResourceId) { 12243294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller target.setDrawable(res, newResourceId); 12253294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller result = true; 12263294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12273294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12283294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12293294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (result) { 12303294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller requestLayout(); // in case any given drawable's size changes 12313294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12323294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12333294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller return result; 12343294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12353294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12363294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller /** 12373294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * Searches the given package for a resource to use to replace the Drawable on the 12383294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * target with the given resource id 12393294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * @param component of the .apk that contains the resource 12403294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * @param name of the metadata in the .apk 12413294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * @param existingResId the resource id of the target to search for 12423294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller * @return true if found in the given package and replaced at least one target Drawables 12433294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller */ 12443294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller public boolean replaceTargetDrawablesIfPresent(ComponentName component, String name, 12453294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller int existingResId) { 12463294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (existingResId == 0) return false; 12473294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller 12483294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller try { 12493294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller PackageManager packageManager = mContext.getPackageManager(); 12503294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller // Look for the search icon specified in the activity meta-data 12513294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller Bundle metaData = packageManager.getActivityInfo( 12523294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller component, PackageManager.GET_META_DATA).metaData; 12533294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (metaData != null) { 12543294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller int iconResId = metaData.getInt(name); 12553294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller if (iconResId != 0) { 12563294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller Resources res = packageManager.getResourcesForActivity(component); 12573294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller return replaceTargetDrawables(res, existingResId, iconResId); 12583294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12593294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12603294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } catch (NameNotFoundException e) { 12613294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller Log.w(TAG, "Failed to swap drawable; " 12623294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller + component.flattenToShortString() + " not found", e); 12633294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } catch (Resources.NotFoundException nfe) { 12643294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller Log.w(TAG, "Failed to swap drawable from " 12653294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller + component.flattenToShortString(), nfe); 12663294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 12673294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller return false; 12683294b6b09b2f52cb44005720051c32c9c851fc9fJim Miller } 1269f058340b2f1c3d8114c48581680b4294122fe371Svetoslav Ganov} 1270