1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.systemui.statusbar.phone; 17 18 import android.animation.Animator; 19 import android.animation.Animator.AnimatorListener; 20 import android.animation.ObjectAnimator; 21 import android.content.Context; 22 import android.util.AttributeSet; 23 import android.view.HapticFeedbackConstants; 24 import android.view.MotionEvent; 25 import android.view.View; 26 import android.view.ViewConfiguration; 27 import android.view.animation.Animation; 28 import android.view.animation.AnimationUtils; 29 30 import com.android.keyguard.AlphaOptimizedImageButton; 31 import com.android.systemui.Interpolators; 32 33 public class SettingsButton extends AlphaOptimizedImageButton { 34 35 private static final long LONG_PRESS_LENGTH = 1000; 36 private static final long ACCEL_LENGTH = 750; 37 private static final long FULL_SPEED_LENGTH = 375; 38 private static final long RUN_DURATION = 350; 39 40 private boolean mUpToSpeed; 41 private ObjectAnimator mAnimator; 42 43 private float mSlop; 44 45 public SettingsButton(Context context, AttributeSet attrs) { 46 super(context, attrs); 47 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 48 } 49 50 public boolean isAnimating() { 51 return mAnimator != null && mAnimator.isRunning(); 52 } 53 54 public boolean isTunerClick() { 55 return mUpToSpeed; 56 } 57 58 @Override 59 public boolean onTouchEvent(MotionEvent event) { 60 switch (event.getActionMasked()) { 61 case MotionEvent.ACTION_DOWN: 62 postDelayed(mLongPressCallback, LONG_PRESS_LENGTH); 63 break; 64 case MotionEvent.ACTION_UP: 65 if (mUpToSpeed) { 66 startExitAnimation(); 67 } else { 68 cancelLongClick(); 69 } 70 break; 71 case MotionEvent.ACTION_CANCEL: 72 cancelLongClick(); 73 break; 74 case MotionEvent.ACTION_MOVE: 75 float x = event.getX(); 76 float y = event.getY(); 77 if ((x < -mSlop) || (y < -mSlop) || (x > getWidth() + mSlop) 78 || (y > getHeight() + mSlop)) { 79 cancelLongClick(); 80 } 81 break; 82 } 83 return super.onTouchEvent(event); 84 } 85 86 private void cancelLongClick() { 87 cancelAnimation(); 88 mUpToSpeed = false; 89 removeCallbacks(mLongPressCallback); 90 } 91 92 private void cancelAnimation() { 93 if (mAnimator != null) { 94 mAnimator.removeAllListeners(); 95 mAnimator.cancel(); 96 mAnimator = null; 97 } 98 } 99 100 private void startExitAnimation() { 101 animate() 102 .translationX(((View) getParent().getParent()).getWidth() - getX()) 103 .alpha(0) 104 .setDuration(RUN_DURATION) 105 .setInterpolator(AnimationUtils.loadInterpolator(mContext, 106 android.R.interpolator.accelerate_cubic)) 107 .setListener(new AnimatorListener() { 108 @Override 109 public void onAnimationStart(Animator animation) { 110 } 111 112 @Override 113 public void onAnimationRepeat(Animator animation) { 114 } 115 116 @Override 117 public void onAnimationEnd(Animator animation) { 118 setAlpha(1f); 119 setTranslationX(0); 120 cancelLongClick(); 121 } 122 123 @Override 124 public void onAnimationCancel(Animator animation) { 125 } 126 }) 127 .start(); 128 } 129 130 protected void startAccelSpin() { 131 cancelAnimation(); 132 mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360); 133 mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, 134 android.R.interpolator.accelerate_quad)); 135 mAnimator.setDuration(ACCEL_LENGTH); 136 mAnimator.addListener(new AnimatorListener() { 137 @Override 138 public void onAnimationStart(Animator animation) { 139 } 140 141 @Override 142 public void onAnimationRepeat(Animator animation) { 143 } 144 145 @Override 146 public void onAnimationEnd(Animator animation) { 147 startContinuousSpin(); 148 } 149 150 @Override 151 public void onAnimationCancel(Animator animation) { 152 } 153 }); 154 mAnimator.start(); 155 } 156 157 protected void startContinuousSpin() { 158 cancelAnimation(); 159 performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 160 mUpToSpeed = true; 161 mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360); 162 mAnimator.setInterpolator(Interpolators.LINEAR); 163 mAnimator.setDuration(FULL_SPEED_LENGTH); 164 mAnimator.setRepeatCount(Animation.INFINITE); 165 mAnimator.start(); 166 } 167 168 private final Runnable mLongPressCallback = new Runnable() { 169 @Override 170 public void run() { 171 startAccelSpin(); 172 } 173 }; 174 } 175