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.accessibility.AccessibilityNodeInfo; 28 import android.view.animation.Animation; 29 import android.view.animation.AnimationUtils; 30 import android.widget.Button; 31 32 import com.android.app.animation.Interpolators; 33 import com.android.systemui.statusbar.AlphaOptimizedImageView; 34 35 public class SettingsButton extends AlphaOptimizedImageView { 36 37 private static final boolean TUNER_ENABLE_AVAILABLE = false; 38 39 private static final long LONG_PRESS_LENGTH = 1000; 40 private static final long ACCEL_LENGTH = 750; 41 private static final long FULL_SPEED_LENGTH = 375; 42 private static final long RUN_DURATION = 350; 43 44 private boolean mUpToSpeed; 45 private ObjectAnimator mAnimator; 46 47 private float mSlop; 48 SettingsButton(Context context, AttributeSet attrs)49 public SettingsButton(Context context, AttributeSet attrs) { 50 super(context, attrs); 51 mSlop = ViewConfiguration.get(getContext()).getScaledTouchSlop(); 52 } 53 isAnimating()54 public boolean isAnimating() { 55 return mAnimator != null && mAnimator.isRunning(); 56 } 57 isTunerClick()58 public boolean isTunerClick() { 59 return mUpToSpeed; 60 } 61 62 @Override onTouchEvent(MotionEvent event)63 public boolean onTouchEvent(MotionEvent event) { 64 switch (event.getActionMasked()) { 65 case MotionEvent.ACTION_DOWN: 66 if (TUNER_ENABLE_AVAILABLE) postDelayed(mLongPressCallback, LONG_PRESS_LENGTH); 67 break; 68 case MotionEvent.ACTION_UP: 69 if (mUpToSpeed) { 70 startExitAnimation(); 71 } else { 72 cancelLongClick(); 73 } 74 break; 75 case MotionEvent.ACTION_CANCEL: 76 cancelLongClick(); 77 break; 78 case MotionEvent.ACTION_MOVE: 79 float x = event.getX(); 80 float y = event.getY(); 81 if ((x < -mSlop) || (y < -mSlop) || (x > getWidth() + mSlop) 82 || (y > getHeight() + mSlop)) { 83 cancelLongClick(); 84 } 85 break; 86 } 87 return super.onTouchEvent(event); 88 } 89 cancelLongClick()90 private void cancelLongClick() { 91 cancelAnimation(); 92 mUpToSpeed = false; 93 removeCallbacks(mLongPressCallback); 94 } 95 cancelAnimation()96 private void cancelAnimation() { 97 if (mAnimator != null) { 98 mAnimator.removeAllListeners(); 99 mAnimator.cancel(); 100 mAnimator = null; 101 } 102 } 103 startExitAnimation()104 private void startExitAnimation() { 105 animate() 106 .translationX(((View) getParent().getParent()).getWidth() - getX()) 107 .alpha(0) 108 .setDuration(RUN_DURATION) 109 .setInterpolator(AnimationUtils.loadInterpolator(mContext, 110 android.R.interpolator.accelerate_cubic)) 111 .setListener(new AnimatorListener() { 112 @Override 113 public void onAnimationStart(Animator animation) { 114 } 115 116 @Override 117 public void onAnimationRepeat(Animator animation) { 118 } 119 120 @Override 121 public void onAnimationEnd(Animator animation) { 122 setAlpha(1f); 123 setTranslationX(0); 124 cancelLongClick(); 125 // Unset the listener, otherwise this may persist for 126 // another view property animation 127 animate().setListener(null); 128 } 129 130 @Override 131 public void onAnimationCancel(Animator animation) { 132 } 133 }) 134 .start(); 135 } 136 startAccelSpin()137 protected void startAccelSpin() { 138 cancelAnimation(); 139 mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360); 140 mAnimator.setInterpolator(AnimationUtils.loadInterpolator(mContext, 141 android.R.interpolator.accelerate_quad)); 142 mAnimator.setDuration(ACCEL_LENGTH); 143 mAnimator.addListener(new AnimatorListener() { 144 @Override 145 public void onAnimationStart(Animator animation) { 146 } 147 148 @Override 149 public void onAnimationRepeat(Animator animation) { 150 } 151 152 @Override 153 public void onAnimationEnd(Animator animation) { 154 startContinuousSpin(); 155 } 156 157 @Override 158 public void onAnimationCancel(Animator animation) { 159 } 160 }); 161 mAnimator.start(); 162 } 163 startContinuousSpin()164 protected void startContinuousSpin() { 165 cancelAnimation(); 166 performHapticFeedback(HapticFeedbackConstants.LONG_PRESS); 167 mUpToSpeed = true; 168 mAnimator = ObjectAnimator.ofFloat(this, View.ROTATION, 0, 360); 169 mAnimator.setInterpolator(Interpolators.LINEAR); 170 mAnimator.setDuration(FULL_SPEED_LENGTH); 171 mAnimator.setRepeatCount(Animation.INFINITE); 172 mAnimator.start(); 173 } 174 175 @Override onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)176 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 177 super.onInitializeAccessibilityNodeInfoInternal(info); 178 info.setClassName(Button.class.getName()); 179 } 180 181 private final Runnable mLongPressCallback = new Runnable() { 182 @Override 183 public void run() { 184 startAccelSpin(); 185 } 186 }; 187 } 188