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