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