1 /* 2 * Copyright (C) 2016 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 17 package com.android.incallui.answer.impl.answermethod; 18 19 import android.animation.Animator; 20 import android.animation.AnimatorListenerAdapter; 21 import android.animation.AnimatorSet; 22 import android.animation.ObjectAnimator; 23 import android.animation.PropertyValuesHolder; 24 import android.animation.ValueAnimator; 25 import android.animation.ValueAnimator.AnimatorUpdateListener; 26 import android.os.Bundle; 27 import android.support.annotation.FloatRange; 28 import android.support.annotation.NonNull; 29 import android.support.annotation.Nullable; 30 import android.text.TextUtils; 31 import android.view.LayoutInflater; 32 import android.view.MotionEvent; 33 import android.view.View; 34 import android.view.View.OnClickListener; 35 import android.view.ViewGroup; 36 import android.widget.TextView; 37 import com.android.dialer.common.Assert; 38 import com.android.dialer.common.LogUtil; 39 import com.android.incallui.answer.impl.answermethod.FlingUpDownTouchHandler.OnProgressChangedListener; 40 import com.android.incallui.util.AccessibilityUtil; 41 42 /** Answer method that shows two buttons for answer/reject. */ 43 public class TwoButtonMethod extends AnswerMethod 44 implements OnClickListener, AnimatorUpdateListener { 45 46 private static final String STATE_HINT_TEXT = "hintText"; 47 private static final String STATE_INCOMING_WILL_DISCONNECT = "incomingWillDisconnect"; 48 49 private View answerButton; 50 private View answerLabel; 51 private View declineButton; 52 private View declineLabel; 53 private TextView hintTextView; 54 private boolean incomingWillDisconnect; 55 private boolean buttonClicked; 56 private CharSequence hintText; 57 @Nullable private FlingUpDownTouchHandler touchHandler; 58 59 @Override onCreate(@ullable Bundle bundle)60 public void onCreate(@Nullable Bundle bundle) { 61 super.onCreate(bundle); 62 if (bundle != null) { 63 incomingWillDisconnect = bundle.getBoolean(STATE_INCOMING_WILL_DISCONNECT); 64 hintText = bundle.getCharSequence(STATE_HINT_TEXT); 65 } 66 } 67 68 @Override onSaveInstanceState(Bundle bundle)69 public void onSaveInstanceState(Bundle bundle) { 70 super.onSaveInstanceState(bundle); 71 bundle.putBoolean(STATE_INCOMING_WILL_DISCONNECT, incomingWillDisconnect); 72 bundle.putCharSequence(STATE_HINT_TEXT, hintText); 73 } 74 75 @Nullable 76 @Override onCreateView( LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle)77 public View onCreateView( 78 LayoutInflater layoutInflater, @Nullable ViewGroup viewGroup, @Nullable Bundle bundle) { 79 View view = layoutInflater.inflate(R.layout.two_button_method, viewGroup, false); 80 81 hintTextView = (TextView) view.findViewById(R.id.two_button_hint_text); 82 updateHintText(); 83 84 answerButton = view.findViewById(R.id.two_button_answer_button); 85 answerLabel = view.findViewById(R.id.two_button_answer_label); 86 declineButton = view.findViewById(R.id.two_button_decline_button); 87 declineLabel = view.findViewById(R.id.two_button_decline_label); 88 89 boolean showLabels = getResources().getBoolean(R.bool.two_button_show_button_labels); 90 answerLabel.setVisibility(showLabels ? View.VISIBLE : View.GONE); 91 declineLabel.setVisibility(showLabels ? View.VISIBLE : View.GONE); 92 93 answerButton.setOnClickListener(this); 94 declineButton.setOnClickListener(this); 95 96 if (AccessibilityUtil.isTouchExplorationEnabled(getContext())) { 97 /* Falsing already handled by AccessibilityManager */ 98 touchHandler = 99 FlingUpDownTouchHandler.attach( 100 view, 101 new OnProgressChangedListener() { 102 @Override 103 public void onProgressChanged(@FloatRange(from = -1f, to = 1f) float progress) {} 104 105 @Override 106 public void onTrackingStart() {} 107 108 @Override 109 public void onTrackingStopped() {} 110 111 @Override 112 public void onMoveReset(boolean showHint) {} 113 114 @Override 115 public void onMoveFinish(boolean accept) { 116 if (accept) { 117 answerCall(); 118 } else { 119 rejectCall(); 120 } 121 } 122 123 @Override 124 public boolean shouldUseFalsing(@NonNull MotionEvent downEvent) { 125 return false; 126 } 127 }, 128 null /* Falsing already handled by AccessibilityManager */); 129 touchHandler.setFlingEnabled(false); 130 } 131 return view; 132 } 133 134 @Override onDestroyView()135 public void onDestroyView() { 136 super.onDestroyView(); 137 if (touchHandler != null) { 138 touchHandler.detach(); 139 touchHandler = null; 140 } 141 } 142 143 @Override setHintText(@ullable CharSequence hintText)144 public void setHintText(@Nullable CharSequence hintText) { 145 this.hintText = hintText; 146 updateHintText(); 147 } 148 149 @Override setShowIncomingWillDisconnect(boolean incomingWillDisconnect)150 public void setShowIncomingWillDisconnect(boolean incomingWillDisconnect) { 151 this.incomingWillDisconnect = incomingWillDisconnect; 152 updateHintText(); 153 } 154 updateHintText()155 private void updateHintText() { 156 if (hintTextView == null) { 157 return; 158 } 159 hintTextView.setVisibility(getActivity().isInMultiWindowMode() ? View.GONE : View.VISIBLE); 160 if (!TextUtils.isEmpty(hintText) && !buttonClicked) { 161 hintTextView.setText(hintText); 162 hintTextView.animate().alpha(1f).start(); 163 } else if (incomingWillDisconnect && !buttonClicked) { 164 hintTextView.setText(R.string.call_incoming_will_disconnect); 165 hintTextView.animate().alpha(1f).start(); 166 } else { 167 hintTextView.animate().alpha(0f).start(); 168 } 169 } 170 171 @Override onClick(View view)172 public void onClick(View view) { 173 if (view == answerButton) { 174 answerCall(); 175 LogUtil.v("TwoButtonMethod.onClick", "Call answered"); 176 } else if (view == declineButton) { 177 rejectCall(); 178 LogUtil.v("TwoButtonMethod.onClick", "two_buttonMethod Call rejected"); 179 } else { 180 Assert.fail("Unknown click from view: " + view); 181 } 182 buttonClicked = true; 183 } 184 answerCall()185 private void answerCall() { 186 ValueAnimator animator = ValueAnimator.ofFloat(0, 1); 187 animator.addUpdateListener(this); 188 animator.addListener( 189 new AnimatorListenerAdapter() { 190 private boolean canceled; 191 192 @Override 193 public void onAnimationCancel(Animator animation) { 194 canceled = true; 195 } 196 197 @Override 198 public void onAnimationEnd(Animator animation) { 199 if (!canceled) { 200 getParent().answerFromMethod(); 201 } 202 } 203 }); 204 AnimatorSet animatorSet = new AnimatorSet(); 205 animatorSet.play(animator).with(createViewHideAnimation()); 206 animatorSet.start(); 207 } 208 rejectCall()209 private void rejectCall() { 210 ValueAnimator animator = ValueAnimator.ofFloat(0, -1); 211 animator.addUpdateListener(this); 212 animator.addListener( 213 new AnimatorListenerAdapter() { 214 private boolean canceled; 215 216 @Override 217 public void onAnimationCancel(Animator animation) { 218 canceled = true; 219 } 220 221 @Override 222 public void onAnimationEnd(Animator animation) { 223 if (!canceled) { 224 getParent().rejectFromMethod(); 225 } 226 } 227 }); 228 AnimatorSet animatorSet = new AnimatorSet(); 229 animatorSet.play(animator).with(createViewHideAnimation()); 230 animatorSet.start(); 231 } 232 233 @Override onAnimationUpdate(ValueAnimator animation)234 public void onAnimationUpdate(ValueAnimator animation) { 235 getParent().onAnswerProgressUpdate(((float) animation.getAnimatedValue())); 236 } 237 createViewHideAnimation()238 private Animator createViewHideAnimation() { 239 ObjectAnimator answerButtonHide = 240 ObjectAnimator.ofPropertyValuesHolder( 241 answerButton, 242 PropertyValuesHolder.ofFloat(View.SCALE_X, 0f), 243 PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f)); 244 245 ObjectAnimator declineButtonHide = 246 ObjectAnimator.ofPropertyValuesHolder( 247 declineButton, 248 PropertyValuesHolder.ofFloat(View.SCALE_X, 0f), 249 PropertyValuesHolder.ofFloat(View.SCALE_Y, 0f)); 250 251 ObjectAnimator answerLabelHide = ObjectAnimator.ofFloat(answerLabel, View.ALPHA, 0f); 252 253 ObjectAnimator declineLabelHide = ObjectAnimator.ofFloat(declineLabel, View.ALPHA, 0f); 254 255 ObjectAnimator hintHide = ObjectAnimator.ofFloat(hintTextView, View.ALPHA, 0f); 256 257 AnimatorSet hideSet = new AnimatorSet(); 258 hideSet 259 .play(answerButtonHide) 260 .with(declineButtonHide) 261 .with(answerLabelHide) 262 .with(declineLabelHide) 263 .with(hintHide); 264 return hideSet; 265 } 266 } 267