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