blob: d4f10f0e3b8f4aa01b9d8dd5ecd9168c7b2c09fb [file] [log] [blame]
Justin Klaassen4b3af052014-05-27 17:53:10 -07001/*
2 * Copyright (C) 2014 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
17package com.android.calculator2;
18
19import android.animation.Animator;
Justin Klaassen5f2a3342014-06-11 17:40:22 -070020import android.animation.Animator.AnimatorListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070021import android.animation.AnimatorListenerAdapter;
22import android.animation.AnimatorSet;
23import android.animation.ArgbEvaluator;
24import android.animation.ObjectAnimator;
25import android.animation.ValueAnimator;
26import android.animation.ValueAnimator.AnimatorUpdateListener;
27import android.app.Activity;
Justin Klaassen8fff1442014-06-19 10:43:29 -070028import android.graphics.Rect;
Justin Klaassen4b3af052014-05-27 17:53:10 -070029import android.os.Bundle;
Justin Klaassen3b4d13d2014-06-06 18:18:37 +010030import android.support.v4.view.ViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -070031import android.text.Editable;
32import android.text.TextUtils;
33import android.text.TextWatcher;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070034import android.view.KeyEvent;
Justin Klaassen4b3af052014-05-27 17:53:10 -070035import android.view.View;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070036import android.view.View.OnKeyListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070037import android.view.View.OnLongClickListener;
Justin Klaassen5f2a3342014-06-11 17:40:22 -070038import android.view.ViewAnimationUtils;
Justin Klaassen8fff1442014-06-19 10:43:29 -070039import android.view.ViewGroupOverlay;
Justin Klaassen4b3af052014-05-27 17:53:10 -070040import android.view.animation.AccelerateDecelerateInterpolator;
41import android.widget.Button;
Justin Klaassenfed941a2014-06-09 18:42:40 +010042import android.widget.TextView;
43
44import com.android.calculator2.CalculatorEditText.OnTextSizeChangeListener;
45import com.android.calculator2.CalculatorExpressionEvaluator.EvaluateCallback;
Justin Klaassen4b3af052014-05-27 17:53:10 -070046
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070047import java.lang.Override;
48
Justin Klaassen04f79c72014-06-27 17:25:35 -070049public class Calculator extends Activity
Justin Klaassenfed941a2014-06-09 18:42:40 +010050 implements OnTextSizeChangeListener, EvaluateCallback, OnLongClickListener {
Justin Klaassen4b3af052014-05-27 17:53:10 -070051
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070052 private static final String NAME = Calculator.class.getName();
53
54 // instance state keys
55 private static final String KEY_CURRENT_STATE = NAME + "_currentState";
56 private static final String KEY_CURRENT_EXPRESSION = NAME + "_currentExpression";
57
58 /**
59 * Constant for an invalid resource id.
60 */
61 public static final int INVALID_RES_ID = -1;
Justin Klaassen4b3af052014-05-27 17:53:10 -070062
63 private enum CalculatorState {
64 INPUT, EVALUATE, RESULT, ERROR
65 }
66
67 private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
68 @Override
69 public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
70 }
71
72 @Override
73 public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
74 }
75
76 @Override
77 public void afterTextChanged(Editable editable) {
78 setState(CalculatorState.INPUT);
Justin Klaassen04f79c72014-06-27 17:25:35 -070079 mEvaluator.evaluate(editable, Calculator.this);
Justin Klaassen4b3af052014-05-27 17:53:10 -070080 }
81 };
82
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070083 private final OnKeyListener mFormulaOnKeyListener = new OnKeyListener() {
84 @Override
85 public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
86 switch (keyCode) {
87 case KeyEvent.KEYCODE_NUMPAD_ENTER:
88 case KeyEvent.KEYCODE_ENTER:
89 if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
90 mCurrentButton = mEqualButton;
91 onEquals();
92 }
93 // ignore all other actions
94 return true;
95 case KeyEvent.KEYCODE_DEL:
96 onDelete();
97 return true;
98 }
99 return false;
100 }
101 };
102
Justin Klaassen741471e2014-06-11 09:43:44 -0700103 private final Editable.Factory mFormulaEditableFactory = new Editable.Factory() {
104 @Override
105 public Editable newEditable(CharSequence source) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700106 final boolean isEdited = mCurrentState == CalculatorState.INPUT
107 || mCurrentState == CalculatorState.ERROR;
108 return new CalculatorExpressionBuilder(source, mTokenizer, isEdited);
Justin Klaassen741471e2014-06-11 09:43:44 -0700109 }
110 };
111
Justin Klaassen4b3af052014-05-27 17:53:10 -0700112 private CalculatorState mCurrentState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700113 private CalculatorExpressionTokenizer mTokenizer;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700114 private CalculatorExpressionEvaluator mEvaluator;
115
116 private CalculatorEditText mFormulaEditText;
117 private CalculatorEditText mResultEditText;
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100118 private ViewPager mPadViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700119 private View mDeleteButton;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700120 private View mEqualButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700121 private View mClearButton;
122
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700123 private View mCurrentButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700124 private Animator mCurrentAnimator;
125
126 @Override
127 protected void onCreate(Bundle savedInstanceState) {
128 super.onCreate(savedInstanceState);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700129 setContentView(R.layout.activity_calculator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700130
131 mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
132 mResultEditText = (CalculatorEditText) findViewById(R.id.result);
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100133 mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700134 mDeleteButton = findViewById(R.id.del);
135 mClearButton = findViewById(R.id.clr);
136
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700137 mEqualButton = findViewById(R.id.pad_numeric).findViewById(R.id.eq);
138 if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
139 mEqualButton = findViewById(R.id.pad_operator).findViewById(R.id.eq);
140 }
141
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700142 mTokenizer = new CalculatorExpressionTokenizer(this);
143 mEvaluator = new CalculatorExpressionEvaluator(mTokenizer);
144
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700145 savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700146 setState(CalculatorState.values()[
147 savedInstanceState.getInt(KEY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);
148 mFormulaEditText.setText(mTokenizer.getLocalizedExpression(
149 savedInstanceState.getString(KEY_CURRENT_EXPRESSION, "")));
150 mEvaluator.evaluate(mFormulaEditText.getText(), this);
Justin Klaassen741471e2014-06-11 09:43:44 -0700151
152 mFormulaEditText.setEditableFactory(mFormulaEditableFactory);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700153 mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700154 mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
Justin Klaassenfed941a2014-06-09 18:42:40 +0100155 mFormulaEditText.setOnTextSizeChangeListener(this);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700156 mDeleteButton.setOnLongClickListener(this);
157 }
158
159 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700160 protected void onSaveInstanceState(Bundle outState) {
161 super.onSaveInstanceState(outState);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700162 outState.putInt(KEY_CURRENT_STATE, mCurrentState.ordinal());
163 outState.putString(KEY_CURRENT_EXPRESSION,
164 mTokenizer.getNormalizedExpression(mFormulaEditText.getText().toString()));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700165 }
166
167 private void setState(CalculatorState state) {
168 if (mCurrentState != state) {
169 mCurrentState = state;
170
171 if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
172 mDeleteButton.setVisibility(View.GONE);
173 mClearButton.setVisibility(View.VISIBLE);
174 } else {
175 mDeleteButton.setVisibility(View.VISIBLE);
176 mClearButton.setVisibility(View.GONE);
177 }
178
179 if (state == CalculatorState.ERROR) {
180 final int errorColor = getResources().getColor(R.color.calculator_error_color);
181 mFormulaEditText.setTextColor(errorColor);
182 mResultEditText.setTextColor(errorColor);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700183 getWindow().setStatusBarColor(errorColor);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700184 } else {
185 mFormulaEditText.setTextColor(
186 getResources().getColor(R.color.display_formula_text_color));
187 mResultEditText.setTextColor(
188 getResources().getColor(R.color.display_result_text_color));
Justin Klaassen8fff1442014-06-19 10:43:29 -0700189 getWindow().setStatusBarColor(
Justin Klaassen4b3af052014-05-27 17:53:10 -0700190 getResources().getColor(R.color.calculator_accent_color));
191 }
192 }
193 }
194
195 @Override
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100196 public void onBackPressed() {
197 if (mPadViewPager == null || mPadViewPager.getCurrentItem() == 0) {
198 // If the user is currently looking at the first pad (or the pad is not paged),
199 // allow the system to handle the Back button.
200 super.onBackPressed();
201 } else {
202 // Otherwise, select the previous pad.
203 mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
204 }
205 }
206
207 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700208 public void onUserInteraction() {
209 super.onUserInteraction();
210
211 // If there's an animation in progress, cancel it so the user interaction can be handled
212 // immediately.
213 if (mCurrentAnimator != null) {
214 mCurrentAnimator.cancel();
215 }
216 }
217
218 public void onButtonClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700219 mCurrentButton = view;
220
Justin Klaassen4b3af052014-05-27 17:53:10 -0700221 switch (view.getId()) {
222 case R.id.eq:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700223 onEquals();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700224 break;
225 case R.id.del:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700226 onDelete();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700227 break;
228 case R.id.clr:
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700229 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700230 break;
231 case R.id.fun_cos:
232 case R.id.fun_ln:
233 case R.id.fun_log:
234 case R.id.fun_sin:
235 case R.id.fun_tan:
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700236 // Add left parenthesis after functions.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700237 mFormulaEditText.append(((Button) view).getText() + "(");
238 break;
239 default:
240 mFormulaEditText.append(((Button) view).getText());
241 break;
242 }
243 }
244
245 @Override
246 public boolean onLongClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700247 mCurrentButton = view;
248
Justin Klaassen4b3af052014-05-27 17:53:10 -0700249 if (view.getId() == R.id.del) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700250 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700251 return true;
252 }
253 return false;
254 }
255
256 @Override
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700257 public void onEvaluate(String expr, String result, int errorResourceId) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700258 if (mCurrentState == CalculatorState.INPUT) {
259 mResultEditText.setText(result);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700260 } else if (errorResourceId != INVALID_RES_ID) {
261 onError(errorResourceId);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700262 } else if (!TextUtils.isEmpty(result)) {
263 onResult(result);
264 } else if (mCurrentState == CalculatorState.EVALUATE) {
265 // The current expression cannot be evaluated -> return to the input state.
266 setState(CalculatorState.INPUT);
267 }
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700268
269 mFormulaEditText.requestFocus();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700270 }
271
Justin Klaassenfed941a2014-06-09 18:42:40 +0100272 @Override
273 public void onTextSizeChanged(final TextView textView, float oldSize) {
274 if (mCurrentState != CalculatorState.INPUT) {
275 // Only animate text changes that occur from user input.
276 return;
277 }
278
279 // Calculate the values needed to perform the scale and translation animations,
280 // maintaining the same apparent baseline for the displayed text.
281 final float textScale = oldSize / textView.getTextSize();
282 final float translationX = (1.0f - textScale) *
283 (textView.getWidth() / 2.0f - textView.getPaddingEnd());
284 final float translationY = (1.0f - textScale) *
285 (textView.getHeight() / 2.0f - textView.getPaddingBottom());
286
287 final AnimatorSet animatorSet = new AnimatorSet();
288 animatorSet.playTogether(
289 ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
290 ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
291 ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
292 ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
Justin Klaassen94db7202014-06-11 11:22:31 -0700293 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
Justin Klaassenfed941a2014-06-09 18:42:40 +0100294 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
295 animatorSet.start();
296 }
297
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700298 private void onEquals() {
299 if (mCurrentState == CalculatorState.INPUT) {
300 setState(CalculatorState.EVALUATE);
301 mEvaluator.evaluate(mFormulaEditText.getText(), this);
302 }
303 }
304
305 private void onDelete() {
306 // Delete works like backspace; remove the last character from the expression.
307 final Editable formulaText = mFormulaEditText.getEditableText();
308 final int formulaLength = formulaText.length();
309 if (formulaLength > 0) {
310 formulaText.delete(formulaLength - 1, formulaLength);
311 }
312 }
313
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700314 private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700315 final View displayView = findViewById(R.id.display);
316 final View decorView = getWindow().getDecorView();
317
318 final Rect displayRect = new Rect();
319 displayView.getGlobalVisibleRect(displayRect);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700320
321 // Make reveal cover the display and status bar.
322 final View revealView = new View(this);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700323 revealView.setBottom(displayRect.bottom);
324 revealView.setLeft(displayRect.left);
325 revealView.setRight(displayRect.right);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700326 revealView.setBackgroundColor(getResources().getColor(colorRes));
327
Justin Klaassen4b3af052014-05-27 17:53:10 -0700328 final int[] clearLocation = new int[2];
329 sourceView.getLocationInWindow(clearLocation);
330 clearLocation[0] += sourceView.getWidth() / 2;
331 clearLocation[1] += sourceView.getHeight() / 2;
332
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700333 final int revealCenterX = clearLocation[0] - revealView.getLeft();
334 final int revealCenterY = clearLocation[1] - revealView.getTop();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700335
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700336 final double x1_2 = Math.pow(revealView.getLeft() - revealCenterX, 2);
337 final double x2_2 = Math.pow(revealView.getRight() - revealCenterX, 2);
338 final double y_2 = Math.pow(revealView.getTop() - revealCenterY, 2);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700339 final float revealRadius = (float) Math.max(Math.sqrt(x1_2 + y_2), Math.sqrt(x2_2 + y_2));
340
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700341 final Animator revealAnimator =
342 ViewAnimationUtils.createCircularReveal(revealView,
ztenghui3d6ecaf2014-06-05 09:56:00 -0700343 revealCenterX, revealCenterY, 0.0f, revealRadius);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700344 revealAnimator.setDuration(
Justin Klaassen4b3af052014-05-27 17:53:10 -0700345 getResources().getInteger(android.R.integer.config_longAnimTime));
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700346 revealAnimator.addListener(listener);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700347
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700348 final Animator alphaAnimator = ObjectAnimator.ofFloat(revealView, View.ALPHA, 0.0f);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700349 alphaAnimator.setDuration(
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700350 getResources().getInteger(android.R.integer.config_mediumAnimTime));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700351
Justin Klaassen8fff1442014-06-19 10:43:29 -0700352 final ViewGroupOverlay groupOverlay = (ViewGroupOverlay) decorView.getOverlay();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700353 final AnimatorSet animatorSet = new AnimatorSet();
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700354 animatorSet.play(revealAnimator).before(alphaAnimator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700355 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
356 animatorSet.addListener(new AnimatorListenerAdapter() {
357 @Override
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700358 public void onAnimationStart(Animator animation) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700359 groupOverlay.add(revealView);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700360 }
361
362 @Override
363 public void onAnimationEnd(Animator animator) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700364 groupOverlay.remove(revealView);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700365 mCurrentAnimator = null;
366 }
367 });
368
369 mCurrentAnimator = animatorSet;
370 animatorSet.start();
371 }
372
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700373 private void onClear() {
374 if (TextUtils.isEmpty(mFormulaEditText.getText())) {
375 return;
376 }
377
378 reveal(mCurrentButton, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
379 @Override
380 public void onAnimationEnd(Animator animation) {
381 mFormulaEditText.getEditableText().clear();
382 }
383 });
384 }
385
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700386 private void onError(final int errorResourceId) {
387 if (mCurrentState != CalculatorState.EVALUATE) {
388 // Only animate error on evaluate.
389 mResultEditText.setText(errorResourceId);
390 return;
391 }
392
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700393 reveal(mCurrentButton, R.color.calculator_error_color, new AnimatorListenerAdapter() {
394 @Override
395 public void onAnimationEnd(Animator animation) {
396 setState(CalculatorState.ERROR);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700397 mResultEditText.setText(errorResourceId);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700398 }
399 });
400 }
401
Justin Klaassen4b3af052014-05-27 17:53:10 -0700402 private void onResult(final String result) {
403 // Calculate the values needed to perform the scale and translation animations,
404 // accounting for how the scale will affect the final position of the text.
405 final float resultScale =
406 mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
407 final float resultTranslationX = (1.0f - resultScale) *
408 (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingEnd());
409 final float resultTranslationY = (1.0f - resultScale) *
410 (mResultEditText.getHeight() / 2.0f - mResultEditText.getPaddingBottom()) +
411 (mFormulaEditText.getBottom() - mResultEditText.getBottom()) +
412 (mResultEditText.getPaddingBottom() - mFormulaEditText.getPaddingBottom());
413 final float formulaTranslationY = -mFormulaEditText.getBottom();
414
415 // Use a value animator to fade to the final text color over the course of the animation.
416 final int resultTextColor = mResultEditText.getCurrentTextColor();
417 final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
418 final ValueAnimator textColorAnimator =
419 ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
420 textColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
421 @Override
422 public void onAnimationUpdate(ValueAnimator valueAnimator) {
423 mResultEditText.setTextColor((int) valueAnimator.getAnimatedValue());
424 }
425 });
426
427 final AnimatorSet animatorSet = new AnimatorSet();
428 animatorSet.playTogether(
429 textColorAnimator,
430 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
431 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
432 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
433 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
434 ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
435 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
436 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
437 animatorSet.addListener(new AnimatorListenerAdapter() {
438 @Override
439 public void onAnimationStart(Animator animation) {
440 mResultEditText.setText(result);
441 }
442
443 @Override
444 public void onAnimationEnd(Animator animation) {
445 // Reset all of the values modified during the animation.
446 mResultEditText.setTextColor(resultTextColor);
447 mResultEditText.setScaleX(1.0f);
448 mResultEditText.setScaleY(1.0f);
449 mResultEditText.setTranslationX(0.0f);
450 mResultEditText.setTranslationY(0.0f);
451 mFormulaEditText.setTranslationY(0.0f);
452
453 // Finally update the formula to use the current result.
454 mFormulaEditText.setText(result);
455 setState(CalculatorState.RESULT);
456
457 mCurrentAnimator = null;
458 }
459 });
460
461 mCurrentAnimator = animatorSet;
462 animatorSet.start();
463 }
464}