Calculator.java revision 8fff144d6cf5d44b2a8e00b3468334019aa4f8b8
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;
303b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassenimport android.support.v4.view.ViewPager;
314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.Editable;
324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.TextUtils;
334b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.text.TextWatcher;
344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.KeyEvent;
354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.View;
364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.View.OnLongClickListener;
375f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassenimport android.view.ViewAnimationUtils;
388fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassenimport android.view.ViewGroupOverlay;
394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.view.animation.AccelerateDecelerateInterpolator;
404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenimport android.widget.Button;
41fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport android.widget.TextView;
42fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
43fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport com.android.calculator2.CalculatorEditText.OnTextSizeChangeListener;
44fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassenimport com.android.calculator2.CalculatorExpressionEvaluator.EvaluateCallback;
454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassenpublic class CalculatorActivity extends Activity
47fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        implements OnTextSizeChangeListener, EvaluateCallback, OnLongClickListener {
484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public static final String CALCULATOR_ACTIVITY_CURRENT_STATE =
504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            CalculatorActivity.class.getSimpleName() + "_currentState";
514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private enum CalculatorState {
534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        INPUT, EVALUATE, RESULT, ERROR
544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
564b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private final TextWatcher mFormulaTextWatcher = new TextWatcher() {
574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
584b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void beforeTextChanged(CharSequence charSequence, int start, int count, int after) {
594b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
604b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void onTextChanged(CharSequence charSequence, int start, int count, int after) {
634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        @Override
664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        public void afterTextChanged(Editable editable) {
674b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            setState(CalculatorState.INPUT);
684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mEvaluator.evaluate(editable, CalculatorActivity.this);
694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    };
714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
72741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen    private final Editable.Factory mFormulaEditableFactory = new Editable.Factory() {
73741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        @Override
74741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        public Editable newEditable(CharSequence source) {
75741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen            return new CalculatorExpressionBuilder(CalculatorActivity.this, source,
76741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen                    mCurrentState == CalculatorState.INPUT);
77741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        }
78741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen    };
79741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen
804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorState mCurrentState;
814b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorExpressionEvaluator mEvaluator;
824b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorEditText mFormulaEditText;
844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private CalculatorEditText mResultEditText;
854b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
863b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    private ViewPager mPadViewPager;
873b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen
884b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private View mDeleteButton;
894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private View mClearButton;
904b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
915f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private View mCurrentButton;
924b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private Animator mCurrentAnimator;
934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
944b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
954b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    protected void onCreate(Bundle savedInstanceState) {
964b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onCreate(savedInstanceState);
975f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        setContentView(R.layout.activity_calculator);
984b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
994b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
1004b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mResultEditText = (CalculatorEditText) findViewById(R.id.result);
1014b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1023b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        mPadViewPager = (ViewPager) findViewById(R.id.pad_pager);
1033b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen
1044b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mDeleteButton = findViewById(R.id.del);
1054b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mClearButton = findViewById(R.id.clr);
1064b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1075f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
108741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        setState(CalculatorState.values()[savedInstanceState.getInt(
109741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen                CALCULATOR_ACTIVITY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);
110741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        mEvaluator = new CalculatorExpressionEvaluator(this);
111741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen
112741471e3e99760acd44f8536534ac6121af9b03fJustin Klaassen        mFormulaEditText.setEditableFactory(mFormulaEditableFactory);
1134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
114fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        mFormulaEditText.setOnTextSizeChangeListener(this);
115fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
1164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mDeleteButton.setOnLongClickListener(this);
1174b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
1204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    protected void onSaveInstanceState(Bundle outState) {
1214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onSaveInstanceState(outState);
1224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        outState.putInt(CALCULATOR_ACTIVITY_CURRENT_STATE, mCurrentState.ordinal());
1234b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1244b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private void setState(CalculatorState state) {
1264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentState != state) {
1274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mCurrentState = state;
1284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
1304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mDeleteButton.setVisibility(View.GONE);
1314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mClearButton.setVisibility(View.VISIBLE);
1324b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            } else {
1334b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mDeleteButton.setVisibility(View.VISIBLE);
1344b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mClearButton.setVisibility(View.GONE);
1354b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
1364b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1374b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            if (state == CalculatorState.ERROR) {
1384b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                final int errorColor = getResources().getColor(R.color.calculator_error_color);
1394b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTextColor(errorColor);
1404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(errorColor);
1418fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                getWindow().setStatusBarColor(errorColor);
1424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            } else {
1434b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTextColor(
1444b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.display_formula_text_color));
1454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(
1464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.display_result_text_color));
1478fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                getWindow().setStatusBarColor(
1484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        getResources().getColor(R.color.calculator_accent_color));
1494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
1504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
1514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
1543b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    public void onBackPressed() {
1553b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        if (mPadViewPager == null || mPadViewPager.getCurrentItem() == 0) {
1563b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // If the user is currently looking at the first pad (or the pad is not paged),
1573b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // allow the system to handle the Back button.
1583b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            super.onBackPressed();
1593b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        } else {
1603b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            // Otherwise, select the previous pad.
1613b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen            mPadViewPager.setCurrentItem(mPadViewPager.getCurrentItem() - 1);
1623b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen        }
1633b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    }
1643b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen
1653b4d13d528a14a0f46e7ae022357dfde90a307adJustin Klaassen    @Override
1664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public void onUserInteraction() {
1674b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        super.onUserInteraction();
1684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // If there's an animation in progress, cancel it so the user interaction can be handled
1704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // immediately.
1714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentAnimator != null) {
1724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mCurrentAnimator.cancel();
1734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
1744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
1754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
1764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public void onButtonClick(View view) {
1775f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        mCurrentButton = view;
1785f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
1794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        switch (view.getId()) {
1804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.eq:
1815a3bbade728daf89454aa0485ca54d74884f4134Justin Klaassen                if (mCurrentState == CalculatorState.INPUT) {
1824b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    setState(CalculatorState.EVALUATE);
1834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                    mEvaluator.evaluate(mFormulaEditText.getText(), this);
1844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                }
1854b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
1864b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.del:
1874b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.dispatchKeyEvent(
1884b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                        new KeyEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_DEL));
1894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
1904b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.clr:
1915f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                onClear();
1924b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
1934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_cos:
1944b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_ln:
1954b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_log:
1964b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_sin:
1974b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            case R.id.fun_tan:
1984b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                // add left paren after functions
1994b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.append(((Button) view).getText() + "(");
2004b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2014b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            default:
2024b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.append(((Button) view).getText());
2034b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                break;
2044b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2054b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2064b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2074b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
2084b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public boolean onLongClick(View view) {
2095f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        mCurrentButton = view;
2105f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
2114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (view.getId() == R.id.del) {
2125f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            onClear();
2134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            return true;
2144b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        return false;
2164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2174b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2184b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    @Override
2194b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    public void onEvaluate(String expr, String result, String error) {
2204b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        if (mCurrentState == CalculatorState.INPUT) {
2214b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            mResultEditText.setText(result);
2224b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        } else if (!TextUtils.isEmpty(error)) {
2235f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            onError(error);
2244b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        } else if (!TextUtils.isEmpty(result)) {
2254b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            onResult(result);
2264b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        } else if (mCurrentState == CalculatorState.EVALUATE) {
2274b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            // The current expression cannot be evaluated -> return to the input state.
2284b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            setState(CalculatorState.INPUT);
2294b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        }
2304b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
2314b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
232fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    @Override
233fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    public void onTextSizeChanged(final TextView textView, float oldSize) {
234fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        if (mCurrentState != CalculatorState.INPUT) {
235fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen            // Only animate text changes that occur from user input.
236fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen            return;
237fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        }
238fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
239fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        // Calculate the values needed to perform the scale and translation animations,
240fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        // maintaining the same apparent baseline for the displayed text.
241fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float textScale = oldSize / textView.getTextSize();
242fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float translationX = (1.0f - textScale) *
243fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                (textView.getWidth() / 2.0f - textView.getPaddingEnd());
244fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final float translationY = (1.0f - textScale) *
245fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                (textView.getHeight() / 2.0f - textView.getPaddingBottom());
246fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
247fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
248fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.playTogether(
249fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.SCALE_X, textScale, 1.0f),
250fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.SCALE_Y, textScale, 1.0f),
251fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.TRANSLATION_X, translationX, 0.0f),
252fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen                ObjectAnimator.ofFloat(textView, View.TRANSLATION_Y, translationY, 0.0f));
25394db720f7daf99923cc8e3d5ba8765b5529913f1Justin Klaassen        animatorSet.setDuration(getResources().getInteger(android.R.integer.config_mediumAnimTime));
254fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
255fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen        animatorSet.start();
256fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen    }
257fed941a1b4b2a3a011b8a17f486060f593db2f3cJustin Klaassen
2585f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private void reveal(View sourceView, int colorRes, AnimatorListener listener) {
2598fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        final View displayView = findViewById(R.id.display);
2608fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        final View decorView = getWindow().getDecorView();
2618fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen
2628fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        final Rect displayRect = new Rect();
2638fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        displayView.getGlobalVisibleRect(displayRect);
2645f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
2655f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        // Make reveal cover the display and status bar.
2665f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final View revealView = new View(this);
2678fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setBottom(displayRect.bottom);
2688fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setLeft(displayRect.left);
2698fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        revealView.setRight(displayRect.right);
2705f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        revealView.setBackgroundColor(getResources().getColor(colorRes));
2715f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
2724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int[] clearLocation = new int[2];
2734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        sourceView.getLocationInWindow(clearLocation);
2744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        clearLocation[0] += sourceView.getWidth() / 2;
2754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        clearLocation[1] += sourceView.getHeight() / 2;
2764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2775f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final int revealCenterX = clearLocation[0] - revealView.getLeft();
2785f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final int revealCenterY = clearLocation[1] - revealView.getTop();
2794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2805f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double x1_2 = Math.pow(revealView.getLeft() - revealCenterX, 2);
2815f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double x2_2 = Math.pow(revealView.getRight() - revealCenterX, 2);
2825f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final double y_2 = Math.pow(revealView.getTop() - revealCenterY, 2);
2834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float revealRadius = (float) Math.max(Math.sqrt(x1_2 + y_2), Math.sqrt(x2_2 + y_2));
2844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2855f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final Animator revealAnimator =
2865f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                ViewAnimationUtils.createCircularReveal(revealView,
2873d6ecaf4d3365eeaff75a4bedb57fbe136cf5f64ztenghui                        revealCenterX, revealCenterY, 0.0f, revealRadius);
2885f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        revealAnimator.setDuration(
2894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                getResources().getInteger(android.R.integer.config_longAnimTime));
2905f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        revealAnimator.addListener(listener);
2914b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2925f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        final Animator alphaAnimator = ObjectAnimator.ofFloat(revealView, View.ALPHA, 0.0f);
2934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        alphaAnimator.setDuration(
2945f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                getResources().getInteger(android.R.integer.config_mediumAnimTime));
2954b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
2968fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen        final ViewGroupOverlay groupOverlay = (ViewGroupOverlay) decorView.getOverlay();
2974b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
2985f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        animatorSet.play(revealAnimator).before(alphaAnimator);
2994b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
3004b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.addListener(new AnimatorListenerAdapter() {
3014b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3025f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            public void onAnimationStart(Animator animation) {
3038fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                groupOverlay.add(revealView);
3044b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3054b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3064b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3074b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationEnd(Animator animator) {
3088fff144d6cf5d44b2a8e00b3468334019aa4f8b8Justin Klaassen                groupOverlay.remove(revealView);
3094b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mCurrentAnimator = null;
3104b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3114b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
3124b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3134b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mCurrentAnimator = animatorSet;
3144b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.start();
3154b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
3164b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3175f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private void onClear() {
3185f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        if (TextUtils.isEmpty(mFormulaEditText.getText())) {
3195f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            return;
3205f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        }
3215f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3225f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        reveal(mCurrentButton, R.color.calculator_accent_color, new AnimatorListenerAdapter() {
3235f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            @Override
3245f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            public void onAnimationEnd(Animator animation) {
3255f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                mFormulaEditText.getEditableText().clear();
3265f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            }
3275f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        });
3285f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    }
3295f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3305f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    private void onError(final String error) {
3315f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        reveal(mCurrentButton, R.color.calculator_error_color, new AnimatorListenerAdapter() {
3325f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            @Override
3335f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            public void onAnimationEnd(Animator animation) {
3345f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                setState(CalculatorState.ERROR);
3355f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen                mResultEditText.setText(error);
3365f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen            }
3375f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen        });
3385f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen    }
3395f2a334a7789cd88e210451ff5cd39bfacfe054eJustin Klaassen
3404b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    private void onResult(final String result) {
3414b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // Calculate the values needed to perform the scale and translation animations,
3424b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // accounting for how the scale will affect the final position of the text.
3434b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultScale =
3444b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
3454b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultTranslationX = (1.0f - resultScale) *
3464b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingEnd());
3474b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float resultTranslationY = (1.0f - resultScale) *
3484b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getHeight() / 2.0f - mResultEditText.getPaddingBottom()) +
3494b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mFormulaEditText.getBottom() - mResultEditText.getBottom()) +
3504b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                (mResultEditText.getPaddingBottom() - mFormulaEditText.getPaddingBottom());
3514b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final float formulaTranslationY = -mFormulaEditText.getBottom();
3524b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3534b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        // Use a value animator to fade to the final text color over the course of the animation.
3544b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int resultTextColor = mResultEditText.getCurrentTextColor();
3554b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
3564b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final ValueAnimator textColorAnimator =
3574b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
3584b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        textColorAnimator.addUpdateListener(new AnimatorUpdateListener() {
3594b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3604b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationUpdate(ValueAnimator valueAnimator) {
3614b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor((int) valueAnimator.getAnimatedValue());
3624b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3634b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
3644b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3654b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        final AnimatorSet animatorSet = new AnimatorSet();
3664b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.playTogether(
3674b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                textColorAnimator,
3684b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
3694b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
3704b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
3714b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
3724b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
3734b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
3744b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
3754b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.addListener(new AnimatorListenerAdapter() {
3764b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3774b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationStart(Animator animation) {
3784b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setText(result);
3794b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3804b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3814b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            @Override
3824b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            public void onAnimationEnd(Animator animation) {
3834b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                // Reset all of the values modified during the animation.
3844b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTextColor(resultTextColor);
3854b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setScaleX(1.0f);
3864b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setScaleY(1.0f);
3874b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTranslationX(0.0f);
3884b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mResultEditText.setTranslationY(0.0f);
3894b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setTranslationY(0.0f);
3904b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3914b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                // Finally update the formula to use the current result.
3924b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mFormulaEditText.setText(result);
3934b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                setState(CalculatorState.RESULT);
3944b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3954b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen                mCurrentAnimator = null;
3964b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen            }
3974b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        });
3984b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen
3994b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        mCurrentAnimator = animatorSet;
4004b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen        animatorSet.start();
4014b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen    }
4024b3af0578b1a44038856bc56244aea8aaeac22d1Justin Klaassen}
403