some weird moving

This commit is contained in:
serso 2011-09-12 22:54:54 +04:00
parent 50b5622e66
commit c9a6416523
24 changed files with 2061 additions and 0 deletions

View File

@ -0,0 +1,426 @@
package org.solovyev.android.calculator;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.text.ClipboardManager;
import android.text.InputType;
import android.util.TypedValue;
import android.view.*;
import android.view.inputmethod.InputMethodManager;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.*;
import org.solovyev.util.StringUtils;
import org.solovyev.util.math.MathEntityType;
import bsh.EvalError;
import bsh.Interpreter;
import android.app.Activity;
import android.os.Bundle;
import android.util.Log;
import android.widget.EditText;
import org.solovyev.util.math.MathUtils;
import org.solovyev.util.math.Point2d;
public class CalculatorActivity extends Activity implements FontSizeAdjuster {
private static final int HVGA_WIDTH_PIXELS = 320;
@NotNull
private EditText editText;
@NotNull
private TextView resultEditText;
@NotNull
private Interpreter interpreter;
@NotNull
private HistoryHelper<CalculatorHistoryState> historyHelper;
@NotNull
private BroadcastReceiver preferencesChangesReceiver;
@NotNull
private List<SimpleOnDragListener> onDragListeners = new ArrayList<SimpleOnDragListener>();
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.main);
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
this.editText = (EditText) findViewById(R.id.editText);
this.editText.setInputType(InputType.TYPE_NULL);
imm.hideSoftInputFromWindow(this.editText.getWindowToken(), 0);
this.resultEditText = (TextView) findViewById(R.id.resultEditText);
this.resultEditText.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final CharSequence text = ((TextView) v).getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(CalculatorActivity.this, "Result copied to clipboard!", Toast.LENGTH_SHORT).show();
}
}
});
final DragButtonCalibrationActivity.Preferences dragPreferences = DragButtonCalibrationActivity.getPreferences(this);
final SimpleOnDragListener onDragListener = new SimpleOnDragListener(new SimpleOnDragListener.DragProcessor() {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
assert dragButton instanceof DirectionDragButton;
processButtonAction(dragButton, getActionText((DirectionDragButton) dragButton, dragDirection));
return true;
}
}, dragPreferences);
onDragListeners.add(onDragListener);
// todo serso: check if there is more convenient method for doing this
final R.id ids = new R.id();
for (Field field : R.id.class.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
try {
final View view = findViewById(field.getInt(ids));
if (view instanceof DragButton) {
((DragButton) view).setOnDragListener(onDragListener);
}
} catch (IllegalArgumentException e) {
Log.e(CalculatorActivity.class.getName(), e.getMessage());
} catch (IllegalAccessException e) {
Log.e(CalculatorActivity.class.getName(), e.getMessage());
}
}
}
final SimpleOnDragListener historyOnDragListener = new SimpleOnDragListener(new HistoryDragProcessor(), dragPreferences);
((DragButton) findViewById(R.id.clearButton)).setOnDragListener(historyOnDragListener);
((DragButton) findViewById(R.id.pasteButton)).setOnDragListener(historyOnDragListener);
onDragListeners.add(historyOnDragListener);
final SimpleOnDragListener toPositionOnDragListener = new SimpleOnDragListener(new SimpleOnDragListener.DragProcessor() {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragButton instanceof DirectionDragButton) {
String text = ((DirectionDragButton) dragButton).getText(dragDirection);
if ("".equals(text)) {
CalculatorActivity.this.editText.setSelection(0);
} else if ("".equals(text)) {
CalculatorActivity.this.editText.setSelection(CalculatorActivity.this.editText.getText().length());
}
}
return result;
}
}, dragPreferences);
((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener);
((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener);
onDragListeners.add(toPositionOnDragListener);
this.interpreter = new Interpreter();
try {
interpreter.eval(Preprocessor.wrap(JsclOperation.importCommands, "/jscl/editorengine/commands"));
} catch (EvalError e) {
Log.e(CalculatorActivity.class.getName(), e.getMessage());
}
this.historyHelper = new SimpleHistoryHelper<CalculatorHistoryState>();
saveHistoryState();
this.preferencesChangesReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DragButtonCalibrationActivity.INTENT_ACTION.equals(intent.getAction())) {
final DragButtonCalibrationActivity.Preferences preferences = DragButtonCalibrationActivity.getPreferences(CalculatorActivity.this);
for (SimpleOnDragListener dragListener : onDragListeners) {
dragListener.setPreferences(preferences);
}
}
}
};
registerReceiver(this.preferencesChangesReceiver, new IntentFilter(DragButtonCalibrationActivity.INTENT_ACTION));
}
private void saveHistoryState() {
historyHelper.addState(getCurrentHistoryState());
}
public void elementaryButtonClickHandler(@NotNull View v) {
eval(JsclOperation.elementary, true);
}
public void numericButtonClickHandler(@NotNull View v) {
eval(JsclOperation.numeric, true);
}
public void eraseButtonClickHandler(@NotNull View v) {
if (editText.getSelectionStart() > 0) {
editText.getText().delete(editText.getSelectionStart() - 1, editText.getSelectionStart());
saveHistoryState();
}
}
public void simplifyButtonClickHandler(@NotNull View v) {
eval(JsclOperation.simplify, true);
}
public void moveLeftButtonClickHandler(@NotNull View v) {
if (editText.getSelectionStart() > 0) {
editText.setSelection(editText.getSelectionStart() - 1);
}
}
public void moveRightButtonClickHandler(@NotNull View v) {
if (editText.getSelectionStart() < editText.getText().length()) {
editText.setSelection(editText.getSelectionStart() + 1);
}
}
public void pasteButtonClickHandler(@NotNull View v) {
final ClipboardManager clipboard = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE);
if ( clipboard.hasText() ) {
editText.getText().insert(editText.getSelectionStart(), clipboard.getText());
eval(JsclOperation.numeric, false);
saveHistoryState();
}
}
public void clearButtonClickHandler(@NotNull View v) {
if (!StringUtils.isEmpty(editText.getText()) || !StringUtils.isEmpty(resultEditText.getText())) {
editText.getText().clear();
resultEditText.setText("");
saveHistoryState();
}
}
private void eval(@NotNull JsclOperation operation, boolean showError) {
try {
final String preprocessedString = Preprocessor.process(String.valueOf(editText.getText()));
String result = String.valueOf(interpreter.eval(Preprocessor.wrap(operation, preprocessedString))).trim();
try {
final Double dResult = Double.valueOf(result);
result = String.valueOf(MathUtils.round(dResult, 5));
} catch (NumberFormatException e) {
}
resultEditText.setText(result);
// result editor might be changed (but main editor - no) => make undo and add new state with saved result
CalculatorHistoryState currentHistoryState = getCurrentHistoryState();
if (this.historyHelper.isUndoAvailable()) {
this.historyHelper.undo(currentHistoryState);
}
this.historyHelper.addState(currentHistoryState);
} catch (EvalError e) {
if (showError) {
Toast.makeText(CalculatorActivity.this, R.string.syntax_error, Toast.LENGTH_SHORT).show();
Log.e(CalculatorActivity.class.getName(), e.getMessage());
}
}
}
public void digitButtonClickHandler(@NotNull View v) {
processButtonAction(v, ((DirectionDragButton) v).getTextMiddle());
}
private final class HistoryDragProcessor implements SimpleOnDragListener.DragProcessor {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
boolean result = false;
Log.d(String.valueOf(dragButton.getId()), "History on drag event start: " + dragDirection);
assert dragButton instanceof DirectionDragButton;
String actionText = getActionText((DirectionDragButton) dragButton, dragDirection);
if (!StringUtils.isEmpty(actionText)) {
try {
result = true;
final HistoryAction historyAction = HistoryAction.valueOf(actionText);
doHistoryAction(historyAction);
} catch (IllegalArgumentException e) {
Log.e(String.valueOf(dragButton.getId()), "Unsupported history action: " + actionText);
}
}
return result;
}
}
private void doHistoryAction(@NotNull HistoryAction historyAction) {
if (historyHelper.isActionAvailable(historyAction)) {
final CalculatorHistoryState newState = historyHelper.doAction(historyAction, getCurrentHistoryState());
if (newState != null) {
setCurrentHistoryState(newState);
}
}
}
@Nullable
private static String getActionText(@NotNull DirectionDragButton dragButton, @NotNull DragDirection direction) {
final String result;
switch (direction) {
case up:
result = dragButton.getTextUp();
break;
case down:
result = dragButton.getTextDown();
break;
default:
result = null;
break;
}
return result;
}
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
setValuesFromHistory(this.editText, editorHistoryState.getEditorState());
setValuesFromHistory(this.resultEditText, editorHistoryState.getResultEditorState());
}
private void setValuesFromHistory(@NotNull TextView editText, EditorHistoryState editorHistoryState) {
editText.setText(editorHistoryState.getText());
if (editText instanceof EditText) {
((EditText) editText).setSelection(editorHistoryState.getCursorPosition());
}
}
@NotNull
public CalculatorHistoryState getCurrentHistoryState() {
return new CalculatorHistoryState(getEditorHistoryState(this.editText), getEditorHistoryState(this.resultEditText));
}
private EditorHistoryState getEditorHistoryState(@NotNull TextView textView) {
final EditorHistoryState result = new EditorHistoryState();
result.setText(String.valueOf(textView.getText()));
result.setCursorPosition(textView.getSelectionStart());
return result;
}
private void processButtonAction(@NotNull View v, @Nullable String text) {
//Toast.makeText(CalculatorActivity.this, text, Toast.LENGTH_SHORT).show();
if (!StringUtils.isEmpty(text)) {
final MathEntityType type = MathEntityType.getType(text);
int cursorPositionOffset = 0;
if (type != null) {
switch (type) {
case function:
text += "()";
cursorPositionOffset = -1;
break;
case group_symbols:
cursorPositionOffset = -1;
break;
default:
break;
}
}
this.editText.getText().insert(this.editText.getSelectionStart(), text);
this.editText.setSelection(this.editText.getSelectionStart() + cursorPositionOffset, this.editText.getSelectionEnd() + cursorPositionOffset);
saveHistoryState();
eval(JsclOperation.numeric, false);
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
doHistoryAction(HistoryAction.undo);
return true;
}
return super.onKeyDown(keyCode, event);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
// todo serso: inflate menu as soon as it will implemented in proper way
/* final MenuInflater menuInflater = getMenuInflater();
menuInflater.inflate(R.menu.main_menu, menu);*/
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result;
switch (item.getItemId()) {
case R.id.menu_item_settings:
showSettings();
result = true;
case R.id.menu_item_help:
showHelp();
result = true;
default:
result = super.onOptionsItemSelected(item);
}
return result;
}
private void showSettings() {
startActivity(new Intent(this, CalculatorPreferencesActivity.class));
}
private void showHelp() {
Log.d(CalculatorActivity.class + "showHelp()", "Show help!");
}
/**
* The font sizes in the layout files are specified for a HVGA display.
* Adjust the font sizes accordingly if we are running on a different
* display.
*/
@Override
public void adjustFontSize(@NotNull TextView view) {
float fontPixelSize = view.getTextSize();
Display display = getWindowManager().getDefaultDisplay();
int h = Math.min(display.getWidth(), display.getHeight());
float ratio = (float) h / HVGA_WIDTH_PIXELS;
view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontPixelSize * ratio);
}
}

View File

@ -0,0 +1,40 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/11/11
* Time: 12:16 AM
*/
public class CalculatorHistoryState {
@NotNull
private EditorHistoryState editorState;
@NotNull
private EditorHistoryState resultEditorState;
public CalculatorHistoryState(@NotNull EditorHistoryState editorState, @NotNull EditorHistoryState resultEditorState) {
this.editorState = editorState;
this.resultEditorState = resultEditorState;
}
@NotNull
public EditorHistoryState getEditorState() {
return editorState;
}
public void setEditorState(@NotNull EditorHistoryState editorState) {
this.editorState = editorState;
}
@NotNull
public EditorHistoryState getResultEditorState() {
return resultEditorState;
}
public void setResultEditorState(@NotNull EditorHistoryState resultEditorState) {
this.resultEditorState = resultEditorState;
}
}

View File

@ -0,0 +1,35 @@
package org.solovyev.android.calculator;
import android.content.Intent;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceActivity;
/**
* User: serso
* Date: 7/16/11
* Time: 6:37 PM
*/
public class CalculatorPreferencesActivity extends PreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
addPreferencesFromResource(R.xml.preferences);
final Preference dragButtonCalibration = findPreference("dragButtonCalibration");
dragButtonCalibration.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
startActivity(new Intent(CalculatorPreferencesActivity.this, DragButtonCalibrationActivity.class));
return true;
}
});
}
@Override
protected void onRestoreInstanceState(Bundle state) {
super.onRestoreInstanceState(state);
}
}

View File

@ -0,0 +1,393 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.util.Log;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.View;
import android.widget.ImageView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.*;
import org.solovyev.common.collections.ManyValuedHashMap;
import org.solovyev.common.collections.ManyValuedMap;
import org.solovyev.common.utils.Interval;
import org.solovyev.util.math.MathUtils;
import org.solovyev.util.math.Point2d;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* User: serso
* Date: 7/16/11
* Time: 7:28 PM
*/
public class DragButtonCalibrationActivity extends Activity {
@NotNull
private final List<DragData> dragHistory = new ArrayList<DragData>();
private final Map<DragButton, CalibrationArrow> map = new HashMap<DragButton, CalibrationArrow>();
public static final String PREFERENCES = "dragButtonPreferences";
public static final String PREFERENCES_FIRST_RUN = "firstRun";
public static final String PREFERENCES_MIN = "min";
public static final String PREFERENCES_MAX = "max";
private static final float DEFAULT_VALUE = -999;
private static final int MIN_HISTORY_FOR_CALIBRATION = 10;
public static final String INTENT_ACTION = "org.solovyev.android.calculator.DragButtonPreferencesChanged";
public static enum PreferenceType {
angle,
distance,
duration
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.drag_button_calibration);
createCalibrationButton(R.id.calibrationButtonRight, R.id.calibrationArrowRight);
createCalibrationButton(R.id.calibrationButtonLeft, R.id.calibrationArrowLeft);
}
private void createCalibrationButton(int buttonId, int arrowId) {
final DragButton calibrationButton = (DragButton) findViewById(buttonId);
calibrationButton.setOnDragListener(new CalibrationOnDragListener());
ImageView imageView = (ImageView) findViewById(arrowId);
CalibrationArrow calibrationArrow = new CalibrationArrow(imageView);
createDragDirection(0, calibrationArrow);
map.put(calibrationButton, calibrationArrow);
}
private void createDragDirection(long timeout, @NotNull final CalibrationArrow calibrationArrow) {
new Handler().postDelayed(new Runnable() {
public void run() {
calibrationArrow.dragDirection = Math.random() > 0.5 ? DragDirection.up : DragDirection.down;
calibrationArrow.calibrationArrow.setImageResource(calibrationArrow.dragDirection == DragDirection.down ? R.drawable.down : R.drawable.up);
}
}, timeout);
}
public void restartClickHandler(View v) {
for (CalibrationArrow calibrationArrow : map.values()) {
createDragDirection(0, calibrationArrow);
}
}
private class CalibrationOnDragListener implements OnDragListener {
@Override
public boolean isSuppressOnClickEvent() {
return true;
}
@Override
public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) {
final Point2d startPoint = event.getStartPoint();
final MotionEvent motionEvent = event.getMotionEvent();
// init end point
final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY());
float distance = MathUtils.getDistance(startPoint, endPoint);
double angle = Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, SimpleOnDragListener.axis), endPoint));
final CalibrationArrow calibrationArrow = map.get(dragButton);
final DragDirection dragDirection = calibrationArrow.dragDirection;
assert dragDirection == DragDirection.up || dragDirection == DragDirection.down;
double deviationAngle = angle;
if (dragDirection == DragDirection.up) {
deviationAngle = 180 - deviationAngle;
}
if (deviationAngle > 45) {
calibrationArrow.calibrationArrow.setImageResource(R.drawable.not_ok);
} else {
calibrationArrow.calibrationArrow.setImageResource(R.drawable.ok);
dragHistory.add(new DragData(dragDirection, distance, angle, (motionEvent.getEventTime() - motionEvent.getDownTime())));
}
createDragDirection(500, calibrationArrow);
return true;
}
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (dragHistory.size() > MIN_HISTORY_FOR_CALIBRATION) {
final ManyValuedMap<DragDirection, Double> anglesByDirection = new ManyValuedHashMap<DragDirection, Double>();
final ManyValuedMap<DragDirection, Double> distancesByDirection = new ManyValuedHashMap<DragDirection, Double>();
final ManyValuedMap<DragDirection, Double> timesByDirection = new ManyValuedHashMap<DragDirection, Double>();
for (DragData dragData : dragHistory) {
anglesByDirection.put(dragData.getDirection(), dragData.getAngle());
distancesByDirection.put(dragData.getDirection(), (double) dragData.getDistance());
timesByDirection.put(dragData.getDirection(), dragData.getTime());
}
final Map<DragDirection, MathUtils.StatData> angleStatData = getStatDataByDirection(anglesByDirection);
final Map<DragDirection, MathUtils.StatData> distanceStatData = getStatDataByDirection(distancesByDirection);
final Map<DragDirection, MathUtils.StatData> timeStatData = getStatDataByDirection(timesByDirection);
Log.d(this.getClass().getName(), "Angle statistics: ");
logStatData(angleStatData);
Log.d(this.getClass().getName(), "Distance statistics: ");
logStatData(distanceStatData);
Log.d(this.getClass().getName(), "Time statistics: ");
logStatData(timeStatData);
final SharedPreferences settings = getSharedPreferences(PREFERENCES, 0);
final SharedPreferences.Editor editor = settings.edit();
setPreferences(angleStatData, editor, PreferenceType.angle);
setPreferences(distanceStatData, editor, PreferenceType.distance);
setPreferences(timeStatData, editor, PreferenceType.duration);
editor.commit();
sendOrderedBroadcast(new Intent(INTENT_ACTION), null);
}
}
return super.onKeyDown(keyCode, event);
}
private void setPreferences(@NotNull Map<DragDirection, MathUtils.StatData> statData, @NotNull SharedPreferences.Editor editor, @NotNull PreferenceType preferenceType) {
for (Map.Entry<DragDirection, MathUtils.StatData> entry : statData.entrySet()) {
final float min = (float) entry.getValue().getMean() - 2 * (float) entry.getValue().getStandardDeviation();
final float max = (float) entry.getValue().getMean() + 2 * (float) entry.getValue().getStandardDeviation();
editor.putFloat(preferenceType.name() + "_" + entry.getKey().name() + "_" + PREFERENCES_MIN, Math.max(0, min));
editor.putFloat(preferenceType.name() + "_" + entry.getKey().name() + "_" + PREFERENCES_MAX, max);
}
}
@NotNull
public static Preferences getPreferences(@NotNull Context context) {
SharedPreferences preferences = context.getSharedPreferences(PREFERENCES, MODE_PRIVATE);
final Preferences result = new Preferences();
for (PreferenceType preferenceType : PreferenceType.values()) {
for (DragDirection dragDirection : DragDirection.values()) {
final float defaultMin;
final float defaultMax;
switch (preferenceType) {
case angle:
switch (dragDirection) {
case up:
defaultMin = 150f;
defaultMax = 180f;
break;
case down:
defaultMin = 0f;
defaultMax = 30f;
break;
default:
defaultMin = DEFAULT_VALUE;
defaultMax = DEFAULT_VALUE;
}
break;
case distance:
defaultMin = 60f;
defaultMax = 140f;
break;
case duration:
defaultMin = 100f;
defaultMax = 300f;
break;
default:
defaultMin = DEFAULT_VALUE;
defaultMax = DEFAULT_VALUE;
}
final float min = preferences.getFloat(preferenceType.name() + "_" + dragDirection.name() + "_" + PREFERENCES_MIN, defaultMin);
final float max = preferences.getFloat(preferenceType.name() + "_" + dragDirection.name() + "_" + PREFERENCES_MAX, defaultMax);
if (min != DEFAULT_VALUE && max != DEFAULT_VALUE) {
final DragPreference directionPreference = new DragPreference(dragDirection, new Interval(min, max));
Preference preference = result.getPreferencesMap().get(preferenceType);
if (preference == null) {
preference = new Preference(preferenceType);
result.getPreferencesMap().put(preferenceType, preference);
}
preference.getDirectionPreferences().put(dragDirection, directionPreference);
} else {
Log.e(DragButtonCalibrationActivity.class.getName(), "New preference type added: default preferences should be defined!");
}
}
}
return result;
}
public static class DragPreference {
@NotNull
private DragDirection direction;
@NotNull
private Interval interval;
public DragPreference(@NotNull DragDirection direction, @NotNull Interval interval) {
this.direction = direction;
this.interval = interval;
}
@NotNull
public DragDirection getDirection() {
return direction;
}
public void setDirection(@NotNull DragDirection direction) {
this.direction = direction;
}
@NotNull
public Interval getInterval() {
return interval;
}
public void setInterval(@NotNull Interval interval) {
this.interval = interval;
}
}
public static class Preference {
@NotNull
private PreferenceType preferenceType;
@NotNull
private Map<DragDirection, DragPreference> directionPreferences = new HashMap<DragDirection, DragPreference>();
public Preference(@NotNull PreferenceType preferenceType) {
this.preferenceType = preferenceType;
}
@NotNull
public PreferenceType getPreferenceType() {
return preferenceType;
}
public void setPreferenceType(@NotNull PreferenceType preferenceType) {
this.preferenceType = preferenceType;
}
@NotNull
public Map<DragDirection, DragPreference> getDirectionPreferences() {
return directionPreferences;
}
public void setDirectionPreferences(@NotNull Map<DragDirection, DragPreference> directionPreferences) {
this.directionPreferences = directionPreferences;
}
}
public static class Preferences {
private final Map<PreferenceType, Preference> preferencesMap = new HashMap<PreferenceType, Preference>();
public Map<PreferenceType, Preference> getPreferencesMap() {
return preferencesMap;
}
}
private void logStatData(@NotNull Map<DragDirection, MathUtils.StatData> statData) {
for (Map.Entry<DragDirection, MathUtils.StatData> entry : statData.entrySet()) {
Log.d(this.getClass().getName(), entry.getKey() + "-> m: " + entry.getValue().getMean() + ", d: " + entry.getValue().getStandardDeviation());
}
}
private Map<DragDirection, MathUtils.StatData> getStatDataByDirection(@NotNull ManyValuedMap<DragDirection, Double> valuesByDirection) {
final Map<DragDirection, MathUtils.StatData> result = new HashMap<DragDirection, MathUtils.StatData>();
for (Map.Entry<DragDirection, List<Double>> entry : valuesByDirection.entrySet()) {
result.put(entry.getKey(), MathUtils.getStatData(entry.getValue()));
}
return result;
}
private class DragData {
private float distance;
private double angle;
private double time;
@NotNull
private DragDirection direction;
private DragData(@NotNull DragDirection direction, float distance, double angle, double time) {
this.distance = distance;
this.angle = angle;
this.direction = direction;
this.time = time;
}
public float getDistance() {
return distance;
}
public double getAngle() {
return angle;
}
@NotNull
public DragDirection getDirection() {
return direction;
}
public double getTime() {
return time;
}
}
private class CalibrationArrow {
@NotNull
private ImageView calibrationArrow;
@NotNull
private DragDirection dragDirection = DragDirection.up;
private CalibrationArrow(@NotNull ImageView calibrationArrow) {
this.calibrationArrow = calibrationArrow;
}
}
}

View File

@ -0,0 +1,37 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.Nullable;
public class EditorHistoryState {
private int cursorPosition;
@Nullable
private String text;
public EditorHistoryState() {
}
public EditorHistoryState( int cursorPosition, @Nullable String text ) {
this.cursorPosition = cursorPosition;
this.text = text;
}
public void setText(String text) {
this.text = text;
}
public String getText() {
return text;
}
public void setCursorPosition(int cursorPosition) {
this.cursorPosition = cursorPosition;
}
public int getCursorPosition() {
return cursorPosition;
}
}

View File

@ -0,0 +1,8 @@
package org.solovyev.android.calculator;
public enum HistoryAction {
redo,
undo;
}

View File

@ -0,0 +1,24 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public interface HistoryHelper<T> {
boolean isUndoAvailable();
@Nullable
T undo (@Nullable T currentState);
boolean isRedoAvailable();
@Nullable
T redo (@Nullable T currentState);
boolean isActionAvailable(@NotNull HistoryAction historyAction);
@Nullable
T doAction(@NotNull HistoryAction historyAction, @Nullable T currentState);
void addState(@Nullable T currentState);
}

View File

@ -0,0 +1,10 @@
package org.solovyev.android.calculator;
public enum JsclOperation {
simplify,
elementary,
importCommands,
numeric;
}

View File

@ -0,0 +1,81 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.util.math.MathEntityType;
public class Preprocessor {
@NotNull
public static String process(@NotNull String s) {
final StringBuilder sb = new StringBuilder();
for (int i = 0; i < s.length(); i++) {
char ch = s.charAt(i);
checkMultiplicationSignBeforeFunction(sb, s, i);
if (ch == '[' || ch == '{') {
sb.append('(');
} else if (ch == ']' || ch == '}') {
sb.append(')');
} else if (ch == 'π') {
sb.append("pi");
} else if (ch == '×' || ch == '∙') {
sb.append("*");
} else if (s.startsWith("ln", i)) {
sb.append("log");
i += 1;
} else if (s.startsWith("tg", i)) {
sb.append("tan");
i += 1;
} else if (s.startsWith("atg", i)) {
sb.append("atan");
i += 2;
} else if (s.startsWith("e(", i)) {
sb.append("exp(");
i += 1;
} else if (ch == 'e') {
sb.append("exp(1)");
} else if (ch == '√') {
sb.append("sqrt");
} else {
sb.append(ch);
}
}
return sb.toString();
}
private static void checkMultiplicationSignBeforeFunction(@NotNull StringBuilder sb, @NotNull String s, int i) {
if (i > 0) {
// get character before function
char chBefore = s.charAt(i - 1);
char ch = s.charAt(i);
final MathEntityType mathTypeBefore = MathEntityType.getType(String.valueOf(chBefore));
final MathEntityType mathType = MathEntityType.getType(String.valueOf(ch));
if (mathTypeBefore != MathEntityType.binary_operation &&
mathTypeBefore != MathEntityType.unary_operation &&
!MathEntityType.openGroupSymbols.contains(chBefore)) {
if (mathType == MathEntityType.constant) {
sb.append("*");
} else if (mathType == MathEntityType.digit && mathTypeBefore != MathEntityType.digit && mathTypeBefore != MathEntityType.dot) {
sb.append("*");
} else {
for (String function : MathEntityType.functions) {
if (s.startsWith(function, i)) {
sb.append("*");
break;
}
}
}
}
}
}
public static String wrap(@NotNull JsclOperation operation, @NotNull String s) {
return operation.name() + "(\"" + s + "\");";
}
}

View File

@ -0,0 +1,91 @@
package org.solovyev.android.calculator;
import java.util.ArrayList;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class SimpleHistoryHelper<T> implements HistoryHelper<T> {
private List<T> history = new ArrayList<T>();
private int currentStateIndex = -1;
@Override
public T undo(@Nullable T currentState) {
if ( !isUndoAvailable() ) {
throw new IndexOutOfBoundsException();
}
currentStateIndex--;
return history.get(currentStateIndex);
}
@Override
public T redo(@Nullable T currentState) {
if (!isRedoAvailable()) {
throw new IndexOutOfBoundsException();
}
currentStateIndex++;
return history.get(currentStateIndex);
}
@Override
public void addState(@Nullable T currentState) {
if (currentStateIndex == history.size() - 1) {
currentStateIndex++;
history.add(currentState);
} else {
assert currentStateIndex < history.size() - 1 : "Invalid history state index!";
currentStateIndex++;
history.set(currentStateIndex, currentState);
while( history.size() > currentStateIndex + 1 ) {
history.remove(history.size() - 1);
}
}
}
@Override
public boolean isUndoAvailable() {
return currentStateIndex > 0;
}
@Override
public boolean isRedoAvailable() {
return currentStateIndex < history.size() - 1;
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
boolean result = false;
switch (historyAction) {
case undo:
result = isUndoAvailable();
break;
case redo:
result = isRedoAvailable();
break;
}
return result;
}
@Override
public T doAction(@NotNull HistoryAction historyAction, @Nullable T currentState) {
T result = null;
switch (historyAction) {
case undo:
result = undo(currentState);
break;
case redo:
result = redo(currentState);
break;
}
return result;
}
}

View File

@ -0,0 +1,155 @@
/*
* 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 org.solovyev.android.view;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.graphics.Paint.Style;
import android.util.AttributeSet;
import android.widget.Button;
import android.view.MotionEvent;
import android.content.res.Resources;
import org.jetbrains.annotations.NotNull;
import org.solovyev.util.math.Point2d;
/**
* NOTE: copied from com.android.calculator2.ColorButton
*/
/**
* Button with click-animation effect.
*/
public class ColorButton extends Button {
int CLICK_FEEDBACK_COLOR;
static final int CLICK_FEEDBACK_INTERVAL = 10;
static final int CLICK_FEEDBACK_DURATION = 350;
@NotNull
private Point2d textPosition;
private long mAnimStart;
private Paint mFeedbackPaint;
public ColorButton(Context context, AttributeSet attrs) {
this(context, attrs, true);
}
public ColorButton(Context context, AttributeSet attrs, boolean init) {
super(context, attrs);
if (init) {
init(context);
}
}
protected void init(Context context) {
Resources res = getResources();
CLICK_FEEDBACK_COLOR = res.getColor(org.solovyev.android.calculator.R.color.magic_flame);
mFeedbackPaint = new Paint();
mFeedbackPaint.setStyle(Style.STROKE);
mFeedbackPaint.setStrokeWidth(2);
getPaint().setColor(res.getColor(org.solovyev.android.calculator.R.color.button_text));
mAnimStart = -1;
if (context instanceof FontSizeAdjuster) {
((FontSizeAdjuster) context).adjustFontSize(this);
}
}
@Override
public void onSizeChanged(int w, int h, int oldW, int oldH) {
measureText();
}
protected void measureText() {
Paint paint = getPaint();
if (getText() != null) {
textPosition = getTextPosition(paint, getText());
}
}
private Point2d getTextPosition(@NotNull Paint paint, @NotNull CharSequence text) {
final Point2d result = new Point2d();
result.setX((getWidth() - paint.measureText(text.toString())) / 2);
float height = getHeight() - paint.ascent() - paint.descent();
result.setY(height / 2);
return result;
}
@Override
protected void onTextChanged(CharSequence text, int start, int before, int after) {
measureText();
}
private void drawMagicFlame(int duration, Canvas canvas) {
int alpha = 255 - 255 * duration / CLICK_FEEDBACK_DURATION;
int color = CLICK_FEEDBACK_COLOR | (alpha << 24);
mFeedbackPaint.setColor(color);
canvas.drawRect(1, 1, getWidth() - 1, getHeight() - 1, mFeedbackPaint);
}
@Override
public void onDraw(Canvas canvas) {
if (mAnimStart != -1) {
int animDuration = (int) (System.currentTimeMillis() - mAnimStart);
if (animDuration >= CLICK_FEEDBACK_DURATION) {
mAnimStart = -1;
} else {
drawMagicFlame(animDuration, canvas);
postInvalidateDelayed(CLICK_FEEDBACK_INTERVAL);
}
} else if (isPressed()) {
drawMagicFlame(0, canvas);
}
CharSequence text = getText();
if (text != null && textPosition != null) {
canvas.drawText(text, 0, text.length(), textPosition.getX(), textPosition.getY(), getPaint());
}
}
public void animateClickFeedback() {
mAnimStart = System.currentTimeMillis();
invalidate();
}
@Override
public boolean onTouchEvent(MotionEvent event) {
boolean result = super.onTouchEvent(event);
switch (event.getAction()) {
case MotionEvent.ACTION_UP:
animateClickFeedback();
break;
case MotionEvent.ACTION_DOWN:
case MotionEvent.ACTION_CANCEL:
invalidate();
break;
}
return result;
}
}

View File

@ -0,0 +1,184 @@
package org.solovyev.android.view;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import org.solovyev.util.StringUtils;
import org.solovyev.util.math.Point2d;
/**
* User: serso
* Date: 7/17/11
* Time: 10:25 PM
*/
public class DirectionDragButton extends DragButton {
@Nullable
private String textUp;
@Nullable
private String textDown;
@Nullable
private String textMiddle;
@NotNull
private Point2d textUpPosition;
@NotNull
private Point2d textDownPosition;
@NotNull
private TextPaint upDownTextPaint;
public DirectionDragButton(Context context, @NotNull AttributeSet attrs) {
super(context, attrs, false);
init(context, attrs);
}
private void init(@NotNull Context context, @NotNull AttributeSet attrs) {
TypedArray a = context.obtainStyledAttributes(attrs, org.solovyev.android.calculator.R.styleable.DragButton);
final int N = a.getIndexCount();
for (int i = 0; i < N; i++) {
int attr = a.getIndex(i);
switch (attr) {
case org.solovyev.android.calculator.R.styleable.DragButton_textUp:
this.textUp = a.getString(attr);
break;
case org.solovyev.android.calculator.R.styleable.DragButton_textDown:
this.textDown = a.getString(attr);
break;
}
}
// backup text
this.textMiddle = String.valueOf(getText());
super.init(context);
}
@Override
protected void measureText() {
super.measureText();
final Paint basePaint = getPaint();
initUpDownTextPaint(basePaint);
if (textUp != null) {
textUpPosition = getTextPosition(upDownTextPaint, basePaint, textUp, 1);
}
if (textDown != null) {
textDownPosition = getTextPosition(upDownTextPaint, basePaint, textDown, -1);
}
if ( textDownPosition != null && textUpPosition != null ) {
if ( textDownPosition.getX() > textUpPosition.getX() ) {
textDownPosition.setX(textUpPosition.getX());
} else {
textUpPosition.setX(textDownPosition.getX());
}
}
}
private Point2d getTextPosition(@NotNull Paint paint, @NotNull Paint basePaint, @NotNull CharSequence text, float direction) {
final Point2d result = new Point2d();
float width = paint.measureText(text.toString() + " ");
result.setX(getWidth() - width);
float selfHeight = paint.ascent() + paint.descent();
basePaint.measureText(StringUtils.getNotEmpty(getText(), "|"));
float height = getHeight() - basePaint.ascent() - basePaint.descent();
if (direction < 0) {
result.setY(height / 2 - direction * height / 3 + selfHeight);
} else {
result.setY(height / 2 - direction * height / 3);
}
return result;
}
@Override
public void onDraw(Canvas canvas) {
super.onDraw(canvas);
initUpDownTextPaint(null);
if (textUp != null && textUpPosition != null) {
canvas.drawText(textUp, 0, textUp.length(), textUpPosition.getX(), textUpPosition.getY(), upDownTextPaint);
}
if (textDown != null && textDownPosition != null) {
canvas.drawText(textDown, 0, textDown.length(), textDownPosition.getX(), textDownPosition.getY(), upDownTextPaint);
}
}
private void initUpDownTextPaint(@Nullable Paint paint) {
if (paint == null) {
paint = getPaint();
}
upDownTextPaint = new TextPaint(paint);
upDownTextPaint.setAlpha(150);
upDownTextPaint.setTextSize(paint.getTextSize() / 2);
}
private String getStyledUpDownText(@Nullable String text) {
return StringUtils.getNotEmpty(text, "&nbsp;");
}
public void setTextUp(@Nullable String textUp) {
this.textUp = textUp;
}
@Nullable
public String getTextUp() {
return textUp;
}
public void setTextDown(@Nullable String textDown) {
this.textDown = textDown;
}
@Nullable
public String getTextDown() {
return textDown;
}
public void setTextMiddle(@Nullable String textMiddle) {
this.textMiddle = textMiddle;
}
@Nullable
public String getTextMiddle() {
return textMiddle;
}
@Nullable
public String getText(@NotNull DragDirection direction) {
final String result;
if (direction == DragDirection.up) {
result = getTextUp();
} else if ( direction == DragDirection.down ) {
result = getTextDown();
} else {
result = null;
}
return result;
}
}

View File

@ -0,0 +1,89 @@
package org.solovyev.android.view;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.util.math.Point2d;
import android.content.Context;
import android.util.AttributeSet;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
public class DragButton extends ColorButton {
@Nullable
private Point2d startPoint = null;
@Nullable
private OnDragListener onDragListener;
private final OnTouchListener onTouchListener = new OnTouchListenerImpl();
public DragButton(Context context, @NotNull AttributeSet attrs) {
this(context, attrs, true);
}
public DragButton(Context context, @NotNull AttributeSet attrs, boolean init) {
super(context, attrs, false);
setOnTouchListener(this.onTouchListener);
if ( init ) {
super.init(context);
}
}
public void setOnDragListener(@Nullable OnDragListener onDragListener) {
this.onDragListener = onDragListener;
}
@Nullable
public OnDragListener getOnDragListener() {
return onDragListener;
}
/**
* OnTouchListener implementation that fires onDrag()
*
* @author serso
*
*/
private final class OnTouchListenerImpl implements OnTouchListener {
@Override
public boolean onTouch(@NotNull View v, @NotNull MotionEvent event) {
// processing on touch event
// in order to avoid possible NPEs
final Point2d localStartPoint = startPoint;
final OnDragListener localOnDragListener = onDragListener;
if (localOnDragListener != null) {
// only if onDrag() listener specified
Log.d(String.valueOf(getId()), "onTouch() for: " + getId() + " . Motion event: " + event);
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
// start tracking: set start point
startPoint = new Point2d(event.getX(), event.getY());
break;
case MotionEvent.ACTION_UP:
// stop tracking
if (localStartPoint != null && localOnDragListener.onDrag(DragButton.this, new DragEvent(localStartPoint, event))) {
if (localOnDragListener.isSuppressOnClickEvent()) {
// prevent on click action
setPressed(false);
}
}
startPoint = null;
break;
}
}
return false;
}
}
}

View File

@ -0,0 +1,9 @@
package org.solovyev.android.view;
public enum DragDirection {
up,
down,
left,
right;
}

View File

@ -0,0 +1,38 @@
package org.solovyev.android.view;
import org.jetbrains.annotations.NotNull;
import org.solovyev.util.math.Point2d;
import android.view.MotionEvent;
public class DragEvent {
@NotNull
private final Point2d startPoint;
@NotNull
private final MotionEvent motionEvent;
public DragEvent(@NotNull Point2d startPoint, @NotNull MotionEvent motionEvent) {
this.startPoint = startPoint;
this.motionEvent = motionEvent;
}
/**
* @return motion event started at start point
*/
@NotNull
public MotionEvent getMotionEvent() {
return motionEvent;
}
/**
* @return start point of dragging
*/
@NotNull
public Point2d getStartPoint() {
return startPoint;
}
}

View File

@ -0,0 +1,14 @@
package org.solovyev.android.view;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/10/11
* Time: 7:21 PM
*/
public interface FontSizeAdjuster {
void adjustFontSize(@NotNull TextView textView);
}

View File

@ -0,0 +1,22 @@
package org.solovyev.android.view;
import org.jetbrains.annotations.NotNull;
public interface OnDragListener {
/**
*
* @return 'true': if drag event has taken place (i.e. onDrag() method returned true) then click action will be suppresed
*/
boolean isSuppressOnClickEvent();
/**
* @param dragButton drag button object for which onDrag listener was set
* @param event drag event
*
* @return 'true' if drag event occurred, 'false' otherwise
*/
boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event);
}

View File

@ -0,0 +1,122 @@
package org.solovyev.android.view;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.DragButtonCalibrationActivity;
import org.solovyev.common.utils.Interval;
import org.solovyev.util.math.MathUtils;
import org.solovyev.util.math.Point2d;
import android.util.Log;
import android.view.MotionEvent;
import java.util.Map;
public class SimpleOnDragListener implements OnDragListener {
@NotNull
public static final Point2d axis = new Point2d(0, 1);
@NotNull
private DragProcessor dragProcessor;
@NotNull
private DragButtonCalibrationActivity.Preferences preferences;
public SimpleOnDragListener(@NotNull DragButtonCalibrationActivity.Preferences preferences) {
this.preferences = preferences;
}
public SimpleOnDragListener(@NotNull DragProcessor dragProcessor, @NotNull DragButtonCalibrationActivity.Preferences preferences) {
this.dragProcessor = dragProcessor;
this.preferences = preferences;
}
public void setPreferences(@NotNull DragButtonCalibrationActivity.Preferences preferences) {
this.preferences = preferences;
}
@Override
public boolean onDrag(@NotNull DragButton dragButton, @NotNull DragEvent event) {
boolean result = false;
logDragEvent(dragButton, event);
final Point2d startPoint = event.getStartPoint();
final MotionEvent motionEvent = event.getMotionEvent();
// init end point
final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY());
final float distance = MathUtils.getDistance(startPoint, endPoint);
final double angle = Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint));
final double duration = motionEvent.getEventTime() - motionEvent.getDownTime();
final DragButtonCalibrationActivity.Preference distancePreferences = preferences.getPreferencesMap().get(DragButtonCalibrationActivity.PreferenceType.distance);
final DragButtonCalibrationActivity.Preference anglePreferences = preferences.getPreferencesMap().get(DragButtonCalibrationActivity.PreferenceType.angle);
DragDirection direction = null;
for (Map.Entry<DragDirection, DragButtonCalibrationActivity.DragPreference> directionEntry : distancePreferences.getDirectionPreferences().entrySet()) {
if (isInInterval(directionEntry.getValue().getInterval(), distance)) {
for (Map.Entry<DragDirection, DragButtonCalibrationActivity.DragPreference> angleEntry : anglePreferences.getDirectionPreferences().entrySet()) {
if (isInInterval(angleEntry.getValue().getInterval(), (float)angle)) {
direction = angleEntry.getKey();
break;
}
}
}
if (direction != null) {
break;
}
}
if (direction != null) {
final DragButtonCalibrationActivity.Preference durationPreferences = preferences.getPreferencesMap().get(DragButtonCalibrationActivity.PreferenceType.duration);
final DragButtonCalibrationActivity.DragPreference durationDragPreferences = durationPreferences.getDirectionPreferences().get(direction);
if (isInInterval(durationDragPreferences.getInterval(), (float)duration)) {
result = dragProcessor.processDragEvent(direction, dragButton, startPoint, motionEvent);
}
}
return result;
}
private boolean isInInterval(@NotNull Interval interval, float value) {
return interval.getStart() - MathUtils.MIN_AMOUNT <= value && value <= interval.getEnd() + MathUtils.MIN_AMOUNT;
}
@Override
public boolean isSuppressOnClickEvent() {
return true;
}
private void logDragEvent(@NotNull DragButton dragButton, @NotNull DragEvent event) {
final Point2d startPoint = event.getStartPoint();
final MotionEvent motionEvent = event.getMotionEvent();
final Point2d endPoint = new Point2d(motionEvent.getX(), motionEvent.getY());
Log.d(String.valueOf(dragButton.getId()), "Start point: " + startPoint + ", End point: " + endPoint);
Log.d(String.valueOf(dragButton.getId()), "Distance: " + MathUtils.getDistance(startPoint, endPoint));
Log.d(String.valueOf(dragButton.getId()), "Angle: " + Math.toDegrees(MathUtils.getAngle(startPoint, MathUtils.sum(startPoint, axis), endPoint)));
Log.d(String.valueOf(dragButton.getId()), "Axis: " + axis + " Vector: " + MathUtils.subtract(endPoint, startPoint));
Log.d(String.valueOf(dragButton.getId()), "Total time: " + (motionEvent.getEventTime() - motionEvent.getDownTime()) + " ms");
}
@NotNull
public DragProcessor getDragProcessor() {
return dragProcessor;
}
public void setDragProcessor(@NotNull DragProcessor dragProcessor) {
this.dragProcessor = dragProcessor;
}
public interface DragProcessor {
boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent);
}
}

View File

@ -0,0 +1,17 @@
package org.solovyev.util;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public class StringUtils {
public static boolean isEmpty ( @Nullable CharSequence s ){
return s == null || s.length() == 0;
}
@NotNull
public static String getNotEmpty ( @Nullable CharSequence s, @NotNull String defaultValue ){
return isEmpty(s) ? defaultValue : s.toString();
}
}

View File

@ -0,0 +1,11 @@
package org.solovyev.util.date;
public class DateUtils {
public static long MS_IN_SECONDS = 1000l;
public static long msToSeconds (long ms) {
return ms / 1000l;
}
}

View File

@ -0,0 +1,37 @@
package org.solovyev.util.math;
import org.jetbrains.annotations.NotNull;
public enum MathEntity {
minus("-"),
equals("="),
factorial("!"),
plus("+"),
multiply("*"),
divide("/"),
power("^"),
sin("sin"),
asin("asin"),
cos("cos"),
acos("acos"),
tg("tg"),
atg("atg"),
exp("exp"),
log("log"),
ln("ln"),
mod("mod"),
sqrt("sqrt");
@NotNull
private final String text;
private MathEntity (@NotNull String text) {
this.text = text;
}
@NotNull
public String getText() {
return text;
}
}

View File

@ -0,0 +1,79 @@
package org.solovyev.util.math;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
public enum MathEntityType {
digit,
constant,
dot,
function,
unary_operation,
binary_operation,
group_symbols,
group_symbol;
public static final List<Character> constants = Arrays.asList('e', 'π');
public static final List<Character> dots = Arrays.asList('.', ',');
public static final List<Character> unaryOperations = Arrays.asList('-', '=', '!');
public static final List<Character> binaryOperations = Arrays.asList('-', '+', '*', '×', '∙', '/', '^' );
public static final List<String> functions = Arrays.asList("sin", "asin", "cos", "acos", "tg", "atg", "log", "ln", "mod", "");
public static final List<String> groupSymbols = Arrays.asList("[]", "()", "{}");
public static final List<Character> openGroupSymbols = Arrays.asList('[', '(', '{');
public static final List<Character> closeGroupSymbols = Arrays.asList(']', ')', '}');
public static final List<Character> singleGroupSymbols;
static {
final List<Character> list = new ArrayList<Character>();
list.addAll(openGroupSymbols);
list.addAll(closeGroupSymbols);
singleGroupSymbols = Collections.unmodifiableList(list);
}
@Nullable
public static MathEntityType getType( @NotNull String s ) {
MathEntityType result = null;
if ( s.length() == 1 ) {
char ch = s.charAt(0);
if ( Character.isDigit(ch) ) {
result = MathEntityType.digit;
} else if ( unaryOperations.contains(ch) ) {
result = MathEntityType.unary_operation;
} else if ( binaryOperations.contains(ch) ) {
result = MathEntityType.binary_operation;
} else if ( singleGroupSymbols.contains(ch) ) {
result = MathEntityType.group_symbol;
} else if ( constants.contains(ch) ) {
result = MathEntityType.constant;
} else if ( dots.contains(ch) ) {
result = MathEntityType.dot;
}
}
if ( result == null ) {
if ( functions.contains(s) ) {
result = MathEntityType.function;
} else if ( groupSymbols.contains(s) ) {
result = MathEntityType.group_symbols;
}
}
return result;
}
}

View File

@ -0,0 +1,102 @@
package org.solovyev.util.math;
import org.jetbrains.annotations.NotNull;
import java.util.List;
public class MathUtils {
public static final float MIN_AMOUNT = 0.05f;
public static double round(@NotNull Double value, int numberOfFractionDigits) {
double roundFactor = Math.pow(10, numberOfFractionDigits);
if (value < Double.MAX_VALUE / roundFactor) {
return ((double)Math.round(value * roundFactor)) / roundFactor;
} else {
return value;
}
}
public static float getDistance(@NotNull Point2d startPoint,
@NotNull Point2d endPoint) {
return getNorm(subtract(endPoint, startPoint));
}
public static Point2d subtract(@NotNull Point2d p1, @NotNull Point2d p2) {
return new Point2d(p1.getX() - p2.getX(), p1.getY() - p2.getY());
}
public static Point2d sum(@NotNull Point2d p1, @NotNull Point2d p2) {
return new Point2d(p1.getX() + p2.getX(), p1.getY() + p2.getY());
}
public static float getNorm(@NotNull Point2d point) {
return (float) Math.pow(
Math.pow(point.getX(), 2) + Math.pow(point.getY(), 2), 0.5);
}
public static float getAngle(@NotNull Point2d startPoint,
@NotNull Point2d axisEndPoint, @NotNull Point2d endPoint) {
final Point2d axisVector = subtract(axisEndPoint, startPoint);
final Point2d vector = subtract(endPoint, startPoint);
double a_2 = Math.pow(getDistance(vector, axisVector), 2);
double b = getNorm(vector);
double b_2 = Math.pow(b, 2);
double c = getNorm(axisVector);
double c_2 = Math.pow(c, 2);
return (float) Math.acos((-a_2 + b_2 + c_2) / (2 * b * c));
}
public static double countMean(@NotNull List<Double> objects) {
double sum = 0d;
for (Double object : objects) {
sum += object;
}
return objects.size() == 0 ? 0d : (sum / objects.size());
}
public static double countStandardDeviation(@NotNull Double mean, @NotNull List<Double> objects) {
double sum = 0d;
for (Double object : objects) {
sum += Math.pow(object - mean, 2);
}
return objects.size() == 0 ? 0d : Math.sqrt(sum / objects.size());
}
public static StatData getStatData(@NotNull List<Double> objects) {
final double mean = countMean(objects);
final double standardDeviation = countStandardDeviation(mean, objects);
return new StatData(mean, standardDeviation);
}
public static class StatData {
private final double mean;
private final double standardDeviation;
public StatData(double mean, double standardDeviation) {
this.mean = mean;
this.standardDeviation = standardDeviation;
}
public double getMean() {
return mean;
}
public double getStandardDeviation() {
return standardDeviation;
}
}
}

View File

@ -0,0 +1,37 @@
package org.solovyev.util.math;
public class Point2d {
private float x = 0;
private float y = 0;
public Point2d() {
}
public Point2d( float x, float y ) {
this.x = x;
this.y = y;
}
public float getX() {
return x;
}
public void setX(float x) {
this.x = x;
}
public float getY() {
return y;
}
public void setY(float y) {
this.y = y;
}
@Override
public String toString() {
return "Point2d [x=" + x + ", y=" + y + "]";
}
}