blob: 5dd04bf881305d5a50ae5717990cbc2e483df8e2 [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;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -070094 }
95 return false;
96 }
97 };
98
Justin Klaassen741471e2014-06-11 09:43:44 -070099 private final Editable.Factory mFormulaEditableFactory = new Editable.Factory() {
100 @Override
101 public Editable newEditable(CharSequence source) {
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700102 final boolean isEdited = mCurrentState == CalculatorState.INPUT
103 || mCurrentState == CalculatorState.ERROR;
104 return new CalculatorExpressionBuilder(source, mTokenizer, isEdited);
Justin Klaassen741471e2014-06-11 09:43:44 -0700105 }
106 };
107
Justin Klaassen4b3af052014-05-27 17:53:10 -0700108 private CalculatorState mCurrentState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700109 private CalculatorExpressionTokenizer mTokenizer;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700110 private CalculatorExpressionEvaluator mEvaluator;
111
Justin Klaassen06360f92014-08-28 11:08:44 -0700112 private View mDisplayView;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700113 private CalculatorEditText mFormulaEditText;
114 private CalculatorEditText mResultEditText;
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100115 private ViewPager mPadViewPager;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700116 private View mDeleteButton;
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700117 private View mEqualButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700118 private View mClearButton;
119
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700120 private View mCurrentButton;
Justin Klaassen4b3af052014-05-27 17:53:10 -0700121 private Animator mCurrentAnimator;
122
123 @Override
124 protected void onCreate(Bundle savedInstanceState) {
125 super.onCreate(savedInstanceState);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700126 setContentView(R.layout.activity_calculator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700127
Justin Klaassen06360f92014-08-28 11:08:44 -0700128 mDisplayView = findViewById(R.id.display);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700129 mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
130 mResultEditText = (CalculatorEditText) findViewById(R.id.result);
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100131 mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700132 mDeleteButton = findViewById(R.id.del);
133 mClearButton = findViewById(R.id.clr);
134
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700135 mEqualButton = findViewById(R.id.pad_numeric).findViewById(R.id.eq);
136 if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
137 mEqualButton = findViewById(R.id.pad_operator).findViewById(R.id.eq);
138 }
139
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700140 mTokenizer = new CalculatorExpressionTokenizer(this);
141 mEvaluator = new CalculatorExpressionEvaluator(mTokenizer);
142
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700143 savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700144 setState(CalculatorState.values()[
145 savedInstanceState.getInt(KEY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);
146 mFormulaEditText.setText(mTokenizer.getLocalizedExpression(
147 savedInstanceState.getString(KEY_CURRENT_EXPRESSION, "")));
148 mEvaluator.evaluate(mFormulaEditText.getText(), this);
Justin Klaassen741471e2014-06-11 09:43:44 -0700149
150 mFormulaEditText.setEditableFactory(mFormulaEditableFactory);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700151 mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700152 mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
Justin Klaassenfed941a2014-06-09 18:42:40 +0100153 mFormulaEditText.setOnTextSizeChangeListener(this);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700154 mDeleteButton.setOnLongClickListener(this);
155 }
156
157 @Override
Justin Klaassenf79d6f62014-08-26 12:27:08 -0700158 protected void onSaveInstanceState(@NonNull Bundle outState) {
159 // If there's an animation in progress, cancel it first to ensure our state is up-to-date.
160 if (mCurrentAnimator != null) {
161 mCurrentAnimator.cancel();
162 }
163
Justin Klaassen4b3af052014-05-27 17:53:10 -0700164 super.onSaveInstanceState(outState);
Justin Klaassenf79d6f62014-08-26 12:27:08 -0700165
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700166 outState.putInt(KEY_CURRENT_STATE, mCurrentState.ordinal());
167 outState.putString(KEY_CURRENT_EXPRESSION,
168 mTokenizer.getNormalizedExpression(mFormulaEditText.getText().toString()));
Justin Klaassen4b3af052014-05-27 17:53:10 -0700169 }
170
171 private void setState(CalculatorState state) {
172 if (mCurrentState != state) {
173 mCurrentState = state;
174
175 if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
176 mDeleteButton.setVisibility(View.GONE);
177 mClearButton.setVisibility(View.VISIBLE);
178 } else {
179 mDeleteButton.setVisibility(View.VISIBLE);
180 mClearButton.setVisibility(View.GONE);
181 }
182
183 if (state == CalculatorState.ERROR) {
184 final int errorColor = getResources().getColor(R.color.calculator_error_color);
185 mFormulaEditText.setTextColor(errorColor);
186 mResultEditText.setTextColor(errorColor);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700187 getWindow().setStatusBarColor(errorColor);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700188 } else {
189 mFormulaEditText.setTextColor(
190 getResources().getColor(R.color.display_formula_text_color));
191 mResultEditText.setTextColor(
192 getResources().getColor(R.color.display_result_text_color));
Justin Klaassen8fff1442014-06-19 10:43:29 -0700193 getWindow().setStatusBarColor(
Justin Klaassen4b3af052014-05-27 17:53:10 -0700194 getResources().getColor(R.color.calculator_accent_color));
195 }
196 }
197 }
198
199 @Override
Justin Klaassen3b4d13d2014-06-06 18:18:37 +0100200 public void onBackPressed() {
201 if (mPadViewPager == null || mPadViewPager.getCurrentItem() == 0) {
202 // If the user is currently looking at the first pad (or the pad is not paged),
203 // allow the system to handle the Back button.
204 super.onBackPressed();
205 } else {
206 // Otherwise, select the previous pad.
207 mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
208 }
209 }
210
211 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700212 public void onUserInteraction() {
213 super.onUserInteraction();
214
215 // If there's an animation in progress, cancel it so the user interaction can be handled
216 // immediately.
217 if (mCurrentAnimator != null) {
218 mCurrentAnimator.cancel();
219 }
220 }
221
222 public void onButtonClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700223 mCurrentButton = view;
224
Justin Klaassen4b3af052014-05-27 17:53:10 -0700225 switch (view.getId()) {
226 case R.id.eq:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700227 onEquals();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700228 break;
229 case R.id.del:
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700230 onDelete();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700231 break;
232 case R.id.clr:
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700233 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700234 break;
235 case R.id.fun_cos:
236 case R.id.fun_ln:
237 case R.id.fun_log:
238 case R.id.fun_sin:
239 case R.id.fun_tan:
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700240 // Add left parenthesis after functions.
Justin Klaassen4b3af052014-05-27 17:53:10 -0700241 mFormulaEditText.append(((Button) view).getText() + "(");
242 break;
243 default:
244 mFormulaEditText.append(((Button) view).getText());
245 break;
246 }
247 }
248
249 @Override
250 public boolean onLongClick(View view) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700251 mCurrentButton = view;
252
Justin Klaassen4b3af052014-05-27 17:53:10 -0700253 if (view.getId() == R.id.del) {
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700254 onClear();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700255 return true;
256 }
257 return false;
258 }
259
260 @Override
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700261 public void onEvaluate(String expr, String result, int errorResourceId) {
Justin Klaassen4b3af052014-05-27 17:53:10 -0700262 if (mCurrentState == CalculatorState.INPUT) {
263 mResultEditText.setText(result);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700264 } else if (errorResourceId != INVALID_RES_ID) {
265 onError(errorResourceId);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700266 } else if (!TextUtils.isEmpty(result)) {
267 onResult(result);
268 } else if (mCurrentState == CalculatorState.EVALUATE) {
269 // The current expression cannot be evaluated -> return to the input state.
270 setState(CalculatorState.INPUT);
271 }
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700272
273 mFormulaEditText.requestFocus();
Justin Klaassen4b3af052014-05-27 17:53:10 -0700274 }
275
Justin Klaassenfed941a2014-06-09 18:42:40 +0100276 @Override
277 public void onTextSizeChanged(final TextView textView, float oldSize) {
278 if (mCurrentState != CalculatorState.INPUT) {
279 // Only animate text changes that occur from user input.
280 return;
281 }
282
283 // Calculate the values needed to perform the scale and translation animations,
284 // maintaining the same apparent baseline for the displayed text.
285 final float textScale = oldSize / textView.getTextSize();
286 final float translationX = (1.0f - textScale) *
287 (textView.getWidth() / 2.0f - textView.getPaddingEnd());
288 final float translationY = (1.0f - textScale) *
289 (textView.getHeight() / 2.0f - textView.getPaddingBottom());
290
291 final AnimatorSet animatorSet = new AnimatorSet();
292 animatorSet.playTogether(
293 ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
294 ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
295 ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
296 ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
Justin Klaassen94db7202014-06-11 11:22:31 -0700297 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
Justin Klaassenfed941a2014-06-09 18:42:40 +0100298 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
299 animatorSet.start();
300 }
301
Budi Kusmiantoroad8e88a2014-08-11 17:21:09 -0700302 private void onEquals() {
303 if (mCurrentState == CalculatorState.INPUT) {
304 setState(CalculatorState.EVALUATE);
305 mEvaluator.evaluate(mFormulaEditText.getText(), this);
306 }
307 }
308
309 private void onDelete() {
310 // Delete works like backspace; remove the last character from the expression.
311 final Editable formulaText = mFormulaEditText.getEditableText();
312 final int formulaLength = formulaText.length();
313 if (formulaLength > 0) {
314 formulaText.delete(formulaLength - 1, formulaLength);
315 }
316 }
317
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700318 private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
Justin Klaassen06360f92014-08-28 11:08:44 -0700319 final ViewGroupOverlay groupOverlay =
320 (ViewGroupOverlay) getWindow().getDecorView().getOverlay();
Justin Klaassen8fff1442014-06-19 10:43:29 -0700321
322 final Rect displayRect = new Rect();
Justin Klaassen06360f92014-08-28 11:08:44 -0700323 mDisplayView.getGlobalVisibleRect(displayRect);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700324
325 // Make reveal cover the display and status bar.
326 final View revealView = new View(this);
Justin Klaassen8fff1442014-06-19 10:43:29 -0700327 revealView.setBottom(displayRect.bottom);
328 revealView.setLeft(displayRect.left);
329 revealView.setRight(displayRect.right);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700330 revealView.setBackgroundColor(getResources().getColor(colorRes));
Justin Klaassen06360f92014-08-28 11:08:44 -0700331 groupOverlay.add(revealView);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700332
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
357 final AnimatorSet animatorSet = new AnimatorSet();
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700358 animatorSet.play(revealAnimator).before(alphaAnimator);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700359 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
360 animatorSet.addListener(new AnimatorListenerAdapter() {
361 @Override
Justin Klaassen4b3af052014-05-27 17:53:10 -0700362 public void onAnimationEnd(Animator animator) {
Justin Klaassen8fff1442014-06-19 10:43:29 -0700363 groupOverlay.remove(revealView);
Justin Klaassen4b3af052014-05-27 17:53:10 -0700364 mCurrentAnimator = null;
365 }
366 });
367
368 mCurrentAnimator = animatorSet;
369 animatorSet.start();
370 }
371
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700372 private void onClear() {
373 if (TextUtils.isEmpty(mFormulaEditText.getText())) {
374 return;
375 }
376
377 reveal(mCurrentButton, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
378 @Override
379 public void onAnimationEnd(Animator animation) {
380 mFormulaEditText.getEditableText().clear();
381 }
382 });
383 }
384
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700385 private void onError(final int errorResourceId) {
386 if (mCurrentState != CalculatorState.EVALUATE) {
387 // Only animate error on evaluate.
388 mResultEditText.setText(errorResourceId);
389 return;
390 }
391
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700392 reveal(mCurrentButton, R.color.calculator_error_color, new AnimatorListenerAdapter() {
393 @Override
394 public void onAnimationEnd(Animator animation) {
395 setState(CalculatorState.ERROR);
Justin Klaassen2be4fdb2014-08-06 19:54:09 -0700396 mResultEditText.setText(errorResourceId);
Justin Klaassen5f2a3342014-06-11 17:40:22 -0700397 }
398 });
399 }
400
Justin Klaassen4b3af052014-05-27 17:53:10 -0700401 private void onResult(final String result) {
402 // Calculate the values needed to perform the scale and translation animations,
403 // accounting for how the scale will affect the final position of the text.
404 final float resultScale =
405 mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
406 final float resultTranslationX = (1.0f - resultScale) *
407 (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingEnd());
408 final float resultTranslationY = (1.0f - resultScale) *
409 (mResultEditText.getHeight() / 2.0f - mResultEditText.getPaddingBottom()) +
410 (mFormulaEditText.getBottom() - mResultEditText.getBottom()) +
411 (mResultEditText.getPaddingBottom() - mFormulaEditText.getPaddingBottom());
412 final float formulaTranslationY = -mFormulaEditText.getBottom();
413
414 // Use a value animator to fade to the final text color over the course of the animation.
415 final int resultTextColor = mResultEditText.getCurrentTextColor();
416 final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
417 final ValueAnimator textColorAnimator =
418 ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
419 textColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
420 @Override
421 public void onAnimationUpdate(ValueAnimator valueAnimator) {
422 mResultEditText.setTextColor((int) valueAnimator.getAnimatedValue());
423 }
424 });
425
426 final AnimatorSet animatorSet = new AnimatorSet();
427 animatorSet.playTogether(
428 textColorAnimator,
429 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
430 ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
431 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
432 ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
433 ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
434 animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
435 animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
436 animatorSet.addListener(new AnimatorListenerAdapter() {
437 @Override
438 public void onAnimationStart(Animator animation) {
439 mResultEditText.setText(result);
440 }
441
442 @Override
443 public void onAnimationEnd(Animator animation) {
444 // Reset all of the values modified during the animation.
445 mResultEditText.setTextColor(resultTextColor);
446 mResultEditText.setScaleX(1.0f);
447 mResultEditText.setScaleY(1.0f);
448 mResultEditText.setTranslationX(0.0f);
449 mResultEditText.setTranslationY(0.0f);
450 mFormulaEditText.setTranslationY(0.0f);
451
452 // Finally update the formula to use the current result.
453 mFormulaEditText.setText(result);
454 setState(CalculatorState.RESULT);
455
456 mCurrentAnimator = null;
457 }
458 });
459
460 mCurrentAnimator = animatorSet;
461 animatorSet.start();
462 }
463}