blob: 83a9bb98da6489cf5bfdd7238150274d52d73659 [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 Klaassenf79d6f62014-08-26 12:27:08 -070030import android.support.annotation.NonNull;
Justin Klaassen3b4d13d2014-06-06 18:18:37 +010031import android.support.v4.view.ViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -070032import android.text.Editable;
33import android.text.TextUtils;
34import android.text.TextWatcher;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070035import android.view.KeyEvent;
Justin Klaassen4b3af052014-05-27 17:53:10 -070036import android.view.View;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070037import android.view.View.OnKeyListener;
Justin Klaassen4b3af052014-05-27 17:53:10 -070038import android.view.View.OnLongClickListener;
Justin Klaassen5f2a3342014-06-11 17:40:22 -070039import android.view.ViewAnimationUtils;
Justin Klaassen8fff1442014-06-19 10:43:29 -070040import android.view.ViewGroupOverlay;
Justin Klaassen4b3af052014-05-27 17:53:10 -070041import android.view.animation.AccelerateDecelerateInterpolator;
42import android.widget.Button;
Justin Klaassenfed941a2014-06-09 18:42:40 +010043import android.widget.TextView;
44
45import com.android.calculator2.CalculatorEditText.OnTextSizeChangeListener;
46import com.android.calculator2.CalculatorExpressionEvaluator.EvaluateCallback;
Justin Klaassen4b3af052014-05-27 17:53:10 -070047
Justin Klaassen04f79c72014-06-27 17:25:35 -070048public class Calculator extends Activity
Justin Klaassenfed941a2014-06-09 18:42:40 +010049 implements OnTextSizeChangeListener, EvaluateCallback, OnLongClickListener {
Justin Klaassen4b3af052014-05-27 17:53:10 -070050
Justin Klaassen2be4fdb2014-08-06 19:54:09 -070051 private static final String NAME = Calculator.class.getName();
52
53 // instance state keys
54 private static final String KEY_CURRENT_STATE = NAME + "_currentState";
55 private static final String KEY_CURRENT_EXPRESSION = NAME + "_currentExpression";
56
57 /**
58 * Constant for an invalid resource id.
59 */
60 public static final int INVALID_RES_ID = -1;
Justin Klaassen4b3af052014-05-27 17:53:10 -070061
62 private enum CalculatorState {
63 INPUT, EVALUATE, RESULT, ERROR
64 }
65
66 private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
67 @Override
68 public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
69 }
70
71 @Override
72 public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
73 }
74
75 @Override
76 public void afterTextChanged(Editable editable) {
77 setState(CalculatorState.INPUT);
Justin Klaassen04f79c72014-06-27 17:25:35 -070078 mEvaluator.evaluate(editable, Calculator.this);
Justin Klaassen4b3af052014-05-27 17:53:10 -070079 }
80 };
81
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070082 private final OnKeyListener mFormulaOnKeyListener = new OnKeyListener() {
83 @Override
84 public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
85 switch (keyCode) {
86 case KeyEvent.KEYCODE_NUMPAD_ENTER:
87 case KeyEvent.KEYCODE_ENTER:
88 if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
89 mCurrentButton = mEqualButton;
90 onEquals();
91 }
92 // ignore all other actions
93 return true;
94 case KeyEvent.KEYCODE_DEL:
95 onDelete();
96 return true;
97 }
98 return false;
99 }
100 };
101
Justin Klaassen741471e2014-06-11 09:43:44 -0700102 private final Editable.Factory mFormulaEditableFactory = new Editable.Factory() {
103 @Override
104 public Editable newEditable(CharSequence source) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700105 final boolean isEdited = mCurrentState == CalculatorState.INPUT
106 || mCurrentState == CalculatorState.ERROR;
107 return new CalculatorExpressionBuilder(source, mTokenizer, isEdited);
Justin Klaassen741471e2014-06-11 09:43:44 -0700108 }
109 };
110
Justin Klaassen4b3af052014-05-27 17:53:10 -0700111 private CalculatorState mCurrentState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700112 private CalculatorExpressionTokenizer mTokenizer;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700113 private CalculatorExpressionEvaluator mEvaluator;
114
115 private CalculatorEditText mFormulaEditText;
116 private CalculatorEditText mResultEditText;
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100117 private ViewPager mPadViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700118 private View mDeleteButton;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700119 private View mEqualButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700120 private View mClearButton;
121
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700122 private View mCurrentButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700123 private Animator mCurrentAnimator;
124
125 @Override
126 protected void onCreate(Bundle savedInstanceState) {
127 super.onCreate(savedInstanceState);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700128 setContentView(R.layout.activity_calculator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700129
130 mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
131 mResultEditText = (CalculatorEditText) findViewById(R.id.result);
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100132 mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700133 mDeleteButton = findViewById(R.id.del);
134 mClearButton = findViewById(R.id.clr);
135
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700136 mEqualButton = findViewById(R.id.pad_numeric).findViewById(R.id.eq);
137 if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
138 mEqualButton = findViewById(R.id.pad_operator).findViewById(R.id.eq);
139 }
140
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700141 mTokenizer = new CalculatorExpressionTokenizer(this);
142 mEvaluator = new CalculatorExpressionEvaluator(mTokenizer);
143
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700144 savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700145 setState(CalculatorState.values()[
146 savedInstanceState.getInt(KEY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);
147 mFormulaEditText.setText(mTokenizer.getLocalizedExpression(
148 savedInstanceState.getString(KEY_CURRENT_EXPRESSION, "")));
149 mEvaluator.evaluate(mFormulaEditText.getText(), this);
Justin Klaassen741471e2014-06-11 09:43:44 -0700150
151 mFormulaEditText.setEditableFactory(mFormulaEditableFactory);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700152 mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700153 mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
Justin Klaassenfed941a2014-06-09 18:42:40 +0100154 mFormulaEditText.setOnTextSizeChangeListener(this);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700155 mDeleteButton.setOnLongClickListener(this);
156 }
157
158 @Override
Justin Klaassenf79d6f62014-08-26 12:27:08 -0700159 protected void onSaveInstanceState(@NonNull Bundle outState) {
160 // If there's an animation in progress, cancel it first to ensure our state is up-to-date.
161 if (mCurrentAnimator != null) {
162 mCurrentAnimator.cancel();
163 }
164
Justin Klaassen4b3af052014-05-27 17:53:10 -0700165 super.onSaveInstanceState(outState);
Justin Klaassenf79d6f62014-08-26 12:27:08 -0700166
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700167 outState.putInt(KEY_CURRENT_STATE, mCurrentState.ordinal());
168 outState.putString(KEY_CURRENT_EXPRESSION,
169 mTokenizer.getNormalizedExpression(mFormulaEditText.getText().toString()));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700170 }
171
172 private void setState(CalculatorState state) {
173 if (mCurrentState != state) {
174 mCurrentState = state;
175
176 if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
177 mDeleteButton.setVisibility(View.GONE);
178 mClearButton.setVisibility(View.VISIBLE);
179 } else {
180 mDeleteButton.setVisibility(View.VISIBLE);
181 mClearButton.setVisibility(View.GONE);
182 }
183
184 if (state == CalculatorState.ERROR) {
185 final int errorColor = getResources().getColor(R.color.calculator_error_color);
186 mFormulaEditText.setTextColor(errorColor);
187 mResultEditText.setTextColor(errorColor);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700188 getWindow().setStatusBarColor(errorColor);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700189 } else {
190 mFormulaEditText.setTextColor(
191 getResources().getColor(R.color.display_formula_text_color));
192 mResultEditText.setTextColor(
193 getResources().getColor(R.color.display_result_text_color));
Justin Klaassen8fff1442014-06-19 10:43:29 -0700194 getWindow().setStatusBarColor(
Justin Klaassen4b3af052014-05-27 17:53:10 -0700195 getResources().getColor(R.color.calculator_accent_color));
196 }
197 }
198 }
199
200 @Override
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100201 public void onBackPressed() {
202 if (mPadViewPager == null || mPadViewPager.getCurrentItem() == 0) {
203 // If the user is currently looking at the first pad (or the pad is not paged),
204 // allow the system to handle the Back button.
205 super.onBackPressed();
206 } else {
207 // Otherwise, select the previous pad.
208 mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
209 }
210 }
211
212 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700213 public void onUserInteraction() {
214 super.onUserInteraction();
215
216 // If there's an animation in progress, cancel it so the user interaction can be handled
217 // immediately.
218 if (mCurrentAnimator != null) {
219 mCurrentAnimator.cancel();
220 }
221 }
222
223 public void onButtonClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700224 mCurrentButton = view;
225
Justin Klaassen4b3af052014-05-27 17:53:10 -0700226 switch (view.getId()) {
227 case R.id.eq:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700228 onEquals();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700229 break;
230 case R.id.del:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700231 onDelete();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700232 break;
233 case R.id.clr:
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700234 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700235 break;
236 case R.id.fun_cos:
237 case R.id.fun_ln:
238 case R.id.fun_log:
239 case R.id.fun_sin:
240 case R.id.fun_tan:
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700241 // Add left parenthesis after functions.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700242 mFormulaEditText.append(((Button) view).getText() + "(");
243 break;
244 default:
245 mFormulaEditText.append(((Button) view).getText());
246 break;
247 }
248 }
249
250 @Override
251 public boolean onLongClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700252 mCurrentButton = view;
253
Justin Klaassen4b3af052014-05-27 17:53:10 -0700254 if (view.getId() == R.id.del) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700255 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700256 return true;
257 }
258 return false;
259 }
260
261 @Override
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700262 public void onEvaluate(String expr, String result, int errorResourceId) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700263 if (mCurrentState == CalculatorState.INPUT) {
264 mResultEditText.setText(result);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700265 } else if (errorResourceId != INVALID_RES_ID) {
266 onError(errorResourceId);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700267 } else if (!TextUtils.isEmpty(result)) {
268 onResult(result);
269 } else if (mCurrentState == CalculatorState.EVALUATE) {
270 // The current expression cannot be evaluated -> return to the input state.
271 setState(CalculatorState.INPUT);
272 }
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700273
274 mFormulaEditText.requestFocus();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700275 }
276
Justin Klaassenfed941a2014-06-09 18:42:40 +0100277 @Override
278 public void onTextSizeChanged(final TextView textView, float oldSize) {
279 if (mCurrentState != CalculatorState.INPUT) {
280 // Only animate text changes that occur from user input.
281 return;
282 }
283
284 // Calculate the values needed to perform the scale and translation animations,
285 // maintaining the same apparent baseline for the displayed text.
286 final float textScale = oldSize / textView.getTextSize();
287 final float translationX = (1.0f - textScale) *
288 (textView.getWidth() / 2.0f - textView.getPaddingEnd());
289 final float translationY = (1.0f - textScale) *
290 (textView.getHeight() / 2.0f - textView.getPaddingBottom());
291
292 final AnimatorSet animatorSet = new AnimatorSet();
293 animatorSet.playTogether(
294 ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
295 ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
296 ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
297 ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
Justin Klaassen94db7202014-06-11 11:22:31 -0700298 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
Justin Klaassenfed941a2014-06-09 18:42:40 +0100299 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
300 animatorSet.start();
301 }
302
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700303 private void onEquals() {
304 if (mCurrentState == CalculatorState.INPUT) {
305 setState(CalculatorState.EVALUATE);
306 mEvaluator.evaluate(mFormulaEditText.getText(), this);
307 }
308 }
309
310 private void onDelete() {
311 // Delete works like backspace; remove the last character from the expression.
312 final Editable formulaText = mFormulaEditText.getEditableText();
313 final int formulaLength = formulaText.length();
314 if (formulaLength > 0) {
315 formulaText.delete(formulaLength - 1, formulaLength);
316 }
317 }
318
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700319 private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700320 final View displayView = findViewById(R.id.display);
321 final View decorView = getWindow().getDecorView();
322
323 final Rect displayRect = new Rect();
324 displayView.getGlobalVisibleRect(displayRect);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700325
326 // Make reveal cover the display and status bar.
327 final View revealView = new View(this);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700328 revealView.setBottom(displayRect.bottom);
329 revealView.setLeft(displayRect.left);
330 revealView.setRight(displayRect.right);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700331 revealView.setBackgroundColor(getResources().getColor(colorRes));
332
Justin Klaassen4b3af052014-05-27 17:53:10 -0700333 final int[] clearLocation = new int[2];
334 sourceView.getLocationInWindow(clearLocation);
335 clearLocation[0] += sourceView.getWidth() / 2;
336 clearLocation[1] += sourceView.getHeight() / 2;
337
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700338 final int revealCenterX = clearLocation[0] - revealView.getLeft();
339 final int revealCenterY = clearLocation[1] - revealView.getTop();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700340
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700341 final double x1_2 = Math.pow(revealView.getLeft() - revealCenterX, 2);
342 final double x2_2 = Math.pow(revealView.getRight() - revealCenterX, 2);
343 final double y_2 = Math.pow(revealView.getTop() - revealCenterY, 2);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700344 final float revealRadius = (float) Math.max(Math.sqrt(x1_2 + y_2), Math.sqrt(x2_2 + y_2));
345
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700346 final Animator revealAnimator =
347 ViewAnimationUtils.createCircularReveal(revealView,
ztenghui3d6ecaf2014-06-05 09:56:00 -0700348 revealCenterX, revealCenterY, 0.0f, revealRadius);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700349 revealAnimator.setDuration(
Justin Klaassen4b3af052014-05-27 17:53:10 -0700350 getResources().getInteger(android.R.integer.config_longAnimTime));
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700351 revealAnimator.addListener(listener);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700352
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700353 final Animator alphaAnimator = ObjectAnimator.ofFloat(revealView, View.ALPHA, 0.0f);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700354 alphaAnimator.setDuration(
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700355 getResources().getInteger(android.R.integer.config_mediumAnimTime));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700356
Justin Klaassen8fff1442014-06-19 10:43:29 -0700357 final ViewGroupOverlay groupOverlay = (ViewGroupOverlay) decorView.getOverlay();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700358 final AnimatorSet animatorSet = new AnimatorSet();
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700359 animatorSet.play(revealAnimator).before(alphaAnimator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700360 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
361 animatorSet.addListener(new AnimatorListenerAdapter() {
362 @Override
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700363 public void onAnimationStart(Animator animation) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700364 groupOverlay.add(revealView);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700365 }
366
367 @Override
368 public void onAnimationEnd(Animator animator) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700369 groupOverlay.remove(revealView);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700370 mCurrentAnimator = null;
371 }
372 });
373
374 mCurrentAnimator = animatorSet;
375 animatorSet.start();
376 }
377
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700378 private void onClear() {
379 if (TextUtils.isEmpty(mFormulaEditText.getText())) {
380 return;
381 }
382
383 reveal(mCurrentButton, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
384 @Override
385 public void onAnimationEnd(Animator animation) {
386 mFormulaEditText.getEditableText().clear();
387 }
388 });
389 }
390
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700391 private void onError(final int errorResourceId) {
392 if (mCurrentState != CalculatorState.EVALUATE) {
393 // Only animate error on evaluate.
394 mResultEditText.setText(errorResourceId);
395 return;
396 }
397
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700398 reveal(mCurrentButton, R.color.calculator_error_color, new AnimatorListenerAdapter() {
399 @Override
400 public void onAnimationEnd(Animator animation) {
401 setState(CalculatorState.ERROR);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700402 mResultEditText.setText(errorResourceId);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700403 }
404 });
405 }
406
Justin Klaassen4b3af052014-05-27 17:53:10 -0700407 private void onResult(final String result) {
408 // Calculate the values needed to perform the scale and translation animations,
409 // accounting for how the scale will affect the final position of the text.
410 final float resultScale =
411 mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
412 final float resultTranslationX = (1.0f - resultScale) *
413 (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingEnd());
414 final float resultTranslationY = (1.0f - resultScale) *
415 (mResultEditText.getHeight() / 2.0f - mResultEditText.getPaddingBottom()) +
416 (mFormulaEditText.getBottom() - mResultEditText.getBottom()) +
417 (mResultEditText.getPaddingBottom() - mFormulaEditText.getPaddingBottom());
418 final float formulaTranslationY = -mFormulaEditText.getBottom();
419
420 // Use a value animator to fade to the final text color over the course of the animation.
421 final int resultTextColor = mResultEditText.getCurrentTextColor();
422 final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
423 final ValueAnimator textColorAnimator =
424 ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
425 textColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
426 @Override
427 public void onAnimationUpdate(ValueAnimator valueAnimator) {
428 mResultEditText.setTextColor((int) valueAnimator.getAnimatedValue());
429 }
430 });
431
432 final AnimatorSet animatorSet = new AnimatorSet();
433 animatorSet.playTogether(
434 textColorAnimator,
435 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
436 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
437 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
438 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
439 ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
440 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
441 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
442 animatorSet.addListener(new AnimatorListenerAdapter() {
443 @Override
444 public void onAnimationStart(Animator animation) {
445 mResultEditText.setText(result);
446 }
447
448 @Override
449 public void onAnimationEnd(Animator animation) {
450 // Reset all of the values modified during the animation.
451 mResultEditText.setTextColor(resultTextColor);
452 mResultEditText.setScaleX(1.0f);
453 mResultEditText.setScaleY(1.0f);
454 mResultEditText.setTranslationX(0.0f);
455 mResultEditText.setTranslationY(0.0f);
456 mFormulaEditText.setTranslationY(0.0f);
457
458 // Finally update the formula to use the current result.
459 mFormulaEditText.setText(result);
460 setState(CalculatorState.RESULT);
461
462 mCurrentAnimator = null;
463 }
464 });
465
466 mCurrentAnimator = animatorSet;
467 animatorSet.start();
468 }
469}