/*
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.numix.calculator.view;
import java.util.Arrays;
import java.util.List;
import android.content.Context;
import android.graphics.Rect;
import android.text.Editable;
import android.text.InputType;
import android.text.Spanned;
import android.text.method.NumberKeyListener;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.animation.TranslateAnimation;
import android.view.inputmethod.EditorInfo;
import android.widget.EditText;
import android.widget.ViewSwitcher;
import com.numix.calculator.CalculatorEditable;
import com.numix.calculator.Logic;
import com.numix.calculator.R;
/**
* Provides vertical scrolling for the input/result EditText.
*/
public class CalculatorDisplay extends ViewSwitcher implements OnLongClickListener {
private static final String ATTR_MAX_DIGITS = "maxDigits";
private static final int DEFAULT_MAX_DIGITS = 10;
// only these chars are accepted from keyboard
private static final char[] ACCEPTED_CHARS = "0123456789.+-*/\u2212\u00d7\u00f7()!%^".toCharArray();
private static final int ANIM_DURATION = 600;
public enum Scroll {
UP, DOWN, NONE
}
TranslateAnimation inAnimUp;
TranslateAnimation outAnimUp;
TranslateAnimation inAnimDown;
TranslateAnimation outAnimDown;
private int mMaxDigits = DEFAULT_MAX_DIGITS;
private final List<String> mKeywords;
public CalculatorDisplay(Context context, AttributeSet attrs) {
super(context, attrs);
mMaxDigits = attrs.getAttributeIntValue(null, ATTR_MAX_DIGITS, DEFAULT_MAX_DIGITS);
String sinString = context.getString(R.string.sin);
String cosString = context.getString(R.string.cos);
String tanString = context.getString(R.string.tan);
String arcsinString = context.getString(R.string.arcsin);
String arccosString = context.getString(R.string.arccos);
String arctanString = context.getString(R.string.arctan);
String logString = context.getString(R.string.lg);
String lnString = context.getString(R.string.ln);
String modString = context.getString(R.string.mod);
String detString = context.getString(R.string.det);
String dx = context.getString(R.string.dx);
String dy = context.getString(R.string.dy);
mKeywords = Arrays.asList(arcsinString + "(", arccosString + "(", arctanString + "(", sinString + "(", cosString + "(", tanString + "(", logString
+ "(", modString + "(", lnString + "(", detString + "(", dx, dy);
setOnLongClickListener(this);
}
public int getMaxDigits() {
return mMaxDigits;
}
public void setLogic(Logic logic) {
NumberKeyListener calculatorKeyListener = new NumberKeyListener() {
@Override
public int getInputType() {
return EditorInfo.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS;
}
@Override
protected char[] getAcceptedChars() {
return ACCEPTED_CHARS;
}
@Override
public CharSequence filter(CharSequence source, int start, int end, Spanned dest, int dstart, int dend) {
/*
* the EditText should still accept letters (eg. 'sin') coming
* from the on-screen touch buttons, so don't filter anything.
*/
return null;
}
@Override
public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
if(keyCode == KeyEvent.KEYCODE_DEL) {
int selectionHandle = getSelectionStart();
if(selectionHandle == 0) {
// Remove the view in front
AdvancedDisplay editor = getAdvancedDisplay();
int index = editor.getChildIndex(getActiveEditText());
if(index > 0) {
editor.removeView(editor.getChildAt(index - 1));
}
}
else {
// Check and remove keywords
String textBeforeInsertionHandle = getActiveEditText().getText().toString().substring(0, selectionHandle);
String textAfterInsertionHandle = getActiveEditText().getText().toString()
.substring(selectionHandle, getActiveEditText().getText().toString().length());
for(String s : mKeywords) {
if(textBeforeInsertionHandle.endsWith(s)) {
int deletionLength = s.length();
String text = textBeforeInsertionHandle.substring(0, textBeforeInsertionHandle.length() - deletionLength)
+ textAfterInsertionHandle;
getActiveEditText().setText(text);
setSelection(selectionHandle - deletionLength);
return true;
}
}
}
}
return super.onKeyDown(view, content, keyCode, event);
}
};
Editable.Factory factory = new CalculatorEditable.Factory(logic);
for(int i = 0; i < 2; ++i) {
AdvancedDisplay text = ((ScrollableDisplay) getChildAt(i)).getView();
text.setLogic(logic);
text.setEditableFactory(factory);
text.setKeyListener(calculatorKeyListener);
text.setLayoutParams(new ScrollableDisplay.LayoutParams(ScrollableDisplay.LayoutParams.WRAP_CONTENT, ScrollableDisplay.LayoutParams.WRAP_CONTENT,
Gravity.RIGHT | Gravity.CENTER_VERTICAL));
}
}
@Override
public void setOnKeyListener(OnKeyListener l) {
getChildAt(0).setOnKeyListener(l);
getChildAt(1).setOnKeyListener(l);
}
@Override
protected void onSizeChanged(int w, int h, int oldW, int oldH) {
inAnimUp = new TranslateAnimation(0, 0, h, 0);
inAnimUp.setDuration(ANIM_DURATION);
outAnimUp = new TranslateAnimation(0, 0, 0, -h);
outAnimUp.setDuration(ANIM_DURATION);
inAnimDown = new TranslateAnimation(0, 0, -h, 0);
inAnimDown.setDuration(ANIM_DURATION);
outAnimDown = new TranslateAnimation(0, 0, 0, h);
outAnimDown.setDuration(ANIM_DURATION);
}
public AdvancedDisplay getAdvancedDisplay() {
return ((ScrollableDisplay) getCurrentView()).getView();
}
public EditText getActiveEditText() {
AdvancedDisplay editor = getAdvancedDisplay();
return editor.getActiveEditText();
}
public void insert(String delta) {
AdvancedDisplay editor = getAdvancedDisplay();
editor.insert(delta);
}
public String getText() {
AdvancedDisplay text = getAdvancedDisplay();
return text.getText();
}
public void setText(CharSequence text, Scroll dir) {
if(getText().length() == 0) {
dir = Scroll.NONE;
}
if(dir == Scroll.UP) {
setInAnimation(inAnimUp);
setOutAnimation(outAnimUp);
}
else if(dir == Scroll.DOWN) {
setInAnimation(inAnimDown);
setOutAnimation(outAnimDown);
}
else { // Scroll.NONE
setInAnimation(null);
setOutAnimation(null);
}
AdvancedDisplay editor = ((ScrollableDisplay) getNextView()).getView();
editor.setText(text.toString());
showNext();
getAdvancedDisplay().getLastView().requestFocus();
}
public int getSelectionStart() {
if(getActiveEditText() == null) return 0;
return getActiveEditText().getSelectionStart();
}
private void setSelection(int position) {
getActiveEditText().setSelection(position);
}
@Override
protected void onFocusChanged(boolean gain, int direction, Rect prev) {
if(!gain) {
requestFocus();
}
}
@Override
public boolean onLongClick(View v) {
return getAdvancedDisplay().performLongClick();
}
}