protected void setState(CalculatorState state) {
    if (mCurrentState != state) {
      mCurrentState = state;
      invalidateEqualsButton();

      if (state == CalculatorState.RESULT || state == CalculatorState.ERROR) {
        mDeleteButton.setVisibility(View.GONE);
        mClearButton.setVisibility(View.VISIBLE);
      } else {
        mDeleteButton.setVisibility(View.VISIBLE);
        mClearButton.setVisibility(View.GONE);
      }

      if (state == CalculatorState.ERROR) {
        final int errorColor = getResources().getColor(R.color.calculator_error_color);
        mFormulaEditText.setTextColor(errorColor);
        mResultEditText.setTextColor(errorColor);
        if (android.os.Build.VERSION.SDK_INT >= 21) {
          getWindow().setStatusBarColor(errorColor);
        }
      } else {
        mFormulaEditText.setTextColor(getResources().getColor(R.color.display_formula_text_color));
        mResultEditText.setTextColor(getResources().getColor(R.color.display_result_text_color));
        if (android.os.Build.VERSION.SDK_INT >= 21) {
          getWindow().setStatusBarColor(getResources().getColor(R.color.calculator_accent_color));
        }
      }
    }
  }
 /**
  * Inserts text into the formula EditText. If an equation was recently solved, it will replace the
  * formula's text instead of appending.
  */
 protected void insert(String text) {
   // Add left parenthesis after functions.
   if (mCurrentState.equals(CalculatorState.INPUT)
       || mCurrentState.equals(CalculatorState.GRAPHING)
       || mFormulaEditText.isCursorModified()) {
     mFormulaEditText.insert(text);
   } else {
     mFormulaEditText.setText(text);
     incrementGroupId();
   }
 }
 private void onEquals() {
   String text = mFormulaEditText.getCleanText();
   if (mCurrentState == CalculatorState.INPUT) {
     switch (mEqualButton.getState()) {
       case EQUALS:
         setState(CalculatorState.EVALUATE);
         mEvaluator.evaluate(text, this);
         break;
       case NEXT:
         mFormulaEditText.next();
         break;
     }
   } else if (mCurrentState == CalculatorState.GRAPHING) {
     setState(CalculatorState.EVALUATE);
     onEvaluate(text, "", INVALID_RES_ID);
   }
 }
 @Override
 protected void onCreate(Bundle savedInstanceState) {
   super.onCreate(savedInstanceState);
   setContentView(R.layout.activity_calculator);
   savedInstanceState = savedInstanceState == null ? Bundle.EMPTY : savedInstanceState;
   initialize(savedInstanceState);
   mEvaluator.evaluate(mFormulaEditText.getCleanText(), this);
 }
 @Override
 public void onEvaluate(String expr, String result, int errorResourceId) {
   if (mCurrentState == CalculatorState.INPUT || mCurrentState == CalculatorState.GRAPHING) {
     if (result == null || Solver.equal(result, expr)) {
       mResultEditText.setText(null);
     } else {
       mResultEditText.setText(result);
     }
   } else if (errorResourceId != INVALID_RES_ID) {
     onError(errorResourceId);
   } else if (saveHistory(expr, result, true)) {
     mDisplayView.scrollToMostRecent();
     onResult(result);
   } else if (mCurrentState == CalculatorState.EVALUATE) {
     // The current expression cannot be evaluated -> return to the input state.
     setState(CalculatorState.INPUT);
   }
   invalidateEqualsButton();
 }
 public void onButtonClick(View view) {
   mCurrentButton = view;
   switch (view.getId()) {
     case R.id.eq:
       onEquals();
       break;
     case R.id.del:
       onDelete();
       break;
     case R.id.clr:
       onClear();
       break;
     case R.id.parentheses:
       mFormulaEditText.setText('(' + mFormulaEditText.getCleanText() + ')');
       break;
     case R.id.fun_cos:
     case R.id.fun_sin:
     case R.id.fun_tan:
     case R.id.fun_ln:
     case R.id.fun_log:
     case R.id.fun_det:
     case R.id.fun_transpose:
     case R.id.fun_inverse:
     case R.id.fun_trace:
     case R.id.fun_norm:
     case R.id.fun_polar:
       // Add left parenthesis after functions.
       insert(((Button) view).getText() + "(");
       break;
     case R.id.op_add:
     case R.id.op_sub:
     case R.id.op_mul:
     case R.id.op_div:
     case R.id.op_fact:
     case R.id.op_pow:
       mFormulaEditText.insert(((Button) view).getText().toString());
       break;
     default:
       insert(((Button) view).getText().toString());
       break;
   }
 }
  @Override
  protected void onSaveInstanceState(@NonNull Bundle outState) {
    // If there's an animation in progress, cancel it first to ensure our state is up-to-date.
    if (mCurrentAnimator != null) {
      mCurrentAnimator.cancel();
    }

    super.onSaveInstanceState(outState);
    outState.putInt(KEY_CURRENT_STATE, mCurrentState.ordinal());
    outState.putString(
        KEY_CURRENT_EXPRESSION,
        mTokenizer.getNormalizedExpression(mFormulaEditText.getCleanText()));
  }
 protected void onClear() {
   if (TextUtils.isEmpty(mFormulaEditText.getCleanText())) {
     return;
   }
   reveal(
       mCurrentButton,
       R.color.calculator_accent_color,
       new AnimationFinishedListener() {
         @Override
         public void onAnimationFinished() {
           mFormulaEditText.clear();
           incrementGroupId();
         }
       });
 }
 @Override
 public boolean onLongClick(View view) {
   mCurrentButton = view;
   switch (view.getId()) {
     case R.id.del:
       saveHistory(mFormulaEditText.getCleanText(), mResultEditText.getCleanText(), true);
       onClear();
       return true;
     case R.id.lparen:
     case R.id.rparen:
       mFormulaEditText.setText('(' + mFormulaEditText.getCleanText() + ')');
       return true;
     case R.id.fun_sin:
       insert(getString(R.string.fun_arcsin) + "(");
       return true;
     case R.id.fun_cos:
       insert(getString(R.string.fun_arccos) + "(");
       return true;
     case R.id.fun_tan:
       insert(getString(R.string.fun_arctan) + "(");
       return true;
   }
   return false;
 }
  protected void initialize(Bundle savedInstanceState) {
    // Rebuild constants. If the user changed their locale, it won't kill the app
    // but it might change a decimal point from . to ,
    Constants.rebuildConstants();

    mDisplayView = (DisplayOverlay) findViewById(R.id.display);
    mDisplayView.setFade(findViewById(R.id.history_fade));
    mDisplayForeground = (ViewGroup) findViewById(R.id.the_clear_animation);
    mFormulaEditText = (CalculatorEditText) findViewById(R.id.formula);
    mResultEditText = (CalculatorEditText) findViewById(R.id.result);
    mPadViewPager = (CalculatorPadView) findViewById(R.id.pad_pager);
    mDeleteButton = findViewById(R.id.del);
    mClearButton = findViewById(R.id.clr);
    mEqualButton = (EqualsImageButton) findViewById(R.id.pad_numeric).findViewById(R.id.eq);

    if (mEqualButton == null || mEqualButton.getVisibility() != View.VISIBLE) {
      mEqualButton = (EqualsImageButton) findViewById(R.id.pad_operator).findViewById(R.id.eq);
    }

    mTokenizer = new CalculatorExpressionTokenizer(this);
    mEvaluator = new CalculatorExpressionEvaluator(mTokenizer);

    setState(
        CalculatorState.values()[
            savedInstanceState.getInt(KEY_CURRENT_STATE, CalculatorState.INPUT.ordinal())]);

    mFormulaEditText.setSolver(mEvaluator.getSolver());
    mResultEditText.setSolver(mEvaluator.getSolver());
    mFormulaEditText.setText(
        mTokenizer.getLocalizedExpression(
            savedInstanceState.getString(KEY_CURRENT_EXPRESSION, "")));
    mFormulaEditText.addTextChangedListener(mFormulaTextWatcher);
    mFormulaEditText.setOnKeyListener(mFormulaOnKeyListener);
    mFormulaEditText.setOnTextSizeChangeListener(this);
    mDeleteButton.setOnLongClickListener(this);
    mResultEditText.setEnabled(false);
    findViewById(R.id.lparen).setOnLongClickListener(this);
    findViewById(R.id.rparen).setOnLongClickListener(this);
    findViewById(R.id.fun_sin).setOnLongClickListener(this);
    findViewById(R.id.fun_cos).setOnLongClickListener(this);
    findViewById(R.id.fun_tan).setOnLongClickListener(this);

    // Disable IME for this application
    getWindow()
        .setFlags(
            WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
            WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);

    Button dot = (Button) findViewById(R.id.dec_point);
    dot.setText(String.valueOf(Constants.DECIMAL_POINT));
  }
  protected void onError(final int errorResourceId) {
    if (mCurrentState != CalculatorState.EVALUATE) {
      // Only animate error on evaluate.
      mResultEditText.setText(errorResourceId);
      return;
    }

    reveal(
        mCurrentButton,
        R.color.calculator_error_color,
        new AnimationFinishedListener() {
          @Override
          public void onAnimationFinished() {
            setState(CalculatorState.ERROR);
            mResultEditText.setText(errorResourceId);
          }
        });
  }
  protected void onResult(final String result) {
    // Calculate the values needed to perform the scale and translation animations,
    // accounting for how the scale will affect the final position of the text.
    final float resultScale =
        mFormulaEditText.getVariableTextSize(result) / mResultEditText.getTextSize();
    final float resultTranslationX =
        (1.0f - resultScale)
            * (mResultEditText.getWidth() / 2.0f - mResultEditText.getPaddingRight());

    // Calculate the height of the formula (without padding)
    final float formulaRealHeight =
        mFormulaEditText.getHeight()
            - mFormulaEditText.getPaddingTop()
            - mFormulaEditText.getPaddingBottom();

    // Calculate the height of the resized result (without padding)
    final float resultRealHeight =
        resultScale
            * (mResultEditText.getHeight()
                - mResultEditText.getPaddingTop()
                - mResultEditText.getPaddingBottom());

    // Now adjust the result upwards!
    final float resultTranslationY =
        // Move the result up (so both formula + result heights match)
        -mFormulaEditText.getHeight()
            // Now switch the result's padding top with the formula's padding top
            - resultScale * mResultEditText.getPaddingTop()
            + mFormulaEditText.getPaddingTop()
            // But the result centers its text! And it's taller now! So adjust for that centered
            // text
            + (formulaRealHeight - resultRealHeight) / 2;

    // Move the formula all the way to the top of the screen
    final float formulaTranslationY = -mFormulaEditText.getBottom();

    // Use a value animator to fade to the final text color over the course of the animation.
    final int resultTextColor = mResultEditText.getCurrentTextColor();
    final int formulaTextColor = mFormulaEditText.getCurrentTextColor();
    final ValueAnimator textColorAnimator =
        ValueAnimator.ofObject(new ArgbEvaluator(), resultTextColor, formulaTextColor);
    textColorAnimator.addUpdateListener(
        new AnimatorUpdateListener() {
          @Override
          public void onAnimationUpdate(ValueAnimator valueAnimator) {
            mResultEditText.setTextColor((Integer) valueAnimator.getAnimatedValue());
          }
        });
    mResultEditText.setText(result);
    mResultEditText.setPivotX(mResultEditText.getWidth() / 2);
    mResultEditText.setPivotY(0f);

    final AnimatorSet animatorSet = new AnimatorSet();
    animatorSet.playTogether(
        textColorAnimator,
        ObjectAnimator.ofFloat(mResultEditText, View.SCALE_X, resultScale),
        ObjectAnimator.ofFloat(mResultEditText, View.SCALE_Y, resultScale),
        ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_X, resultTranslationX),
        ObjectAnimator.ofFloat(mResultEditText, View.TRANSLATION_Y, resultTranslationY),
        ObjectAnimator.ofFloat(mFormulaEditText, View.TRANSLATION_Y, formulaTranslationY));
    animatorSet.setDuration(getResources().getInteger(android.R.integer.config_longAnimTime));
    animatorSet.setInterpolator(new AccelerateDecelerateInterpolator());
    animatorSet.addListener(
        new AnimationFinishedListener() {
          @Override
          public void onAnimationFinished() {
            // Reset all of the values modified during the animation.
            mResultEditText.setPivotY(mResultEditText.getHeight() / 2);
            mResultEditText.setTextColor(resultTextColor);
            mResultEditText.setScaleX(1.0f);
            mResultEditText.setScaleY(1.0f);
            mResultEditText.setTranslationX(0.0f);
            mResultEditText.setTranslationY(0.0f);
            mFormulaEditText.setTranslationY(0.0f);

            // Finally update the formula to use the current result.
            mFormulaEditText.setText(result);
            setState(CalculatorState.RESULT);
          }
        });

    play(animatorSet);
  }
 protected void onDelete() {
   // Delete works like backspace; remove the last character from the expression.
   mFormulaEditText.backspace();
 }
 @Override
 protected void onPause() {
   super.onPause();
   saveHistory(mFormulaEditText.getCleanText(), mResultEditText.getCleanText(), true);
   mPersist.save();
 }