14b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen/*
24b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Copyright (C) 2014 The Android Open Source Project
34b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
44b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Licensed under the Apache License, Version 2.0 (the "License");
54b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * you may not use this file except in compliance with the License.
64b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * You may obtain a copy of the License at
74b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
84b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *   http://www.apache.org/licenses/LICENSE-2.0
94b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen *
104b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * Unless required by applicable law or agreed to in writing, software
114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * distributed under the License is distributed on an "AS IS" BASIS,
124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * See the License for the specific language governing permissions and
144b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen * limitations under the License.
154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen */
164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
174b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenpackage com.android.calculator2;
184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.Animator;
205f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassenimport android.animation.Animator.AnimatorListener;
214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.AnimatorListenerAdapter;
224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.AnimatorSet;
234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.ArgbEvaluator;
244b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.ObjectAnimator;
254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.ValueAnimator;
264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.animation.ValueAnimator.AnimatorUpdateListener;
274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.app.Activity;
288fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassenimport android.graphics.Rect;
294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.os.Bundle;
30f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassenimport android.support.annotation.NonNull;
313b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassenimport android.support.v4.view.ViewPager;
324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.Editable;
334b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.TextUtils;
344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.TextWatcher;
35ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoroimport android.view.KeyEvent;
364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.View;
37ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoroimport android.view.View.OnKeyListener;
384b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.View.OnLongClickListener;
395f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassenimport android.view.ViewAnimationUtils;
408fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassenimport android.view.ViewGroupOverlay;
414b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.animation.AccelerateDecelerateInterpolator;
424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.widget.Button;
43fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport android.widget.TextView;
44fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
45fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport com.android.calculator2.CalculatorEditText.OnTextSizeChangeListener;
46fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport com.android.calculator2.CalculatorExpressionEvaluator.EvaluateCallback;
474b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4804f79c7a5f46f6c2cf07a164ce352ab54dfe7f47Justin Klaassenpublic class Calculator extends Activity
49fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        implements OnTextSizeChangeListener, EvaluateCallback, OnLongClickListener {
504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
512be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    private static final String NAME = Calculator.class.getName();
522be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen
532be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    // instance state keys
542be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    private static final String KEY_CURRENT_STATE = NAME + "_currentState";
552be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    private static final String KEY_CURRENT_EXPRESSION = NAME + "_currentExpression";
562be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen
572be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    /**
582be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen     * Constant for an invalid resource id.
592be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen     */
602be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    public static final int INVALID_RES_ID = -1;
614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private enum CalculatorState {
634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        INPUT, EVALUATE, RESULT, ERROR
644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
674b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void afterTextChanged(Editable editable) {
774b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            setState(CalculatorState.INPUT);
7804f79c7a5f46f6c2cf07a164ce352ab54dfe7f47Justin Klaassen            mEvaluator.evaluate(editable, Calculator.this);
794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    };
814b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
82ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    private final OnKeyListener mFormulaOnKeyListener = new OnKeyListener() {
83ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        @Override
84ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        public boolean onKey(View view, int keyCode, KeyEvent keyEvent) {
85ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            switch (keyCode) {
86ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                case KeyEvent.KEYCODE_NUMPAD_ENTER:
87ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                case KeyEvent.KEYCODE_ENTER:
88ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                    if (keyEvent.getAction() == KeyEvent.ACTION_UP) {
89ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                        onEquals();
90ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                    }
91ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                    // ignore all other actions
92ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                    return true;
93ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            }
94ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            return false;
95ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        }
96ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    };
97ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro
98741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen    private final Editable.Factory mFormulaEditableFactory = new Editable.Factory() {
99741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        @Override
100741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        public Editable newEditable(CharSequence source) {
1012be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            final boolean isEdited = mCurrentState == CalculatorState.INPUT
1022be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                    || mCurrentState == CalculatorState.ERROR;
1032be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            return new CalculatorExpressionBuilder(source, mTokenizer, isEdited);
104741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        }
105741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen    };
106741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen
1074b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorState mCurrentState;
1082be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    private CalculatorExpressionTokenizer mTokenizer;
1094b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorExpressionEvaluator mEvaluator;
1104b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
11106360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen    private View mDisplayView;
1124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorEditText mFormulaEditText;
1134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorEditText mResultEditText;
1143b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    private ViewPager mPadViewPager;
1154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private View mDeleteButton;
1164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private View mClearButton;
1171283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen    private View mEqualButton;
1184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private Animator mCurrentAnimator;
1204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
1224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    protected void onCreate(Bundle savedInstanceState) {
1234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onCreate(savedInstanceState);
1245f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        setContentView(R.layout.activity_calculator);
1254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
12606360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen        mDisplayView = findViewById(R.id.display);
1274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
1284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mResultEditText = (CalculatorEditText) findViewById(R.id.result);
1293b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
1304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mDeleteButton = findViewById(R.id.del);
1314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mClearButton = findViewById(R.id.clr);
1324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
133ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        mEqualButton = findViewById(R.id.pad_numeric).findViewById(R.id.eq);
134ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
135ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            mEqualButton = findViewById(R.id.pad_operator).findViewById(R.id.eq);
136ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        }
137ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro
1382be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        mTokenizer = new CalculatorExpressionTokenizer(this);
1392be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        mEvaluator = new CalculatorExpressionEvaluator(mTokenizer);
1402be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen
1415f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
1422be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        setState(CalculatorState.values()[
1432be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                savedInstanceState.getInt(KEY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);
1442be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        mFormulaEditText.setText(mTokenizer.getLocalizedExpression(
1452be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                savedInstanceState.getString(KEY_CURRENT_EXPRESSION, "")));
1462be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        mEvaluator.evaluate(mFormulaEditText.getText(), this);
147741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen
148741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        mFormulaEditText.setEditableFactory(mFormulaEditableFactory);
1494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
150ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
151fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        mFormulaEditText.setOnTextSizeChangeListener(this);
1524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mDeleteButton.setOnLongClickListener(this);
1534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
156f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassen    protected void onSaveInstanceState(@NonNull Bundle outState) {
1571283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        // If there's an animation in progress, end it immediately to ensure the state is
1581283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        // up-to-date before it is serialized.
159f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassen        if (mCurrentAnimator != null) {
1601283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen            mCurrentAnimator.end();
161f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassen        }
162f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassen
1634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onSaveInstanceState(outState);
164f79d6f699b04a735e1627b47a059760ff40c26b9Justin Klaassen
1652be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        outState.putInt(KEY_CURRENT_STATE, mCurrentState.ordinal());
1662be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        outState.putString(KEY_CURRENT_EXPRESSION,
1672be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                mTokenizer.getNormalizedExpression(mFormulaEditText.getText().toString()));
1684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private void setState(CalculatorState state) {
1714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentState != state) {
1724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mCurrentState = state;
1734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
1754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mDeleteButton.setVisibility(View.GONE);
1764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mClearButton.setVisibility(View.VISIBLE);
1774b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            } else {
1784b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mDeleteButton.setVisibility(View.VISIBLE);
1794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mClearButton.setVisibility(View.GONE);
1804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
1814b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1824b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            if (state == CalculatorState.ERROR) {
1834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                final int errorColor = getResources().getColor(R.color.calculator_error_color);
1844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTextColor(errorColor);
1854b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(errorColor);
1868fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                getWindow().setStatusBarColor(errorColor);
1874b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            } else {
1884b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTextColor(
1894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.display_formula_text_color));
1904b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(
1914b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.display_result_text_color));
1928fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                getWindow().setStatusBarColor(
1934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.calculator_accent_color));
1944b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
1954b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
1964b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1974b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1984b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
1993b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    public void onBackPressed() {
2003b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        if (mPadViewPager == null || mPadViewPager.getCurrentItem() == 0) {
2013b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // If the user is currently looking at the first pad (or the pad is not paged),
2023b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // allow the system to handle the Back button.
2033b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            super.onBackPressed();
2043b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        } else {
2053b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // Otherwise, select the previous pad.
2063b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
2073b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        }
2083b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    }
2093b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen
2103b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    @Override
2114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public void onUserInteraction() {
2124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onUserInteraction();
2134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2141283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        // If there's an animation in progress, end it immediately to ensure the state is
2151283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        // up-to-date before the pending user interaction is handled.
2164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentAnimator != null) {
2171283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen            mCurrentAnimator.end();
2184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public void onButtonClick(View view) {
2224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        switch (view.getId()) {
2234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.eq:
224ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                onEquals();
2254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.del:
227ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro                onDelete();
2284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.clr:
2305f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                onClear();
2314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_cos:
2334b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_ln:
2344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_log:
2354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_sin:
2364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_tan:
2372be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                // Add left parenthesis after functions.
2384b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.append(((Button) view).getText() + "(");
2394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            default:
2414b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.append(((Button) view).getText());
2424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2434b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2444b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
2474b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public boolean onLongClick(View view) {
2484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (view.getId() == R.id.del) {
2495f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            onClear();
2504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            return true;
2514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        return false;
2534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
2562be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    public void onEvaluate(String expr, String result, int errorResourceId) {
2574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentState == CalculatorState.INPUT) {
2584b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mResultEditText.setText(result);
2592be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        } else if (errorResourceId != INVALID_RES_ID) {
2602be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            onError(errorResourceId);
2614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        } else if (!TextUtils.isEmpty(result)) {
2624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            onResult(result);
2634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        } else if (mCurrentState == CalculatorState.EVALUATE) {
2644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            // The current expression cannot be evaluated -> return to the input state.
2654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            setState(CalculatorState.INPUT);
2664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
267ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro
268ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        mFormulaEditText.requestFocus();
2694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
271fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    @Override
272fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    public void onTextSizeChanged(final TextView textView, float oldSize) {
273fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        if (mCurrentState != CalculatorState.INPUT) {
274fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen            // Only animate text changes that occur from user input.
275fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen            return;
276fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        }
277fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
278fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        // Calculate the values needed to perform the scale and translation animations,
279fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        // maintaining the same apparent baseline for the displayed text.
280fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float textScale = oldSize / textView.getTextSize();
281fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float translationX = (1.0f - textScale) *
282fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                (textView.getWidth() / 2.0f - textView.getPaddingEnd());
283fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float translationY = (1.0f - textScale) *
284fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                (textView.getHeight() / 2.0f - textView.getPaddingBottom());
285fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
286fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
287fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.playTogether(
288fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
289fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
290fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
291fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
29294db720f7daf99923cc8e3d5ba8765b5529913f1Justin Klaassen        animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
293fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
294fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.start();
295fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    }
296fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
297ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    private void onEquals() {
298ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        if (mCurrentState == CalculatorState.INPUT) {
299ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            setState(CalculatorState.EVALUATE);
300ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            mEvaluator.evaluate(mFormulaEditText.getText(), this);
301ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        }
302ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    }
303ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro
304ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    private void onDelete() {
305ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        // Delete works like backspace; remove the last character from the expression.
306ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        final Editable formulaText = mFormulaEditText.getEditableText();
307ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        final int formulaLength = formulaText.length();
308ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        if (formulaLength > 0) {
309ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro            formulaText.delete(formulaLength - 1, formulaLength);
310ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro        }
311ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro    }
312ad8e88a5d28ba7f5b8dfa63f92b00b3b96ada360Budi Kusmiantoro
3135f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
31406360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen        final ViewGroupOverlay groupOverlay =
31506360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen                (ViewGroupOverlay) getWindow().getDecorView().getOverlay();
3168fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen
3178fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        final Rect displayRect = new Rect();
31806360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen        mDisplayView.getGlobalVisibleRect(displayRect);
3195f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3205f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        // Make reveal cover the display and status bar.
3215f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final View revealView = new View(this);
3228fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setBottom(displayRect.bottom);
3238fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setLeft(displayRect.left);
3248fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setRight(displayRect.right);
3255f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        revealView.setBackgroundColor(getResources().getColor(colorRes));
32606360f9211fc2c6df4c5749bebb65202e1bb12a8Justin Klaassen        groupOverlay.add(revealView);
3275f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int[] clearLocation = new int[2];
3294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        sourceView.getLocationInWindow(clearLocation);
3304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        clearLocation[0] += sourceView.getWidth() / 2;
3314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        clearLocation[1] += sourceView.getHeight() / 2;
3324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3335f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final int revealCenterX = clearLocation[0] - revealView.getLeft();
3345f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final int revealCenterY = clearLocation[1] - revealView.getTop();
3354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3365f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double x1_2 = Math.pow(revealView.getLeft() - revealCenterX, 2);
3375f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double x2_2 = Math.pow(revealView.getRight() - revealCenterX, 2);
3385f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double y_2 = Math.pow(revealView.getTop() - revealCenterY, 2);
3394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float revealRadius = (float) Math.max(Math.sqrt(x1_2 + y_2), Math.sqrt(x2_2 + y_2));
3404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3415f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final Animator revealAnimator =
3425f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                ViewAnimationUtils.createCircularReveal(revealView,
3433d6ecaf4d3365eeaff75a4bedb57fbe136cf5f64ztenghui                        revealCenterX, revealCenterY, 0.0f, revealRadius);
3445f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        revealAnimator.setDuration(
3454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                getResources().getInteger(android.R.integer.config_longAnimTime));
3464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3475f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final Animator alphaAnimator = ObjectAnimator.ofFloat(revealView, View.ALPHA, 0.0f);
3484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        alphaAnimator.setDuration(
3495f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                getResources().getInteger(android.R.integer.config_mediumAnimTime));
3501283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        alphaAnimator.addListener(listener);
3514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
3535f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        animatorSet.play(revealAnimator).before(alphaAnimator);
3544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
3554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.addListener(new AnimatorListenerAdapter() {
3564b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationEnd(Animator animator) {
3588fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                groupOverlay.remove(revealView);
3594b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mCurrentAnimator = null;
3604b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
3624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mCurrentAnimator = animatorSet;
3644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.start();
3654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
3664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3675f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private void onClear() {
3685f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        if (TextUtils.isEmpty(mFormulaEditText.getText())) {
3695f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            return;
3705f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        }
3715f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3721283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        final View sourceView = mClearButton.getVisibility() == View.VISIBLE
3731283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen                ? mClearButton : mDeleteButton;
3741283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        reveal(sourceView, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
3755f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            @Override
3761283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen            public void onAnimationStart(Animator animation) {
3775f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                mFormulaEditText.getEditableText().clear();
3785f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            }
3795f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        });
3805f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    }
3815f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3822be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen    private void onError(final int errorResourceId) {
3832be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        if (mCurrentState != CalculatorState.EVALUATE) {
3842be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            // Only animate error on evaluate.
3852be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            mResultEditText.setText(errorResourceId);
3862be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen            return;
3872be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen        }
3882be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen
3891283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen        reveal(mEqualButton, R.color.calculator_error_color, new AnimatorListenerAdapter() {
3905f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            @Override
3911283ee748254cce69fda2d1a3d2af1cf881b2738Justin Klaassen            public void onAnimationStart(Animator animation) {
3925f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                setState(CalculatorState.ERROR);
3932be4fdbfcd5f16bc12066d1ccac181bca3dfaa7aJustin Klaassen                mResultEditText.setText(errorResourceId);
3945f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            }
3955f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        });
3965f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    }
3975f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3984b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private void onResult(final String result) {
3994b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // Calculate the values needed to perform the scale and translation animations,
4004b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // accounting for how the scale will affect the final position of the text.
4014b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultScale =
4024b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
4034b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultTranslationX = (1.0f - resultScale) *
4044b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingEnd());
4054b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultTranslationY = (1.0f - resultScale) *
4064b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getHeight() / 2.0f - mResultEditText.getPaddingBottom()) +
4074b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mFormulaEditText.getBottom() - mResultEditText.getBottom()) +
4084b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getPaddingBottom() - mFormulaEditText.getPaddingBottom());
4094b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float formulaTranslationY = -mFormulaEditText.getBottom();
4104b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // Use a value animator to fade to the final text color over the course of the animation.
4124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int resultTextColor = mResultEditText.getCurrentTextColor();
4134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
4144b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final ValueAnimator textColorAnimator =
4154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
4164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        textColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
4174b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
4184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationUpdate(ValueAnimator valueAnimator) {
4194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor((int) valueAnimator.getAnimatedValue());
4204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
4214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
4224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
4244b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.playTogether(
4254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                textColorAnimator,
4264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
4274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
4284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
4294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
4304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
4314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
4324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
4334b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.addListener(new AnimatorListenerAdapter() {
4344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
4354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationStart(Animator animation) {
4364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setText(result);
4374b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
4384b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
4404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationEnd(Animator animation) {
4414b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                // Reset all of the values modified during the animation.
4424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(resultTextColor);
4434b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setScaleX(1.0f);
4444b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setScaleY(1.0f);
4454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTranslationX(0.0f);
4464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTranslationY(0.0f);
4474b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTranslationY(0.0f);
4484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                // Finally update the formula to use the current result.
4504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setText(result);
4514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                setState(CalculatorState.RESULT);
4524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mCurrentAnimator = null;
4544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
4554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
4564b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
4574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mCurrentAnimator = animatorSet;
4584b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.start();
4594b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
4604b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen}
461