This commit is contained in:
Sergey Solovyev
2012-11-30 16:16:09 +04:00
parent 476f6352d2
commit 324bbda552
673 changed files with 16589 additions and 11 deletions

View File

@@ -0,0 +1,14 @@
package org.solovyev.android;
/**
* User: Solovyev_S
* Date: 03.10.12
* Time: 10:48
*/
public final class AndroidUtils2 {
private AndroidUtils2() {
throw new AssertionError();
}
}

View File

@@ -0,0 +1,251 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.view.AngleUnitsButton;
import org.solovyev.android.calculator.view.NumeralBasesButton;
import org.solovyev.android.calculator.view.OnDragListenerVibrator;
import org.solovyev.android.history.HistoryDragProcessor;
import org.solovyev.android.view.drag.*;
import org.solovyev.common.Announcer;
import org.solovyev.common.math.Point2d;
import java.lang.reflect.Field;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 9/28/12
* Time: 12:12 AM
*/
public abstract class AbstractCalculatorHelper implements SharedPreferences.OnSharedPreferenceChangeListener {
@NotNull
private CalculatorPreferences.Gui.Layout layout;
@NotNull
private CalculatorPreferences.Gui.Theme theme;
@Nullable
private Vibrator vibrator;
@NotNull
private final Announcer<DragPreferencesChangeListener> dpclRegister = new Announcer<DragPreferencesChangeListener>(DragPreferencesChangeListener.class);
@NotNull
private String logTag = "CalculatorActivity";
protected AbstractCalculatorHelper() {
}
protected AbstractCalculatorHelper(@NotNull String logTag) {
this.logTag = logTag;
}
protected void onCreate(@NotNull Activity activity) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
vibrator = (Vibrator) activity.getSystemService(Activity.VIBRATOR_SERVICE);
layout = CalculatorPreferences.Gui.layout.getPreferenceNoError(preferences);
theme = CalculatorPreferences.Gui.theme.getPreferenceNoError(preferences);
preferences.registerOnSharedPreferenceChangeListener(this);
// let's disable locking of screen for monkeyrunner
if (CalculatorApplication.isMonkeyRunner(activity)) {
final KeyguardManager km = (KeyguardManager) activity.getSystemService(Context.KEYGUARD_SERVICE);
km.newKeyguardLock(activity.getClass().getName()).disableKeyguard();
}
}
public void logDebug(@NotNull String message) {
Log.d(logTag, message);
}
public void logError(@NotNull String message) {
Log.e(logTag, message);
}
public void processButtons(@NotNull final Activity activity, @NotNull View root) {
dpclRegister.clear();
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
final SimpleOnDragListener.Preferences dragPreferences = SimpleOnDragListener.getPreferences(preferences, activity);
setOnDragListeners(root, dragPreferences, preferences);
final OnDragListener historyOnDragListener = new OnDragListenerVibrator(newOnDragListener(new HistoryDragProcessor<CalculatorHistoryState>(getCalculator()), dragPreferences), vibrator, preferences);
final DragButton historyButton = getButton(root, R.id.historyButton);
if (historyButton != null) {
historyButton.setOnDragListener(historyOnDragListener);
}
final DragButton subtractionButton = getButton(root, R.id.subtractionButton);
if (subtractionButton != null) {
subtractionButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new SimpleOnDragListener.DragProcessor() {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
if (dragDirection == DragDirection.down) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.show_operators, null);
return true;
}
return false;
}
}, dragPreferences), vibrator, preferences));
}
final OnDragListener toPositionOnDragListener = new OnDragListenerVibrator(new SimpleOnDragListener(new CursorDragProcessor(), dragPreferences), vibrator, preferences);
final DragButton rightButton = getButton(root, R.id.rightButton);
if (rightButton != null) {
rightButton.setOnDragListener(toPositionOnDragListener);
}
final DragButton leftButton = getButton(root, R.id.leftButton);
if (leftButton != null) {
leftButton.setOnDragListener(toPositionOnDragListener);
}
final DragButton equalsButton = getButton(root, R.id.equalsButton);
if (equalsButton != null) {
equalsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new EvalDragProcessor(), dragPreferences), vibrator, preferences));
}
final AngleUnitsButton angleUnitsButton = (AngleUnitsButton) getButton(root, R.id.sixDigitButton);
if (angleUnitsButton != null) {
angleUnitsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.AngleUnitsChanger(activity), dragPreferences), vibrator, preferences));
}
final NumeralBasesButton clearButton = (NumeralBasesButton) getButton(root, R.id.clearButton);
if (clearButton != null) {
clearButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.NumeralBasesChanger(activity), dragPreferences), vibrator, preferences));
}
final DragButton varsButton = getButton(root, R.id.vars_button);
if (varsButton != null) {
varsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.VarsDragProcessor(activity), dragPreferences), vibrator, preferences));
}
final DragButton functionsButton = getButton(root, R.id.functions_button);
if (functionsButton != null) {
functionsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.FunctionsDragProcessor(activity), dragPreferences), vibrator, preferences));
}
final DragButton roundBracketsButton = getButton(root, R.id.roundBracketsButton);
if (roundBracketsButton != null) {
roundBracketsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.RoundBracketsDragProcessor(), dragPreferences), vibrator, preferences));
}
if (layout == CalculatorPreferences.Gui.Layout.simple) {
toggleButtonDirectionText(root, R.id.oneDigitButton, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.twoDigitButton, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.threeDigitButton, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.sixDigitButton, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.sevenDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.eightDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.clearButton, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.fourDigitButton, false, DragDirection.down);
toggleButtonDirectionText(root, R.id.fiveDigitButton, false, DragDirection.down);
toggleButtonDirectionText(root, R.id.nineDigitButton, false, DragDirection.left);
toggleButtonDirectionText(root, R.id.multiplicationButton, false, DragDirection.left);
toggleButtonDirectionText(root, R.id.plusButton, false, DragDirection.down, DragDirection.up);
}
CalculatorButtons.processButtons(theme, layout, root);
CalculatorButtons.toggleEqualsButton(preferences, activity);
CalculatorButtons.initMultiplicationButton(root);
NumeralBaseButtons.toggleNumericDigits(activity, preferences);
}
private void toggleButtonDirectionText(@NotNull View root, int id, boolean showDirectionText, @NotNull DragDirection... dragDirections) {
final View v = getButton(root, id);
if (v instanceof DirectionDragButton ) {
final DirectionDragButton button = (DirectionDragButton)v;
for (DragDirection dragDirection : dragDirections) {
button.showDirectionText(showDirectionText, dragDirection);
}
}
}
@NotNull
private Calculator getCalculator() {
return Locator.getInstance().getCalculator();
}
private void setOnDragListeners(@NotNull View root, @NotNull SimpleOnDragListener.Preferences dragPreferences, @NotNull SharedPreferences preferences) {
final OnDragListener onDragListener = new OnDragListenerVibrator(newOnDragListener(new DigitButtonDragProcessor(getKeyboard()), dragPreferences), vibrator, preferences);
final List<Integer> dragButtonIds = new ArrayList<Integer>();
for (Field field : R.id.class.getDeclaredFields()) {
int modifiers = field.getModifiers();
if (Modifier.isFinal(modifiers) && Modifier.isStatic(modifiers)) {
try {
int viewId = field.getInt(R.id.class);
final View view = root.findViewById(viewId);
if (view instanceof DragButton) {
dragButtonIds.add(viewId);
}
} catch (IllegalAccessException e) {
Log.e(R.id.class.getName(), e.getMessage());
}
}
}
for (Integer dragButtonId : dragButtonIds) {
final DragButton button = getButton(root, dragButtonId);
if (button != null) {
button.setOnDragListener(onDragListener);
}
}
}
@NotNull
private CalculatorKeyboard getKeyboard() {
return Locator.getInstance().getKeyboard();
}
@Nullable
private <T extends DragButton> T getButton(@NotNull View root, int buttonId) {
return (T) root.findViewById(buttonId);
}
@NotNull
private SimpleOnDragListener newOnDragListener(@NotNull SimpleOnDragListener.DragProcessor dragProcessor,
@NotNull SimpleOnDragListener.Preferences dragPreferences) {
final SimpleOnDragListener onDragListener = new SimpleOnDragListener(dragProcessor, dragPreferences);
dpclRegister.addListener(onDragListener);
return onDragListener;
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (key != null && key.startsWith("org.solovyev.android.calculator.DragButtonCalibrationActivity")) {
dpclRegister.announce().onDragPreferencesChange(SimpleOnDragListener.getPreferences(preferences, CalculatorApplication.getInstance()));
}
}
public void onDestroy(@NotNull Activity activity) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
preferences.unregisterOnSharedPreferenceChangeListener(this);
}
}

View File

@@ -0,0 +1,247 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
import jscl.NumeralBase;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.history.CalculatorHistoryState;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.msg.Message;
import java.util.List;
/**
* User: serso
* Date: 9/22/12
* Time: 5:42 PM
*/
public class AndroidCalculator implements Calculator, CalculatorEventListener, SharedPreferences.OnSharedPreferenceChangeListener {
@NotNull
private final CalculatorImpl calculator = new CalculatorImpl();
@NotNull
private final Application context;
public AndroidCalculator(@NotNull Application application) {
this.context = application;
this.calculator.addCalculatorEventListener(this);
PreferenceManager.getDefaultSharedPreferences(application).registerOnSharedPreferenceChangeListener(this);
}
public static void showEvaluationError(@NotNull Context context, @NotNull final String errorMessage) {
final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Activity.LAYOUT_INFLATER_SERVICE);
final View errorMessageView = layoutInflater.inflate(R.layout.display_error_message, null);
((TextView) errorMessageView.findViewById(R.id.error_message_text_view)).setText(errorMessage);
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setPositiveButton(R.string.c_cancel, null)
.setView(errorMessageView);
builder.create().show();
}
public void init(@NotNull final Activity activity) {
setEditor(activity);
setDisplay(activity);
}
public void setDisplay(@NotNull Activity activity) {
final AndroidCalculatorDisplayView displayView = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculator_display);
setDisplay(activity, displayView);
}
public void setDisplay(@NotNull Context context, @NotNull AndroidCalculatorDisplayView displayView) {
displayView.init(context);
Locator.getInstance().getDisplay().setView(displayView);
}
public void setEditor(@NotNull Activity activity) {
final AndroidCalculatorEditorView editorView = (AndroidCalculatorEditorView) activity.findViewById(R.id.calculator_editor);
setEditor(activity, editorView);
}
public void setEditor(@NotNull Context context, @NotNull AndroidCalculatorEditorView editorView) {
editorView.init(context);
Locator.getInstance().getEditor().setView(editorView);
}
/*
**********************************************************************
*
* DELEGATED TO CALCULATOR
*
**********************************************************************
*/
@Override
@NotNull
public CalculatorEventData evaluate(@NotNull JsclOperation operation, @NotNull String expression) {
return calculator.evaluate(operation, expression);
}
@Override
@NotNull
public CalculatorEventData evaluate(@NotNull JsclOperation operation, @NotNull String expression, @NotNull Long sequenceId) {
return calculator.evaluate(operation, expression, sequenceId);
}
@Override
public boolean isConversionPossible(@NotNull Generic generic, @NotNull NumeralBase from, @NotNull NumeralBase to) {
return calculator.isConversionPossible(generic, from, to);
}
@Override
@NotNull
public CalculatorEventData convert(@NotNull Generic generic, @NotNull NumeralBase to) {
return calculator.convert(generic, to);
}
@Override
@NotNull
public CalculatorEventData fireCalculatorEvent(@NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
return calculator.fireCalculatorEvent(calculatorEventType, data);
}
@NotNull
@Override
public CalculatorEventData fireCalculatorEvent(@NotNull CalculatorEventType calculatorEventType, @Nullable Object data, @NotNull Object source) {
return calculator.fireCalculatorEvent(calculatorEventType, data, source);
}
@Override
@NotNull
public CalculatorEventData fireCalculatorEvent(@NotNull CalculatorEventType calculatorEventType, @Nullable Object data, @NotNull Long sequenceId) {
return calculator.fireCalculatorEvent(calculatorEventType, data, sequenceId);
}
@NotNull
@Override
public PreparedExpression prepareExpression(@NotNull String expression) throws CalculatorParseException {
return calculator.prepareExpression(expression);
}
@Override
public void init() {
this.calculator.init();
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
this.calculator.setCalculateOnFly(CalculatorPreferences.Calculations.calculateOnFly.getPreference(prefs));
}
@Override
public void addCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculator.addCalculatorEventListener(calculatorEventListener);
}
@Override
public void removeCalculatorEventListener(@NotNull CalculatorEventListener calculatorEventListener) {
calculator.removeCalculatorEventListener(calculatorEventListener);
}
@Override
public void fireCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
calculator.fireCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
@Override
public void fireCalculatorEvents(@NotNull List<CalculatorEvent> calculatorEvents) {
calculator.fireCalculatorEvents(calculatorEvents);
}
@Override
public void doHistoryAction(@NotNull HistoryAction historyAction) {
calculator.doHistoryAction(historyAction);
}
@Override
public void setCurrentHistoryState(@NotNull CalculatorHistoryState editorHistoryState) {
calculator.setCurrentHistoryState(editorHistoryState);
}
@Override
@NotNull
public CalculatorHistoryState getCurrentHistoryState() {
return calculator.getCurrentHistoryState();
}
@Override
public void evaluate() {
calculator.evaluate();
}
@Override
public void evaluate(@NotNull Long sequenceId) {
calculator.evaluate(sequenceId);
}
@Override
public void simplify() {
calculator.simplify();
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case calculation_messages:
CalculatorActivityLauncher.showCalculationMessagesDialog(CalculatorApplication.getInstance(), (List<Message>) data);
break;
case show_history:
CalculatorActivityLauncher.showHistory(CalculatorApplication.getInstance());
break;
case show_history_detached:
CalculatorActivityLauncher.showHistory(CalculatorApplication.getInstance(), true);
break;
case show_functions:
CalculatorActivityLauncher.showFunctions(CalculatorApplication.getInstance());
break;
case show_functions_detached:
CalculatorActivityLauncher.showFunctions(CalculatorApplication.getInstance(), true);
break;
case show_operators:
CalculatorActivityLauncher.showOperators(CalculatorApplication.getInstance());
break;
case show_operators_detached:
CalculatorActivityLauncher.showOperators(CalculatorApplication.getInstance(), true);
break;
case show_vars:
CalculatorActivityLauncher.showVars(CalculatorApplication.getInstance());
break;
case show_vars_detached:
CalculatorActivityLauncher.showVars(CalculatorApplication.getInstance(), true);
break;
case show_settings:
CalculatorActivityLauncher.showSettings(CalculatorApplication.getInstance());
break;
case show_settings_detached:
CalculatorActivityLauncher.showSettings(CalculatorApplication.getInstance(), true);
break;
case show_like_dialog:
CalculatorActivityLauncher.likeButtonPressed(CalculatorApplication.getInstance());
break;
case open_app:
CalculatorActivityLauncher.openApp(CalculatorApplication.getInstance());
break;
}
}
@Override
public void onSharedPreferenceChanged(@NotNull SharedPreferences prefs, @NotNull String key) {
if ( CalculatorPreferences.Calculations.calculateOnFly.getKey().equals(key) ) {
this.calculator.setCalculateOnFly(CalculatorPreferences.Calculations.calculateOnFly.getPreference(prefs));
}
}
}

View File

@@ -0,0 +1,43 @@
package org.solovyev.android.calculator;
import android.app.Application;
import android.content.Context;
import android.text.ClipboardManager;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 9/22/12
* Time: 1:35 PM
*/
public class AndroidCalculatorClipboard implements CalculatorClipboard {
@NotNull
private final Context context;
public AndroidCalculatorClipboard(@NotNull Application application) {
this.context = application;
}
@Override
public String getText() {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
if ( clipboard.hasText() ) {
return String.valueOf(clipboard.getText());
} else {
return null;
}
}
@Override
public void setText(@NotNull String text) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
@Override
public void setText(@NotNull CharSequence text) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Context.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
}

View File

@@ -0,0 +1,191 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Html;
import android.util.AttributeSet;
import android.util.TypedValue;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.view.AutoResizeTextView;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
/**
* User: serso
* Date: 9/17/11
* Time: 10:58 PM
*/
public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView {
/*
**********************************************************************
*
* STATIC FIELDS
*
**********************************************************************
*/
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false);
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private volatile CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
private volatile boolean viewStateChange = false;
@NotNull
private final Object lock = new Object();
@NotNull
private final Handler uiHandler = new Handler();
@NotNull
private final ExecutorService bgExecutor = Executors.newSingleThreadExecutor();
private volatile boolean initialized = false;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
public AndroidCalculatorDisplayView(Context context) {
super(context);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
@Override
public void setState(@NotNull final CalculatorDisplayViewState state) {
uiHandler.post(new Runnable() {
@Override
public void run() {
synchronized (lock) {
try {
viewStateChange = true;
final CharSequence text = prepareText(state.getStringResult(), state.isValid());
AndroidCalculatorDisplayView.this.state = state;
if (state.isValid()) {
setTextColor(getResources().getColor(R.color.cpp_default_text_color));
setText(text);
adjustTextSize();
} else {
// update text in order to get rid of HTML tags
setText(getText().toString());
setTextColor(getResources().getColor(R.color.cpp_display_error_text_color));
// error messages are never shown -> just greyed out text (error message will be shown on click)
//setText(state.getErrorMessage());
//redraw();
}
} finally {
viewStateChange = false;
}
}
}
});
}
@NotNull
@Override
public CalculatorDisplayViewState getState() {
synchronized (lock) {
return this.state;
}
}
@Nullable
private static CharSequence prepareText(@Nullable String text, boolean valid) {
CharSequence result;
if (valid && text != null) {
//Log.d(this.getClass().getName(), text);
try {
final TextHighlighter.Result processedText = textHighlighter.process(text);
text = processedText.toString();
result = Html.fromHtml(text);
} catch (CalculatorParseException e) {
result = text;
}
} else {
result = text;
}
return result;
}
private void adjustTextSize() {
// todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize())
setAddEllipsis(false);
setMinTextSize(10);
resizeText();
}
public synchronized void init(@NotNull Context context) {
this.init(context, true);
}
public synchronized void init(@NotNull Context context, boolean fromApp) {
if (!initialized) {
if (fromApp) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.getLayout(preferences);
if ( layout == CalculatorPreferences.Gui.Layout.main_calculator_mobile ) {
setTextSize(TypedValue.COMPLEX_UNIT_SP, getResources().getDimension(R.dimen.cpp_display_text_size_mobile));
}
this.setOnClickListener(new CalculatorDisplayOnClickListener(context));
}
this.initialized = true;
}
}
}

View File

@@ -0,0 +1,221 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.SharedPreferences;
import android.graphics.Color;
import android.os.Build;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.text.Editable;
import android.text.Html;
import android.text.TextWatcher;
import android.util.AttributeSet;
import android.util.Log;
import android.view.ContextMenu;
import android.widget.EditText;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.text.TextProcessor;
import org.solovyev.android.calculator.view.TextHighlighter;
import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.common.collections.CollectionsUtils;
/**
* User: serso
* Date: 9/17/11
* Time: 12:25 AM
*/
public class AndroidCalculatorEditorView extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEditorView {
@NotNull
private static final BooleanPreference colorDisplay = new BooleanPreference("org.solovyev.android.calculator.CalculatorModel_color_display", true);
private volatile boolean initialized = false;
private boolean highlightText = true;
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false);
@NotNull
private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance();
private volatile boolean viewStateChange = false;
@Nullable
private final Object viewLock = new Object();
@NotNull
private final Handler uiHandler = new Handler();
public AndroidCalculatorEditorView(Context context) {
super(context);
}
public AndroidCalculatorEditorView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
@Override
public boolean onCheckIsTextEditor() {
// NOTE: code below can be used carefully and should not be copied without special intention
// The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc
if (Build.VERSION.SDK_INT >= 11) {
// fix for missing cursor in android 3 and higher
try {
// IDEA: return false always except if method was called from TextView.isCursorVisible() method
for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) {
if ("isCursorVisible".equals(stackTraceElement.getMethodName())) {
return true;
}
}
} catch (RuntimeException e) {
// just in case...
}
return false;
} else {
return false;
}
}
@Override
protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
menu.removeItem(android.R.id.selectAll);
}
@Nullable
private CharSequence prepareText(@NotNull String text, boolean highlightText) {
CharSequence result;
if (highlightText) {
try {
final TextHighlighter.Result processesText = textHighlighter.process(text);
assert processesText.getOffset() == 0;
result = Html.fromHtml(processesText.toString());
} catch (CalculatorParseException e) {
// set raw text
result = text;
Log.e(this.getClass().getName(), e.getMessage(), e);
}
} else {
result = text;
}
return result;
}
public boolean isHighlightText() {
return highlightText;
}
public void setHighlightText(boolean highlightText) {
this.highlightText = highlightText;
Locator.getInstance().getEditor().updateViewState();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (colorDisplay.getKey().equals(key)) {
this.setHighlightText(colorDisplay.getPreference(preferences));
}
}
public synchronized void init(@NotNull Context context) {
if (!initialized) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
preferences.registerOnSharedPreferenceChangeListener(this);
this.addTextChangedListener(new TextWatcherImpl());
onSharedPreferenceChanged(preferences, colorDisplay.getKey());
initialized = true;
}
}
@Override
public void setState(@NotNull final CalculatorEditorViewState viewState) {
if (viewLock != null) {
synchronized (viewLock) {
final CharSequence text = prepareText(viewState.getText(), highlightText);
uiHandler.post(new Runnable() {
@Override
public void run() {
final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this;
synchronized (viewLock) {
try {
editorView.viewStateChange = true;
editorView.viewState = viewState;
editorView.setText(text, BufferType.EDITABLE);
editorView.setSelection(viewState.getSelection());
} finally {
editorView.viewStateChange = false;
}
}
}
});
}
}
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
if (viewLock != null) {
synchronized (viewLock) {
if (!viewStateChange) {
// external text change => need to notify editor
super.onSelectionChanged(selStart, selEnd);
Locator.getInstance().getEditor().setSelection(selStart);
}
}
}
}
public void handleTextChange(Editable s) {
if (viewLock != null) {
synchronized (viewLock) {
if (!viewStateChange) {
// external text change => need to notify editor
Locator.getInstance().getEditor().setText(String.valueOf(s));
}
}
}
}
private final class TextWatcherImpl implements TextWatcher {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
handleTextChange(s);
}
}
}

View File

@@ -0,0 +1,86 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Vibrator;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.VibratorContainer;
/**
* User: serso
* Date: 11/18/12
* Time: 6:05 PM
*/
public class AndroidCalculatorKeyboard implements CalculatorKeyboard {
@NotNull
private final CalculatorKeyboard calculatorKeyboard;
@NotNull
private final Context context;
private VibratorContainer vibrator;
public AndroidCalculatorKeyboard(@NotNull Application application,
@NotNull CalculatorKeyboard calculatorKeyboard) {
this.context = application;
this.calculatorKeyboard = calculatorKeyboard;
}
@Override
public void buttonPressed(@Nullable String text) {
vibrate();
calculatorKeyboard.buttonPressed(text);
}
private void vibrate() {
if (this.vibrator == null) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final Vibrator vibrator = (Vibrator) context.getSystemService(Activity.VIBRATOR_SERVICE);
this.vibrator = new VibratorContainer(vibrator, preferences, 0.5f);
}
this.vibrator.vibrate();
}
@Override
public void roundBracketsButtonPressed() {
vibrate();
calculatorKeyboard.roundBracketsButtonPressed();
}
@Override
public void pasteButtonPressed() {
vibrate();
calculatorKeyboard.pasteButtonPressed();
}
@Override
public void clearButtonPressed() {
vibrate();
calculatorKeyboard.clearButtonPressed();
}
@Override
public void copyButtonPressed() {
vibrate();
calculatorKeyboard.copyButtonPressed();
}
@Override
public void moveCursorLeft() {
vibrate();
calculatorKeyboard.moveCursorLeft();
}
@Override
public void moveCursorRight() {
vibrate();
calculatorKeyboard.moveCursorRight();
}
}

View File

@@ -0,0 +1,36 @@
package org.solovyev.android.calculator;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/11/12
* Time: 12:15 AM
*/
public class AndroidCalculatorLogger implements CalculatorLogger {
@NotNull
private static final String TAG = AndroidCalculatorLogger.class.getSimpleName();
@Override
public void debug(@Nullable String tag, @NotNull String message) {
Log.d(getTag(tag), message);
}
@NotNull
private String getTag(@Nullable String tag) {
return tag != null ? tag : TAG;
}
@Override
public void debug(@Nullable String tag, @NotNull String message, @NotNull Throwable e) {
Log.d(getTag(tag), message, e);
}
@Override
public void error(@Nullable String tag, @NotNull String message, @NotNull Throwable e) {
Log.e(getTag(tag), message, e);
}
}

View File

@@ -0,0 +1,69 @@
package org.solovyev.android.calculator;
import android.app.Application;
import android.os.Handler;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import java.util.List;
/**
* User: serso
* Date: 9/22/12
* Time: 2:00 PM
*/
public class AndroidCalculatorNotifier implements CalculatorNotifier {
@NotNull
private final Application application;
@NotNull
private final Handler uiHandler = new Handler();
public AndroidCalculatorNotifier(@NotNull Application application) {
assert AndroidUtils.isUiThread();
this.application = application;
}
@Override
public void showMessage(@NotNull Message message) {
showMessageInUiThread(message.getLocalizedMessage());
}
@Override
public void showMessage(@NotNull Integer messageCode, @NotNull MessageType messageType, @NotNull List<Object> parameters) {
showMessage(new AndroidMessage(messageCode, messageType, application, parameters));
}
@Override
public void showMessage(@NotNull Integer messageCode, @NotNull MessageType messageType, @Nullable Object... parameters) {
showMessage(new AndroidMessage(messageCode, messageType, application, parameters));
}
@Override
public void showDebugMessage(@Nullable final String tag, @NotNull final String message) {
/*if (AndroidUtils.isDebuggable(application)) {
showMessageInUiThread(tag == null ? message : tag + ": " + message);
}*/
}
private void showMessageInUiThread(@NotNull final String message) {
if (AndroidUtils.isUiThread()) {
Toast.makeText(application, message, Toast.LENGTH_SHORT).show();
} else {
uiHandler.post(new Runnable() {
@Override
public void run() {
Toast.makeText(application,message, Toast.LENGTH_SHORT).show();
}
});
}
}
}

View File

@@ -0,0 +1,94 @@
package org.solovyev.android.calculator;
import android.app.Application;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import jscl.AngleUnit;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.msg.AndroidMessage;
import org.solovyev.common.msg.MessageType;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 11/17/12
* Time: 7:46 PM
*/
public class AndroidCalculatorPreferenceService implements CalculatorPreferenceService {
// one hour
private static final Long PREFERRED_PREFS_INTERVAL_TIME = 1000L * 60L * 60L;
@NotNull
private final Application application;
public AndroidCalculatorPreferenceService(@NotNull Application application) {
this.application = application;
}
@Override
public void checkPreferredPreferences(boolean force) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(application);
final Long currentTime = System.currentTimeMillis();
if ( force || ( CalculatorPreferences.Calculations.showCalculationMessagesDialog.getPreference(prefs) && isTimeForCheck(currentTime, prefs))) {
final NumeralBase preferredNumeralBase = CalculatorPreferences.Calculations.preferredNumeralBase.getPreference(prefs);
final NumeralBase numeralBase = AndroidCalculatorEngine.Preferences.numeralBase.getPreference(prefs);
final AngleUnit preferredAngleUnits = CalculatorPreferences.Calculations.preferredAngleUnits.getPreference(prefs);
final AngleUnit angleUnits = AndroidCalculatorEngine.Preferences.angleUnit.getPreference(prefs);
final List<CalculatorFixableMessage> messages = new ArrayList<CalculatorFixableMessage>(2);
if ( numeralBase != preferredNumeralBase ) {
messages.add(new CalculatorFixableMessage(application.getString(R.string.preferred_numeral_base_message, preferredNumeralBase.name(), numeralBase.name()), MessageType.warning, CalculatorFixableError.preferred_numeral_base));
}
if ( angleUnits != preferredAngleUnits ) {
messages.add(new CalculatorFixableMessage(application.getString(R.string.preferred_angle_units_message, preferredAngleUnits.name(), angleUnits.name()), MessageType.warning, CalculatorFixableError.preferred_angle_units));
}
CalculatorMessagesDialog.showDialog(messages, application);
CalculatorPreferences.Calculations.lastPreferredPreferencesCheck.putPreference(prefs, currentTime);
}
}
private boolean isTimeForCheck(@NotNull Long currentTime, @NotNull SharedPreferences preferences) {
final Long lastPreferredPreferencesCheckTime = CalculatorPreferences.Calculations.lastPreferredPreferencesCheck.getPreference(preferences);
return currentTime - lastPreferredPreferencesCheckTime > PREFERRED_PREFS_INTERVAL_TIME;
}
@Override
public void setPreferredAngleUnits() {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(application);
setAngleUnits(CalculatorPreferences.Calculations.preferredAngleUnits.getPreference(preferences));
}
@Override
public void setAngleUnits(@NotNull AngleUnit angleUnit) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(application);
AndroidCalculatorEngine.Preferences.angleUnit.putPreference(preferences, angleUnit);
Locator.getInstance().getNotifier().showMessage(new AndroidMessage(R.string.c_angle_units_changed_to, MessageType.info, application, angleUnit.name()));
}
@Override
public void setPreferredNumeralBase() {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(application);
setNumeralBase(CalculatorPreferences.Calculations.preferredNumeralBase.getPreference(preferences));
}
@Override
public void setNumeralBase(@NotNull NumeralBase numeralBase) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(application);
AndroidCalculatorEngine.Preferences.numeralBase.putPreference(preferences, numeralBase);
Locator.getInstance().getNotifier().showMessage(new AndroidMessage(R.string.c_numeral_base_changed_to, MessageType.info, application, numeralBase.name()));
}
}

View File

@@ -0,0 +1,39 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/7/12
* Time: 7:17 PM
*/
public enum AndroidFunctionCategory {
trigonometric(R.string.c_fun_category_trig),
hyperbolic_trigonometric(R.string.c_fun_category_hyper_trig),
comparison(R.string.c_fun_category_comparison),
my(R.string.c_fun_category_my),
common(R.string.c_fun_category_common);
private final int captionId;
AndroidFunctionCategory(int captionId) {
this.captionId = captionId;
}
public int getCaptionId() {
return captionId;
}
@Nullable
public static AndroidFunctionCategory valueOf( @NotNull FunctionCategory functionCategory ) {
for (AndroidFunctionCategory androidFunctionCategory : values()) {
if ( androidFunctionCategory.name().equals(functionCategory.name()) ) {
return androidFunctionCategory;
}
}
return null;
}
}

View File

@@ -0,0 +1,109 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.units.CalculatorNumeralBase;
import org.solovyev.android.view.drag.DirectionDragButton;
import org.solovyev.android.view.drag.DragDirection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 4/21/12
* Time: 8:00 PM
*/
public enum AndroidNumeralBase {
bin(CalculatorNumeralBase.bin) {
@NotNull
@Override
public List<Integer> getButtonIds() {
return Arrays.asList(R.id.zeroDigitButton, R.id.oneDigitButton);
}
},
oct(CalculatorNumeralBase.oct) {
@NotNull
@Override
public List<Integer> getButtonIds() {
final List<Integer> result = new ArrayList<Integer>(bin.getButtonIds());
result.addAll(Arrays.asList(R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton, R.id.sevenDigitButton));
return result;
}
},
dec(CalculatorNumeralBase.dec) {
@NotNull
@Override
public List<Integer> getButtonIds() {
final List<Integer> result = new ArrayList<Integer>(oct.getButtonIds());
result.addAll(Arrays.asList(R.id.eightDigitButton, R.id.nineDigitButton));
return result;
}
},
hex(CalculatorNumeralBase.hex) {
@NotNull
private List<Integer> specialHexButtonIds = Arrays.asList(R.id.oneDigitButton, R.id.twoDigitButton, R.id.threeDigitButton, R.id.fourDigitButton, R.id.fiveDigitButton, R.id.sixDigitButton);
@NotNull
@Override
public List<Integer> getButtonIds() {
return dec.getButtonIds();
}
@Override
protected void toggleButton(boolean show, @NotNull DirectionDragButton button) {
super.toggleButton(show, button);
if (specialHexButtonIds.contains(button.getId())) {
button.showDirectionText(show, DragDirection.left);
button.invalidate();
}
}
};
@NotNull
private final CalculatorNumeralBase calculatorNumeralBase;
private AndroidNumeralBase(@NotNull CalculatorNumeralBase calculatorNumeralBase) {
this.calculatorNumeralBase = calculatorNumeralBase;
}
@NotNull
public abstract List<Integer> getButtonIds();
public void toggleButtons(boolean show, @NotNull Activity activity) {
for (Integer buttonId : getButtonIds()) {
final DirectionDragButton button = (DirectionDragButton) activity.findViewById(buttonId);
if (button != null) {
toggleButton(show, button);
}
}
}
protected void toggleButton(boolean show, @NotNull DirectionDragButton button) {
button.setShowText(show);
button.invalidate();
}
@NotNull
public NumeralBase getNumeralBase() {
return calculatorNumeralBase.getNumeralBase();
}
@NotNull
public static AndroidNumeralBase valueOf(@NotNull NumeralBase nb) {
for (AndroidNumeralBase androidNumeralBase : values()) {
if (androidNumeralBase.calculatorNumeralBase.getNumeralBase() == nb) {
return androidNumeralBase;
}
}
throw new IllegalArgumentException(nb + " is not supported numeral base!");
}
}

View File

@@ -0,0 +1,38 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/7/12
* Time: 7:41 PM
*/
public enum AndroidOperatorCategory {
derivatives(R.string.derivatives),
other(R.string.other),
my(R.string.c_fun_category_my),
common(R.string.c_fun_category_common);
private final int captionId;
AndroidOperatorCategory(int captionId) {
this.captionId = captionId;
}
public int getCaptionId() {
return captionId;
}
@Nullable
public static AndroidOperatorCategory valueOf(@NotNull OperatorCategory operatorCategory) {
for (AndroidOperatorCategory androidOperatorCategory : values()) {
if ( androidOperatorCategory.name().equals(operatorCategory.name()) ) {
return androidOperatorCategory;
}
}
return null;
}
}

View File

@@ -0,0 +1,36 @@
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 10/7/12
* Time: 7:56 PM
*/
public enum AndroidVarCategory {
system(R.string.c_var_system),
my(R.string.c_var_my);
private final int captionId;
AndroidVarCategory(int captionId) {
this.captionId = captionId;
}
public int getCaptionId() {
return captionId;
}
@Nullable
public static AndroidVarCategory valueOf(@NotNull VarCategory varCategory) {
for (AndroidVarCategory androidVarCategory : values()) {
if ( androidVarCategory.name().equals(varCategory.name()) ) {
return androidVarCategory;
}
}
return null;
}
}

View File

@@ -0,0 +1,324 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.app.AlertDialog;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.ActivityInfo;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.util.Log;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.about.CalculatorReleaseNotesFragment;
import org.solovyev.android.fragments.FragmentUtils;
import org.solovyev.android.prefs.Preference;
import org.solovyev.common.equals.EqualsTool;
import org.solovyev.common.history.HistoryAction;
import org.solovyev.common.text.StringUtils;
public class CalculatorActivity extends SherlockFragmentActivity implements SharedPreferences.OnSharedPreferenceChangeListener {
@NotNull
public static final String TAG = CalculatorActivity.class.getSimpleName();
private boolean useBackAsPrev;
@NotNull
private CalculatorActivityHelper activityHelper;
/**
* Called when the activity is first created.
*/
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.layout.getPreferenceNoError(preferences);
activityHelper = CalculatorApplication.getInstance().createActivityHelper(layout.getLayoutId(), TAG);
activityHelper.logDebug("onCreate");
activityHelper.onCreate(this, savedInstanceState);
super.onCreate(savedInstanceState);
activityHelper.logDebug("super.onCreate");
if (findViewById(R.id.main_second_pane) != null) {
activityHelper.addTab(this, CalculatorFragmentType.history, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.saved_history, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.variables, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.functions, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.operators, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.plotter, null, R.id.main_second_pane);
activityHelper.addTab(this, CalculatorFragmentType.faq, null, R.id.main_second_pane);
} else {
getSupportActionBar().hide();
}
FragmentUtils.createFragment(this, CalculatorEditorFragment.class, R.id.editorContainer, "editor");
FragmentUtils.createFragment(this, CalculatorDisplayFragment.class, R.id.displayContainer, "display");
FragmentUtils.createFragment(this, CalculatorKeyboardFragment.class, R.id.keyboardContainer, "keyboard");
this.useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences);
firstTimeInit(preferences, this);
toggleOrientationChange(preferences);
preferences.registerOnSharedPreferenceChangeListener(this);
Locator.getInstance().getPreferenceService().checkPreferredPreferences(false);
if ( CalculatorApplication.isMonkeyRunner(this) ) {
Locator.getInstance().getKeyboard().buttonPressed("123");
Locator.getInstance().getKeyboard().buttonPressed("+");
Locator.getInstance().getKeyboard().buttonPressed("321");
}
}
@NotNull
private AndroidCalculator getCalculator() {
return ((AndroidCalculator) Locator.getInstance().getCalculator());
}
private static void firstTimeInit(@NotNull SharedPreferences preferences, @NotNull Context context) {
final Integer appOpenedCounter = CalculatorPreferences.appOpenedCounter.getPreference(preferences);
if (appOpenedCounter != null) {
CalculatorPreferences.appOpenedCounter.putPreference(preferences, appOpenedCounter + 1);
}
final Integer savedVersion = CalculatorPreferences.appVersion.getPreference(preferences);
final int appVersion = AndroidUtils.getAppVersionCode(context, CalculatorActivity.class.getPackage().getName());
CalculatorPreferences.appVersion.putPreference(preferences, appVersion);
if (!CalculatorApplication.isMonkeyRunner(context)) {
boolean dialogShown = false;
if (EqualsTool.areEqual(savedVersion, CalculatorPreferences.appVersion.getDefaultValue())) {
// new start
final AlertDialog.Builder builder = new AlertDialog.Builder(context).setMessage(R.string.c_first_start_text);
builder.setPositiveButton(android.R.string.ok, null);
builder.setTitle(R.string.c_first_start_text_title);
builder.create().show();
dialogShown = true;
} else {
if (savedVersion < appVersion) {
final boolean showReleaseNotes = CalculatorPreferences.Gui.showReleaseNotes.getPreference(preferences);
if (showReleaseNotes) {
final String releaseNotes = CalculatorReleaseNotesFragment.getReleaseNotes(context, savedVersion + 1);
if (!StringUtils.isEmpty(releaseNotes)) {
final AlertDialog.Builder builder = new AlertDialog.Builder(context).setMessage(Html.fromHtml(releaseNotes));
builder.setPositiveButton(android.R.string.ok, null);
builder.setTitle(R.string.c_release_notes);
builder.create().show();
dialogShown = true;
}
}
}
}
//Log.d(this.getClass().getName(), "Application was opened " + appOpenedCounter + " time!");
if (!dialogShown) {
if (appOpenedCounter != null && appOpenedCounter > 10) {
dialogShown = showSpecialWindow(preferences, CalculatorPreferences.Gui.feedbackWindowShown, R.layout.feedback, R.id.feedbackText, context);
}
}
}
}
private static boolean showSpecialWindow(@NotNull SharedPreferences preferences, @NotNull Preference<Boolean> specialWindowShownPref, int layoutId, int textViewId, @NotNull Context context) {
boolean result = false;
final Boolean specialWindowShown = specialWindowShownPref.getPreference(preferences);
if ( specialWindowShown != null && !specialWindowShown ) {
final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(LAYOUT_INFLATER_SERVICE);
final View view = layoutInflater.inflate(layoutId, null);
final TextView feedbackTextView = (TextView) view.findViewById(textViewId);
feedbackTextView.setMovementMethod(LinkMovementMethod.getInstance());
final AlertDialog.Builder builder = new AlertDialog.Builder(context).setView(view);
builder.setPositiveButton(android.R.string.ok, null);
builder.create().show();
result = true;
specialWindowShownPref.putPreference(preferences, true);
}
return result;
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_BACK) {
if (useBackAsPrev) {
getCalculator().doHistoryAction(HistoryAction.undo);
return true;
}
}
return super.onKeyDown(keyCode, event);
}
@SuppressWarnings({"UnusedDeclaration"})
public void equalsButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.equals);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onResume() {
super.onResume();
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
final CalculatorPreferences.Gui.Layout newLayout = CalculatorPreferences.Gui.layout.getPreference(preferences);
if ( newLayout != activityHelper.getLayout() ) {
AndroidUtils.restartActivity(this);
}
this.activityHelper.onResume(this);
}
@Override
protected void onDestroy() {
activityHelper.onDestroy(this);
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) {
if ( CalculatorPreferences.Gui.usePrevAsBack.getKey().equals(key) ) {
useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences);
}
if ( CalculatorPreferences.Gui.autoOrientation.getKey().equals(key) ) {
toggleOrientationChange(preferences);
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
private void toggleOrientationChange(@Nullable SharedPreferences preferences) {
preferences = preferences == null ? PreferenceManager.getDefaultSharedPreferences(this) : preferences;
if (CalculatorPreferences.Gui.autoOrientation.getPreference(preferences)) {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
} else {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_PORTRAIT);
}
}
/*
**********************************************************************
*
* BUTTON HANDLERS
*
**********************************************************************
*/
@SuppressWarnings({"UnusedDeclaration"})
public void elementaryButtonClickHandler(@NotNull View v) {
throw new UnsupportedOperationException("Not implemented yet!");
}
@SuppressWarnings({"UnusedDeclaration"})
public void historyButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.history);
}
@SuppressWarnings({"UnusedDeclaration"})
public void eraseButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.erase);
}
@SuppressWarnings({"UnusedDeclaration"})
public void simplifyButtonClickHandler(@NotNull View v) {
throw new UnsupportedOperationException("Not implemented yet!");
}
@SuppressWarnings({"UnusedDeclaration"})
public void pasteButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.paste);
}
@SuppressWarnings({"UnusedDeclaration"})
public void copyButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.copy);
}
@NotNull
private static CalculatorKeyboard getKeyboard() {
return Locator.getInstance().getKeyboard();
}
@SuppressWarnings({"UnusedDeclaration"})
public void clearButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.clear);
}
@SuppressWarnings({"UnusedDeclaration"})
public void digitButtonClickHandler(@NotNull View v) {
Log.d(String.valueOf(v.getId()), "digitButtonClickHandler() for: " + v.getId() + ". Pressed: " + v.isPressed());
if (v instanceof Button) {
buttonPressed(((Button)v).getText().toString());
}
}
private void buttonPressed(@NotNull CalculatorSpecialButton button) {
buttonPressed(button.getActionCode());
}
private void buttonPressed(@NotNull String text) {
getKeyboard().buttonPressed(text);
}
@SuppressWarnings({"UnusedDeclaration"})
public void functionsButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.functions);
}
@SuppressWarnings({"UnusedDeclaration"})
public void operatorsButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.operators);
}
@SuppressWarnings({"UnusedDeclaration"})
public void varsButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.vars);
}
@SuppressWarnings({"UnusedDeclaration"})
public void likeButtonClickHandler(@NotNull View v) {
buttonPressed(CalculatorSpecialButton.like);
}
}

View File

@@ -0,0 +1,65 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.view.View;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 9/25/12
* Time: 10:31 PM
*/
public interface CalculatorActivityHelper {
void onCreate(@NotNull SherlockFragmentActivity activity, @Nullable Bundle savedInstanceState);
void onCreate(@NotNull Activity activity, @Nullable Bundle savedInstanceState);
void onSaveInstanceState(@NotNull SherlockFragmentActivity activity, @NotNull Bundle outState);
void onSaveInstanceState(@NotNull Activity activity, @NotNull Bundle outState);
int getLayoutId();
@NotNull
CalculatorPreferences.Gui.Theme getTheme();
@NotNull
CalculatorPreferences.Gui.Layout getLayout();
void onResume(@NotNull SherlockFragmentActivity activity);
void onResume(@NotNull Activity activity);
void onPause(@NotNull Activity activity);
void onPause(@NotNull SherlockFragmentActivity activity);
void onDestroy(@NotNull SherlockFragmentActivity activity);
void onDestroy(@NotNull Activity activity);
void addTab(@NotNull SherlockFragmentActivity activity,
@NotNull String tag,
@NotNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle fragmentArgs,
int captionResId,
int parentViewId);
void addTab(@NotNull SherlockFragmentActivity activity,
@NotNull CalculatorFragmentType fragmentType,
@Nullable Bundle fragmentArgs,
int parentViewId);
void setFragment(@NotNull SherlockFragmentActivity activity,
@NotNull CalculatorFragmentType fragmentType,
@Nullable Bundle fragmentArgs,
int parentViewId);
void logDebug(@NotNull String message);
void processButtons(@NotNull Activity activity, @NotNull View root);
void logError(@NotNull String message);
}

View File

@@ -0,0 +1,314 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.graphics.Color;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import android.util.DisplayMetrics;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.sherlock.tabs.ActionBarFragmentTabListener;
/**
* User: serso
* Date: 9/25/12
* Time: 10:32 PM
*/
public class CalculatorActivityHelperImpl extends AbstractCalculatorHelper implements CalculatorActivityHelper {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
private int layoutId;
private boolean homeIcon = false;
@NotNull
private CalculatorPreferences.Gui.Theme theme;
@NotNull
private CalculatorPreferences.Gui.Layout layout;
private int selectedNavigationIndex = 0;
public CalculatorActivityHelperImpl(int layoutId, @NotNull String logTag) {
super(logTag);
this.layoutId = layoutId;
}
public CalculatorActivityHelperImpl(int layoutId, boolean homeIcon) {
this.layoutId = layoutId;
this.homeIcon = homeIcon;
}
@Override
public void onCreate(@NotNull Activity activity, @Nullable Bundle savedInstanceState) {
super.onCreate(activity);
if (activity instanceof CalculatorEventListener) {
Locator.getInstance().getCalculator().addCalculatorEventListener((CalculatorEventListener) activity);
}
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
this.theme = CalculatorPreferences.Gui.getTheme(preferences);
activity.setTheme(this.theme.getThemeId());
this.layout = CalculatorPreferences.Gui.getLayout(preferences);
activity.setContentView(layoutId);
final View root = activity.findViewById(R.id.main_layout);
if (root != null) {
processButtons(activity, root);
addHelpInfo(activity, root);
} else {
Log.e(CalculatorActivityHelperImpl.class.getSimpleName(), "Root is null for " + activity.getClass().getName());
}
}
@Override
public void onCreate(@NotNull final SherlockFragmentActivity activity, @Nullable Bundle savedInstanceState) {
this.onCreate((Activity) activity, savedInstanceState);
final ActionBar actionBar = activity.getSupportActionBar();
actionBar.setDisplayUseLogoEnabled(false);
actionBar.setDisplayHomeAsUpEnabled(homeIcon);
actionBar.setHomeButtonEnabled(false);
actionBar.setDisplayShowHomeEnabled(true);
toggleTitle(activity, true);
actionBar.setIcon(R.drawable.ab_icon);
actionBar.setNavigationMode(ActionBar.NAVIGATION_MODE_TABS);
}
private void toggleTitle(@NotNull SherlockFragmentActivity activity, boolean showTitle) {
final ActionBar actionBar = activity.getSupportActionBar();
if (activity instanceof CalculatorActivity) {
if (AndroidUtils.getScreenOrientation(activity) == Configuration.ORIENTATION_PORTRAIT) {
actionBar.setDisplayShowTitleEnabled(true);
} else {
actionBar.setDisplayShowTitleEnabled(false);
}
} else {
actionBar.setDisplayShowTitleEnabled(showTitle);
}
}
public void restoreSavedTab(@NotNull SherlockFragmentActivity activity) {
final ActionBar actionBar = activity.getSupportActionBar();
if (selectedNavigationIndex >= 0 && selectedNavigationIndex < actionBar.getTabCount()) {
actionBar.setSelectedNavigationItem(selectedNavigationIndex);
}
}
@Override
public void onSaveInstanceState(@NotNull SherlockFragmentActivity activity, @NotNull Bundle outState) {
onSaveInstanceState((Activity) activity, outState);
}
@Override
public void onSaveInstanceState(@NotNull Activity activity, @NotNull Bundle outState) {
}
@Override
public void onResume(@NotNull Activity activity) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
final CalculatorPreferences.Gui.Theme newTheme = CalculatorPreferences.Gui.theme.getPreference(preferences);
if (!theme.equals(newTheme)) {
AndroidUtils.restartActivity(activity);
}
}
@Override
public void onPause(@NotNull Activity activity) {
}
@Override
public void onPause(@NotNull SherlockFragmentActivity activity) {
onPause((Activity) activity);
final int selectedNavigationIndex = activity.getSupportActionBar().getSelectedNavigationIndex();
if (selectedNavigationIndex >= 0) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
final SharedPreferences.Editor editor = preferences.edit();
editor.putInt(getSavedTabPreferenceName(activity), selectedNavigationIndex);
editor.commit();
}
}
@NotNull
private String getSavedTabPreferenceName(@NotNull Activity activity) {
return "tab_" + activity.getClass().getSimpleName();
}
@Override
public void onDestroy(@NotNull Activity activity) {
super.onDestroy(activity);
if (activity instanceof CalculatorEventListener) {
Locator.getInstance().getCalculator().removeCalculatorEventListener((CalculatorEventListener) activity);
}
}
@Override
public void onDestroy(@NotNull SherlockFragmentActivity activity) {
this.onDestroy((Activity) activity);
}
@Override
public void addTab(@NotNull SherlockFragmentActivity activity,
@NotNull String tag,
@NotNull Class<? extends Fragment> fragmentClass,
@Nullable Bundle fragmentArgs,
int captionResId,
int parentViewId) {
final ActionBar actionBar = activity.getSupportActionBar();
final ActionBar.Tab tab = actionBar.newTab();
tab.setTag(tag);
tab.setText(captionResId);
final ActionBarFragmentTabListener listener = new ActionBarFragmentTabListener(activity, tag, fragmentClass, fragmentArgs, parentViewId);
tab.setTabListener(listener);
actionBar.addTab(tab);
}
@Override
public void addTab(@NotNull SherlockFragmentActivity activity, @NotNull CalculatorFragmentType fragmentType, @Nullable Bundle fragmentArgs, int parentViewId) {
addTab(activity, fragmentType.getFragmentTag(), fragmentType.getFragmentClass(), fragmentArgs, fragmentType.getDefaultTitleResId(), parentViewId);
}
@Override
public void setFragment(@NotNull SherlockFragmentActivity activity, @NotNull CalculatorFragmentType fragmentType, @Nullable Bundle fragmentArgs, int parentViewId) {
final FragmentManager fm = activity.getSupportFragmentManager();
Fragment fragment = fm.findFragmentByTag(fragmentType.getFragmentTag());
if (fragment == null) {
fragment = Fragment.instantiate(activity, fragmentType.getFragmentClass().getName(), fragmentArgs);
final FragmentTransaction ft = fm.beginTransaction();
ft.add(parentViewId, fragment, fragmentType.getFragmentTag());
ft.commit();
} else {
if ( fragment.isDetached() ) {
final FragmentTransaction ft = fm.beginTransaction();
ft.attach(fragment);
ft.commit();
}
}
}
@Override
public int getLayoutId() {
return layoutId;
}
@Override
@NotNull
public CalculatorPreferences.Gui.Theme getTheme() {
return theme;
}
@Override
@NotNull
public CalculatorPreferences.Gui.Layout getLayout() {
return layout;
}
@Override
public void onResume(@NotNull SherlockFragmentActivity activity) {
onResume((Activity) activity);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(activity);
selectedNavigationIndex = preferences.getInt(getSavedTabPreferenceName(activity), -1);
restoreSavedTab(activity);
}
private void addHelpInfo(@NotNull Activity activity, @NotNull View root) {
if ( CalculatorApplication.isMonkeyRunner(activity) ) {
if ( root instanceof ViewGroup) {
final TextView helperTextView = new TextView(activity);
final DisplayMetrics dm = new DisplayMetrics();
activity.getWindowManager().getDefaultDisplay().getMetrics(dm);
helperTextView.setTextSize(15);
helperTextView.setTextColor(Color.WHITE);
final Configuration c = activity.getResources().getConfiguration();
final StringBuilder helpText = new StringBuilder();
helpText.append("Size: ");
if (AndroidUtils.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_XLARGE, c)) {
helpText.append("xlarge");
} else if (AndroidUtils.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE, c)) {
helpText.append("large");
} else if (AndroidUtils.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_NORMAL, c)) {
helpText.append("normal");
} else if (AndroidUtils.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_SMALL, c)) {
helpText.append("small");
} else {
helpText.append("unknown");
}
helpText.append(" (").append(dm.widthPixels).append("x").append(dm.heightPixels).append(")");
helpText.append(" Density: ");
switch(dm.densityDpi){
case DisplayMetrics.DENSITY_LOW:
helpText.append("ldpi");
break;
case DisplayMetrics.DENSITY_MEDIUM:
helpText.append("mdpi");
break;
case DisplayMetrics.DENSITY_HIGH:
helpText.append("hdpi");
break;
case DisplayMetrics.DENSITY_XHIGH:
helpText.append("xhdpi");
break;
case DisplayMetrics.DENSITY_TV:
helpText.append("tv");
break;
}
helpText.append(" (").append(dm.densityDpi).append(")");
helperTextView.setText(helpText);
((ViewGroup) root).addView(helperTextView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
}
}

View File

@@ -0,0 +1,169 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.achartengine.ChartFactory;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.about.CalculatorAboutActivity;
import org.solovyev.android.calculator.function.FunctionEditDialogFragment;
import org.solovyev.android.calculator.help.CalculatorHelpActivity;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
import org.solovyev.android.calculator.math.edit.*;
import org.solovyev.android.calculator.plot.CalculatorPlotActivity;
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
import java.util.List;
/**
* User: serso
* Date: 11/2/11
* Time: 2:18 PM
*/
public class CalculatorActivityLauncher {
public static void showHistory(@NotNull final Context context) {
showHistory(context, false);
}
public static void showHistory(@NotNull final Context context, boolean detached) {
final Intent intent = new Intent(context, CalculatorHistoryActivity.class);
addFlags(intent, detached);
context.startActivity(intent);
}
public static void showHelp(@NotNull final Context context) {
context.startActivity(new Intent(context, CalculatorHelpActivity.class));
}
public static void showSettings(@NotNull final Context context) {
showSettings(context, false);
}
public static void showSettings(@NotNull final Context context, boolean detached) {
final Intent intent = new Intent(context, CalculatorPreferencesActivity.class);
addFlags(intent, detached);
context.startActivity(intent);
}
private static void addFlags(@NotNull Intent intent, boolean detached) {
int flags = Intent.FLAG_ACTIVITY_NEW_TASK;
if (detached) {
flags = flags | Intent.FLAG_ACTIVITY_NO_HISTORY;
}
intent.setFlags(flags);
}
public static void showAbout(@NotNull final Context context) {
context.startActivity(new Intent(context, CalculatorAboutActivity.class));
}
public static void showFunctions(@NotNull final Context context) {
showFunctions(context, false);
}
public static void showFunctions(@NotNull final Context context, boolean detached) {
final Intent intent = new Intent(context, CalculatorFunctionsActivity.class);
addFlags(intent, detached);
context.startActivity(intent);
}
public static void showOperators(@NotNull final Context context) {
showOperators(context, false);
}
public static void showOperators(@NotNull final Context context, boolean detached) {
final Intent intent = new Intent(context, CalculatorOperatorsActivity.class);
addFlags(intent, detached);
context.startActivity(intent);
}
public static void showVars(@NotNull final Context context) {
showVars(context, false);
}
public static void showVars(@NotNull final Context context, boolean detached) {
final Intent intent = new Intent(context, CalculatorVarsActivity.class);
addFlags(intent, detached);
context.startActivity(intent);
}
public static void plotGraph(@NotNull final Context context, @NotNull Generic generic, @NotNull Constant constant){
final Intent intent = new Intent();
intent.putExtra(ChartFactory.TITLE, context.getString(R.string.c_graph));
intent.putExtra(CalculatorPlotFragment.INPUT, new CalculatorPlotFragment.Input(generic.toString(), constant.getName()));
intent.setClass(context, CalculatorPlotActivity.class);
context.startActivity(intent);
}
public static void createVar(@NotNull final Context context, @NotNull CalculatorDisplay calculatorDisplay) {
final CalculatorDisplayViewState viewState = calculatorDisplay.getViewState();
if (viewState.isValid() ) {
final String varValue = viewState.getText();
if (!StringUtils.isEmpty(varValue)) {
if (CalculatorVarsFragment.isValidValue(varValue)) {
if (context instanceof SherlockFragmentActivity) {
VarEditDialogFragment.showDialog(VarEditDialogFragment.Input.newFromValue(varValue), ((SherlockFragmentActivity) context).getSupportFragmentManager());
} else {
final Intent intent = new Intent(context, CalculatorVarsActivity.class);
intent.putExtra(CalculatorVarsFragment.CREATE_VAR_EXTRA_STRING, varValue);
context.startActivity(intent);
}
} else {
Locator.getInstance().getNotifier().showMessage(R.string.c_value_is_not_a_number, MessageType.error);
}
} else {
Locator.getInstance().getNotifier().showMessage(R.string.empty_var_error, MessageType.error);
}
} else {
Locator.getInstance().getNotifier().showMessage(R.string.not_valid_result, MessageType.error);
}
}
public static void createFunction(@NotNull final Context context, @NotNull CalculatorDisplay calculatorDisplay) {
final CalculatorDisplayViewState viewState = calculatorDisplay.getViewState();
if (viewState.isValid() ) {
final String functionValue = viewState.getText();
if (!StringUtils.isEmpty(functionValue)) {
FunctionEditDialogFragment.showDialog(FunctionEditDialogFragment.Input.newFromDisplay(viewState), context);
} else {
Locator.getInstance().getNotifier().showMessage(R.string.empty_function_error, MessageType.error);
}
} else {
Locator.getInstance().getNotifier().showMessage(R.string.not_valid_result, MessageType.error);
}
}
public static void openApp(@NotNull Context context) {
final Intent intent = new Intent(context, CalculatorActivity.class);
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK | Intent.FLAG_ACTIVITY_CLEAR_TOP);
context.startActivity(intent);
}
public static void likeButtonPressed(@NotNull final Context context) {
final Intent intent = new Intent(Intent.ACTION_VIEW, Uri.parse(CalculatorApplication.FACEBOOK_APP_URL));
addFlags(intent, false);
context.startActivity(intent);
}
public static void showCalculationMessagesDialog(@NotNull Context context, @NotNull List<Message> messages) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
if ( CalculatorPreferences.Calculations.showCalculationMessagesDialog.getPreference(prefs) ) {
CalculatorMessagesDialog.showDialogForMessages(messages, context);
}
}
}

View File

@@ -0,0 +1,26 @@
package org.solovyev.android.calculator;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 11/25/12
* Time: 2:34 PM
*/
public class CalculatorActivityMobile extends CalculatorActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
CalculatorPreferences.Gui.layout.putPreference(prefs, CalculatorPreferences.Gui.Layout.main_calculator_mobile);
super.onCreate(savedInstanceState);
if ( !CalculatorApplication.isMonkeyRunner(this) ) {
this.finish();
}
}
}

View File

@@ -0,0 +1,179 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.pm.PackageManager;
import android.preference.PreferenceManager;
import android.util.Log;
import net.robotmedia.billing.BillingController;
import net.robotmedia.billing.helper.DefaultBillingObserver;
import net.robotmedia.billing.model.BillingDB;
import org.acra.ACRA;
import org.acra.ReportingInteractionMode;
import org.acra.annotation.ReportsCrashes;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.external.AndroidExternalListenersContainer;
import org.solovyev.android.calculator.history.AndroidCalculatorHistory;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
/**
* User: serso
* Date: 12/1/11
* Time: 1:21 PM
*/
/*@ReportsCrashes(formKey = "dEhDaW1nZU1qcFdsVUpiSnhON0c0ZHc6MQ",
mode = ReportingInteractionMode.TOAST)*/
@ReportsCrashes(formKey = "",
mailTo = "se.solovyev+programming+calculatorpp+crashes+1.5@gmail.com",
mode = ReportingInteractionMode.DIALOG,
resToastText = R.string.crashed,
resDialogTitle = R.string.crash_dialog_title,
resDialogText = R.string.crash_dialog_text)
public class CalculatorApplication extends android.app.Application {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
private static final String TAG = "Calculator++ Application";
public static final String FACEBOOK_APP_URL = "http://www.facebook.com/calculatorpp";
public static final String AD_FREE_PRODUCT_ID = "ad_free";
public static final String AD_FREE_P_KEY = "org.solovyev.android.calculator_ad_free";
public static final String ADMOB_USER_ID = "a14f02cf9c80cbc";
@NotNull
private static CalculatorApplication instance;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
public CalculatorApplication() {
instance = this;
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
@Override
public void onCreate() {
ACRA.init(this);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
CalculatorPreferences.setDefaultValues(preferences);
setTheme(preferences);
super.onCreate();
final AndroidCalculator calculator = new AndroidCalculator(this);
Locator.getInstance().init(calculator,
new AndroidCalculatorEngine(this),
new AndroidCalculatorClipboard(this),
new AndroidCalculatorNotifier(this),
new AndroidCalculatorHistory(this, calculator),
new AndroidCalculatorLogger(),
new AndroidCalculatorPreferenceService(this),
new AndroidCalculatorKeyboard(this, new CalculatorKeyboardImpl(calculator)),
new AndroidExternalListenersContainer(calculator));
Locator.getInstance().getCalculator().init();
BillingDB.init(CalculatorApplication.this);
AdsController.getInstance().init(ADMOB_USER_ID, AD_FREE_PRODUCT_ID, new BillingController.IConfiguration() {
@Override
public byte[] getObfuscationSalt() {
return new byte[]{81, -114, 32, -127, -32, -104, -40, -15, -47, 57, -13, -41, -33, 67, -114, 7, -11, 53, 126, 82};
}
@Override
public String getPublicKey() {
return CalculatorSecurity.getPK();
}
});
BillingController.registerObserver(new DefaultBillingObserver(this, null));
// init billing controller
new Thread(new Runnable() {
@Override
public void run() {
BillingController.checkBillingSupported(CalculatorApplication.this);
AdsController.getInstance().isAdFree(CalculatorApplication.this);
try {
// prepare engine
Locator.getInstance().getEngine().getMathEngine0().evaluate("1+1");
Locator.getInstance().getEngine().getMathEngine0().evaluate("1*1");
} catch (Throwable e) {
Log.e(TAG, e.getMessage(), e);
}
}
}).start();
Locator.getInstance().getLogger().debug(TAG, "Application started!");
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Application started!");
}
private void setTheme(@NotNull SharedPreferences preferences) {
final CalculatorPreferences.Gui.Theme theme = CalculatorPreferences.Gui.getTheme(preferences);
setTheme(theme.getThemeId());
}
@NotNull
public CalculatorActivityHelper createActivityHelper(int layoutResId, @NotNull String logTag) {
return new CalculatorActivityHelperImpl(layoutResId, logTag);
}
@NotNull
public CalculatorFragmentHelper createFragmentHelper(int layoutId) {
return new CalculatorFragmentHelperImpl(layoutId);
}
@NotNull
public CalculatorFragmentHelper createFragmentHelper(int layoutId, int titleResId) {
return new CalculatorFragmentHelperImpl(layoutId, titleResId);
}
@NotNull
public CalculatorFragmentHelper createFragmentHelper(int layoutId, int titleResId, boolean listenersOnCreate) {
return new CalculatorFragmentHelperImpl(layoutId, titleResId, listenersOnCreate);
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
@NotNull
public static CalculatorApplication getInstance() {
return instance;
}
public static boolean isMonkeyRunner(@NotNull Context context) {
// NOTE: this code is only for monkeyrunner
return context.checkCallingOrSelfPermission(android.Manifest.permission.DISABLE_KEYGUARD) == PackageManager.PERMISSION_GRANTED;
}
}

View File

@@ -0,0 +1,257 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Configuration;
import android.preference.PreferenceManager;
import android.util.Log;
import android.util.TypedValue;
import android.view.MotionEvent;
import android.view.View;
import android.widget.Button;
import android.widget.RemoteViews;
import jscl.AngleUnit;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.calculator.view.AngleUnitsButton;
import org.solovyev.android.calculator.view.NumeralBasesButton;
import org.solovyev.android.view.drag.DragButton;
import org.solovyev.android.view.drag.DragDirection;
import org.solovyev.android.view.drag.SimpleOnDragListener;
import org.solovyev.common.math.Point2d;
/**
* User: serso
* Date: 9/28/12
* Time: 12:06 AM
*/
public final class CalculatorButtons {
private CalculatorButtons () {
}
public static void processButtons(@NotNull CalculatorPreferences.Gui.Theme theme,
@NotNull CalculatorPreferences.Gui.Layout layout,
@NotNull View root) {
if ( layout == CalculatorPreferences.Gui.Layout.main_calculator_mobile ) {
final float textSize = root.getContext().getResources().getDimension(R.dimen.cpp_keyboard_button_text_size_mobile);
AndroidUtils.processViewsOfType(root, DragButton.class, new AndroidUtils.ViewProcessor<DragButton>() {
@Override
public void process(@NotNull DragButton button) {
button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, textSize);
}
});
}
}
static void initMultiplicationButton(@NotNull View root) {
final View multiplicationButton = root.findViewById(R.id.multiplicationButton);
if ( multiplicationButton instanceof Button) {
((Button) multiplicationButton).setText(Locator.getInstance().getEngine().getMultiplicationSign());
}
}
public static void initMultiplicationButton(@NotNull RemoteViews views) {
views.setTextViewText(R.id.multiplicationButton, Locator.getInstance().getEngine().getMultiplicationSign());
}
public static void toggleEqualsButton(@Nullable SharedPreferences preferences,
@NotNull Activity activity) {
preferences = preferences == null ? PreferenceManager.getDefaultSharedPreferences(activity) : preferences;
final boolean large = AndroidUtils.isLayoutSizeAtLeast(Configuration.SCREENLAYOUT_SIZE_LARGE, activity.getResources().getConfiguration()) &&
CalculatorPreferences.Gui.getLayout(preferences) != CalculatorPreferences.Gui.Layout.main_calculator_mobile;
if (!large) {
if (AndroidUtils.getScreenOrientation(activity) == Configuration.ORIENTATION_PORTRAIT
|| !CalculatorPreferences.Gui.autoOrientation.getPreference(preferences)) {
final DragButton equalsButton = (DragButton)activity.findViewById(R.id.equalsButton);
if (equalsButton != null) {
if (CalculatorPreferences.Gui.showEqualsButton.getPreference(preferences)) {
equalsButton.setVisibility(View.VISIBLE);
} else {
equalsButton.setVisibility(View.GONE);
}
}
}
}
}
/*
**********************************************************************
*
* STATIC CLASSES
*
**********************************************************************
*/
static class RoundBracketsDragProcessor implements SimpleOnDragListener.DragProcessor {
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
final boolean result;
if (dragDirection == DragDirection.left) {
getKeyboard().roundBracketsButtonPressed();
result = true;
} else {
result = new DigitButtonDragProcessor(getKeyboard()).processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent);
}
return result;
}
}
@NotNull
private static CalculatorKeyboard getKeyboard() {
return Locator.getInstance().getKeyboard();
}
static class VarsDragProcessor implements SimpleOnDragListener.DragProcessor {
@NotNull
private Context context;
VarsDragProcessor(@NotNull Context context) {
this.context = context;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection,
@NotNull DragButton dragButton,
@NotNull Point2d startPoint2d,
@NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragDirection == DragDirection.up) {
CalculatorActivityLauncher.createVar(context, Locator.getInstance().getDisplay());
result = true;
}
return result;
}
}
static class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor {
@NotNull
private final DigitButtonDragProcessor processor;
@NotNull
private final Context context;
AngleUnitsChanger(@NotNull Context context) {
this.context = context;
this.processor = new DigitButtonDragProcessor(Locator.getInstance().getKeyboard());
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection,
@NotNull DragButton dragButton,
@NotNull Point2d startPoint2d,
@NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragButton instanceof AngleUnitsButton) {
if (dragDirection != DragDirection.left) {
final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection);
if (directionText != null) {
try {
final AngleUnit angleUnits = AngleUnit.valueOf(directionText);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final AngleUnit oldAngleUnits = AndroidCalculatorEngine.Preferences.angleUnit.getPreference(preferences);
if (oldAngleUnits != angleUnits) {
Locator.getInstance().getPreferenceService().setAngleUnits(angleUnits);
}
result = true;
} catch (IllegalArgumentException e) {
Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText);
}
}
} else if (dragDirection == DragDirection.left) {
result = processor.processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent);
}
}
return result;
}
}
static class NumeralBasesChanger implements SimpleOnDragListener.DragProcessor {
@NotNull
private final Context context;
NumeralBasesChanger(@NotNull Context context) {
this.context = context;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection,
@NotNull DragButton dragButton,
@NotNull Point2d startPoint2d,
@NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragButton instanceof NumeralBasesButton) {
final String directionText = ((NumeralBasesButton) dragButton).getText(dragDirection);
if (directionText != null) {
try {
final NumeralBase numeralBase = NumeralBase.valueOf(directionText);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
final NumeralBase oldNumeralBase = AndroidCalculatorEngine.Preferences.numeralBase.getPreference(preferences);
if (oldNumeralBase != numeralBase) {
Locator.getInstance().getPreferenceService().setNumeralBase(numeralBase);
}
result = true;
} catch (IllegalArgumentException e) {
Log.d(this.getClass().getName(), "Unsupported numeral base: " + directionText);
}
}
}
return result;
}
}
static class FunctionsDragProcessor implements SimpleOnDragListener.DragProcessor {
@NotNull
private Context context;
FunctionsDragProcessor(@NotNull Context context) {
this.context = context;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection,
@NotNull DragButton dragButton,
@NotNull Point2d startPoint2d,
@NotNull MotionEvent motionEvent) {
boolean result = false;
if (dragDirection == DragDirection.up) {
CalculatorActivityLauncher.createFunction(context, Locator.getInstance().getDisplay());
result = true;
}
return result;
}
}
}

View File

@@ -0,0 +1,76 @@
package org.solovyev.android.calculator;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 25.09.12
* Time: 12:03
*/
public class CalculatorDisplayFragment extends SherlockFragment {
@NotNull
private CalculatorFragmentHelper fragmentHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.getLayout(prefs);
if (layout == CalculatorPreferences.Gui.Layout.main_calculator_mobile) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_display_mobile, R.string.result);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_display, R.string.result);
}
fragmentHelper.onCreate(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
((AndroidCalculator) Locator.getInstance().getCalculator()).setDisplay(getActivity());
fragmentHelper.onViewCreated(this, root);
}
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onResume() {
super.onResume();
fragmentHelper.onResume(this);
}
@Override
public void onPause() {
fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onDestroy() {
fragmentHelper.onDestroy(this);
super.onDestroy();
}
}

View File

@@ -0,0 +1,115 @@
package org.solovyev.android.calculator;
import android.content.Context;
import jscl.math.Generic;
import jscl.math.function.Constant;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.collections.CollectionsUtils;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 10:55
*/
public enum CalculatorDisplayMenuItem implements LabeledMenuItem<CalculatorDisplayViewState> {
copy(R.string.c_copy) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
Locator.getInstance().getKeyboard().copyButtonPressed();
}
},
convert_to_bin(R.string.convert_to_bin) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_bin.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_bin.isItemVisibleFor(generic, operation);
}
},
convert_to_dec(R.string.convert_to_dec) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_dec.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_dec.isItemVisibleFor(generic, operation);
}
},
convert_to_hex(R.string.convert_to_hex) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
ConversionMenuItem.convert_to_hex.onClick(data, context);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return ConversionMenuItem.convert_to_hex.isItemVisibleFor(generic, operation);
}
},
convert(R.string.c_convert) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic result = data.getResult();
if (result != null) {
new NumeralBaseConverterDialog(result.toString()).show(context);
}
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return operation == JsclOperation.numeric && generic.getConstants().isEmpty();
}
},
plot(R.string.c_plot) {
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic generic = data.getResult();
assert generic != null;
final Constant constant = CollectionsUtils.getFirstCollectionElement(CalculatorUtils.getNotSystemConstants(generic));
assert constant != null;
CalculatorActivityLauncher.plotGraph(context, generic, constant);
}
@Override
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return CalculatorUtils.isPlotPossible(generic, operation);
}
};
private final int captionId;
CalculatorDisplayMenuItem(int captionId) {
this.captionId = captionId;
}
public final boolean isItemVisible(@NotNull CalculatorDisplayViewState displayViewState) {
//noinspection ConstantConditions
return displayViewState.isValid() && displayViewState.getResult() != null && isItemVisibleFor(displayViewState.getResult(), displayViewState.getOperation());
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
return true;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}

View File

@@ -0,0 +1,53 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.view.View;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.MenuImpl;
import java.util.ArrayList;
import java.util.List;
/**
* User: Solovyev_S
* Date: 21.09.12
* Time: 10:58
*/
public class CalculatorDisplayOnClickListener implements View.OnClickListener {
@NotNull
private final Context context;
public CalculatorDisplayOnClickListener(@NotNull Context context) {
this.context = context;
}
@Override
public void onClick(View v) {
if (v instanceof CalculatorDisplayView) {
final CalculatorDisplay cd = Locator.getInstance().getDisplay();
final CalculatorDisplayViewState displayViewState = cd.getViewState();
if (displayViewState.isValid()) {
final List<CalculatorDisplayMenuItem> filteredMenuItems = new ArrayList<CalculatorDisplayMenuItem>(CalculatorDisplayMenuItem.values().length);
for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) {
if (menuItem.isItemVisible(displayViewState)) {
filteredMenuItems.add(menuItem);
}
}
if (!filteredMenuItems.isEmpty()) {
AMenuBuilder.newInstance(context, MenuImpl.newInstance(filteredMenuItems)).create(displayViewState).show();
}
} else {
final String errorMessage = displayViewState.getErrorMessage();
if (errorMessage != null) {
AndroidCalculator.showEvaluationError(context, errorMessage);
}
}
}
}
}

View File

@@ -0,0 +1,122 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.menu.ActivityMenu;
import org.solovyev.android.menu.ListActivityMenu;
import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
/**
* User: Solovyev_S
* Date: 25.09.12
* Time: 10:49
*/
public class CalculatorEditorFragment extends SherlockFragment {
@NotNull
private CalculatorFragmentHelper fragmentHelper;
@NotNull
private ActivityMenu<Menu, MenuItem> menu = ListActivityMenu.fromList(CalculatorMenu.class, SherlockMenuHelper.getInstance());
public CalculatorEditorFragment() {
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentHelper.onViewCreated(this, view);
((AndroidCalculator) Locator.getInstance().getCalculator()).setEditor(getActivity());
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.getLayout(prefs);
if (layout == CalculatorPreferences.Gui.Layout.main_calculator_mobile) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_editor_mobile, R.string.editor);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_editor, R.string.editor);
}
fragmentHelper.onCreate(this);
setHasOptionsMenu(true);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onResume() {
super.onResume();
this.fragmentHelper.onResume(this);
}
@Override
public void onPause() {
this.fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onDestroy() {
fragmentHelper.onDestroy(this);
super.onDestroy();
}
@Override
public void onDetach() {
super.onDetach();
}
/*
**********************************************************************
*
* MENU
*
**********************************************************************
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
this.menu.onCreateOptionsMenu(this.getActivity(), menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
this.menu.onPrepareOptionsMenu(this.getActivity(), menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return this.menu.onOptionsItemSelected(this.getActivity(), item);
}
}

View File

@@ -0,0 +1,87 @@
package org.solovyev.android.calculator;
import android.os.Parcel;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.msg.Message;
import org.solovyev.common.msg.MessageType;
/**
* User: serso
* Date: 11/17/12
* Time: 6:54 PM
*/
public class CalculatorFixableMessage implements Parcelable {
public static final Creator<CalculatorFixableMessage> CREATOR = new Creator<CalculatorFixableMessage>() {
@Override
public CalculatorFixableMessage createFromParcel(@NotNull Parcel in) {
return CalculatorFixableMessage.fromParcel(in);
}
@Override
public CalculatorFixableMessage[] newArray(int size) {
return new CalculatorFixableMessage[size];
}
};
@NotNull
private static CalculatorFixableMessage fromParcel(@NotNull Parcel in) {
final String message = in.readString();
final MessageType messageType = (MessageType) in.readSerializable();
final CalculatorFixableError fixableError = (CalculatorFixableError) in.readSerializable();
return new CalculatorFixableMessage(message, messageType, fixableError);
}
@NotNull
private final String message;
@NotNull
private final MessageType messageType;
@Nullable
private final CalculatorFixableError fixableError;
public CalculatorFixableMessage(@NotNull Message message) {
this.message = message.getLocalizedMessage();
this.messageType = message.getMessageType();
this.fixableError = CalculatorFixableError.getErrorByMessageCode(message.getMessageCode());
}
public CalculatorFixableMessage(@NotNull String message,
@NotNull MessageType messageType,
@Nullable CalculatorFixableError fixableError) {
this.message = message;
this.messageType = messageType;
this.fixableError = fixableError;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeString(message);
out.writeSerializable(messageType);
out.writeSerializable(fixableError);
}
@NotNull
public String getMessage() {
return message;
}
@NotNull
public MessageType getMessageType() {
return messageType;
}
@Nullable
public CalculatorFixableError getFixableError() {
return fixableError;
}
}

View File

@@ -0,0 +1,87 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.os.Bundle;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: Solovyev_S
* Date: 03.10.12
* Time: 14:18
*/
public abstract class CalculatorFragment extends SherlockFragment {
@NotNull
private final CalculatorFragmentHelper fragmentHelper;
protected CalculatorFragment(int layoutResId, int titleResId) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(layoutResId, titleResId);
}
protected CalculatorFragment(@NotNull CalculatorFragmentType fragmentType) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(fragmentType.getDefaultLayoutId(), fragmentType.getDefaultTitleResId());
}
protected CalculatorFragment(@NotNull CalculatorFragmentHelper fragmentHelper) {
this.fragmentHelper = fragmentHelper;
}
@Override
public void onAttach(Activity activity) {
super.onAttach(activity);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentHelper.onCreate(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
fragmentHelper.onViewCreated(this, view);
}
@Override
public void onResume() {
super.onResume();
this.fragmentHelper.onResume(this);
}
@Override
public void onPause() {
this.fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onDestroyView() {
super.onDestroyView();
}
@Override
public void onDestroy() {
fragmentHelper.onDestroy(this);
super.onDestroy();
}
@Override
public void onDetach() {
super.onDetach();
}
}

View File

@@ -0,0 +1,65 @@
package org.solovyev.android.calculator;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
/**
* User: Solovyev_S
* Date: 03.10.12
* Time: 14:07
*/
public abstract class CalculatorFragmentActivity extends SherlockFragmentActivity {
@NotNull
private final CalculatorActivityHelper activityHelper;
protected CalculatorFragmentActivity() {
this(R.layout.main_empty);
}
protected CalculatorFragmentActivity(int layoutResId) {
this.activityHelper = CalculatorApplication.getInstance().createActivityHelper(layoutResId, getClass().getSimpleName());
}
@NotNull
protected CalculatorActivityHelper getActivityHelper() {
return activityHelper;
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityHelper.onCreate(this, savedInstanceState);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
@Override
protected void onResume() {
super.onResume();
activityHelper.onResume(this);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
activityHelper.onDestroy(this);
}
}

View File

@@ -0,0 +1,33 @@
package org.solovyev.android.calculator;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 9/26/12
* Time: 10:14 PM
*/
public interface CalculatorFragmentHelper {
boolean isPane(@NotNull Fragment fragment);
void setPaneTitle(@NotNull Fragment fragment, int titleResId);
void onCreate(@NotNull Fragment fragment);
@NotNull
View onCreateView(@NotNull Fragment fragment, @NotNull LayoutInflater inflater, @Nullable ViewGroup container);
void onViewCreated(@NotNull Fragment fragment, @NotNull View root);
void onResume(@NotNull Fragment fragment);
void onPause(@NotNull Fragment fragment);
void onDestroy(@NotNull Fragment fragment);
}

View File

@@ -0,0 +1,131 @@
package org.solovyev.android.calculator;
import android.support.v4.app.Fragment;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import com.google.ads.AdView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.ads.AdsController;
/**
* User: serso
* Date: 9/26/12
* Time: 10:14 PM
*/
public class CalculatorFragmentHelperImpl extends AbstractCalculatorHelper implements CalculatorFragmentHelper {
@Nullable
private AdView adView;
private int layoutId;
private int titleResId = -1;
private boolean listenersOnCreate = true;
public CalculatorFragmentHelperImpl(int layoutId) {
this.layoutId = layoutId;
}
public CalculatorFragmentHelperImpl(int layoutId, int titleResId) {
this.layoutId = layoutId;
this.titleResId = titleResId;
}
public CalculatorFragmentHelperImpl(int layoutId, int titleResId, boolean listenersOnCreate) {
this.layoutId = layoutId;
this.titleResId = titleResId;
this.listenersOnCreate = listenersOnCreate;
}
@Override
public boolean isPane(@NotNull Fragment fragment) {
return fragment.getActivity() instanceof CalculatorActivity;
}
public void setPaneTitle(@NotNull Fragment fragment, int titleResId) {
final TextView fragmentTitle = (TextView) fragment.getView().findViewById(R.id.fragment_title);
if (fragmentTitle != null) {
if (!isPane(fragment)) {
fragmentTitle.setVisibility(View.GONE);
} else {
fragmentTitle.setText(fragment.getString(titleResId).toUpperCase());
}
}
}
@Override
public void onCreate(@NotNull Fragment fragment) {
super.onCreate(fragment.getActivity());
if (listenersOnCreate) {
if ( fragment instanceof CalculatorEventListener ) {
Locator.getInstance().getCalculator().addCalculatorEventListener((CalculatorEventListener) fragment);
}
}
}
@Override
public void onResume(@NotNull Fragment fragment) {
if (!listenersOnCreate) {
if ( fragment instanceof CalculatorEventListener ) {
Locator.getInstance().getCalculator().addCalculatorEventListener((CalculatorEventListener) fragment);
}
}
}
@Override
public void onPause(@NotNull Fragment fragment) {
if (!listenersOnCreate) {
if ( fragment instanceof CalculatorEventListener ) {
Locator.getInstance().getCalculator().removeCalculatorEventListener((CalculatorEventListener) fragment);
}
}
}
@Override
public void onViewCreated(@NotNull Fragment fragment, @NotNull View root) {
final ViewGroup adParentView = (ViewGroup) root.findViewById(R.id.ad_parent_view);
final ViewGroup mainFragmentLayout = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
if (fragment instanceof CalculatorDisplayFragment || fragment instanceof CalculatorEditorFragment || fragment instanceof CalculatorKeyboardFragment) {
// no ads in those fragments
} else {
if (adParentView != null) {
adView = AdsController.getInstance().inflateAd(fragment.getActivity(), adParentView, R.id.ad_parent_view);
} else if ( mainFragmentLayout != null ) {
adView = AdsController.getInstance().inflateAd(fragment.getActivity(), mainFragmentLayout, R.id.main_fragment_layout);
}
}
processButtons(fragment.getActivity(), root);
if (titleResId >= 0) {
this.setPaneTitle(fragment, titleResId);
}
}
@Override
public void onDestroy(@NotNull Fragment fragment) {
super.onDestroy(fragment.getActivity());
if (listenersOnCreate) {
if ( fragment instanceof CalculatorEventListener ) {
Locator.getInstance().getCalculator().removeCalculatorEventListener((CalculatorEventListener) fragment);
}
}
if (this.adView != null) {
this.adView.destroy();
}
}
@NotNull
@Override
public View onCreateView(@NotNull Fragment fragment, @NotNull LayoutInflater inflater, @Nullable ViewGroup container) {
return inflater.inflate(layoutId, container, false);
}
}

View File

@@ -0,0 +1,147 @@
package org.solovyev.android.calculator;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.app.SherlockFragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
/**
* User: Solovyev_S
* Date: 25.09.12
* Time: 12:25
*/
public class CalculatorKeyboardFragment extends SherlockFragment implements SharedPreferences.OnSharedPreferenceChangeListener {
@NotNull
private CalculatorPreferences.Gui.Theme theme;
@NotNull
private CalculatorFragmentHelper fragmentHelper;
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final CalculatorPreferences.Gui.Layout layout = CalculatorPreferences.Gui.getLayout(preferences);
if (layout == CalculatorPreferences.Gui.Layout.main_calculator_mobile) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_keyboard_mobile);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.calc_keyboard);
}
fragmentHelper.onCreate(this);
preferences.registerOnSharedPreferenceChangeListener(this);
theme = CalculatorPreferences.Gui.theme.getPreferenceNoError(preferences);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
fragmentHelper.onViewCreated(this, root);
}
@Override
public void onResume() {
super.onResume();
this.fragmentHelper.onResume(this);
}
@Override
public void onPause() {
this.fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onDestroy() {
super.onDestroy();
fragmentHelper.onDestroy(this);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
preferences.unregisterOnSharedPreferenceChangeListener(this);
}
/* private static void setMarginsForView(@Nullable View view, int marginLeft, int marginBottom, @NotNull Context context) {
// IMPORTANT: this is workaround for probably android bug
// currently margin values set in styles are not applied for some reasons to the views (using include tag) => set them manually
if (view != null) {
final DisplayMetrics dm = context.getResources().getDisplayMetrics();
if (view.getLayoutParams() instanceof LinearLayout.LayoutParams) {
final LinearLayout.LayoutParams oldParams = (LinearLayout.LayoutParams) view.getLayoutParams();
final LinearLayout.LayoutParams newParams = new LinearLayout.LayoutParams(oldParams.width, oldParams.height, oldParams.weight);
newParams.setMargins(AndroidUtils.toPixels(dm, marginLeft), 0, 0, AndroidUtils.toPixels(dm, marginBottom));
view.setLayoutParams(newParams);
}
}
}*/
@Override
public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (AndroidCalculatorEngine.Preferences.numeralBase.getKey().equals(key) ||
CalculatorPreferences.Gui.hideNumeralBaseDigits.getKey().equals(key) ) {
NumeralBaseButtons.toggleNumericDigits(this.getActivity(), preferences);
}
if ( AndroidCalculatorEngine.Preferences.angleUnit.getKey().equals(key) ||
AndroidCalculatorEngine.Preferences.numeralBase.getKey().equals(key) ) {
View view = getView();
if ( view != null) {
// we should update state of angle units/numeral base button => we can achieve it by invalidating the whole view
view.invalidate();
}
}
if ( CalculatorPreferences.Gui.showEqualsButton.getKey().equals(key) ) {
CalculatorButtons.toggleEqualsButton(preferences, this.getActivity());
}
if ( AndroidCalculatorEngine.Preferences.multiplicationSign.getKey().equals(key) ) {
CalculatorButtons.initMultiplicationButton(getView());
}
}
@Nullable
private static AndroidCalculatorDisplayView getCalculatorDisplayView() {
return (AndroidCalculatorDisplayView) Locator.getInstance().getDisplay().getView();
}
@NotNull
private Calculator getCalculator() {
return Locator.getInstance().getCalculator();
}
@NotNull
private static CalculatorKeyboard getKeyboard() {
return Locator.getInstance().getKeyboard();
}
}

View File

@@ -0,0 +1,75 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.Context;
import android.util.Log;
import com.actionbarsherlock.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.view.NumeralBaseConverterDialog;
import org.solovyev.android.menu.LabeledMenuItem;
/**
* User: serso
* Date: 4/23/12
* Time: 2:25 PM
*/
enum CalculatorMenu implements LabeledMenuItem<MenuItem> {
settings(R.string.c_settings) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
CalculatorActivityLauncher.showSettings(context);
}
},
history(R.string.c_history) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
CalculatorActivityLauncher.showHistory(context);
}
},
about(R.string.c_about) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
CalculatorActivityLauncher.showAbout(context);
}
},
help(R.string.c_help) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
CalculatorActivityLauncher.showHelp(context);
}
},
conversion_tool(R.string.c_conversion_tool) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
new NumeralBaseConverterDialog(null).show(context);
}
},
exit(R.string.c_exit) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
if (context instanceof Activity) {
((Activity) context).finish();
} else {
Log.e(CalculatorActivity.TAG, "Activity menu used with context");
}
}
};
private final int captionResId;
private CalculatorMenu(int captionResId) {
this.captionResId = captionResId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionResId);
}
}

View File

@@ -0,0 +1,229 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Button;
import android.widget.CheckBox;
import android.widget.TextView;
import com.actionbarsherlock.app.SherlockActivity;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.msg.Message;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: serso
* Date: 11/17/12
* Time: 3:37 PM
*/
public class CalculatorMessagesDialog extends SherlockActivity {
private static final String INPUT = "input";
@NotNull
private Input input = new Input(Collections.<CalculatorFixableMessage>emptyList());
public CalculatorMessagesDialog() {
}
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.calculator_messages_dialog);
final Intent intent = getIntent();
if (intent != null) {
parseIntent(intent);
}
final CheckBox doNotShowCalculationMessagesCheckbox = (CheckBox) findViewById(R.id.do_not_show_calculation_messages_checkbox);
final Button closeButton = (Button) findViewById(R.id.close_button);
closeButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (doNotShowCalculationMessagesCheckbox.isChecked()) {
final SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(CalculatorMessagesDialog.this);
CalculatorPreferences.Calculations.showCalculationMessagesDialog.putPreference(prefs, false);
}
CalculatorMessagesDialog.this.finish();
}
});
}
private void parseIntent(@NotNull Intent intent) {
final Input input = intent.getParcelableExtra(INPUT);
if (input != null) {
this.input = input;
onInputChanged();
}
}
private void onInputChanged() {
final ViewGroup viewGroup = (ViewGroup) findViewById(R.id.calculation_messages_container);
viewGroup.removeAllViews();
final LayoutInflater layoutInflater = (LayoutInflater) this.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final List<CalculatorFixableMessage> messages = input.getMessages();
for (final CalculatorFixableMessage message : messages) {
final View view = layoutInflater.inflate(R.layout.calculator_messages_dialog_message, null);
final TextView calculationMessagesTextView = (TextView) view.findViewById(R.id.calculation_messages_text_view);
calculationMessagesTextView.setText(message.getMessage());
final Button fixButton = (Button) view.findViewById(R.id.fix_button);
final CalculatorFixableError fixableError = message.getFixableError();
if (fixableError == null) {
fixButton.setVisibility(View.GONE);
fixButton.setOnClickListener(null);
} else {
fixButton.setVisibility(View.VISIBLE);
fixButton.setOnClickListener(new FixErrorOnClickListener(messages, message));
}
viewGroup.addView(view, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
}
}
@Override
protected void onNewIntent(@NotNull Intent intent) {
super.onNewIntent(intent);
parseIntent(intent);
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
public static void showDialogForMessages(@NotNull List<Message> messages, @NotNull Context context) {
if (!messages.isEmpty()) {
final Intent intent = new Intent(context, CalculatorMessagesDialog.class);
intent.putExtra(INPUT, Input.fromMessages(messages));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
public static void showDialog(@NotNull List<CalculatorFixableMessage> messages, @NotNull Context context) {
if (!messages.isEmpty()) {
final Intent intent = new Intent(context, CalculatorMessagesDialog.class);
intent.putExtra(INPUT, new Input(messages));
intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
context.startActivity(intent);
}
}
private static final class Input implements Parcelable {
public static final Creator<Input> CREATOR = new Creator<Input>() {
@Override
public Input createFromParcel(@NotNull Parcel in) {
return Input.fromParcel(in);
}
@Override
public Input[] newArray(int size) {
return new Input[size];
}
};
@NotNull
private static Input fromParcel(@NotNull Parcel in) {
final List<CalculatorFixableMessage> messages = new ArrayList<CalculatorFixableMessage>();
in.readTypedList(messages, CalculatorFixableMessage.CREATOR);
return new Input(messages);
}
@NotNull
private List<CalculatorFixableMessage> messages = new ArrayList<CalculatorFixableMessage>();
private Input(@NotNull List<CalculatorFixableMessage> messages) {
this.messages = messages;
}
@NotNull
public List<CalculatorFixableMessage> getMessages() {
return messages;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeTypedList(messages);
}
@NotNull
public static Input fromMessages(@NotNull List<Message> messages) {
final List<CalculatorFixableMessage> stringMessages = new ArrayList<CalculatorFixableMessage>(messages.size());
for (Message message : messages) {
stringMessages.add(new CalculatorFixableMessage(message));
}
return new Input(stringMessages);
}
}
private class FixErrorOnClickListener implements View.OnClickListener {
@NotNull
private final List<CalculatorFixableMessage> messages;
@NotNull
private final CalculatorFixableMessage currentMessage;
public FixErrorOnClickListener(@NotNull List<CalculatorFixableMessage> messages,
@NotNull CalculatorFixableMessage message) {
this.messages = messages;
this.currentMessage = message;
}
@Override
public void onClick(View v) {
final List<CalculatorFixableMessage> filteredMessages = new ArrayList<CalculatorFixableMessage>(messages.size() - 1);
for (CalculatorFixableMessage message : messages) {
if ( message.getFixableError() == null ) {
filteredMessages.add(message);
} else if ( message.getFixableError() != currentMessage.getFixableError() ) {
filteredMessages.add(message);
}
}
currentMessage.getFixableError().fix();
if (!filteredMessages.isEmpty()) {
CalculatorMessagesDialog.this.input = new Input(filteredMessages);
onInputChanged();
} else {
CalculatorMessagesDialog.this.finish();
}
}
}
}

View File

@@ -0,0 +1,17 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.os.Bundle;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
public class CalculatorOnscreenStartActivity extends Activity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
CalculatorOnscreenService.showOnscreenView(this);
this.finish();
}
}

View File

@@ -0,0 +1,212 @@
package org.solovyev.android.calculator;
import android.content.SharedPreferences;
import jscl.AngleUnit;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.calculator.plot.GraphLineColor;
import org.solovyev.android.prefs.*;
import org.solovyev.android.view.VibratorContainer;
import java.text.DecimalFormatSymbols;
import java.util.Locale;
/**
* User: serso
* Date: 4/20/12
* Time: 12:42 PM
*/
public final class CalculatorPreferences {
private CalculatorPreferences() {
throw new AssertionError();
}
public static final Preference<Integer> appVersion = new IntegerPreference("application.version", -1);
public static final Preference<Integer> appOpenedCounter = new IntegerPreference("app_opened_counter", 0);
public static class OnscreenCalculator {
public static final Preference<Boolean> startOnBoot = new BooleanPreference("onscreen_start_on_boot", false);
}
public static class Calculations {
public static final Preference<Boolean> calculateOnFly = new BooleanPreference("calculations_calculate_on_fly", true);
public static final Preference<Boolean> showCalculationMessagesDialog = new BooleanPreference("show_calculation_messages_dialog", true);
public static final Preference<NumeralBase> preferredNumeralBase = StringPreference.newInstance("preferred_numeral_base", AndroidCalculatorEngine.Preferences.numeralBase.getDefaultValue(), NumeralBase.class);
public static final Preference<AngleUnit> preferredAngleUnits = StringPreference.newInstance("preferred_angle_units", AndroidCalculatorEngine.Preferences.angleUnit.getDefaultValue(), AngleUnit.class);
public static final Preference<Long> lastPreferredPreferencesCheck = new LongPreference("preferred_preferences_check_time", 0L);
}
public static class Gui {
public static final Preference<Theme> theme = StringPreference.newInstance("org.solovyev.android.calculator.CalculatorActivity_calc_theme", Theme.metro_blue_theme, Theme.class);
public static final Preference<Layout> layout = StringPreference.newInstance("org.solovyev.android.calculator.CalculatorActivity_calc_layout", Layout.main_calculator, Layout.class);
public static final Preference<Boolean> feedbackWindowShown = new BooleanPreference("feedback_window_shown", false);
public static final Preference<Boolean> notesppAnnounceShown = new BooleanPreference("notespp_announce_shown", false);
public static final Preference<Boolean> showReleaseNotes = new BooleanPreference("org.solovyev.android.calculator.CalculatorActivity_show_release_notes", true);
public static final Preference<Boolean> usePrevAsBack = new BooleanPreference("org.solovyev.android.calculator.CalculatorActivity_use_back_button_as_prev", false);
public static final Preference<Boolean> showEqualsButton = new BooleanPreference("showEqualsButton", true);
public static final Preference<Boolean> autoOrientation = new BooleanPreference("autoOrientation", true);
public static final Preference<Boolean> hideNumeralBaseDigits = new BooleanPreference("hideNumeralBaseDigits", true);
@NotNull
public static Theme getTheme(@NotNull SharedPreferences preferences) {
return theme.getPreferenceNoError(preferences);
}
@NotNull
public static Layout getLayout(@NotNull SharedPreferences preferences) {
return layout.getPreferenceNoError(preferences);
}
public static enum Theme {
default_theme(ThemeType.other, R.style.cpp_gray_theme),
violet_theme(ThemeType.other, R.style.cpp_violet_theme),
light_blue_theme(ThemeType.other, R.style.cpp_light_blue_theme),
metro_blue_theme(ThemeType.metro, R.style.cpp_metro_blue_theme),
metro_purple_theme(ThemeType.metro, R.style.cpp_metro_purple_theme),
metro_green_theme(ThemeType.metro, R.style.cpp_metro_green_theme);
@NotNull
private final ThemeType themeType;
@NotNull
private final Integer themeId;
Theme(@NotNull ThemeType themeType, @NotNull Integer themeId) {
this.themeType = themeType;
this.themeId = themeId;
}
@NotNull
public ThemeType getThemeType() {
return themeType;
}
@NotNull
public Integer getThemeId() {
return themeId;
}
}
public static enum ThemeType {
metro,
other
}
public static enum Layout {
main_calculator(R.layout.main_calculator),
main_calculator_mobile(R.layout.main_calculator_mobile),
// not used anymore
@Deprecated
main_cellphone(R.layout.main_calculator),
simple(R.layout.main_calculator);
private final int layoutId;
Layout(int layoutId) {
this.layoutId = layoutId;
}
public int getLayoutId() {
return layoutId;
}
}
}
public static class Graph {
public static final Preference<Boolean> interpolate = new BooleanPreference("graph_interpolate", true);
public static final Preference<GraphLineColor> lineColorReal = StringPreference.newInstance("graph_line_color_real", GraphLineColor.white, GraphLineColor.class);
public static final Preference<GraphLineColor> lineColorImag = StringPreference.newInstance("graph_line_color_imag", GraphLineColor.blue, GraphLineColor.class);
}
public static class History {
public static final Preference<Boolean> showIntermediateCalculations = new BooleanPreference("history_show_intermediate_calculations", false);
}
static void setDefaultValues(@NotNull SharedPreferences preferences) {
if (!AndroidCalculatorEngine.Preferences.groupingSeparator.isSet(preferences)) {
final Locale locale = Locale.getDefault();
if (locale != null) {
final DecimalFormatSymbols decimalFormatSymbols = new DecimalFormatSymbols(locale);
int index = MathType.grouping_separator.getTokens().indexOf(String.valueOf(decimalFormatSymbols.getGroupingSeparator()));
final String groupingSeparator;
if (index >= 0) {
groupingSeparator = MathType.grouping_separator.getTokens().get(index);
} else {
groupingSeparator = " ";
}
AndroidCalculatorEngine.Preferences.groupingSeparator.putPreference(preferences, groupingSeparator);
}
}
if (!AndroidCalculatorEngine.Preferences.angleUnit.isSet(preferences)) {
AndroidCalculatorEngine.Preferences.angleUnit.putDefault(preferences);
}
if (!AndroidCalculatorEngine.Preferences.numeralBase.isSet(preferences)) {
AndroidCalculatorEngine.Preferences.numeralBase.putDefault(preferences);
}
if (!AndroidCalculatorEngine.Preferences.multiplicationSign.isSet(preferences)) {
if (AndroidUtils.isPhoneModel(AndroidUtils.PhoneModel.samsung_galaxy_s) || AndroidUtils.isPhoneModel(AndroidUtils.PhoneModel.samsung_galaxy_s_2)) {
// workaround ofr samsung galaxy s phones
AndroidCalculatorEngine.Preferences.multiplicationSign.putPreference(preferences, "*");
}
}
applyDefaultPreference(preferences, Gui.theme);
applyDefaultPreference(preferences, Gui.layout);
if (Gui.layout.getPreference(preferences) == Gui.Layout.main_cellphone) {
Gui.layout.putDefault(preferences);
}
applyDefaultPreference(preferences, Gui.feedbackWindowShown);
applyDefaultPreference(preferences, Gui.notesppAnnounceShown);
applyDefaultPreference(preferences, Gui.showReleaseNotes);
applyDefaultPreference(preferences, Gui.usePrevAsBack);
applyDefaultPreference(preferences, Gui.showEqualsButton);
applyDefaultPreference(preferences, Gui.autoOrientation);
applyDefaultPreference(preferences, Gui.hideNumeralBaseDigits);
applyDefaultPreference(preferences, Graph.interpolate);
applyDefaultPreference(preferences, Graph.lineColorImag);
applyDefaultPreference(preferences, Graph.lineColorReal);
applyDefaultPreference(preferences, History.showIntermediateCalculations);
applyDefaultPreference(preferences, Calculations.calculateOnFly);
applyDefaultPreference(preferences, Calculations.preferredAngleUnits);
applyDefaultPreference(preferences, Calculations.preferredNumeralBase);
// renew value after each application start
Calculations.showCalculationMessagesDialog.putDefault(preferences);
Calculations.lastPreferredPreferencesCheck.putDefault(preferences);
if (!VibratorContainer.Preferences.hapticFeedbackEnabled.isSet(preferences)) {
VibratorContainer.Preferences.hapticFeedbackEnabled.putPreference(preferences, true);
}
if (!VibratorContainer.Preferences.hapticFeedbackDuration.isSet(preferences)) {
VibratorContainer.Preferences.hapticFeedbackDuration.putPreference(preferences, 60L);
}
}
private static void applyDefaultPreference(@NotNull SharedPreferences preferences, @NotNull Preference<?> preference) {
if (!preference.isSet(preferences)) {
preference.putDefault(preferences);
}
}
}

View File

@@ -0,0 +1,204 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.app.AlertDialog;
import android.app.PendingIntent;
import android.content.Context;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.preference.Preference;
import android.preference.PreferenceManager;
import android.util.Log;
import android.widget.Toast;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import net.robotmedia.billing.BillingController;
import net.robotmedia.billing.IBillingObserver;
import net.robotmedia.billing.ResponseCode;
import net.robotmedia.billing.helper.AbstractBillingObserver;
import net.robotmedia.billing.model.Transaction;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.ads.AdsController;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
import org.solovyev.android.view.VibratorContainer;
/**
* User: serso
* Date: 7/16/11
* Time: 6:37 PM
*/
public class CalculatorPreferencesActivity extends SherlockPreferenceActivity implements SharedPreferences.OnSharedPreferenceChangeListener, IBillingObserver {
public static final String CLEAR_BILLING_INFO = "clear_billing_info";
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//noinspection deprecation
addPreferencesFromResource(R.xml.preferences);
//noinspection deprecation
addPreferencesFromResource(R.xml.preferences_calculations);
addPreferencesFromResource(R.xml.preferences_appearance);
addPreferencesFromResource(R.xml.preferences_plot);
addPreferencesFromResource(R.xml.preferences_other);
addPreferencesFromResource(R.xml.preferences_onscreen);
final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY);
adFreePreference.setEnabled(false);
// observer must be set before net.robotmedia.billing.BillingController.checkBillingSupported()
BillingController.registerObserver(this);
BillingController.checkBillingSupported(CalculatorPreferencesActivity.this);
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
preferences.registerOnSharedPreferenceChangeListener(this);
onSharedPreferenceChanged(preferences, AndroidCalculatorEngine.Preferences.roundResult.getKey());
onSharedPreferenceChanged(preferences, VibratorContainer.Preferences.hapticFeedbackEnabled.getKey());
final Preference clearBillingInfoPreference = findPreference(CLEAR_BILLING_INFO);
if (clearBillingInfoPreference != null) {
clearBillingInfoPreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
@Override
public boolean onPreferenceClick(Preference preference) {
Toast.makeText(CalculatorPreferencesActivity.this, R.string.c_calc_clearing, Toast.LENGTH_SHORT).show();
removeBillingInformation(CalculatorPreferencesActivity.this, PreferenceManager.getDefaultSharedPreferences(CalculatorPreferencesActivity.this));
return true;
}
});
}
}
public static void removeBillingInformation(@NotNull Context context, @NotNull SharedPreferences preferences) {
final SharedPreferences.Editor editor = preferences.edit();
editor.putBoolean(AbstractBillingObserver.KEY_TRANSACTIONS_RESTORED, false);
editor.commit();
BillingController.dropBillingData(context);
}
private void setAdFreeAction() {
final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY);
if (!AdsController.getInstance().isAdFree(this)) {
Log.d(CalculatorPreferencesActivity.class.getName(), "Ad free is not purchased - enable preference!");
adFreePreference.setOnPreferenceClickListener(new Preference.OnPreferenceClickListener() {
public boolean onPreferenceClick(Preference preference) {
// check billing availability
if (BillingController.checkBillingSupported(CalculatorPreferencesActivity.this) != BillingController.BillingStatus.SUPPORTED) {
Log.d(CalculatorPreferencesActivity.class.getName(), "Billing is not supported - warn user!");
// warn about not supported billing
new AlertDialog.Builder(CalculatorPreferencesActivity.this).setTitle(R.string.c_error).setMessage(R.string.c_billing_error).create().show();
} else {
Log.d(CalculatorPreferencesActivity.class.getName(), "Billing is supported - continue!");
if (!AdsController.getInstance().isAdFree(CalculatorPreferencesActivity.this)) {
Log.d(CalculatorPreferencesActivity.class.getName(), "Item not purchased - try to purchase!");
// not purchased => purchasing
Toast.makeText(CalculatorPreferencesActivity.this, R.string.c_calc_purchasing, Toast.LENGTH_SHORT).show();
// show purchase window for user
BillingController.requestPurchase(CalculatorPreferencesActivity.this, CalculatorApplication.AD_FREE_PRODUCT_ID, true);
} else {
// disable preference
adFreePreference.setEnabled(false);
// and show message to user
Toast.makeText(CalculatorPreferencesActivity.this, R.string.c_calc_already_purchased, Toast.LENGTH_SHORT).show();
}
}
return true;
}
});
adFreePreference.setEnabled(true);
} else {
Log.d(CalculatorPreferencesActivity.class.getName(), "Ad free is not purchased - disable preference!");
adFreePreference.setEnabled(false);
}
}
@Override
protected void onDestroy() {
BillingController.unregisterObserver(this);
PreferenceManager.getDefaultSharedPreferences(this).unregisterOnSharedPreferenceChangeListener(this);
super.onDestroy();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, String key) {
if (AndroidCalculatorEngine.Preferences.roundResult.getKey().equals(key)) {
findPreference(AndroidCalculatorEngine.Preferences.precision.getKey()).setEnabled(preferences.getBoolean(key, AndroidCalculatorEngine.Preferences.roundResult.getDefaultValue()));
} else if (VibratorContainer.Preferences.hapticFeedbackEnabled.getKey().equals(key)) {
findPreference(VibratorContainer.Preferences.hapticFeedbackDuration.getKey()).setEnabled(VibratorContainer.Preferences.hapticFeedbackEnabled.getPreference(preferences));
}
}
@Override
public void onCheckBillingSupportedResponse(boolean supported) {
if (supported) {
setAdFreeAction();
} else {
final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY);
adFreePreference.setEnabled(false);
Log.d(CalculatorPreferencesActivity.class.getName(), "Billing is not supported!");
}
}
@Override
public void onPurchaseIntentOK(@NotNull String productId, @NotNull PendingIntent purchaseIntent) {
// do nothing
}
@Override
public void onPurchaseIntentFailure(@NotNull String productId, @NotNull ResponseCode responseCode) {
// do nothing
}
@Override
public void onPurchaseStateChanged(@NotNull String itemId, @NotNull Transaction.PurchaseState state) {
if (CalculatorApplication.AD_FREE_PRODUCT_ID.equals(itemId)) {
final Preference adFreePreference = findPreference(CalculatorApplication.AD_FREE_P_KEY);
if (adFreePreference != null) {
switch (state) {
case PURCHASED:
adFreePreference.setEnabled(false);
// restart activity to disable ads
AndroidUtils.restartActivity(this);
break;
case CANCELLED:
adFreePreference.setEnabled(true);
break;
case REFUNDED:
adFreePreference.setEnabled(true);
break;
}
} else {
}
}
}
@Override
public void onRequestPurchaseResponse(@NotNull String itemId, @NotNull ResponseCode response) {
// do nothing
}
@Override
public void onTransactionsRestored() {
// do nothing
}
@Override
public void onErrorRestoreTransactions(@NotNull ResponseCode responseCode) {
// do nothing
}
}

View File

@@ -0,0 +1,35 @@
/*
* Copyright (c) 2009-2012. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 1/4/12
* Time: 1:23 AM
*/
public final class CalculatorSecurity {
private CalculatorSecurity() {
}
@NotNull
public static String getPK() {
final StringBuilder result = new StringBuilder();
result.append("MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8A");
result.append("MIIBCgKCAQEAquP2a7dEhTaJEQeXtSyreH5dCmTDOd");
result.append("dElCfg0ijOeB8JTxBiJTXLWnLA0kMaT/sRXswUaYI61YCQOoik82");
result.append("qrFH7W4+OFtiLb8WGX+YPEpQQ/IBZu9qm3xzS9Nolu79EBff0/CLa1FuT9RtjO");
result.append("iTW8Q0VP9meQdJEkfqJEyVCgHain+MGoQaRXI45EzkYmkz8TBx6X6aJF5NBAXnAWeyD0wPX1");
result.append("uedHH7+LgLcjnPVw82YjyJSzYnaaD2GX0Y7PGoFe6J5K4yJGGX5mih45pe2HWcG5lAkQhu1uX2hCcCBdF3");
result.append("W7paRq9mJvCsbn+BNTh9gq8QKui0ltmiWpa5U+/9L+FQIDAQAB");
return result.toString();
}
}

View File

@@ -0,0 +1,38 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.common.JPredicate;
/**
* User: serso
* Date: 10/3/11
* Time: 12:54 AM
*/
public class CharacterAtPositionFinder implements JPredicate<Character> {
private int i;
@NotNull
private final String targetString;
public CharacterAtPositionFinder(@NotNull String targetString, int i) {
this.targetString = targetString;
this.i = i;
}
@Override
public boolean apply(@Nullable Character s) {
return s != null && s.equals(targetString.charAt(i));
}
public void setI(int i) {
this.i = i;
}
}

View File

@@ -0,0 +1,52 @@
package org.solovyev.android.calculator;
import android.content.Context;
import jscl.NumeralBase;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.menu.AMenuItem;
/**
* User: serso
* Date: 9/21/12
* Time: 12:11 AM
*/
enum ConversionMenuItem implements AMenuItem<CalculatorDisplayViewState> {
convert_to_bin(NumeralBase.bin),
convert_to_dec(NumeralBase.dec),
convert_to_hex(NumeralBase.hex);
@NotNull
private final NumeralBase toNumeralBase;
ConversionMenuItem(@NotNull NumeralBase toNumeralBase) {
this.toNumeralBase = toNumeralBase;
}
protected boolean isItemVisibleFor(@NotNull Generic generic, @NotNull JsclOperation operation) {
boolean result = false;
if (operation == JsclOperation.numeric) {
if (generic.getConstants().isEmpty()) {
// conversion possible => return true
final NumeralBase fromNumeralBase = Locator.getInstance().getEngine().getNumeralBase();
if (fromNumeralBase != toNumeralBase) {
result = Locator.getInstance().getCalculator().isConversionPossible(generic, fromNumeralBase, this.toNumeralBase);
}
}
}
return result;
}
@Override
public void onClick(@NotNull CalculatorDisplayViewState data, @NotNull Context context) {
final Generic result = data.getResult();
if (result != null) {
Locator.getInstance().getCalculator().convert(result, this.toNumeralBase);
}
}
}

View File

@@ -0,0 +1,43 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.drag.DirectionDragButton;
import org.solovyev.android.view.drag.DragButton;
import org.solovyev.android.view.drag.DragDirection;
import org.solovyev.android.view.drag.SimpleOnDragListener;
import org.solovyev.common.math.Point2d;
/**
* User: serso
* Date: 9/16/11
* Time: 11:45 PM
*/
public class CursorDragProcessor implements SimpleOnDragListener.DragProcessor{
public CursorDragProcessor() {
}
@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)) {
Locator.getInstance().getEditor().setCursorOnStart();
result = true;
} else if ("▶▶".equals(text)) {
Locator.getInstance().getEditor().setCursorOnEnd();
result = true;
}
}
return result;
}
}

View File

@@ -0,0 +1,37 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.drag.DirectionDragButton;
import org.solovyev.android.view.drag.DragButton;
import org.solovyev.android.view.drag.DragDirection;
import org.solovyev.android.view.drag.SimpleOnDragListener;
import org.solovyev.common.math.Point2d;
/**
* User: serso
* Date: 9/16/11
* Time: 11:48 PM
*/
public class DigitButtonDragProcessor implements SimpleOnDragListener.DragProcessor {
@NotNull
private CalculatorKeyboard calculatorKeyboard;
public DigitButtonDragProcessor(@NotNull CalculatorKeyboard calculatorKeyboard) {
this.calculatorKeyboard = calculatorKeyboard;
}
@Override
public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) {
assert dragButton instanceof DirectionDragButton;
calculatorKeyboard.buttonPressed(((DirectionDragButton) dragButton).getText(dragDirection));
return true;
}
}

View File

@@ -0,0 +1,41 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator;
import android.view.MotionEvent;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.view.drag.DragDirection;
import org.solovyev.android.view.drag.SimpleOnDragListener;
import org.solovyev.android.view.drag.DirectionDragButton;
import org.solovyev.android.view.drag.DragButton;
import org.solovyev.common.math.Point2d;
/**
* User: serso
* Date: 10/24/11
* Time: 9:52 PM
*/
public class EvalDragProcessor implements SimpleOnDragListener.DragProcessor {
public EvalDragProcessor() {
}
@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)) {
Locator.getInstance().getCalculator().simplify();
result = true;
}
}
return result;
}
}

View File

@@ -0,0 +1,35 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.content.SharedPreferences;
import jscl.NumeralBase;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
/**
* User: serso
* Date: 4/20/12
* Time: 5:03 PM
*/
public class NumeralBaseButtons {
public static void toggleNumericDigits(@NotNull Activity activity, @NotNull NumeralBase currentNumeralBase) {
for (NumeralBase numeralBase : NumeralBase.values()) {
if ( currentNumeralBase != numeralBase ) {
AndroidNumeralBase.valueOf(numeralBase).toggleButtons(false, activity);
}
}
AndroidNumeralBase.valueOf(currentNumeralBase).toggleButtons(true, activity);
}
public static void toggleNumericDigits(@NotNull Activity activity, @NotNull SharedPreferences preferences) {
if (CalculatorPreferences.Gui.hideNumeralBaseDigits.getPreference(preferences)) {
final NumeralBase nb = AndroidCalculatorEngine.Preferences.numeralBase.getPreference(preferences);
toggleNumericDigits(activity, nb);
} else {
// set HEX to show all digits
AndroidNumeralBase.valueOf(NumeralBase.hex).toggleButtons(true, activity);
}
}
}

View File

@@ -0,0 +1,107 @@
package org.solovyev.android.calculator;
import android.os.Parcel;
import android.os.Parcelable;
import jscl.math.Generic;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.jscl.JsclOperation;
/**
* User: serso
* Date: 11/18/12
* Time: 1:40 PM
*/
public final class ParcelableCalculatorDisplayViewState implements CalculatorDisplayViewState, Parcelable {
public static final Creator<ParcelableCalculatorDisplayViewState> CREATOR = new Creator<ParcelableCalculatorDisplayViewState>() {
@Override
public ParcelableCalculatorDisplayViewState createFromParcel(@NotNull Parcel in) {
return ParcelableCalculatorDisplayViewState.fromParcel(in);
}
@Override
public ParcelableCalculatorDisplayViewState[] newArray(int size) {
return new ParcelableCalculatorDisplayViewState[size];
}
};
@NotNull
private static ParcelableCalculatorDisplayViewState fromParcel(@NotNull Parcel in) {
final int selection = in.readInt();
final boolean valid = in.readInt() == 1;
final String stringResult = in.readString();
final String errorMessage = in.readString();
final JsclOperation operation = (JsclOperation) in.readSerializable();
CalculatorDisplayViewState calculatorDisplayViewState;
if (valid) {
calculatorDisplayViewState = CalculatorDisplayViewStateImpl.newValidState(operation, null, stringResult, selection);
} else {
calculatorDisplayViewState = CalculatorDisplayViewStateImpl.newErrorState(operation, errorMessage);
}
return new ParcelableCalculatorDisplayViewState(calculatorDisplayViewState);
}
@NotNull
private CalculatorDisplayViewState viewState;
public ParcelableCalculatorDisplayViewState(@NotNull CalculatorDisplayViewState viewState) {
this.viewState = viewState;
}
@Override
@NotNull
public String getText() {
return viewState.getText();
}
@Override
public int getSelection() {
return viewState.getSelection();
}
@Override
@Nullable
public Generic getResult() {
return viewState.getResult();
}
@Override
public boolean isValid() {
return viewState.isValid();
}
@Override
@Nullable
public String getErrorMessage() {
return viewState.getErrorMessage();
}
@Override
@NotNull
public JsclOperation getOperation() {
return viewState.getOperation();
}
@Override
@Nullable
public String getStringResult() {
return viewState.getStringResult();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeInt(viewState.getSelection());
out.writeInt(viewState.isValid() ? 1 : 0);
out.writeString(viewState.getStringResult());
out.writeString(viewState.getErrorMessage());
out.writeSerializable(viewState.getOperation());
}
}

View File

@@ -0,0 +1,61 @@
package org.solovyev.android.calculator;
import android.os.Parcel;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 11/18/12
* Time: 1:40 PM
*/
public final class ParcelableCalculatorEditorViewState implements CalculatorEditorViewState, Parcelable {
public static final Creator<ParcelableCalculatorEditorViewState> CREATOR = new Creator<ParcelableCalculatorEditorViewState>() {
@Override
public ParcelableCalculatorEditorViewState createFromParcel(@NotNull Parcel in) {
return ParcelableCalculatorEditorViewState.fromParcel(in);
}
@Override
public ParcelableCalculatorEditorViewState[] newArray(int size) {
return new ParcelableCalculatorEditorViewState[size];
}
};
@NotNull
private CalculatorEditorViewState viewState;
public ParcelableCalculatorEditorViewState(@NotNull CalculatorEditorViewState viewState) {
this.viewState = viewState;
}
@NotNull
private static ParcelableCalculatorEditorViewState fromParcel(@NotNull Parcel in) {
final String text = in.readString();
final int selection = in.readInt();
return new ParcelableCalculatorEditorViewState(CalculatorEditorViewStateImpl.newInstance(text, selection));
}
@Override
@NotNull
public String getText() {
return viewState.getText();
}
@Override
public int getSelection() {
return viewState.getSelection();
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeString(viewState.getText());
out.writeInt(viewState.getSelection());
}
}

View File

@@ -0,0 +1,27 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.about;
import android.os.Bundle;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorFragmentActivity;
import org.solovyev.android.calculator.R;
/**
* User: serso
* Date: 9/16/11
* Time: 11:52 PM
*/
public class CalculatorAboutActivity extends CalculatorFragmentActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityHelper().addTab(this, CalculatorFragmentType.about, null, R.id.main_layout);
getActivityHelper().addTab(this, CalculatorFragmentType.release_notes, null, R.id.main_layout);
}
}

View File

@@ -0,0 +1,34 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.about;
import android.os.Bundle;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.TextView;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.R;
/**
* User: serso
* Date: 12/24/11
* Time: 11:55 PM
*/
public class CalculatorAboutFragment extends CalculatorFragment {
public CalculatorAboutFragment() {
super(CalculatorFragmentType.about);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
final TextView about = (TextView) root.findViewById(R.id.aboutTextView);
about.setMovementMethod(LinkMovementMethod.getInstance());
}
}

View File

@@ -0,0 +1,76 @@
package org.solovyev.android.calculator.about;
import android.support.v4.app.Fragment;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorEditorFragment;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.help.CalculatorHelpFaqFragment;
import org.solovyev.android.calculator.help.CalculatorHelpHintsFragment;
import org.solovyev.android.calculator.help.CalculatorHelpScreensFragment;
import org.solovyev.android.calculator.history.CalculatorHistoryFragment;
import org.solovyev.android.calculator.history.CalculatorSavedHistoryFragment;
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsFragment;
import org.solovyev.android.calculator.math.edit.CalculatorOperatorsFragment;
import org.solovyev.android.calculator.math.edit.CalculatorVarsFragment;
import org.solovyev.android.calculator.plot.CalculatorPlotFragment;
/**
* User: Solovyev_S
* Date: 03.10.12
* Time: 11:30
*/
public enum CalculatorFragmentType {
editor(CalculatorEditorFragment.class, R.layout.calc_editor, R.string.editor),
//display(CalculatorHistoryFragment.class, "history", R.layout.history_fragment, R.string.c_history),
//keyboard(CalculatorHistoryFragment.class, "history", R.layout.history_fragment, R.string.c_history),
history(CalculatorHistoryFragment.class, R.layout.history_fragment, R.string.c_history),
saved_history(CalculatorSavedHistoryFragment.class, R.layout.history_fragment, R.string.c_saved_history),
variables(CalculatorVarsFragment.class, R.layout.vars_fragment, R.string.c_vars),
functions(CalculatorFunctionsFragment.class, R.layout.math_entities_fragment, R.string.c_functions),
operators(CalculatorOperatorsFragment.class, R.layout.math_entities_fragment, R.string.c_operators),
plotter(CalculatorPlotFragment.class, R.layout.plot_fragment, R.string.c_graph),
about(CalculatorAboutFragment.class, R.layout.about_fragment, R.string.c_about),
faq(CalculatorHelpFaqFragment.class, R.layout.help_faq_fragment, R.string.c_faq),
hints(CalculatorHelpHintsFragment.class, R.layout.help_hints_fragment, R.string.c_hints),
screens(CalculatorHelpScreensFragment.class, R.layout.help_screens_fragment, R.string.c_screens),
release_notes(CalculatorReleaseNotesFragment.class, R.layout.release_notes_fragment, R.string.c_release_notes);
@NotNull
private Class<? extends Fragment> fragmentClass;
private final int defaultLayoutId;
private int defaultTitleResId;
private CalculatorFragmentType(@NotNull Class<? extends Fragment> fragmentClass,
int defaultLayoutId,
int defaultTitleResId) {
this.fragmentClass = fragmentClass;
this.defaultLayoutId = defaultLayoutId;
this.defaultTitleResId = defaultTitleResId;
}
@NotNull
public String getFragmentTag() {
return this.name();
}
public int getDefaultTitleResId() {
return defaultTitleResId;
}
@NotNull
public Class<? extends Fragment> getFragmentClass() {
return fragmentClass;
}
public int getDefaultLayoutId() {
return defaultLayoutId;
}
@NotNull
public String createSubFragmentTag(@NotNull String subFragmentTag) {
return this.getFragmentTag() + "_" + subFragmentTag;
}
}

View File

@@ -0,0 +1,75 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.about;
import android.content.Context;
import android.os.Bundle;
import android.text.Html;
import android.text.method.LinkMovementMethod;
import android.view.View;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.R;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/25/11
* Time: 12:00 AM
*/
public class CalculatorReleaseNotesFragment extends CalculatorFragment {
public CalculatorReleaseNotesFragment() {
super(CalculatorFragmentType.release_notes);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
final TextView releaseNotes = (TextView) root.findViewById(R.id.releaseNotesTextView);
releaseNotes.setMovementMethod(LinkMovementMethod.getInstance());
releaseNotes.setText(Html.fromHtml(getReleaseNotes(this.getActivity())));
}
@NotNull
public static String getReleaseNotes(@NotNull Context context) {
return getReleaseNotes(context, 0);
}
@NotNull
public static String getReleaseNotes(@NotNull Context context, int minVersion) {
final StringBuilder result = new StringBuilder();
final String releaseNotesForTitle = context.getString(R.string.c_release_notes_for_title);
final int version = AndroidUtils.getAppVersionCode(context, CalculatorApplication.class.getPackage().getName());
final TextHelper textHelper = new TextHelper(context.getResources(), CalculatorApplication.class.getPackage().getName());
boolean first = true;
for ( int i = version; i >= minVersion; i-- ) {
String releaseNotesForVersion = textHelper.getText("c_release_notes_for_" + i);
if (!StringUtils.isEmpty(releaseNotesForVersion)){
assert releaseNotesForVersion != null;
if ( !first ) {
result.append("<br/><br/>");
} else {
first = false;
}
releaseNotesForVersion = releaseNotesForVersion.replace("\n", "<br/>");
result.append("<b>").append(releaseNotesForTitle).append(i).append("</b><br/><br/>");
result.append(releaseNotesForVersion);
}
}
return result.toString();
}
}

View File

@@ -0,0 +1,37 @@
package org.solovyev.android.calculator.about;
import android.content.Context;
import android.content.res.Resources;
import android.util.Log;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
/**
* User: serso
* Date: 4/20/12
* Time: 3:31 PM
*/
public class TextHelper {
@NotNull
public String packageName;
@NotNull
public Resources resources;
public TextHelper(@NotNull Resources resources, @NotNull String packageName) {
this.packageName = packageName;
this.resources = resources;
}
@Nullable
public String getText(@NotNull String stringName) {
final int stringId = this.resources.getIdentifier(stringName, "string", this.packageName);
try {
return resources.getString(stringId);
} catch (Resources.NotFoundException e) {
return null;
}
}
}

View File

@@ -0,0 +1,126 @@
package org.solovyev.android.calculator.external;
import android.content.Context;
import android.content.Intent;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.CalculatorDisplayChangeEventData;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEditorChangeEventData;
import org.solovyev.android.calculator.CalculatorEditorViewState;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventHolder;
import org.solovyev.android.calculator.CalculatorEventListener;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorUtils;
import org.solovyev.android.calculator.ParcelableCalculatorDisplayViewState;
import org.solovyev.android.calculator.ParcelableCalculatorEditorViewState;
import java.util.HashSet;
import java.util.Set;
/**
* User: serso
* Date: 10/19/12
* Time: 11:11 PM
*/
public class AndroidExternalListenersContainer implements CalculatorExternalListenersContainer, CalculatorEventListener {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
public static final String EVENT_ID_EXTRA = "eventId";
public static final String INIT_ACTION = "org.solovyev.android.calculator.INIT";
public static final String INIT_ACTION_CREATE_VIEW_EXTRA = "createView";
public static final String EDITOR_STATE_CHANGED_ACTION = "org.solovyev.android.calculator.EDITOR_STATE_CHANGED";
public static final String EDITOR_STATE_EXTRA = "editorState";
public static final String DISPLAY_STATE_CHANGED_ACTION = "org.solovyev.android.calculator.DISPLAY_STATE_CHANGED";
public static final String DISPLAY_STATE_EXTRA = "displayState";
private static final String TAG = "Calculator++ External Listener Helper";
private final Set<Class<?>> externalListeners = new HashSet<Class<?>>();
@NotNull
private final CalculatorEventHolder lastEvent = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
public AndroidExternalListenersContainer(@NotNull Calculator calculator) {
calculator.addCalculatorEventListener(this);
}
public void onEditorStateChanged(@NotNull Context context,
@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorEditorViewState editorViewState) {
for (Class<?> externalListener : externalListeners) {
final Intent intent = new Intent(EDITOR_STATE_CHANGED_ACTION);
intent.setClass(context, externalListener);
intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
intent.putExtra(EDITOR_STATE_EXTRA, (Parcelable) new ParcelableCalculatorEditorViewState(editorViewState));
context.sendBroadcast(intent);
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Editor state changed broadcast sent");
}
}
private void onDisplayStateChanged(@NotNull Context context,
@NotNull CalculatorEventData calculatorEventData,
@NotNull CalculatorDisplayViewState displayViewState) {
for (Class<?> externalListener : externalListeners) {
final Intent intent = new Intent(DISPLAY_STATE_CHANGED_ACTION);
intent.setClass(context, externalListener);
intent.putExtra(EVENT_ID_EXTRA, calculatorEventData.getEventId());
intent.putExtra(DISPLAY_STATE_EXTRA, (Parcelable) new ParcelableCalculatorDisplayViewState(displayViewState));
context.sendBroadcast(intent);
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Display state changed broadcast sent");
}
}
@Override
public void addExternalListener(@NotNull Class<?> externalCalculatorClass) {
externalListeners.add(externalCalculatorClass);
}
@Override
public boolean removeExternalListener(@NotNull Class<?> externalCalculatorClass) {
return externalListeners.remove(externalCalculatorClass);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
final CalculatorEventHolder.Result result = lastEvent.apply(calculatorEventData);
if (result.isNewAfter()) {
switch (calculatorEventType) {
case editor_state_changed_light:
case editor_state_changed:
final CalculatorEditorChangeEventData editorChangeData = (CalculatorEditorChangeEventData) data;
final CalculatorEditorViewState newEditorState = editorChangeData.getNewValue();
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Editor state changed: " + newEditorState.getText());
onEditorStateChanged(CalculatorApplication.getInstance(), calculatorEventData, newEditorState);
break;
case display_state_changed:
final CalculatorDisplayChangeEventData displayChangeData = (CalculatorDisplayChangeEventData) data;
final CalculatorDisplayViewState newDisplayState = displayChangeData.getNewValue();
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Display state changed: " + newDisplayState.getText());
onDisplayStateChanged(CalculatorApplication.getInstance(), calculatorEventData, newDisplayState);
break;
}
}
}
}

View File

@@ -0,0 +1,92 @@
package org.solovyev.android.calculator.external;
import android.content.Context;
import android.content.Intent;
import android.os.Parcelable;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEditorViewState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.common.MutableObject;
/**
* User: serso
* Date: 11/20/12
* Time: 10:34 PM
*/
public class DefaultExternalCalculatorIntentHandler implements ExternalCalculatorIntentHandler {
private static final String TAG = ExternalCalculatorIntentHandler.class.getSimpleName();
@NotNull
private final MutableObject<Long> lastDisplayEventId = new MutableObject<Long>(0L);
@NotNull
private final MutableObject<Long> lastEditorEventId = new MutableObject<Long>(0L);
@NotNull
private final ExternalCalculatorStateUpdater stateUpdater;
public DefaultExternalCalculatorIntentHandler(@NotNull ExternalCalculatorStateUpdater stateUpdater) {
this.stateUpdater = stateUpdater;
}
@Override
public void onIntent(@NotNull Context context, @NotNull Intent intent) {
if (AndroidExternalListenersContainer.EDITOR_STATE_CHANGED_ACTION.equals(intent.getAction())) {
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Editor state changed broadcast received!");
final Long eventId = intent.getLongExtra(AndroidExternalListenersContainer.EVENT_ID_EXTRA, 0L);
boolean updateEditor = false;
synchronized (lastEditorEventId) {
if (eventId > lastEditorEventId.getObject()) {
lastEditorEventId.setObject(eventId);
updateEditor = true;
}
}
if (updateEditor) {
final Parcelable object = intent.getParcelableExtra(AndroidExternalListenersContainer.EDITOR_STATE_EXTRA);
if (object instanceof CalculatorEditorViewState) {
onEditorStateChanged(context, (CalculatorEditorViewState) object);
}
}
} else if (AndroidExternalListenersContainer.DISPLAY_STATE_CHANGED_ACTION.equals(intent.getAction())) {
Locator.getInstance().getNotifier().showDebugMessage(TAG, "Display state changed broadcast received!");
final Long eventId = intent.getLongExtra(AndroidExternalListenersContainer.EVENT_ID_EXTRA, 0L);
boolean updateDisplay = false;
synchronized (lastDisplayEventId) {
if (eventId > lastDisplayEventId.getObject()) {
lastDisplayEventId.setObject(eventId);
updateDisplay = true;
}
}
if (updateDisplay) {
final Parcelable object = intent.getParcelableExtra(AndroidExternalListenersContainer.DISPLAY_STATE_EXTRA);
if (object instanceof CalculatorDisplayViewState) {
onDisplayStateChanged(context, (CalculatorDisplayViewState) object);
}
}
} else if ( AndroidExternalListenersContainer.INIT_ACTION.equals(intent.getAction()) ) {
updateState(context, Locator.getInstance().getEditor().getViewState(), Locator.getInstance().getDisplay().getViewState());
}
}
protected void updateState(@NotNull Context context,
@NotNull CalculatorEditorViewState editorViewState,
@NotNull CalculatorDisplayViewState displayViewState) {
stateUpdater.updateState(context, editorViewState, displayViewState);
}
protected void onDisplayStateChanged(@NotNull Context context, @NotNull CalculatorDisplayViewState displayViewState) {
updateState(context, Locator.getInstance().getEditor().getViewState(), displayViewState);
}
protected void onEditorStateChanged(@NotNull Context context, @NotNull CalculatorEditorViewState editorViewState) {
updateState(context, editorViewState, Locator.getInstance().getDisplay().getViewState());
}
}

View File

@@ -0,0 +1,15 @@
package org.solovyev.android.calculator.external;
import android.content.Context;
import android.content.Intent;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 11/20/12
* Time: 10:33 PM
*/
public interface ExternalCalculatorIntentHandler {
void onIntent(@NotNull Context context, @NotNull Intent intent);
}

View File

@@ -0,0 +1,18 @@
package org.solovyev.android.calculator.external;
import android.content.Context;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEditorViewState;
/**
* User: serso
* Date: 11/20/12
* Time: 10:42 PM
*/
public interface ExternalCalculatorStateUpdater {
void updateState(@NotNull Context context,
@NotNull CalculatorEditorViewState editorState,
@NotNull CalculatorDisplayViewState displayState);
}

View File

@@ -0,0 +1,327 @@
package org.solovyev.android.calculator.function;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Parcel;
import android.os.Parcelable;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.math.function.CustomFunction;
import jscl.math.function.Function;
import jscl.math.function.IFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsActivity;
import org.solovyev.android.calculator.math.edit.CalculatorFunctionsFragment;
import org.solovyev.android.calculator.math.edit.MathEntityRemover;
import org.solovyev.android.calculator.model.AFunction;
import org.solovyev.android.sherlock.AndroidSherlockUtils;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
/**
* User: serso
* Date: 11/13/12
* Time: 11:34 PM
*/
public class FunctionEditDialogFragment extends DialogFragment implements CalculatorEventListener {
private static final String INPUT = "input";
@NotNull
private Input input;
public FunctionEditDialogFragment() {
this(Input.newInstance());
}
public FunctionEditDialogFragment(@NotNull Input input) {
this.input = input;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
final View result = inflater.inflate(R.layout.function_edit, container, false);
if (savedInstanceState != null) {
final Parcelable input = savedInstanceState.getParcelable(INPUT);
if ( input instanceof Input ) {
this.input = (Input)input;
}
}
return result;
}
@Override
public void onViewCreated(@NotNull View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
final FunctionParamsView paramsView = (FunctionParamsView) root.findViewById(R.id.function_params_layout);
final AFunction.Builder builder;
final AFunction function = input.getFunction();
if (function != null) {
builder = new AFunction.Builder(function);
} else {
builder = new AFunction.Builder();
}
final List<String> parameterNames = input.getParameterNames();
if (parameterNames != null) {
paramsView.init(parameterNames);
} else {
paramsView.init();
}
final EditText editName = (EditText) root.findViewById(R.id.function_edit_name);
// show soft keyboard automatically
editName.requestFocus();
editName.setText(input.getName());
final EditText editDescription = (EditText) root.findViewById(R.id.function_edit_description);
editDescription.setText(input.getDescription());
final EditText editContent = (EditText) root.findViewById(R.id.function_edit_value);
editContent.setText(input.getContent());
root.findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
root.findViewById(R.id.save_button).setOnClickListener(new FunctionEditorSaver(builder, function, root, Locator.getInstance().getEngine().getFunctionsRegistry(), this));
if ( function == null ) {
// CREATE MODE
getDialog().setTitle(R.string.function_create_function);
root.findViewById(R.id.remove_button).setVisibility(View.GONE);
} else {
// EDIT MODE
getDialog().setTitle(R.string.function_edit_function);
final Function customFunction = new CustomFunction.Builder(function).create();
root.findViewById(R.id.remove_button).setOnClickListener(MathEntityRemover.newFunctionRemover(customFunction, null, this.getActivity(), FunctionEditDialogFragment.this));
}
}
@Override
public void onSaveInstanceState(@NotNull Bundle out) {
super.onSaveInstanceState(out);
out.putParcelable(INPUT, FunctionEditorSaver.readInput(input.getFunction(), getView()));
}
@Override
public void onResume() {
super.onResume();
Locator.getInstance().getCalculator().addCalculatorEventListener(this);
}
@Override
public void onPause() {
Locator.getInstance().getCalculator().removeCalculatorEventListener(this);
super.onPause();
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case function_removed:
case function_added:
case function_changed:
if ( calculatorEventData.getSource() == FunctionEditDialogFragment.this ) {
dismiss();
}
break;
}
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
public static void showDialog(@NotNull Input input, @NotNull Context context) {
if (context instanceof SherlockFragmentActivity) {
FunctionEditDialogFragment.showDialog(input, ((SherlockFragmentActivity) context).getSupportFragmentManager());
} else {
final Intent intent = new Intent(context, CalculatorFunctionsActivity.class);
intent.putExtra(CalculatorFunctionsFragment.CREATE_FUNCTION_EXTRA, input);
context.startActivity(intent);
}
}
public static void showDialog(@NotNull Input input, @NotNull FragmentManager fm) {
AndroidSherlockUtils.showDialog(new FunctionEditDialogFragment(input), "function-editor", fm);
}
public static class Input implements Parcelable {
public static final Parcelable.Creator<Input> CREATOR = new Creator<Input>() {
@Override
public Input createFromParcel(@NotNull Parcel in) {
return Input.fromParcel(in);
}
@Override
public Input[] newArray(int size) {
return new Input[size];
}
};
private static final Parcelable.Creator<String> STRING_CREATOR = new Creator<String>() {
@Override
public String createFromParcel(@NotNull Parcel in) {
return in.readString();
}
@Override
public String[] newArray(int size) {
return new String[size];
}
};
@NotNull
private static Input fromParcel(@NotNull Parcel in) {
final Input result = new Input();
result.name = in.readString();
result.content = in.readString();
result.description = in.readString();
final List<String> parameterNames = new ArrayList<String>();
in.readTypedList(parameterNames, STRING_CREATOR);
result.parameterNames = parameterNames;
result.function = (AFunction) in.readSerializable();
return result;
}
@Nullable
private AFunction function;
@Nullable
private String name;
@Nullable
private String content;
@Nullable
private String description;
@Nullable
private List<String> parameterNames;
private Input() {
}
@NotNull
public static Input newInstance() {
return new Input();
}
@NotNull
public static Input newFromFunction(@NotNull IFunction function) {
final Input result = new Input();
result.function = AFunction.fromIFunction(function);
return result;
}
@NotNull
public static Input newInstance(@Nullable IFunction function,
@Nullable String name,
@Nullable String value,
@Nullable String description,
@NotNull List<String> parameterNames) {
final Input result = new Input();
if (function != null) {
result.function = AFunction.fromIFunction(function);
}
result.name = name;
result.content = value;
result.description = description;
result.parameterNames = new ArrayList<String>(parameterNames);
return result;
}
@Nullable
public AFunction getFunction() {
return function;
}
@Nullable
public String getName() {
return name == null ? (function == null ? null : function.getName()) : name;
}
@Nullable
public String getContent() {
return content == null ? (function == null ? null : function.getContent()) : content;
}
@Nullable
public String getDescription() {
return description == null ? (function == null ? null : function.getDescription()) : description;
}
@Nullable
public List<String> getParameterNames() {
return parameterNames == null ? (function == null ? null : function.getParameterNames()) : parameterNames;
}
@Override
public int describeContents() {
return 0;
}
@Override
public void writeToParcel(@NotNull Parcel out, int flags) {
out.writeString(name);
out.writeString(content);
out.writeString(description);
out.writeList(parameterNames);
out.writeSerializable(function);
}
@NotNull
public static Input newFromDisplay(@NotNull CalculatorDisplayViewState viewState) {
final Input result = new Input();
result.content = viewState.getText();
final Generic generic = viewState.getResult();
if ( generic != null ) {
final Set<Constant> constants = CalculatorUtils.getNotSystemConstants(generic);
final List<String> parameterNames = new ArrayList<String>(constants.size());
for (Constant constant : constants) {
parameterNames.add(constant.getName());
}
result.parameterNames = parameterNames;
}
return result;
}
}
}

View File

@@ -0,0 +1,180 @@
package org.solovyev.android.calculator.function;
import android.view.View;
import android.widget.EditText;
import jscl.CustomFunctionCalculationException;
import jscl.math.function.CustomFunction;
import jscl.math.function.Function;
import jscl.math.function.IFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorFunctionsMathRegistry;
import org.solovyev.android.calculator.CalculatorMathRegistry;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.math.edit.VarEditorSaver;
import org.solovyev.android.calculator.model.AFunction;
import org.solovyev.android.calculator.model.MathEntityBuilder;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
import java.util.Collections;
import java.util.List;
public class FunctionEditorSaver implements View.OnClickListener {
@NotNull
private final Object source;
@NotNull
private final AFunction.Builder builder;
@Nullable
private final IFunction editedInstance;
@NotNull
private final View view;
@NotNull
private final CalculatorMathRegistry<Function> mathRegistry;
public FunctionEditorSaver(@NotNull AFunction.Builder builder,
@Nullable IFunction editedInstance,
@NotNull View view,
@NotNull CalculatorMathRegistry<Function> registry,
@NotNull Object source) {
this.builder = builder;
this.editedInstance = editedInstance;
this.view = view;
this.mathRegistry = registry;
this.source = source;
}
@Override
public void onClick(View v) {
final Integer error;
final FunctionEditDialogFragment.Input input = readInput(null, view);
final String name = input.getName();
final String content = input.getContent();
final String description = input.getDescription();
List<String> parameterNames = input.getParameterNames();
if ( parameterNames == null ) {
parameterNames = Collections.emptyList();
}
if (VarEditorSaver.isValidName(name)) {
boolean canBeSaved = false;
final Function entityFromRegistry = mathRegistry.get(name);
if (entityFromRegistry == null) {
canBeSaved = true;
} else if (editedInstance != null && entityFromRegistry.getId().equals(editedInstance.getId())) {
canBeSaved = true;
}
if (canBeSaved) {
if (validateParameters(parameterNames)) {
if (!StringUtils.isEmpty(content)) {
builder.setParameterNames(parameterNames);
builder.setName(name);
builder.setDescription(description);
builder.setValue(content);
error = null;
} else {
error = R.string.function_is_empty;
}
} else {
error = R.string.function_param_not_empty;
}
} else {
error = R.string.function_already_exists;
}
} else {
error = R.string.function_name_is_not_valid;
}
if (error != null) {
Locator.getInstance().getNotifier().showMessage(error, MessageType.error);
} else {
try {
CalculatorFunctionsMathRegistry.saveFunction(mathRegistry, new BuilderAdapter(builder), editedInstance, source, true);
} catch (CustomFunctionCalculationException e) {
Locator.getInstance().getNotifier().showMessage(e);
} catch (AFunction.Builder.CreationException e) {
Locator.getInstance().getNotifier().showMessage(e);
}
}
}
@NotNull
public static FunctionEditDialogFragment.Input readInput(@Nullable IFunction function, @NotNull View root) {
final EditText editName = (EditText) root.findViewById(R.id.function_edit_name);
String name = editName.getText().toString();
final EditText editValue = (EditText) root.findViewById(R.id.function_edit_value);
String content = editValue.getText().toString();
final EditText editDescription = (EditText) root.findViewById(R.id.function_edit_description);
String description = editDescription.getText().toString();
final FunctionParamsView editParams = (FunctionParamsView) root.findViewById(R.id.function_params_layout);
List<String> parameterNames = editParams.getParameterNames();
return FunctionEditDialogFragment.Input.newInstance(function, name, content, description, parameterNames);
}
private boolean validateParameters(@NotNull List<String> parameterNames) {
for (String parameterName : parameterNames) {
if ( !VarEditorSaver.isValidName(parameterName) ) {
return false;
}
}
return true;
}
private static final class BuilderAdapter implements MathEntityBuilder<Function> {
@NotNull
private final AFunction.Builder nestedBuilder;
public BuilderAdapter(@NotNull AFunction.Builder nestedBuilder) {
this.nestedBuilder = nestedBuilder;
}
@NotNull
@Override
public MathEntityBuilder<Function> setName(@NotNull String name) {
nestedBuilder.setName(name);
return this;
}
@NotNull
@Override
public MathEntityBuilder<Function> setDescription(@Nullable String description) {
nestedBuilder.setDescription(description);
return this;
}
@NotNull
@Override
public MathEntityBuilder<Function> setValue(@Nullable String value) {
nestedBuilder.setValue(value);
return this;
}
@NotNull
@Override
public Function create() {
final AFunction function = nestedBuilder.create();
return new CustomFunction.Builder(function).create();
}
}
}

View File

@@ -0,0 +1,34 @@
package org.solovyev.android.calculator.function;
import android.content.Context;
import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.AbsSavedState;
import android.widget.EditText;
public class FunctionParamEditText extends EditText {
public FunctionParamEditText(Context context) {
super(context);
}
public FunctionParamEditText(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FunctionParamEditText(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
// we restore state manually outside
@Override
public Parcelable onSaveInstanceState() {
super.onSaveInstanceState();
return AbsSavedState.EMPTY_STATE;
}
@Override
public void onRestoreInstanceState(Parcelable state) {
super.onRestoreInstanceState(null);
}
}

View File

@@ -0,0 +1,189 @@
package org.solovyev.android.calculator.function;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import jscl.text.MutableInt;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.R;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
public class FunctionParamsView extends LinearLayout {
@NotNull
private final MutableInt paramsCount = new MutableInt(0);
@NotNull
private final List<Integer> paramIds = new ArrayList<Integer>(10);
private static final String PARAM_TAG_PREFIX = "function_param_";
public FunctionParamsView(Context context) {
super(context);
}
public FunctionParamsView(Context context, AttributeSet attrs) {
super(context, attrs);
}
public FunctionParamsView(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init() {
init(Collections.<String>emptyList());
}
public void init(@NotNull List<String> parameters) {
this.setOrientation(VERTICAL);
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View addParamView = inflater.inflate(R.layout.function_add_param, null);
final View addParamButton = addParamView.findViewById(R.id.function_add_param_button);
addParamButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
addParam(null);
}
});
this.addView(addParamView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
for (String parameter : parameters) {
addParam(parameter);
}
}
public void addParam(@Nullable String name) {
synchronized (paramsCount) {
final LayoutInflater inflater = (LayoutInflater) getContext().getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final Integer id = paramsCount.intValue();
final View editParamView = inflater.inflate(R.layout.function_edit_param, null);
editParamView.setTag(getParamTag(id));
final EditText paramNameEditText = (EditText) editParamView.findViewById(R.id.function_param_edit_text);
paramNameEditText.setText(name);
final View removeParamButton = editParamView.findViewById(R.id.function_remove_param_button);
removeParamButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
removeParam(id);
}
});
final View upParamButton = editParamView.findViewById(R.id.function_up_param_button);
upParamButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
upParam(id);
}
});
final View downParamButton = editParamView.findViewById(R.id.function_down_param_button);
downParamButton.setOnClickListener(new OnClickListener() {
@Override
public void onClick(View v) {
downParam(id);
}
});
this.addView(editParamView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT));
paramIds.add(id);
paramsCount.increment();
}
}
private void downParam(@NotNull Integer id) {
synchronized (paramsCount) {
int index = paramIds.indexOf(id);
if (index < paramIds.size() - 1) {
swap(index, index + 1);
}
}
}
private void upParam(@NotNull Integer id) {
synchronized (paramsCount) {
int index = paramIds.indexOf(id);
if (index > 0) {
swap(index, index - 1);
}
}
}
private void swap(int index1, int index2) {
final View editParamView1 = getParamView(paramIds.get(index1));
final View editParamView2 = getParamView(paramIds.get(index2));
if (editParamView1 != null && editParamView2 != null) {
final EditText paramNameEditText1 = (EditText) editParamView1.findViewById(R.id.function_param_edit_text);
final EditText paramNameEditText2 = (EditText) editParamView2.findViewById(R.id.function_param_edit_text);
swap(paramNameEditText1, paramNameEditText2);
}
}
private void swap(@NotNull TextView first,
@NotNull TextView second) {
final CharSequence tmp = first.getText();
first.setText(second.getText());
second.setText(tmp);
}
@Nullable
private View getParamView(@NotNull Integer id) {
final String tag = getParamTag(id);
return this.findViewWithTag(tag);
}
@NotNull
private String getParamTag(@NotNull Integer index) {
return PARAM_TAG_PREFIX + index;
}
public void removeParam(@NotNull Integer id) {
synchronized (paramsCount) {
if (paramIds.contains(id)) {
final View editParamView = getParamView(id);
if (editParamView != null) {
this.removeView(editParamView);
paramIds.remove(id);
}
}
}
}
@NotNull
public List<String> getParameterNames() {
synchronized (paramsCount) {
final List<String> result = new ArrayList<String>(paramsCount.intValue());
for (Integer id : paramIds) {
final View paramView = getParamView(id);
if ( paramView != null ) {
final EditText paramNameEditText = (EditText) paramView.findViewById(R.id.function_param_edit_text);
result.add(paramNameEditText.getText().toString());
}
}
return result;
}
}
}

View File

@@ -0,0 +1,30 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.help;
import android.os.Bundle;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorFragmentActivity;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 11/19/11
* Time: 11:35 AM
*/
public class CalculatorHelpActivity extends CalculatorFragmentActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getActivityHelper().addTab(this, CalculatorFragmentType.faq, null, R.id.main_layout);
getActivityHelper().addTab(this, CalculatorFragmentType.hints, null, R.id.main_layout);
getActivityHelper().addTab(this, CalculatorFragmentType.screens, null, R.id.main_layout);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.help;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 11/19/11
* Time: 11:37 AM
*/
public class CalculatorHelpFaqFragment extends CalculatorFragment {
public CalculatorHelpFaqFragment() {
super(CalculatorFragmentType.faq);
}
}

View File

@@ -0,0 +1,22 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.help;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 11/19/11
* Time: 11:37 AM
*/
public class CalculatorHelpHintsFragment extends CalculatorFragment {
public CalculatorHelpHintsFragment() {
super(CalculatorFragmentType.hints);
}
}

View File

@@ -0,0 +1,23 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.help;
import org.solovyev.android.calculator.CalculatorFragment;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 11/19/11
* Time: 11:38 AM
*/
public class CalculatorHelpScreensFragment extends CalculatorFragment {
public CalculatorHelpScreensFragment() {
super(CalculatorFragmentType.screens);
}
}

View File

@@ -0,0 +1,358 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.ListView;
import com.actionbarsherlock.app.SherlockListFragment;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.jscl.JsclOperation;
import org.solovyev.android.menu.*;
import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.equals.Equalizer;
import org.solovyev.common.filter.Filter;
import org.solovyev.common.filter.FilterRule;
import org.solovyev.common.filter.FilterRulesChain;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
/**
* User: serso
* Date: 10/15/11
* Time: 1:13 PM
*/
public abstract class AbstractCalculatorHistoryFragment extends SherlockListFragment implements CalculatorEventListener {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
@NotNull
private static final String TAG = "CalculatorHistoryFragment";
public static final Comparator<CalculatorHistoryState> COMPARATOR = new Comparator<CalculatorHistoryState>() {
@Override
public int compare(CalculatorHistoryState state1, CalculatorHistoryState state2) {
if (state1.isSaved() == state2.isSaved()) {
long l = state2.getTime() - state1.getTime();
return l > 0l ? 1 : (l < 0l ? -1 : 0);
} else if (state1.isSaved()) {
return -1;
} else if (state2.isSaved()) {
return 1;
}
return 0;
}
};
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private ArrayAdapter<CalculatorHistoryState> adapter;
@NotNull
private CalculatorFragmentHelper fragmentHelper;
private ActivityMenu<Menu, MenuItem> menu = ListActivityMenu.fromResource(org.solovyev.android.calculator.R.menu.history_menu, HistoryMenu.class, SherlockMenuHelper.getInstance());
protected AbstractCalculatorHistoryFragment(@NotNull CalculatorFragmentType fragmentType) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(fragmentType.getDefaultLayoutId(), fragmentType.getDefaultTitleResId(), false);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
fragmentHelper.onCreate(this);
setHasOptionsMenu(true);
logDebug("onCreate");
}
private int logDebug(@NotNull String msg) {
return Log.d(TAG + ": " + getTag(), msg);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
logDebug("onViewCreated");
fragmentHelper.onViewCreated(this, root);
adapter = new HistoryArrayAdapter(this.getActivity(), getItemLayoutId(), org.solovyev.android.calculator.R.id.history_item, new ArrayList<CalculatorHistoryState>());
setListAdapter(adapter);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
useHistoryItem((CalculatorHistoryState) parent.getItemAtPosition(position));
}
});
lv.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, final int position, long id) {
final CalculatorHistoryState historyState = (CalculatorHistoryState) parent.getItemAtPosition(position);
final Context context = getActivity();
final HistoryItemMenuData data = new HistoryItemMenuData(historyState, adapter);
final List<HistoryItemMenuItem> menuItems = CollectionsUtils.asList(HistoryItemMenuItem.values());
if (historyState.isSaved()) {
menuItems.remove(HistoryItemMenuItem.save);
} else {
if (isAlreadySaved(historyState)) {
menuItems.remove(HistoryItemMenuItem.save);
}
menuItems.remove(HistoryItemMenuItem.remove);
menuItems.remove(HistoryItemMenuItem.edit);
}
if (historyState.getDisplayState().isValid() && StringUtils.isEmpty(historyState.getDisplayState().getEditorState().getText())) {
menuItems.remove(HistoryItemMenuItem.copy_result);
}
final AMenuBuilder<HistoryItemMenuItem, HistoryItemMenuData> menuBuilder = AMenuBuilder.newInstance(context, MenuImpl.newInstance(menuItems));
menuBuilder.create(data).show();
return true;
}
});
}
@Override
public void onResume() {
super.onResume();
this.fragmentHelper.onResume(this);
updateAdapter();
}
@Override
public void onPause() {
this.fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onDestroy() {
logDebug("onDestroy");
fragmentHelper.onDestroy(this);
super.onDestroy();
}
protected abstract int getItemLayoutId();
private void updateAdapter() {
final List<CalculatorHistoryState> historyList = getHistoryList();
final ArrayAdapter<CalculatorHistoryState> adapter = getAdapter();
try {
adapter.setNotifyOnChange(false);
adapter.clear();
for (CalculatorHistoryState historyState : historyList) {
adapter.add(historyState);
}
} finally {
adapter.setNotifyOnChange(true);
}
adapter.notifyDataSetChanged();
}
public static boolean isAlreadySaved(@NotNull CalculatorHistoryState historyState) {
assert !historyState.isSaved();
boolean result = false;
try {
historyState.setSaved(true);
if ( CollectionsUtils.contains(historyState, Locator.getInstance().getHistory().getSavedHistory(), new Equalizer<CalculatorHistoryState>() {
@Override
public boolean equals(@Nullable CalculatorHistoryState first, @Nullable CalculatorHistoryState second) {
return first != null && second != null &&
first.getTime() == second.getTime() &&
first.getDisplayState().equals(second.getDisplayState()) &&
first.getEditorState().equals(second.getEditorState());
}
}) ) {
result = true;
}
} finally {
historyState.setSaved(false);
}
return result;
}
public static void useHistoryItem(@NotNull final CalculatorHistoryState historyState) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.use_history_state, historyState);
}
@NotNull
private List<CalculatorHistoryState> getHistoryList() {
final List<CalculatorHistoryState> calculatorHistoryStates = getHistoryItems();
Collections.sort(calculatorHistoryStates, COMPARATOR);
final FilterRulesChain<CalculatorHistoryState> filterRulesChain = new FilterRulesChain<CalculatorHistoryState>();
filterRulesChain.addFilterRule(new FilterRule<CalculatorHistoryState>() {
@Override
public boolean isFiltered(CalculatorHistoryState object) {
return object == null || StringUtils.isEmpty(object.getEditorState().getText());
}
});
new Filter<CalculatorHistoryState>(filterRulesChain).filter(calculatorHistoryStates.iterator());
return calculatorHistoryStates;
}
@NotNull
protected abstract List<CalculatorHistoryState> getHistoryItems();
@NotNull
public static String getHistoryText(@NotNull CalculatorHistoryState state) {
final StringBuilder result = new StringBuilder();
result.append(state.getEditorState().getText());
result.append(getIdentitySign(state.getDisplayState().getJsclOperation()));
final String expressionResult = state.getDisplayState().getEditorState().getText();
if (expressionResult != null) {
result.append(expressionResult);
}
return result.toString();
}
@NotNull
private static String getIdentitySign(@NotNull JsclOperation jsclOperation) {
return jsclOperation == JsclOperation.simplify ? "" : "=";
}
protected abstract void clearHistory();
@NotNull
protected ArrayAdapter<CalculatorHistoryState> getAdapter() {
return adapter;
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case history_state_added:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
logDebug("onCalculatorEvent");
updateAdapter();
}
});
break;
case clear_history_requested:
getActivity().runOnUiThread(new Runnable() {
@Override
public void run() {
clearHistory();
}
});
break;
}
}
/*
**********************************************************************
*
* MENU
*
**********************************************************************
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
this.menu.onCreateOptionsMenu(this.getActivity(), menu);
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
this.menu.onPrepareOptionsMenu(this.getActivity(), menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return this.menu.onOptionsItemSelected(this.getActivity(), item);
}
private static enum HistoryMenu implements IdentifiableMenuItem<MenuItem> {
clear_history(org.solovyev.android.calculator.R.id.history_menu_clear_history) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.clear_history_requested, null);
}
};
private final int itemId;
HistoryMenu(int itemId) {
this.itemId = itemId;
}
@NotNull
@Override
public Integer getItemId() {
return this.itemId;
}
}
}

View File

@@ -0,0 +1,158 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Calculator;
import org.solovyev.android.calculator.CalculatorEventData;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.R;
import org.solovyev.common.history.HistoryAction;
import java.util.List;
/**
* User: serso
* Date: 10/9/11
* Time: 6:35 PM
*/
public class AndroidCalculatorHistory implements CalculatorHistory {
@NotNull
private final CalculatorHistoryImpl calculatorHistory;
@NotNull
private final Context context;
public AndroidCalculatorHistory(@NotNull Application application, @NotNull Calculator calculator) {
this.context = application;
calculatorHistory = new CalculatorHistoryImpl(calculator);
}
@Override
public void load() {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences != null) {
final String value = preferences.getString(context.getString(R.string.p_calc_history), null);
if (value != null) {
calculatorHistory.fromXml(value);
}
}
}
public void save() {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
editor.putString(context.getString(R.string.p_calc_history), calculatorHistory.toXml());
editor.commit();
}
public void clearSavedHistory() {
calculatorHistory.clearSavedHistory();
save();
}
public void removeSavedHistory(@NotNull CalculatorHistoryState historyState) {
historyState.setSaved(false);
calculatorHistory.removeSavedHistory(historyState);
save();
}
@Override
public boolean isEmpty() {
return calculatorHistory.isEmpty();
}
@Override
public CalculatorHistoryState getLastHistoryState() {
return calculatorHistory.getLastHistoryState();
}
@Override
public boolean isUndoAvailable() {
return calculatorHistory.isUndoAvailable();
}
@Override
public CalculatorHistoryState undo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.undo(currentState);
}
@Override
public boolean isRedoAvailable() {
return calculatorHistory.isRedoAvailable();
}
@Override
public CalculatorHistoryState redo(@Nullable CalculatorHistoryState currentState) {
return calculatorHistory.redo(currentState);
}
@Override
public boolean isActionAvailable(@NotNull HistoryAction historyAction) {
return calculatorHistory.isActionAvailable(historyAction);
}
@Override
public CalculatorHistoryState doAction(@NotNull HistoryAction historyAction, @Nullable CalculatorHistoryState currentState) {
return calculatorHistory.doAction(historyAction, currentState);
}
@Override
public void addState(@Nullable CalculatorHistoryState currentState) {
calculatorHistory.addState(currentState);
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates() {
return calculatorHistory.getStates();
}
@NotNull
@Override
public List<CalculatorHistoryState> getStates(boolean includeIntermediateStates) {
return calculatorHistory.getStates(includeIntermediateStates);
}
@Override
public void clear() {
calculatorHistory.clear();
}
@NotNull
public List<CalculatorHistoryState> getSavedHistory() {
return calculatorHistory.getSavedHistory();
}
@NotNull
public CalculatorHistoryState addSavedState(@NotNull CalculatorHistoryState historyState) {
return calculatorHistory.addSavedState(historyState);
}
@Override
public void fromXml(@NotNull String xml) {
calculatorHistory.fromXml(xml);
}
@Override
public String toXml() {
return calculatorHistory.toXml();
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
calculatorHistory.onCalculatorEvent(calculatorEventData, calculatorEventType, data);
}
}

View File

@@ -0,0 +1,71 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 12/18/11
* Time: 7:37 PM
*/
public class CalculatorHistoryActivity extends SherlockFragmentActivity implements CalculatorEventListener {
@NotNull
private final CalculatorActivityHelper activityHelper = CalculatorApplication.getInstance().createActivityHelper(R.layout.main_empty, CalculatorHistoryActivity.class.getSimpleName());
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityHelper.onCreate(this, savedInstanceState);
activityHelper.addTab(this, CalculatorFragmentType.history, null, R.id.main_layout);
activityHelper.addTab(this, CalculatorFragmentType.saved_history, null, R.id.main_layout);
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
@Override
protected void onResume() {
super.onResume();
activityHelper.onResume(this);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
activityHelper.onDestroy(this);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
if ( calculatorEventType == CalculatorEventType.use_history_state ) {
this.finish();
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorPreferences;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:39 PM
*/
public class CalculatorHistoryFragment extends AbstractCalculatorHistoryFragment {
public CalculatorHistoryFragment() {
super(CalculatorFragmentType.history);
}
@Override
protected int getItemLayoutId() {
return R.layout.history_item;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
final boolean showIntermediateCalculations = CalculatorPreferences.History.showIntermediateCalculations.getPreference(PreferenceManager.getDefaultSharedPreferences(getActivity()));
return new ArrayList<CalculatorHistoryState>(Locator.getInstance().getHistory().getStates(showIntermediateCalculations));
}
@Override
protected void clearHistory() {
Locator.getInstance().getHistory().clear();
getAdapter().clear();
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import java.util.ArrayList;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:40 PM
*/
public class CalculatorSavedHistoryFragment extends AbstractCalculatorHistoryFragment {
public CalculatorSavedHistoryFragment() {
super(CalculatorFragmentType.saved_history);
}
@Override
protected int getItemLayoutId() {
return R.layout.saved_history_item;
}
@NotNull
@Override
protected List<CalculatorHistoryState> getHistoryItems() {
return new ArrayList<CalculatorHistoryState>(Locator.getInstance().getHistory().getSavedHistory());
}
@Override
protected void clearHistory() {
Locator.getInstance().getHistory().clearSavedHistory();
getAdapter().clear();
}
}

View File

@@ -0,0 +1,78 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.content.Context;
import android.view.View;
import android.view.ViewGroup;
import android.widget.ArrayAdapter;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.R;
import org.solovyev.common.text.StringUtils;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.List;
/**
* User: serso
* Date: 12/18/11
* Time: 7:39 PM
*/
public class HistoryArrayAdapter extends ArrayAdapter<CalculatorHistoryState> {
HistoryArrayAdapter(Context context, int resource, int textViewResourceId, @NotNull List<CalculatorHistoryState> historyList) {
super(context, resource, textViewResourceId, historyList);
}
@Override
public View getView(int position, View convertView, ViewGroup parent) {
final ViewGroup result = (ViewGroup) super.getView(position, convertView, parent);
final CalculatorHistoryState state = getItem(position);
final TextView time = (TextView) result.findViewById(R.id.history_time);
time.setText(new SimpleDateFormat().format(new Date(state.getTime())));
final TextView editor = (TextView) result.findViewById(R.id.history_item);
editor.setText(AbstractCalculatorHistoryFragment.getHistoryText(state));
final TextView commentView = (TextView) result.findViewById(R.id.history_item_comment);
if (commentView != null) {
final String comment = state.getComment();
if (!StringUtils.isEmpty(comment)) {
commentView.setText(comment);
} else {
commentView.setText("");
}
}
final TextView status = (TextView) result.findViewById(R.id.history_item_status);
if (status != null) {
if (state.isSaved()) {
status.setText(getContext().getString(R.string.c_history_item_saved));
} else {
if ( AbstractCalculatorHistoryFragment.isAlreadySaved(state) ) {
status.setText(getContext().getString(R.string.c_history_item_already_saved));
} else {
status.setText(getContext().getString(R.string.c_history_item_not_saved));
}
}
}
return result;
}
@Override
public void notifyDataSetChanged() {
this.setNotifyOnChange(false);
this.sort(AbstractCalculatorHistoryFragment.COMPARATOR);
this.setNotifyOnChange(true);
super.notifyDataSetChanged();
}
}

View File

@@ -0,0 +1,39 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.widget.ArrayAdapter;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 12/18/11
* Time: 3:10 PM
*/
public class HistoryItemMenuData {
@NotNull
private final ArrayAdapter<CalculatorHistoryState> adapter;
@NotNull
private final CalculatorHistoryState historyState;
public HistoryItemMenuData(@NotNull CalculatorHistoryState historyState, ArrayAdapter<CalculatorHistoryState> adapter) {
this.historyState = historyState;
this.adapter = adapter;
}
@NotNull
public CalculatorHistoryState getHistoryState() {
return historyState;
}
@NotNull
public ArrayAdapter<CalculatorHistoryState> getAdapter() {
return adapter;
}
}

View File

@@ -0,0 +1,150 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.text.ClipboardManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/18/11
* Time: 3:09 PM
*/
public enum HistoryItemMenuItem implements LabeledMenuItem<HistoryItemMenuData> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
AbstractCalculatorHistoryFragment.useHistoryItem(data.getHistoryState());
}
},
copy_expression(R.string.c_copy_expression) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_expression_copied), Toast.LENGTH_SHORT).show();
}
}
},
copy_result(R.string.c_copy_result) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState calculatorHistoryState = data.getHistoryState();
final String text = calculatorHistoryState.getDisplayState().getEditorState().getText();
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
Toast.makeText(context, context.getText(R.string.c_result_copied), Toast.LENGTH_SHORT).show();
}
}
},
save(R.string.c_save) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (!historyState.isSaved()) {
createEditHistoryDialog(data, context, true);
} else {
Toast.makeText(context, context.getText(R.string.c_history_already_saved), Toast.LENGTH_LONG).show();
}
}
},
edit(R.string.c_edit) {
@Override
public void onClick(@NotNull final HistoryItemMenuData data, @NotNull final Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
createEditHistoryDialog(data, context, false);
} else {
Toast.makeText(context, context.getText(R.string.c_history_must_be_saved), Toast.LENGTH_LONG).show();
}
}
},
remove(R.string.c_remove) {
@Override
public void onClick(@NotNull HistoryItemMenuData data, @NotNull Context context) {
final CalculatorHistoryState historyState = data.getHistoryState();
if (historyState.isSaved()) {
data.getAdapter().remove(historyState);
Locator.getInstance().getHistory().removeSavedHistory(historyState);
Toast.makeText(context, context.getText(R.string.c_history_was_removed), Toast.LENGTH_LONG).show();
data.getAdapter().notifyDataSetChanged();
}
}
};
private static void createEditHistoryDialog(@NotNull final HistoryItemMenuData data, @NotNull final Context context, final boolean save) {
final CalculatorHistoryState historyState = data.getHistoryState();
final LayoutInflater layoutInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
final View editView = layoutInflater.inflate(R.layout.history_edit, null);
final TextView historyExpression = (TextView)editView.findViewById(R.id.history_edit_expression);
historyExpression.setText(AbstractCalculatorHistoryFragment.getHistoryText(historyState));
final EditText comment = (EditText)editView.findViewById(R.id.history_edit_comment);
comment.setText(historyState.getComment());
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setTitle(save ? R.string.c_save_history : R.string.c_edit_history)
.setCancelable(true)
.setNegativeButton(R.string.c_cancel, null)
.setPositiveButton(R.string.c_save, new DialogInterface.OnClickListener() {
@Override
public void onClick(DialogInterface dialog, int which) {
if (save) {
final CalculatorHistoryState savedHistoryItem = Locator.getInstance().getHistory().addSavedState(historyState);
savedHistoryItem.setComment(comment.getText().toString());
Locator.getInstance().getHistory().save();
// we don't need to add element to the adapter as adapter of another activity must be updated and not this
//data.getAdapter().add(savedHistoryItem);
} else {
historyState.setComment(comment.getText().toString());
Locator.getInstance().getHistory().save();
}
data.getAdapter().notifyDataSetChanged();
Toast.makeText(context, context.getText(R.string.c_history_saved), Toast.LENGTH_LONG).show();
}
})
.setView(editView);
builder.create().show();
}
private final int captionId;
private HistoryItemMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}

View File

@@ -0,0 +1,50 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.history;
import android.widget.EditText;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Editor;
/**
* User: serso
* Date: 12/17/11
* Time: 9:39 PM
*/
public class TextViewEditorAdapter implements Editor {
@NotNull
private final TextView textView;
public TextViewEditorAdapter(@NotNull TextView textView) {
this.textView = textView;
}
@Override
public CharSequence getText() {
return textView.getText().toString();
}
@Override
public void setText(@Nullable CharSequence text) {
textView.setText(text);
}
@Override
public int getSelection() {
return textView.getSelectionStart();
}
@Override
public void setSelection(int selection) {
if ( textView instanceof EditText ) {
((EditText) textView).setSelection(selection);
}
}
}

View File

@@ -0,0 +1,354 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.app.Activity;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.*;
import com.actionbarsherlock.app.SherlockListFragment;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.menu.AMenuBuilder;
import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.android.menu.MenuImpl;
import org.solovyev.common.equals.EqualsTool;
import org.solovyev.common.filter.Filter;
import org.solovyev.common.filter.FilterRule;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.text.StringUtils;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
/**
* User: serso
* Date: 12/21/11
* Time: 9:24 PM
*/
public abstract class AbstractMathEntityListFragment<T extends MathEntity> extends SherlockListFragment implements CalculatorEventListener {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
public static final String MATH_ENTITY_CATEGORY_EXTRA_STRING = "org.solovyev.android.calculator.CalculatorVarsActivity_math_entity_category";
protected final static List<Character> acceptableChars = Arrays.asList(StringUtils.toObject("1234567890abcdefghijklmnopqrstuvwxyzйцукенгшщзхъфывапролджэячсмитьбюё_".toCharArray()));
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@Nullable
private MathEntityArrayAdapter<T> adapter;
@Nullable
private String category;
@NotNull
private final CalculatorFragmentHelper fragmentHelper;
@NotNull
private final Handler uiHandler = new Handler();
protected AbstractMathEntityListFragment(@NotNull CalculatorFragmentType fragmentType) {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(fragmentType.getDefaultLayoutId(),fragmentType.getDefaultTitleResId());
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle bundle = getArguments();
if (bundle != null) {
category = bundle.getString(MATH_ENTITY_CATEGORY_EXTRA_STRING);
}
fragmentHelper.onCreate(this);
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return fragmentHelper.onCreateView(this, inflater, container);
}
@Override
public void onViewCreated(View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
fragmentHelper.onViewCreated(this, root);
final ListView lv = getListView();
lv.setTextFilterEnabled(true);
lv.setOnItemClickListener(new AdapterView.OnItemClickListener() {
public void onItemClick(final AdapterView<?> parent,
final View view,
final int position,
final long id) {
final AMenuItem<T> onClick = getOnClickAction();
if (onClick != null) {
onClick.onClick(((T) parent.getItemAtPosition(position)), getActivity());
}
}
});
getListView().setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
@Override
public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
final T item = (T) parent.getItemAtPosition(position);
final List<LabeledMenuItem<T>> menuItems = getMenuItemsOnLongClick(item);
if (!menuItems.isEmpty()) {
final AMenuBuilder<LabeledMenuItem<T>, T> menuBuilder = AMenuBuilder.newInstance(AbstractMathEntityListFragment.this.getActivity(), MenuImpl.newInstance(menuItems));
menuBuilder.create(item).show();
}
return true;
}
});
}
@Nullable
protected abstract AMenuItem<T> getOnClickAction();
@Override
public void onDestroy() {
fragmentHelper.onDestroy(this);
super.onDestroy();
}
@NotNull
protected abstract List<LabeledMenuItem<T>> getMenuItemsOnLongClick(@NotNull T item);
@Override
public void onPause() {
this.fragmentHelper.onPause(this);
super.onPause();
}
@Override
public void onResume() {
super.onResume();
this.fragmentHelper.onResume(this);
adapter = new MathEntityArrayAdapter<T>(getDescriptionGetter(), this.getActivity(), R.layout.math_entity, R.id.math_entity_text, getMathEntitiesByCategory());
setListAdapter(adapter);
sort();
}
@NotNull
private List<T> getMathEntitiesByCategory() {
final List<T> result = getMathEntities();
new Filter<T>(new FilterRule<T>() {
@Override
public boolean isFiltered(T t) {
return !isInCategory(t);
}
}).filter(result.iterator());
return result;
}
protected boolean isInCategory(@Nullable T t) {
return t != null && (category == null || EqualsTool.areEqual(getMathEntityCategory(t), category));
}
@NotNull
protected abstract MathEntityDescriptionGetter getDescriptionGetter();
@NotNull
protected abstract List<T> getMathEntities();
@Nullable
abstract String getMathEntityCategory(@NotNull T t);
protected void sort() {
final MathEntityArrayAdapter<T> localAdapter = adapter;
if (localAdapter != null) {
localAdapter.sort(new Comparator<T>() {
@Override
public int compare(T function1, T function2) {
return function1.getName().compareTo(function2.getName());
}
});
localAdapter.notifyDataSetChanged();
}
}
protected static class MathEntityArrayAdapter<T extends MathEntity> extends ArrayAdapter<T> {
@NotNull
private final MathEntityDescriptionGetter descriptionGetter;
private MathEntityArrayAdapter(@NotNull MathEntityDescriptionGetter descriptionGetter,
@NotNull Context context,
int resource,
int textViewResourceId,
@NotNull List<T> objects) {
super(context, resource, textViewResourceId, objects);
this.descriptionGetter = descriptionGetter;
}
@Override
public View getView(int position, @Nullable View convertView, ViewGroup parent) {
final ViewGroup result;
if (convertView == null) {
result = (ViewGroup) super.getView(position, convertView, parent);
fillView(position, result);
} else {
result = (ViewGroup) convertView;
fillView(position, result);
}
return result;
}
private void fillView(int position, @NotNull ViewGroup result) {
final T mathEntity = getItem(position);
final TextView text = (TextView) result.findViewById(R.id.math_entity_text);
text.setText(String.valueOf(mathEntity));
final String mathEntityDescription = descriptionGetter.getDescription(getContext(), mathEntity.getName());
final TextView description = (TextView) result.findViewById(R.id.math_entity_description);
if (!StringUtils.isEmpty(mathEntityDescription)) {
description.setVisibility(View.VISIBLE);
description.setText(mathEntityDescription);
} else {
description.setVisibility(View.GONE);
}
}
}
protected static class MathEntityDescriptionGetterImpl implements MathEntityDescriptionGetter {
@NotNull
private final CalculatorMathRegistry<?> mathRegistry;
public MathEntityDescriptionGetterImpl(@NotNull CalculatorMathRegistry<?> mathRegistry) {
this.mathRegistry = mathRegistry;
}
@Override
public String getDescription(@NotNull Context context, @NotNull String mathEntityName) {
return this.mathRegistry.getDescription(mathEntityName);
}
}
protected static interface MathEntityDescriptionGetter {
@Nullable
String getDescription(@NotNull Context context, @NotNull String mathEntityName);
}
public void addToAdapter(@NotNull T mathEntity) {
if (this.adapter != null) {
this.adapter.add(mathEntity);
}
}
public void removeFromAdapter(@NotNull T mathEntity) {
if (this.adapter != null) {
this.adapter.remove(mathEntity);
}
}
public void notifyAdapter() {
if (this.adapter != null) {
this.adapter.notifyDataSetChanged();
}
}
@Nullable
protected MathEntityArrayAdapter<T> getAdapter() {
return adapter;
}
@NotNull
protected Handler getUiHandler() {
return uiHandler;
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
static void createTab(@NotNull Context context,
@NotNull TabHost tabHost,
@NotNull String tabId,
@NotNull String categoryId,
int tabCaptionId,
@NotNull Class<? extends Activity> activityClass,
@Nullable Intent parentIntent) {
TabHost.TabSpec spec;
final Intent intent;
if (parentIntent != null) {
intent = new Intent(parentIntent);
} else {
intent = new Intent();
}
intent.setClass(context, activityClass);
intent.putExtra(MATH_ENTITY_CATEGORY_EXTRA_STRING, categoryId);
// Initialize a TabSpec for each tab and add it to the TabHost
spec = tabHost.newTabSpec(tabId).setIndicator(context.getString(tabCaptionId)).setContent(intent);
tabHost.addTab(spec);
}
@NotNull
public static Bundle createBundleFor(@NotNull String categoryId) {
final Bundle result = new Bundle(1);
putCategory(result, categoryId);
return result;
}
static void putCategory(@NotNull Bundle bundle, @NotNull String categoryId) {
bundle.putString(MATH_ENTITY_CATEGORY_EXTRA_STRING, categoryId);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
}
}

View File

@@ -0,0 +1,104 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.content.Intent;
import android.os.Bundle;
import android.util.Log;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
/**
* User: serso
* Date: 12/21/11
* Time: 10:33 PM
*/
public class CalculatorFunctionsActivity extends SherlockFragmentActivity implements CalculatorEventListener {
@NotNull
private final CalculatorActivityHelper activityHelper = CalculatorApplication.getInstance().createActivityHelper(R.layout.main_empty, CalculatorHistoryActivity.class.getSimpleName());
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityHelper.onCreate(this, savedInstanceState);
final Bundle bundle;
final Intent intent = getIntent();
if (intent != null) {
bundle = intent.getExtras();
} else {
bundle = null;
}
final CalculatorFragmentType fragmentType = CalculatorFragmentType.functions;
for (FunctionCategory category : FunctionCategory.getCategoriesByTabOrder()) {
final AndroidFunctionCategory androidCategory = AndroidFunctionCategory.valueOf(category);
if (androidCategory != null) {
final Bundle fragmentParameters;
if (category == FunctionCategory.my && bundle != null) {
AbstractMathEntityListFragment.putCategory(bundle, category.name());
fragmentParameters = bundle;
} else {
fragmentParameters = AbstractMathEntityListFragment.createBundleFor(category.name());
}
activityHelper.addTab(this, fragmentType.createSubFragmentTag(category.name()), fragmentType.getFragmentClass(), fragmentParameters, androidCategory.getCaptionId(), R.id.main_layout);
} else {
Log.e(CalculatorFunctionsActivity.class.getSimpleName(), "Unable to find android function category for " + category);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
@Override
protected void onResume() {
super.onResume();
activityHelper.onResume(this);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
this.activityHelper.onDestroy(this);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case use_function:
this.finish();
break;
}
}
}

View File

@@ -0,0 +1,268 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.app.Activity;
import android.content.Context;
import android.os.Bundle;
import android.os.Parcelable;
import android.text.ClipboardManager;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import jscl.math.function.Function;
import jscl.math.function.IFunction;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.function.FunctionEditDialogFragment;
import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 10/29/11
* Time: 4:55 PM
*/
public class CalculatorFunctionsFragment extends AbstractMathEntityListFragment<Function> {
public static final String CREATE_FUNCTION_EXTRA = "create_function";
public CalculatorFunctionsFragment() {
super(CalculatorFragmentType.functions);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle bundle = getArguments();
if (bundle != null) {
final Parcelable parcelable = bundle.getParcelable(CREATE_FUNCTION_EXTRA);
if (parcelable instanceof FunctionEditDialogFragment.Input) {
FunctionEditDialogFragment.showDialog((FunctionEditDialogFragment.Input) parcelable, this.getActivity().getSupportFragmentManager());
// in order to stop intent for other tabs
bundle.remove(CREATE_FUNCTION_EXTRA);
}
}
setHasOptionsMenu(true);
}
@Override
protected AMenuItem<Function> getOnClickAction() {
return LongClickMenuItem.use;
}
@NotNull
@Override
protected List<LabeledMenuItem<Function>> getMenuItemsOnLongClick(@NotNull Function item) {
List<LabeledMenuItem<Function>> result = new ArrayList<LabeledMenuItem<Function>>(Arrays.asList(LongClickMenuItem.values()));
final CalculatorMathRegistry<Function> functionsRegistry = Locator.getInstance().getEngine().getFunctionsRegistry();
if ( StringUtils.isEmpty(functionsRegistry.getDescription(item.getName())) ) {
result.remove(LongClickMenuItem.copy_description);
}
final Function function = functionsRegistry.get(item.getName());
if (function == null || function.isSystem()) {
result.remove(LongClickMenuItem.edit);
result.remove(LongClickMenuItem.remove);
}
return result;
}
@NotNull
@Override
protected MathEntityDescriptionGetter getDescriptionGetter() {
return new MathEntityDescriptionGetterImpl(Locator.getInstance().getEngine().getFunctionsRegistry());
}
@NotNull
@Override
protected List<Function> getMathEntities() {
return new ArrayList<Function>(Locator.getInstance().getEngine().getFunctionsRegistry().getEntities());
}
@Override
protected String getMathEntityCategory(@NotNull Function function) {
return Locator.getInstance().getEngine().getFunctionsRegistry().getCategory(function);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
super.onCalculatorEvent(calculatorEventData, calculatorEventType, data);
switch (calculatorEventType) {
case function_added:
processFunctionAdded((Function) data);
break;
case function_changed:
processFunctionChanged((Change<IFunction>) data);
break;
case function_removed:
processFunctionRemoved((Function) data);
break;
}
}
private void processFunctionRemoved(@NotNull final Function function) {
if (this.isInCategory(function)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
removeFromAdapter(function);
notifyAdapter();
}
});
}
}
private void processFunctionChanged(@NotNull final Change<IFunction> change) {
final IFunction newFunction = change.getNewValue();
if (newFunction instanceof Function) {
if (this.isInCategory((Function)newFunction)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
IFunction oldValue = change.getOldValue();
if (oldValue.isIdDefined()) {
final MathEntityArrayAdapter<Function> adapter = getAdapter();
if ( adapter != null ) {
for (int i = 0; i < adapter.getCount(); i++) {
final Function functionFromAdapter = adapter.getItem(i);
if ( functionFromAdapter.isIdDefined() && oldValue.getId().equals(functionFromAdapter.getId()) ) {
adapter.remove(functionFromAdapter);
break;
}
}
}
}
addToAdapter((Function)newFunction);
sort();
}
});
}
} else {
throw new IllegalArgumentException("Function must be instance of jscl.math.function.Function class!");
}
}
private void processFunctionAdded(@NotNull final Function function) {
if (this.isInCategory(function)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
addToAdapter(function);
sort();
}
});
}
}
/*
**********************************************************************
*
* MENU
*
**********************************************************************
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.functions_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result;
switch (item.getItemId()) {
case R.id.functions_menu_add_function:
FunctionEditDialogFragment.showDialog(FunctionEditDialogFragment.Input.newInstance(), this.getActivity().getSupportFragmentManager());
result = true;
break;
default:
result = super.onOptionsItemSelected(item);
}
return result;
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static enum LongClickMenuItem implements LabeledMenuItem<Function> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull Function function, @NotNull Context context) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.use_function, function);
}
},
edit(R.string.c_edit) {
@Override
public void onClick(@NotNull Function function, @NotNull Context context) {
if (function instanceof IFunction) {
FunctionEditDialogFragment.showDialog(FunctionEditDialogFragment.Input.newFromFunction((IFunction) function), ((SherlockFragmentActivity) context).getSupportFragmentManager());
}
}
},
remove(R.string.c_remove) {
@Override
public void onClick(@NotNull Function function, @NotNull Context context) {
MathEntityRemover.newFunctionRemover(function, null, context, context).showConfirmationDialog();
}
},
copy_description(R.string.c_copy_description) {
@Override
public void onClick(@NotNull Function function, @NotNull Context context) {
final String text = Locator.getInstance().getEngine().getFunctionsRegistry().getDescription(function.getName());
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
}
};
private final int captionId;
LongClickMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
}

View File

@@ -0,0 +1,82 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
/**
* User: serso
* Date: 12/21/11
* Time: 10:33 PM
*/
public class CalculatorOperatorsActivity extends SherlockFragmentActivity implements CalculatorEventListener {
@NotNull
private final CalculatorActivityHelper activityHelper = CalculatorApplication.getInstance().createActivityHelper(R.layout.main_empty, CalculatorHistoryActivity.class.getSimpleName());
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityHelper.onCreate(this, savedInstanceState);
final CalculatorFragmentType fragmentType = CalculatorFragmentType.operators;
for (OperatorCategory category : OperatorCategory.getCategoriesByTabOrder()) {
final AndroidOperatorCategory androidCategory = AndroidOperatorCategory.valueOf(category);
if (androidCategory != null) {
activityHelper.addTab(this, fragmentType.createSubFragmentTag(category.name()), fragmentType.getFragmentClass(), AbstractMathEntityListFragment.createBundleFor(category.name()), androidCategory.getCaptionId(), R.id.main_layout);
} else {
activityHelper.logError("Unable to find android operator category for " + category);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
@Override
protected void onResume() {
super.onResume();
activityHelper.onResume(this);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
this.activityHelper.onDestroy(this);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case use_operator:
this.finish();
break;
}
}
}

View File

@@ -0,0 +1,133 @@
package org.solovyev.android.calculator.math.edit;
import android.app.Activity;
import android.content.Context;
import android.text.ClipboardManager;
import jscl.math.operator.Operator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 11/17/11
* Time: 1:53 PM
*/
public class CalculatorOperatorsFragment extends AbstractMathEntityListFragment<Operator> {
public CalculatorOperatorsFragment() {
super(CalculatorFragmentType.operators);
}
@Override
protected AMenuItem<Operator> getOnClickAction() {
return LongClickMenuItem.use;
}
@NotNull
@Override
protected List<LabeledMenuItem<Operator>> getMenuItemsOnLongClick(@NotNull Operator item) {
final List<LabeledMenuItem<Operator>> result = new ArrayList<LabeledMenuItem<Operator>>(Arrays.asList(LongClickMenuItem.values()));
if ( StringUtils.isEmpty(OperatorDescriptionGetter.instance.getDescription(this.getActivity(), item.getName())) ) {
result.remove(LongClickMenuItem.copy_description);
}
return result;
}
@NotNull
@Override
protected MathEntityDescriptionGetter getDescriptionGetter() {
return OperatorDescriptionGetter.instance;
}
@NotNull
@Override
protected List<Operator> getMathEntities() {
final List<Operator> result = new ArrayList<Operator>();
result.addAll(Locator.getInstance().getEngine().getOperatorsRegistry().getEntities());
result.addAll(Locator.getInstance().getEngine().getPostfixFunctionsRegistry().getEntities());
return result;
}
@Override
protected String getMathEntityCategory(@NotNull Operator operator) {
String result = Locator.getInstance().getEngine().getOperatorsRegistry().getCategory(operator);
if (result == null) {
result = Locator.getInstance().getEngine().getPostfixFunctionsRegistry().getCategory(operator);
}
return result;
}
private static enum OperatorDescriptionGetter implements MathEntityDescriptionGetter {
instance;
@Override
public String getDescription(@NotNull Context context, @NotNull String mathEntityName) {
String result = Locator.getInstance().getEngine().getOperatorsRegistry().getDescription(mathEntityName);
if (StringUtils.isEmpty(result)) {
result = Locator.getInstance().getEngine().getPostfixFunctionsRegistry().getDescription(mathEntityName);
}
return result;
}
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static enum LongClickMenuItem implements LabeledMenuItem<Operator> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull Operator data, @NotNull Context context) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.use_operator, data);
}
},
copy_description(R.string.c_copy_description) {
@Override
public void onClick(@NotNull Operator data, @NotNull Context context) {
final String text = OperatorDescriptionGetter.instance.getDescription(context, data.getName());
if (!StringUtils.isEmpty(text)) {
final ClipboardManager clipboard = (ClipboardManager) context.getSystemService(Activity.CLIPBOARD_SERVICE);
clipboard.setText(text);
}
}
};
private final int captionId;
LongClickMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
}

View File

@@ -0,0 +1,107 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.content.Intent;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.history.CalculatorHistoryActivity;
import org.solovyev.android.calculator.AndroidVarCategory;
import org.solovyev.android.calculator.VarCategory;
/**
* User: serso
* Date: 12/21/11
* Time: 11:05 PM
*/
public class CalculatorVarsActivity extends SherlockFragmentActivity implements CalculatorEventListener {
@NotNull
private final CalculatorActivityHelper activityHelper = CalculatorApplication.getInstance().createActivityHelper(R.layout.main_empty, CalculatorHistoryActivity.class.getSimpleName());
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
activityHelper.onCreate(this, savedInstanceState);
final Bundle bundle;
final Intent intent = getIntent();
if (intent != null) {
bundle = intent.getExtras();
} else {
bundle = null;
}
final CalculatorFragmentType fragmentType = CalculatorFragmentType.variables;
for (VarCategory category : VarCategory.getCategoriesByTabOrder()) {
final Bundle fragmentParameters;
if (category == VarCategory.my && bundle != null) {
AbstractMathEntityListFragment.putCategory(bundle, category.name());
fragmentParameters = bundle;
} else {
fragmentParameters = AbstractMathEntityListFragment.createBundleFor(category.name());
}
final AndroidVarCategory androidVarCategory = AndroidVarCategory.valueOf(category);
if (androidVarCategory != null) {
activityHelper.addTab(this, fragmentType.createSubFragmentTag(category.name()), fragmentType.getFragmentClass(), fragmentParameters, androidVarCategory.getCaptionId(), R.id.main_layout);
} else {
activityHelper.logError("Unable to find android var category for " + category);
}
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
super.onSaveInstanceState(outState);
activityHelper.onSaveInstanceState(this, outState);
}
@Override
protected void onResume() {
super.onResume();
activityHelper.onResume(this);
}
@Override
protected void onPause() {
this.activityHelper.onPause(this);
super.onPause();
}
@Override
protected void onDestroy() {
super.onDestroy();
this.activityHelper.onDestroy(this);
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case use_constant:
this.finish();
break;
}
}
}

View File

@@ -0,0 +1,281 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.content.Context;
import android.os.Bundle;
import android.view.View;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.menu.AMenuItem;
import org.solovyev.android.menu.LabeledMenuItem;
import org.solovyev.common.JPredicate;
import org.solovyev.common.collections.CollectionsUtils;
import org.solovyev.common.text.StringUtils;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
/**
* User: serso
* Date: 9/28/11
* Time: 10:55 PM
*/
public class CalculatorVarsFragment extends AbstractMathEntityListFragment<IConstant> {
public static final String CREATE_VAR_EXTRA_STRING = "create_var";
public CalculatorVarsFragment() {
super(CalculatorFragmentType.variables);
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle bundle = getArguments();
if (bundle != null) {
final String varValue = bundle.getString(CREATE_VAR_EXTRA_STRING);
if (!StringUtils.isEmpty(varValue)) {
VarEditDialogFragment.showDialog(VarEditDialogFragment.Input.newFromValue(varValue), this.getActivity().getSupportFragmentManager());
// in order to stop intent for other tabs
bundle.remove(CREATE_VAR_EXTRA_STRING);
}
}
setHasOptionsMenu(true);
}
@Override
protected AMenuItem<IConstant> getOnClickAction() {
return LongClickMenuItem.use;
}
@NotNull
@Override
protected List<LabeledMenuItem<IConstant>> getMenuItemsOnLongClick(@NotNull IConstant item) {
final List<LabeledMenuItem<IConstant>> result = new ArrayList<LabeledMenuItem<IConstant>>(Arrays.asList(LongClickMenuItem.values()));
if (item.isSystem()) {
result.remove(LongClickMenuItem.edit);
result.remove(LongClickMenuItem.remove);
}
if (StringUtils.isEmpty(Locator.getInstance().getEngine().getVarsRegistry().getDescription(item.getName()))) {
result.remove(LongClickMenuItem.copy_description);
}
if (StringUtils.isEmpty(item.getValue())) {
result.remove(LongClickMenuItem.copy_value);
}
return result;
}
@NotNull
@Override
protected MathEntityDescriptionGetter getDescriptionGetter() {
return new MathEntityDescriptionGetterImpl(Locator.getInstance().getEngine().getVarsRegistry());
}
@SuppressWarnings({"UnusedDeclaration"})
public void addVarButtonClickHandler(@NotNull View v) {
VarEditDialogFragment.showDialog(VarEditDialogFragment.Input.newInstance(), this.getActivity().getSupportFragmentManager());
}
@NotNull
@Override
protected List<IConstant> getMathEntities() {
final List<IConstant> result = new ArrayList<IConstant>(Locator.getInstance().getEngine().getVarsRegistry().getEntities());
CollectionsUtils.removeAll(result, new JPredicate<IConstant>() {
@Override
public boolean apply(@Nullable IConstant var) {
return var != null && CollectionsUtils.contains(var.getName(), MathType.INFINITY_JSCL, MathType.NAN);
}
});
return result;
}
@Override
protected String getMathEntityCategory(@NotNull IConstant var) {
return Locator.getInstance().getEngine().getVarsRegistry().getCategory(var);
}
public static boolean isValidValue(@NotNull String value) {
try {
final PreparedExpression expression = ToJsclTextProcessor.getInstance().process(value);
final List<IConstant> constants = expression.getUndefinedVars();
return constants.isEmpty();
} catch (RuntimeException e) {
return true;
} catch (CalculatorParseException e) {
return true;
}
}
/*
**********************************************************************
*
* MENU
*
**********************************************************************
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
inflater.inflate(R.menu.vars_menu, menu);
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
boolean result;
switch (item.getItemId()) {
case R.id.var_menu_add_var:
VarEditDialogFragment.showDialog(VarEditDialogFragment.Input.newInstance(), this.getActivity().getSupportFragmentManager());
result = true;
break;
default:
result = super.onOptionsItemSelected(item);
}
return result;
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
super.onCalculatorEvent(calculatorEventData, calculatorEventType, data);
switch (calculatorEventType) {
case constant_added:
processConstantAdded((IConstant) data);
break;
case constant_changed:
processConstantChanged((Change<IConstant>) data);
break;
case constant_removed:
processConstantRemoved((IConstant) data);
break;
}
}
private void processConstantRemoved(@NotNull final IConstant constant) {
if (this.isInCategory(constant)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
removeFromAdapter(constant);
notifyAdapter();
}
});
}
}
private void processConstantChanged(@NotNull final Change<IConstant> change) {
final IConstant newConstant = change.getNewValue();
if (this.isInCategory(newConstant)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
removeFromAdapter(change.getOldValue());
addToAdapter(newConstant);
sort();
}
});
}
}
private void processConstantAdded(@NotNull final IConstant constant) {
if (this.isInCategory(constant)) {
getUiHandler().post(new Runnable() {
@Override
public void run() {
addToAdapter(constant);
sort();
}
});
}
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static enum LongClickMenuItem implements LabeledMenuItem<IConstant> {
use(R.string.c_use) {
@Override
public void onClick(@NotNull IConstant data, @NotNull Context context) {
Locator.getInstance().getCalculator().fireCalculatorEvent(CalculatorEventType.use_constant, data);
}
},
edit(R.string.c_edit) {
@Override
public void onClick(@NotNull IConstant constant, @NotNull Context context) {
VarEditDialogFragment.showDialog(VarEditDialogFragment.Input.newFromConstant(constant), ((SherlockFragmentActivity) context).getSupportFragmentManager());
}
},
remove(R.string.c_remove) {
@Override
public void onClick(@NotNull IConstant constant, @NotNull Context context) {
MathEntityRemover.newConstantRemover(constant, null, context, context).showConfirmationDialog();
}
},
copy_value(R.string.c_copy_value) {
@Override
public void onClick(@NotNull IConstant data, @NotNull Context context) {
final String text = data.getValue();
if (!StringUtils.isEmpty(text)) {
assert text != null;
Locator.getInstance().getClipboard().setText(text);
}
}
},
copy_description(R.string.c_copy_description) {
@Override
public void onClick(@NotNull IConstant data, @NotNull Context context) {
final String text = Locator.getInstance().getEngine().getVarsRegistry().getDescription(data.getName());
if (!StringUtils.isEmpty(text)) {
assert text != null;
Locator.getInstance().getClipboard().setText(text);
}
}
};
private final int captionId;
LongClickMenuItem(int captionId) {
this.captionId = captionId;
}
@NotNull
@Override
public String getCaption(@NotNull Context context) {
return context.getString(captionId);
}
}
}

View File

@@ -0,0 +1,176 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.view.View;
import android.widget.TextView;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorEventType;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorMathRegistry;
import org.solovyev.android.calculator.R;
import org.solovyev.common.math.MathEntity;
/**
* User: serso
* Date: 12/22/11
* Time: 9:36 PM
*/
public class MathEntityRemover<T extends MathEntity> implements View.OnClickListener, DialogInterface.OnClickListener {
@NotNull
private final T mathEntity;
@Nullable
private final DialogInterface.OnClickListener callbackOnCancel;
private final boolean confirmed;
@NotNull
private final CalculatorMathRegistry<? super T> varsRegistry;
@NotNull
private Context context;
@NotNull
private final Object source;
@NotNull
private final Params params;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
private MathEntityRemover(@NotNull T mathEntity,
@Nullable DialogInterface.OnClickListener callbackOnCancel,
boolean confirmed,
@NotNull CalculatorMathRegistry<? super T> varsRegistry,
@NotNull Context context,
@NotNull Object source,
@NotNull Params params) {
this.mathEntity = mathEntity;
this.callbackOnCancel = callbackOnCancel;
this.confirmed = confirmed;
this.varsRegistry = varsRegistry;
this.context = context;
this.source = source;
this.params = params;
}
public static MathEntityRemover<IConstant> newConstantRemover(@NotNull IConstant constant,
@Nullable DialogInterface.OnClickListener callbackOnCancel,
@NotNull Context context,
@NotNull Object source) {
return new MathEntityRemover<IConstant>(constant, callbackOnCancel, false, Locator.getInstance().getEngine().getVarsRegistry(), context, source, Params.newConstantInstance());
}
public static MathEntityRemover<Function> newFunctionRemover(@NotNull Function function,
@Nullable DialogInterface.OnClickListener callbackOnCancel,
@NotNull Context context,
@NotNull Object source) {
return new MathEntityRemover<Function>(function, callbackOnCancel, false, Locator.getInstance().getEngine().getFunctionsRegistry(), context, source, Params.newFunctionInstance());
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
public void showConfirmationDialog() {
final TextView question = new TextView(context);
question.setText(String.format(context.getString(params.getRemovalConfirmationQuestionResId()), mathEntity.getName()));
question.setPadding(6, 6, 6, 6);
final AlertDialog.Builder builder = new AlertDialog.Builder(context)
.setCancelable(true)
.setView(question)
.setTitle(params.getRemovalConfirmationTitleResId())
.setNegativeButton(R.string.c_no, callbackOnCancel)
.setPositiveButton(R.string.c_yes, new MathEntityRemover<T>(mathEntity, callbackOnCancel, true, varsRegistry, context, source, params));
builder.create().show();
}
@Override
public void onClick(@Nullable View v) {
if (!confirmed) {
showConfirmationDialog();
} else {
varsRegistry.remove(mathEntity);
varsRegistry.save();
Locator.getInstance().getCalculator().fireCalculatorEvent(params.getCalculatorEventType(), mathEntity, source);
}
}
@Override
public void onClick(DialogInterface dialog, int which) {
onClick(null);
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static final class Params {
private int removalConfirmationTitleResId;
private int removalConfirmationQuestionResId;
private CalculatorEventType calculatorEventType;
private Params() {
}
public int getRemovalConfirmationTitleResId() {
return removalConfirmationTitleResId;
}
public int getRemovalConfirmationQuestionResId() {
return removalConfirmationQuestionResId;
}
public CalculatorEventType getCalculatorEventType() {
return calculatorEventType;
}
private static <T extends MathEntity> Params newConstantInstance() {
final Params result = new Params();
result.removalConfirmationTitleResId = R.string.removal_confirmation;
result.removalConfirmationQuestionResId = R.string.c_var_removal_confirmation_question;
result.calculatorEventType = CalculatorEventType.constant_removed;
return result;
}
private static <T extends MathEntity> Params newFunctionInstance() {
final Params result = new Params();
result.removalConfirmationTitleResId = R.string.removal_confirmation;
result.removalConfirmationQuestionResId = R.string.function_removal_confirmation_question;
result.calculatorEventType = CalculatorEventType.function_removed;
return result;
}
}
}

View File

@@ -0,0 +1,220 @@
package org.solovyev.android.calculator.math.edit;
import android.os.Bundle;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.WindowManager;
import android.widget.EditText;
import android.widget.Toast;
import jscl.math.function.IConstant;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.model.Var;
import org.solovyev.android.sherlock.AndroidSherlockUtils;
/**
* User: Solovyev_S
* Date: 01.10.12
* Time: 17:41
*/
public class VarEditDialogFragment extends DialogFragment implements CalculatorEventListener {
@NotNull
private final Input input;
public VarEditDialogFragment() {
this(Input.newInstance());
}
public VarEditDialogFragment(@NotNull Input input) {
this.input = input;
}
@Override
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.var_edit, container, false);
}
@Override
public void onResume() {
super.onResume();
Locator.getInstance().getCalculator().addCalculatorEventListener(this);
}
@Override
public void onPause() {
Locator.getInstance().getCalculator().removeCalculatorEventListener(this);
super.onPause();
}
@Override
public void onViewCreated(@NotNull View root, Bundle savedInstanceState) {
super.onViewCreated(root, savedInstanceState);
final String errorMsg = this.getString(R.string.c_char_is_not_accepted);
final EditText editName = (EditText) root.findViewById(R.id.var_edit_name);
editName.setText(input.getName());
editName.addTextChangedListener(new TextWatcher() {
@Override
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
}
@Override
public void afterTextChanged(Editable s) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!AbstractMathEntityListFragment.acceptableChars.contains(c)) {
s.delete(i, i + 1);
Toast.makeText(getActivity(), String.format(errorMsg, c), Toast.LENGTH_SHORT).show();
}
}
}
});
// show soft keyboard automatically
editName.requestFocus();
getDialog().getWindow().setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_VISIBLE);
final EditText editValue = (EditText) root.findViewById(R.id.var_edit_value);
editValue.setText(input.getValue());
final EditText editDescription = (EditText) root.findViewById(R.id.var_edit_description);
editDescription.setText(input.getDescription());
final Var.Builder varBuilder;
final IConstant constant = input.getConstant();
if (constant != null) {
varBuilder = new Var.Builder(constant);
} else {
varBuilder = new Var.Builder();
}
root.findViewById(R.id.cancel_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
dismiss();
}
});
root.findViewById(R.id.save_button).setOnClickListener(new VarEditorSaver<IConstant>(varBuilder, constant, root, Locator.getInstance().getEngine().getVarsRegistry(), this));
if ( constant == null ) {
// CREATE MODE
getDialog().setTitle(R.string.c_var_create_var);
root.findViewById(R.id.remove_button).setVisibility(View.GONE);
} else {
// EDIT MODE
getDialog().setTitle(R.string.c_var_edit_var);
root.findViewById(R.id.remove_button).setOnClickListener(MathEntityRemover.newConstantRemover(constant, null, getActivity(), VarEditDialogFragment.this));
}
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType) {
case constant_removed:
case constant_added:
case constant_changed:
if ( calculatorEventData.getSource() == this ) {
dismiss();
}
break;
}
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
public static void showDialog(@NotNull Input input, @NotNull FragmentManager fm) {
AndroidSherlockUtils.showDialog(new VarEditDialogFragment(input), "constant-editor", fm);
}
public static class Input {
@Nullable
private IConstant constant;
@Nullable
private String name;
@Nullable
private String value;
@Nullable
private String description;
private Input() {
}
@NotNull
public static Input newInstance() {
return new Input();
}
@NotNull
public static Input newFromConstant(@NotNull IConstant constant) {
final Input result = new Input();
result.constant = constant;
return result;
}
@NotNull
public static Input newFromValue(@Nullable String value) {
final Input result = new Input();
result.value = value;
return result;
}
@NotNull
public static Input newInstance(@Nullable IConstant constant, @Nullable String name, @Nullable String value, @Nullable String description) {
final Input result = new Input();
result.constant = constant;
result.name = name;
result.value = value;
result.description = description;
return result;
}
@Nullable
public IConstant getConstant() {
return constant;
}
@Nullable
public String getName() {
return name == null ? (constant == null ? null : constant.getName()) : name;
}
@Nullable
public String getValue() {
return value == null ? (constant == null ? null : constant.getValue()) : value;
}
@Nullable
public String getDescription() {
return description == null ? (constant == null ? null : constant.getDescription()) : description;
}
}
}

View File

@@ -0,0 +1,141 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.math.edit;
import android.view.View;
import android.widget.EditText;
import jscl.text.Identifier;
import jscl.text.MutableInt;
import jscl.text.ParseException;
import jscl.text.Parser;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorMathRegistry;
import org.solovyev.android.calculator.CalculatorVarsRegistry;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.model.MathEntityBuilder;
import org.solovyev.common.math.MathEntity;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
/**
* User: serso
* Date: 12/22/11
* Time: 9:52 PM
*/
public class VarEditorSaver<T extends MathEntity> implements View.OnClickListener {
@NotNull
private final MathEntityBuilder<? extends T> varBuilder;
@Nullable
private final T editedInstance;
@NotNull
private final CalculatorMathRegistry<T> mathRegistry;
@NotNull
private final Object source;
@NotNull
private View editView;
public VarEditorSaver(@NotNull MathEntityBuilder<? extends T> varBuilder,
@Nullable T editedInstance,
@NotNull View editView,
@NotNull CalculatorMathRegistry<T> mathRegistry,
@NotNull Object source) {
this.varBuilder = varBuilder;
this.editedInstance = editedInstance;
this.editView = editView;
this.mathRegistry = mathRegistry;
this.source = source;
}
@Override
public void onClick(View v) {
final Integer error;
final EditText editName = (EditText) editView.findViewById(R.id.var_edit_name);
String name = editName.getText().toString();
final EditText editValue = (EditText) editView.findViewById(R.id.var_edit_value);
String value = editValue.getText().toString();
final EditText editDescription = (EditText) editView.findViewById(R.id.var_edit_description);
String description = editDescription.getText().toString();
if (isValidName(name)) {
boolean canBeSaved = false;
final T entityFromRegistry = mathRegistry.get(name);
if (entityFromRegistry == null) {
canBeSaved = true;
} else if (editedInstance != null && entityFromRegistry.getId().equals(editedInstance.getId())) {
canBeSaved = true;
}
if (canBeSaved) {
final MathType.Result mathType = MathType.getType(name, 0, false);
if (mathType.getMathType() == MathType.text || mathType.getMathType() == MathType.constant) {
if (StringUtils.isEmpty(value)) {
// value is empty => undefined variable
varBuilder.setName(name);
varBuilder.setDescription(description);
varBuilder.setValue(null);
error = null;
} else {
// value is not empty => must be a number
boolean valid = CalculatorVarsFragment.isValidValue(value);
if (valid) {
varBuilder.setName(name);
varBuilder.setDescription(description);
varBuilder.setValue(value);
error = null;
} else {
error = R.string.c_value_is_not_a_number;
}
}
} else {
error = R.string.c_var_name_clashes;
}
} else {
error = R.string.c_var_already_exists;
}
} else {
error = R.string.c_name_is_not_valid;
}
if (error != null) {
Locator.getInstance().getNotifier().showMessage(error, MessageType.error);
} else {
CalculatorVarsRegistry.saveVariable(mathRegistry, varBuilder, editedInstance, source, true);
}
}
public static boolean isValidName(@Nullable String name) {
boolean result = false;
if (!StringUtils.isEmpty(name)) {
try {
assert name != null;
Identifier.parser.parse(Parser.Parameters.newInstance(name, new MutableInt(0), Locator.getInstance().getEngine().getMathEngine0()), null);
result = true;
} catch (ParseException e) {
// not valid name;
}
}
return result;
}
}

View File

@@ -0,0 +1,294 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
*/
package org.solovyev.android.calculator.model;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import jscl.AngleUnit;
import jscl.JsclMathEngine;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import jscl.math.operator.Operator;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorEngine;
import org.solovyev.android.calculator.CalculatorEngineImpl;
import org.solovyev.android.calculator.CalculatorFunctionsMathRegistry;
import org.solovyev.android.calculator.CalculatorMathEngine;
import org.solovyev.android.calculator.CalculatorMathRegistry;
import org.solovyev.android.calculator.CalculatorOperatorsMathRegistry;
import org.solovyev.android.calculator.CalculatorPostfixFunctionsRegistry;
import org.solovyev.android.calculator.CalculatorVarsRegistry;
import org.solovyev.android.calculator.MathPersistenceEntity;
import org.solovyev.android.calculator.R;
import org.solovyev.android.prefs.BooleanPreference;
import org.solovyev.android.prefs.Preference;
import org.solovyev.android.prefs.StringPreference;
import org.solovyev.common.text.EnumMapper;
import org.solovyev.common.text.NumberMapper;
import org.solovyev.common.text.StringUtils;
import java.text.DecimalFormatSymbols;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
/**
* User: serso
* Date: 9/12/11
* Time: 11:38 PM
*/
public class AndroidCalculatorEngine implements CalculatorEngine, SharedPreferences.OnSharedPreferenceChangeListener {
private static final String GROUPING_SEPARATOR_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_grouping_separator";
private static final String MULTIPLICATION_SIGN_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_calc_multiplication_sign";
private static final String MULTIPLICATION_SIGN_DEFAULT = "×";
private static final String MAX_CALCULATION_TIME_P_KEY = "calculation.max_calculation_time";
private static final String MAX_CALCULATION_TIME_DEFAULT = "5";
private static final String SCIENCE_NOTATION_P_KEY = "calculation.output.science_notation";
private static final boolean SCIENCE_NOTATION_DEFAULT = false;
private static final String ROUND_RESULT_P_KEY = "org.solovyev.android.calculator.CalculatorModel_round_result";
private static final boolean ROUND_RESULT_DEFAULT = true;
private static final String RESULT_PRECISION_P_KEY = "org.solovyev.android.calculator.CalculatorModel_result_precision";
private static final String RESULT_PRECISION_DEFAULT = "5";
private static final String NUMERAL_BASES_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_numeral_bases";
private static final String NUMERAL_BASES_DEFAULT = "dec";
private static final String ANGLE_UNITS_P_KEY = "org.solovyev.android.calculator.CalculatorActivity_angle_units";
private static final String ANGLE_UNITS_DEFAULT = "deg";
public static class Preferences {
public static final Preference<String> groupingSeparator = StringPreference.newInstance(GROUPING_SEPARATOR_P_KEY, JsclMathEngine.GROUPING_SEPARATOR_DEFAULT);
public static final Preference<String> multiplicationSign = StringPreference.newInstance(MULTIPLICATION_SIGN_P_KEY, MULTIPLICATION_SIGN_DEFAULT);
public static final Preference<Integer> precision = StringPreference.newInstance(RESULT_PRECISION_P_KEY, RESULT_PRECISION_DEFAULT, new NumberMapper<Integer>(Integer.class));
public static final Preference<Boolean> roundResult = new BooleanPreference(ROUND_RESULT_P_KEY, ROUND_RESULT_DEFAULT);
public static final Preference<NumeralBase> numeralBase = StringPreference.newInstance(NUMERAL_BASES_P_KEY, NUMERAL_BASES_DEFAULT, EnumMapper.newInstance(NumeralBase.class));
public static final Preference<AngleUnit> angleUnit = StringPreference.newInstance(ANGLE_UNITS_P_KEY, ANGLE_UNITS_DEFAULT, EnumMapper.newInstance(AngleUnit.class));
public static final Preference<Boolean> scienceNotation = new BooleanPreference(SCIENCE_NOTATION_P_KEY, SCIENCE_NOTATION_DEFAULT);
public static final Preference<Integer> maxCalculationTime = StringPreference.newInstance(MAX_CALCULATION_TIME_P_KEY, MAX_CALCULATION_TIME_DEFAULT, new NumberMapper<Integer>(Integer.class));
private static final List<String> preferenceKeys = new ArrayList<String>();
static {
preferenceKeys.add(groupingSeparator.getKey());
preferenceKeys.add(multiplicationSign.getKey());
preferenceKeys.add(precision.getKey());
preferenceKeys.add(roundResult.getKey());
preferenceKeys.add(numeralBase.getKey());
preferenceKeys.add(angleUnit.getKey());
preferenceKeys.add(scienceNotation.getKey());
preferenceKeys.add(maxCalculationTime.getKey());
}
@NotNull
public static List<String> getPreferenceKeys() {
return Collections.unmodifiableList(preferenceKeys);
}
}
@NotNull
private final Context context;
@NotNull
private final CalculatorEngine calculatorEngine;
@NotNull
private final Object lock;
public AndroidCalculatorEngine(@NotNull Application application) {
this.context = application;
PreferenceManager.getDefaultSharedPreferences(application).registerOnSharedPreferenceChangeListener(this);
this.lock = new Object();
final JsclMathEngine engine = JsclMathEngine.getInstance();
this.calculatorEngine = new CalculatorEngineImpl(engine,
new CalculatorVarsRegistry(engine.getConstantsRegistry(), new AndroidMathEntityDao<Var>(R.string.p_calc_vars, application, Vars.class)),
new CalculatorFunctionsMathRegistry(engine.getFunctionsRegistry(), new AndroidMathEntityDao<AFunction>(R.string.p_calc_functions, application, Functions.class)),
new CalculatorOperatorsMathRegistry(engine.getOperatorsRegistry(), new AndroidMathEntityDao<MathPersistenceEntity>(null, application, null)),
new CalculatorPostfixFunctionsRegistry(engine.getPostfixFunctionsRegistry(), new AndroidMathEntityDao<MathPersistenceEntity>(null, application, null)),
this.lock);
}
@Override
@NotNull
public CalculatorMathRegistry<IConstant> getVarsRegistry() {
return calculatorEngine.getVarsRegistry();
}
@Override
@NotNull
public CalculatorMathRegistry<Function> getFunctionsRegistry() {
return calculatorEngine.getFunctionsRegistry();
}
@Override
@NotNull
public CalculatorMathRegistry<Operator> getOperatorsRegistry() {
return calculatorEngine.getOperatorsRegistry();
}
@Override
@NotNull
public CalculatorMathRegistry<Operator> getPostfixFunctionsRegistry() {
return calculatorEngine.getPostfixFunctionsRegistry();
}
@Override
@NotNull
public CalculatorMathEngine getMathEngine() {
return calculatorEngine.getMathEngine();
}
@NotNull
@Override
public MathEngine getMathEngine0() {
return calculatorEngine.getMathEngine0();
}
@NotNull
@Override
public NumeralBase getNumeralBase() {
return calculatorEngine.getNumeralBase();
}
@Override
public void init() {
synchronized (lock) {
reset();
}
}
@Override
public void reset() {
synchronized (lock) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
softReset(preferences);
calculatorEngine.reset();
}
}
@Override
public void softReset() {
synchronized (lock) {
softReset(PreferenceManager.getDefaultSharedPreferences(context));
calculatorEngine.softReset();
}
}
@Override
public void setUseGroupingSeparator(boolean useGroupingSeparator) {
calculatorEngine.setUseGroupingSeparator(useGroupingSeparator);
}
@Override
public void setGroupingSeparator(char groupingSeparator) {
calculatorEngine.setGroupingSeparator(groupingSeparator);
}
@Override
public void setPrecision(@NotNull Integer precision) {
calculatorEngine.setPrecision(precision);
}
@Override
public void setRoundResult(@NotNull Boolean round) {
calculatorEngine.setRoundResult(round);
}
@NotNull
@Override
public AngleUnit getAngleUnits() {
return calculatorEngine.getAngleUnits();
}
@Override
public void setAngleUnits(@NotNull AngleUnit angleUnits) {
calculatorEngine.setAngleUnits(angleUnits);
}
@Override
public void setNumeralBase(@NotNull NumeralBase numeralBase) {
calculatorEngine.setNumeralBase(numeralBase);
}
@Override
public void setMultiplicationSign(@NotNull String multiplicationSign) {
calculatorEngine.setMultiplicationSign(multiplicationSign);
}
@Override
public void setScienceNotation(@NotNull Boolean scienceNotation) {
calculatorEngine.setScienceNotation(scienceNotation);
}
@Override
public void setTimeout(@NotNull Integer timeout) {
calculatorEngine.setTimeout(timeout);
}
private void softReset(@NotNull SharedPreferences preferences) {
this.setPrecision(Preferences.precision.getPreference(preferences));
this.setRoundResult(Preferences.roundResult.getPreference(preferences));
this.setAngleUnits(getAngleUnitsFromPrefs(preferences));
this.setNumeralBase(getNumeralBaseFromPrefs(preferences));
this.setMultiplicationSign(Preferences.multiplicationSign.getPreference(preferences));
this.setScienceNotation(Preferences.scienceNotation.getPreference(preferences));
this.setTimeout(Preferences.maxCalculationTime.getPreference(preferences));
final String groupingSeparator = Preferences.groupingSeparator.getPreference(preferences);
if (StringUtils.isEmpty(groupingSeparator)) {
this.setUseGroupingSeparator(false);
} else {
this.setUseGroupingSeparator(true);
setGroupingSeparator(groupingSeparator.charAt(0));
}
}
@NotNull
public NumeralBase getNumeralBaseFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.numeralBase.getPreference(preferences);
}
@NotNull
public AngleUnit getAngleUnitsFromPrefs(@NotNull SharedPreferences preferences) {
return Preferences.angleUnit.getPreference(preferences);
}
//for tests only
public void setDecimalGroupSymbols(@NotNull DecimalFormatSymbols decimalGroupSymbols) {
this.calculatorEngine.setDecimalGroupSymbols(decimalGroupSymbols);
}
@Override
@NotNull
public String getMultiplicationSign() {
return calculatorEngine.getMultiplicationSign();
}
@Override
public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
if ( Preferences.getPreferenceKeys().contains(key) ) {
this.softReset();
}
}
}

View File

@@ -0,0 +1,99 @@
package org.solovyev.android.calculator.model;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.Resources;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.simpleframework.xml.Serializer;
import org.simpleframework.xml.core.Persister;
import org.solovyev.android.calculator.CalculatorApplication;
import org.solovyev.android.calculator.MathEntityDao;
import org.solovyev.android.calculator.MathEntityPersistenceContainer;
import org.solovyev.android.calculator.MathPersistenceEntity;
import java.io.StringWriter;
/**
* User: serso
* Date: 10/7/12
* Time: 6:46 PM
*/
public class AndroidMathEntityDao<T extends MathPersistenceEntity> implements MathEntityDao<T> {
@NotNull
private static final String TAG = AndroidMathEntityDao.class.getSimpleName();
@Nullable
private final Integer preferenceStringId;
@NotNull
private final Context context;
@Nullable
private final Class<? extends MathEntityPersistenceContainer<T>> persistenceContainerClass;
public AndroidMathEntityDao(@Nullable Integer preferenceStringId,
@NotNull Application application,
@Nullable Class<? extends MathEntityPersistenceContainer<T>> persistenceContainerClass) {
this.preferenceStringId = preferenceStringId;
this.context = application;
this.persistenceContainerClass = persistenceContainerClass;
}
@Override
public void save(@NotNull MathEntityPersistenceContainer<T> container) {
if (preferenceStringId != null) {
final SharedPreferences settings = PreferenceManager.getDefaultSharedPreferences(context);
final SharedPreferences.Editor editor = settings.edit();
final StringWriter sw = new StringWriter();
final Serializer serializer = new Persister();
try {
serializer.write(container, sw);
} catch (Exception e) {
throw new RuntimeException(e);
}
editor.putString(context.getString(preferenceStringId), sw.toString());
editor.commit();
}
}
@Nullable
@Override
public MathEntityPersistenceContainer<T> load() {
if (persistenceContainerClass != null && preferenceStringId != null) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if (preferences != null) {
final String value = preferences.getString(context.getString(preferenceStringId), null);
if (value != null) {
final Serializer serializer = new Persister();
try {
return serializer.read(persistenceContainerClass, value);
} catch (Exception e) {
throw new RuntimeException(e);
}
}
}
}
return null;
}
@Nullable
public String getDescription(@NotNull String descriptionId) {
final Resources resources = context.getResources();
final int stringId = resources.getIdentifier(descriptionId, "string", CalculatorApplication.class.getPackage().getName());
try {
return resources.getString(stringId);
} catch (Resources.NotFoundException e) {
return null;
}
}
}

View File

@@ -0,0 +1,34 @@
package org.solovyev.android.calculator.model;
/**
* User: serso
* Date: 11/25/11
* Time: 1:40 PM
*/
public final class Messages {
// not intended for instantiation
private Messages() {
throw new AssertionError();
}
/** Arithmetic error occurred: {0} */
public static final String msg_1 = "msg_1";
/** Too complex expression */
public static final String msg_2 = "msg_2";
/** Too long execution time - check the expression */
public static final String msg_3 = "msg_3";
/** Evaluation was cancelled */
public static final String msg_4 = "msg_4";
/** No parameters are specified for function: {0} */
public static final String msg_5 = "msg_5";
/** Infinite loop is detected in expression */
public static final String msg_6 = "msg_6";
}

View File

@@ -0,0 +1,35 @@
package org.solovyev.android.calculator.onscreen;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.CalculatorPreferences;
/**
* User: serso
* Date: 11/20/12
* Time: 11:05 PM
*/
public final class CalculatorOnscreenBroadcastReceiver extends BroadcastReceiver {
public CalculatorOnscreenBroadcastReceiver() {
}
@Override
public void onReceive(@NotNull Context context,
@NotNull Intent intent) {
if ( intent.getAction().equals(Intent.ACTION_BOOT_COMPLETED) ) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context);
if ( CalculatorPreferences.OnscreenCalculator.startOnBoot.getPreferenceNoError(preferences) ) {
CalculatorOnscreenService.showNotification(context);
}
} else {
final Intent newIntent = new Intent(intent);
newIntent.setClass(context, CalculatorOnscreenService.class);
context.startService(newIntent);
}
}
}

View File

@@ -0,0 +1,217 @@
package org.solovyev.android.calculator.onscreen;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.app.Service;
import android.content.Context;
import android.content.Intent;
import android.os.IBinder;
import android.support.v4.app.NotificationCompat;
import android.util.DisplayMetrics;
import android.view.WindowManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.AndroidUtils;
import org.solovyev.android.calculator.CalculatorDisplayViewState;
import org.solovyev.android.calculator.CalculatorEditorViewState;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.external.*;
/**
* User: serso
* Date: 11/20/12
* Time: 9:42 PM
*/
public class CalculatorOnscreenService extends Service implements ExternalCalculatorStateUpdater, OnscreenViewListener {
private static final int NOTIFICATION_ID = 9031988; // my birthday =)
@NotNull
private final ExternalCalculatorIntentHandler intentHandler = new DefaultExternalCalculatorIntentHandler(this);
public static final Class<CalculatorOnscreenBroadcastReceiver> INTENT_LISTENER_CLASS = CalculatorOnscreenBroadcastReceiver.class;
@Nullable
private static String cursorColor;
@NotNull
private CalculatorOnscreenView view;
private boolean compatibilityStart = true;
private boolean viewCreated = false;
@Override
public IBinder onBind(Intent intent) {
return null;
}
@Override
public void onCreate() {
super.onCreate();
}
private void createView() {
if (!viewCreated) {
final WindowManager wm = ((WindowManager) this.getSystemService(Context.WINDOW_SERVICE));
final DisplayMetrics dm = getResources().getDisplayMetrics();
int twoThirdWidth = 2 * wm.getDefaultDisplay().getWidth() / 3;
int twoThirdHeight = 2 * wm.getDefaultDisplay().getHeight() / 3;
twoThirdWidth = Math.min(twoThirdWidth, twoThirdHeight);
twoThirdHeight = Math.max(twoThirdWidth, getHeight(twoThirdWidth));
final int baseWidth = AndroidUtils.toPixels(dm, 300);
final int width0 = Math.min(twoThirdWidth, baseWidth);
final int height0 = Math.min(twoThirdHeight, getHeight(baseWidth));
final int width = Math.min(width0, height0);
final int height = Math.max(width0, height0);
view = CalculatorOnscreenView.newInstance(this, CalculatorOnscreenViewDef.newInstance(width, height, -1, -1), getCursorColor(this), this);
view.show();
startCalculatorListening();
viewCreated = true;
}
}
private int getHeight(int width) {
return 4 * width / 3;
}
private void startCalculatorListening() {
Locator.getInstance().getExternalListenersContainer().addExternalListener(getIntentListenerClass());
}
@NotNull
private static Class<?> getIntentListenerClass() {
return INTENT_LISTENER_CLASS;
}
private void stopCalculatorListening() {
Locator.getInstance().getExternalListenersContainer().removeExternalListener(getIntentListenerClass());
}
@Override
public void onDestroy() {
stopCalculatorListening();
this.view.hide();
super.onDestroy();
}
@Override
public void updateState(@NotNull Context context, @NotNull CalculatorEditorViewState editorState, @NotNull CalculatorDisplayViewState displayState) {
view.updateDisplayState(displayState);
view.updateEditorState(editorState);
}
@NotNull
private static String getCursorColor(@NotNull Context context) {
if (cursorColor == null) {
cursorColor = Integer.toHexString(context.getResources().getColor(R.color.cpp_widget_cursor_color)).substring(2);
}
return cursorColor;
}
@Override
public void onStart(Intent intent, int startId) {
super.onStart(intent, startId);
if (this.compatibilityStart) {
handleStart(intent);
}
}
@Override
public int onStartCommand(Intent intent, int flags, int startId) {
final int result;
try {
this.compatibilityStart = false;
result = super.onStartCommand(intent, flags, startId);
handleStart(intent);
} finally {
this.compatibilityStart = true;
}
return result;
}
private void handleStart(@Nullable Intent intent) {
if ( intent != null ) {
if (isInitIntent(intent)) {
boolean createView = intent.getBooleanExtra(AndroidExternalListenersContainer.INIT_ACTION_CREATE_VIEW_EXTRA, false);
if (createView) {
hideNotification();
createView();
} else {
showNotification();
}
}
if (viewCreated) {
intentHandler.onIntent(this, intent);
}
}
}
private boolean isInitIntent(@NotNull Intent intent) {
return intent.getAction().equals(AndroidExternalListenersContainer.INIT_ACTION);
}
private void hideNotification() {
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.cancel(NOTIFICATION_ID);
}
@Override
public void onViewMinimized() {
showNotification();
stopSelf();
}
@Override
public void onViewHidden() {
stopSelf();
}
private void showNotification() {
final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
builder.setSmallIcon(R.drawable.kb_logo);
builder.setContentTitle(getText(R.string.c_app_name));
builder.setContentText(getString(R.string.open_onscreen_calculator));
final Intent intent = createShowOnscreenViewIntent(this);
builder.setContentIntent(PendingIntent.getBroadcast(this, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT));
final NotificationManager nm = (NotificationManager) getSystemService(Context.NOTIFICATION_SERVICE);
nm.notify(NOTIFICATION_ID, builder.getNotification());
}
public static void showNotification(@NotNull Context context) {
final Intent intent = new Intent(AndroidExternalListenersContainer.INIT_ACTION);
intent.setClass(context, getIntentListenerClass());
context.sendBroadcast(intent);
}
public static void showOnscreenView(@NotNull Context context) {
final Intent intent = createShowOnscreenViewIntent(context);
context.sendBroadcast(intent);
}
@NotNull
private static Intent createShowOnscreenViewIntent(@NotNull Context context) {
final Intent intent = new Intent(AndroidExternalListenersContainer.INIT_ACTION);
intent.setClass(context, getIntentListenerClass());
intent.putExtra(AndroidExternalListenersContainer.INIT_ACTION_CREATE_VIEW_EXTRA, true);
return intent;
}
}

View File

@@ -0,0 +1,461 @@
package org.solovyev.android.calculator.onscreen;
import android.content.Context;
import android.graphics.PixelFormat;
import android.util.Log;
import android.view.MotionEvent;
import android.view.View;
import android.view.WindowManager;
import android.widget.ImageView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.widget.WidgetButton;
/**
* User: serso
* Date: 11/21/12
* Time: 9:03 PM
*/
public class CalculatorOnscreenView {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
private static final String TAG = CalculatorOnscreenView.class.getSimpleName();
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private View root;
@NotNull
private View content;
@NotNull
private View header;
@NotNull
private AndroidCalculatorEditorView editorView;
@NotNull
private AndroidCalculatorDisplayView displayView;
@NotNull
private Context context;
private int width;
private int height;
private int unfoldedHeight;
@NotNull
private String cursorColor;
@Nullable
private OnscreenViewListener viewListener;
/*
**********************************************************************
*
* STATES
*
**********************************************************************
*/
private boolean minimized = false;
private boolean attached = false;
private boolean folded = false;
private boolean initialized = false;
private boolean hidden = true;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
private CalculatorOnscreenView() {
}
public static CalculatorOnscreenView newInstance(@NotNull Context context,
@NotNull CalculatorOnscreenViewDef def,
@NotNull String cursorColor,
@Nullable OnscreenViewListener viewListener) {
final CalculatorOnscreenView result = new CalculatorOnscreenView();
result.root = View.inflate(context, R.layout.onscreen_layout, null);
result.context = context;
result.width = def.getWidth();
result.height = def.getHeight();
result.cursorColor = cursorColor;
result.viewListener = viewListener;
return result;
}
/*
**********************************************************************
*
* METHODS
*
**********************************************************************
*/
public void updateDisplayState(@NotNull CalculatorDisplayViewState displayState) {
checkInit();
displayView.setState(displayState);
}
public void updateEditorState(@NotNull CalculatorEditorViewState editorState) {
checkInit();
editorView.setState(editorState);
}
private void setHeight(int height) {
checkInit();
this.height = height;
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
params.height = height;
getWindowManager().updateViewLayout(root, params);
}
/*
**********************************************************************
*
* LIFECYCLE
*
**********************************************************************
*/
private void init() {
if (!initialized) {
for (final WidgetButton widgetButton : WidgetButton.values()) {
final View button = root.findViewById(widgetButton.getButtonId());
if (button != null) {
button.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
widgetButton.onClick(context);
if ( widgetButton == WidgetButton.app ) {
minimize();
}
}
});
button.setOnLongClickListener(new View.OnLongClickListener() {
@Override
public boolean onLongClick(View v) {
widgetButton.onLongClick(context);
return true;
}
});
}
}
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
header = root.findViewById(R.id.onscreen_header);
content = root.findViewById(R.id.onscreen_content);
displayView = (AndroidCalculatorDisplayView) root.findViewById(R.id.calculator_display);
displayView.init(this.context, false);
editorView = (AndroidCalculatorEditorView) root.findViewById(R.id.calculator_editor);
editorView.init(this.context);
final View onscreenFoldButton = root.findViewById(R.id.onscreen_fold_button);
onscreenFoldButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if (folded) {
unfold();
} else {
fold();
}
}
});
final View onscreenHideButton = root.findViewById(R.id.onscreen_minimize_button);
onscreenHideButton.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
minimize();
}
});
root.findViewById(R.id.onscreen_close_button).setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
hide();
}
});
final ImageView onscreenTitleImageView = (ImageView) root.findViewById(R.id.onscreen_title);
onscreenTitleImageView.setOnTouchListener(new WindowDragTouchListener(wm, root));
initialized = true;
}
}
private void checkInit() {
if (!initialized) {
throw new IllegalStateException("init() must be called!");
}
}
public void show() {
if (hidden) {
init();
attach();
hidden = false;
}
}
public void attach() {
checkInit();
final WindowManager wm = (WindowManager) context.getSystemService(Context.WINDOW_SERVICE);
if (!attached) {
final WindowManager.LayoutParams params = new WindowManager.LayoutParams(
width,
height,
WindowManager.LayoutParams.TYPE_SYSTEM_ALERT,
WindowManager.LayoutParams.FLAG_NOT_FOCUSABLE | WindowManager.LayoutParams.FLAG_NOT_TOUCH_MODAL | WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH,
PixelFormat.TRANSLUCENT);
wm.addView(root, params);
attached = true;
}
}
private void fold() {
if (!folded) {
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
unfoldedHeight = params.height;
int newHeight = header.getHeight();
content.setVisibility(View.GONE);
setHeight(newHeight);
folded = true;
}
}
private void unfold() {
if (folded) {
content.setVisibility(View.VISIBLE);
setHeight(unfoldedHeight);
folded = false;
}
}
public void detach() {
checkInit();
if (attached) {
getWindowManager().removeView(root);
attached = false;
}
}
public void minimize() {
checkInit();
if (!minimized) {
detach();
if (viewListener != null) {
viewListener.onViewMinimized();
}
minimized = true;
}
}
public void hide() {
checkInit();
if (!hidden) {
detach();
if (viewListener != null) {
viewListener.onViewHidden();
}
hidden = true;
}
}
@NotNull
private WindowManager getWindowManager() {
return ((WindowManager) context.getSystemService(Context.WINDOW_SERVICE));
}
@NotNull
public CalculatorOnscreenViewDef getCalculatorOnscreenViewDef() {
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) root.getLayoutParams();
return CalculatorOnscreenViewDef.newInstance(width, height, params.x, params.y);
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static class WindowDragTouchListener implements View.OnTouchListener {
/*
**********************************************************************
*
* CONSTANTS
*
**********************************************************************
*/
private static final float DIST_EPS = 0f;
private static final float DIST_MAX = 100000f;
private static final long TIME_EPS = 0L;
/*
**********************************************************************
*
* FIELDS
*
**********************************************************************
*/
@NotNull
private final WindowManager wm;
private float x0;
private float y0;
private long time = 0;
@NotNull
private final View view;
/*
**********************************************************************
*
* CONSTRUCTORS
*
**********************************************************************
*/
public WindowDragTouchListener(@NotNull WindowManager wm,
@NotNull View view) {
this.wm = wm;
this.view = view;
}
@Override
public boolean onTouch(View v, MotionEvent event) {
//Log.d(TAG, "Action: " + event.getAction());
final float x1 = event.getX();
final float y1 = event.getY();
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN:
Log.d(TAG, "0:" + toString(x0, y0) + ", 1: " + toString(x1, y1));
x0 = x1;
y0 = y1;
return true;
case MotionEvent.ACTION_MOVE:
final long currentTime = System.currentTimeMillis();
if ( currentTime - time >= TIME_EPS ) {
time = currentTime;
for (int i = 0; i < event.getHistorySize(); i++) {
final float xi = event.getHistoricalX(i);
final float yi = event.getHistoricalY(i);
processMove(xi, yi);
}
processMove(x1, y1);
}
return true;
}
return false;
}
private void processMove(float x1, float y1) {
final float Δx = x1 - x0;
final float Δy = y1 - y0;
final WindowManager.LayoutParams params = (WindowManager.LayoutParams) view.getLayoutParams();
Log.d(TAG, "0:" + toString(x0, y0) + ", 1: " + toString(x1, y1) + ", Δ: " + toString(Δx, Δy) + ", params: " + toString(params.x, params.y));
boolean xInBounds = isDistanceInBounds(Δx);
boolean yInBounds = isDistanceInBounds(Δy);
if (xInBounds || yInBounds) {
if (xInBounds) {
params.x = (int) (params.x + Δx);
}
if (yInBounds) {
params.y = (int) (params.y + Δy);
}
wm.updateViewLayout(view, params);
if (xInBounds) {
x0 = x1;
}
if (yInBounds) {
y0 = y1;
}
}
}
private boolean isDistanceInBounds(float δx) {
δx = Math.abs(δx);
return δx >= DIST_EPS && δx < DIST_MAX;
}
@NotNull
private static String toString(float x, float y) {
return "(" + formatFloat(x) + ", " + formatFloat(y) + ")";
}
private static String formatFloat(float value) {
if (value >= 0) {
return "+" + String.format("%.2f", value);
} else {
return String.format("%.2f", value);
}
}
}
}

View File

@@ -0,0 +1,61 @@
package org.solovyev.android.calculator.onscreen;
import org.jetbrains.annotations.NotNull;
/**
* User: serso
* Date: 11/21/12
* Time: 10:55 PM
*/
public class CalculatorOnscreenViewDef {
private int width;
private int height;
private int x;
private int y;
private CalculatorOnscreenViewDef() {
}
@NotNull
public static CalculatorOnscreenViewDef newInstance(int width, int height, int x, int y) {
final CalculatorOnscreenViewDef result = new CalculatorOnscreenViewDef();
result.width = width;
result.height = height;
result.x = x;
result.y = y;
return result;
}
public int getWidth() {
return width;
}
public void setWidth(int width) {
this.width = width;
}
public int getHeight() {
return height;
}
public void setHeight(int height) {
this.height = height;
}
public int getX() {
return x;
}
public void setX(int x) {
this.x = x;
}
public int getY() {
return y;
}
public void setY(int y) {
this.y = y;
}
}

View File

@@ -0,0 +1,15 @@
package org.solovyev.android.calculator.onscreen;
/**
* User: serso
* Date: 11/21/12
* Time: 9:45 PM
*/
public interface OnscreenViewListener {
// view minimized == view is in the action bar
void onViewMinimized();
// view hidden == view closed
void onViewHidden();
}

View File

@@ -0,0 +1,34 @@
package org.solovyev.android.calculator.plot;
import android.app.ActionBar;
import android.content.Intent;
import android.os.Bundle;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.CalculatorFragmentActivity;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.about.CalculatorFragmentType;
/**
* User: serso
* Date: 9/30/12
* Time: 4:56 PM
*/
public class CalculatorPlotActivity extends CalculatorFragmentActivity {
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Intent intent = getIntent();
final Bundle arguments;
if (intent != null) {
arguments = intent.getExtras();
} else {
arguments = null;
}
getSupportActionBar().setNavigationMode(ActionBar.NAVIGATION_MODE_STANDARD);
getActivityHelper().setFragment(this, CalculatorFragmentType.plotter, arguments, R.id.main_layout);
}
}

View File

@@ -0,0 +1,582 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.plot;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.os.Handler;
import android.preference.PreferenceManager;
import android.support.v4.app.FragmentActivity;
import android.util.Log;
import android.view.View;
import android.view.ViewGroup;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuInflater;
import com.actionbarsherlock.view.MenuItem;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.function.Constant;
import jscl.text.ParseException;
import org.achartengine.GraphicalView;
import org.achartengine.chart.XYChart;
import org.achartengine.model.XYSeries;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.tools.PanListener;
import org.achartengine.tools.ZoomEvent;
import org.achartengine.tools.ZoomListener;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.*;
import org.solovyev.android.menu.ActivityMenu;
import org.solovyev.android.menu.IdentifiableMenuItem;
import org.solovyev.android.menu.ListActivityMenu;
import org.solovyev.android.sherlock.menu.SherlockMenuHelper;
import org.solovyev.common.MutableObject;
import org.solovyev.common.collections.CollectionsUtils;
import java.io.Serializable;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* User: serso
* Date: 12/1/11
* Time: 12:40 AM
*/
public class CalculatorPlotFragment extends CalculatorFragment implements CalculatorEventListener {
private static final String TAG = CalculatorPlotFragment.class.getSimpleName();
private static final int DEFAULT_MIN_NUMBER = -10;
private static final int DEFAULT_MAX_NUMBER = 10;
public static final String INPUT = "plotter_input";
private static final String PLOT_BOUNDARIES = "plot_boundaries";
public static final long EVAL_DELAY_MILLIS = 200;
@Nullable
private XYChart chart;
/**
* The encapsulated graphical view.
*/
@Nullable
private GraphicalView graphicalView;
// thread which calculated data for graph view
@NotNull
private final Executor plotExecutor = Executors.newSingleThreadExecutor();
// thread for applying UI changes
@NotNull
private final Handler uiHandler = new Handler();
@NotNull
private PreparedInput preparedInput;
@Nullable
private Input input;
@NotNull
private final CalculatorEventHolder lastEventHolder = new CalculatorEventHolder(CalculatorUtils.createFirstEventDataId());
private int bgColor;
@NotNull
private ActivityMenu<Menu, MenuItem> fragmentMenu = ListActivityMenu.fromResource(R.menu.plot_menu, PlotMenu.class, SherlockMenuHelper.getInstance());
public CalculatorPlotFragment() {
super(CalculatorApplication.getInstance().createFragmentHelper(R.layout.plot_fragment, R.string.c_graph, false));
}
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
final Bundle arguments = getArguments();
if (arguments != null) {
input = (Input) arguments.getSerializable(INPUT);
}
if (input == null) {
this.bgColor = getResources().getColor(R.color.cpp_pane_background);
} else {
this.bgColor = getResources().getColor(android.R.color.transparent);
}
setHasOptionsMenu(true);
}
@NotNull
private static PreparedInput prepareInputFromDisplay(@NotNull CalculatorDisplayViewState displayState, @Nullable Bundle savedInstanceState) {
try {
if (displayState.isValid() && displayState.getResult() != null) {
final Generic expression = displayState.getResult();
if (CalculatorUtils.isPlotPossible(expression, displayState.getOperation())) {
final Constant constant = CollectionsUtils.getFirstCollectionElement(CalculatorUtils.getNotSystemConstants(expression));
final Input input = new Input(expression.toString(), constant.getName());
return prepareInput(input, false, savedInstanceState);
}
}
} catch (RuntimeException e) {
Log.e(TAG, e.getLocalizedMessage(), e);
}
return PreparedInput.newErrorInstance(false);
}
@NotNull
private static PreparedInput prepareInput(@NotNull Input input, boolean fromInputArgs, @Nullable Bundle savedInstanceState) {
PreparedInput result;
try {
final PreparedExpression preparedExpression = ToJsclTextProcessor.getInstance().process(input.getExpression());
final Generic expression = Expression.valueOf(preparedExpression.getExpression());
final Constant variable = new Constant(input.getVariableName());
PlotBoundaries plotBoundaries = null;
if ( savedInstanceState != null ) {
plotBoundaries = (PlotBoundaries)savedInstanceState.getSerializable(PLOT_BOUNDARIES);
}
result = PreparedInput.newInstance(input, expression, variable, fromInputArgs, plotBoundaries);
} catch (ParseException e) {
result = PreparedInput.newErrorInstance(fromInputArgs);
Locator.getInstance().getNotifier().showMessage(e);
} catch (CalculatorParseException e) {
result = PreparedInput.newErrorInstance(fromInputArgs);
Locator.getInstance().getNotifier().showMessage(e);
}
return result;
}
private void createChart() {
if (!preparedInput.isError()) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final Boolean interpolate = CalculatorPreferences.Graph.interpolate.getPreference(preferences);
final GraphLineColor realLineColor = CalculatorPreferences.Graph.lineColorReal.getPreference(preferences);
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
//noinspection ConstantConditions
try {
this.chart = PlotUtils.prepareChart(getMinValue(null), getMaxValue(null), preparedInput.getExpression(), preparedInput.getVariable(), bgColor, interpolate, realLineColor.getColor(), imagLineColor.getColor());
} catch (ArithmeticException e) {
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
}
} else {
onError();
}
}
@Override
public void onViewCreated(View view, Bundle savedInstanceState) {
super.onViewCreated(view, savedInstanceState);
if (input == null) {
this.preparedInput = prepareInputFromDisplay(Locator.getInstance().getDisplay().getViewState(), savedInstanceState);
} else {
this.preparedInput = prepareInput(input, true, savedInstanceState);
}
}
@Override
public void onSaveInstanceState(Bundle out) {
super.onSaveInstanceState(out);
if (chart != null) {
out.putSerializable(PLOT_BOUNDARIES, new PlotBoundaries(chart.getRenderer()));
}
}
@Override
public void onResume() {
super.onResume();
createChart();
createGraphicalView(getView(), this.preparedInput.getPlotBoundaries());
}
private void createGraphicalView(@NotNull View root, @Nullable PlotBoundaries plotBoundaries) {
final ViewGroup graphContainer = (ViewGroup) root.findViewById(R.id.main_fragment_layout);
if (graphicalView != null) {
graphContainer.removeView(graphicalView);
}
if (!preparedInput.isError()) {
final XYChart chart = this.chart;
assert chart != null;
double minValue = getMinValue(plotBoundaries);
double maxValue = getMaxValue(plotBoundaries);
// reverting boundaries (as in prepareChart() we add some cached values )
double minX = Double.MAX_VALUE;
double minY = Double.MAX_VALUE;
double maxX = Double.MIN_VALUE;
double maxY = Double.MIN_VALUE;
for (XYSeries series : chart.getDataset().getSeries()) {
minX = Math.min(minX, series.getMinX());
minY = Math.min(minY, series.getMinY());
maxX = Math.max(maxX, series.getMaxX());
maxY = Math.max(maxY, series.getMaxY());
}
if (plotBoundaries == null) {
chart.getRenderer().setXAxisMin(Math.max(minX, minValue));
chart.getRenderer().setYAxisMin(Math.max(minY, minValue));
chart.getRenderer().setXAxisMax(Math.min(maxX, maxValue));
chart.getRenderer().setYAxisMax(Math.min(maxY, maxValue));
} else {
chart.getRenderer().setXAxisMin(plotBoundaries.xMin);
chart.getRenderer().setYAxisMin(plotBoundaries.yMin);
chart.getRenderer().setXAxisMax(plotBoundaries.xMax);
chart.getRenderer().setYAxisMax(plotBoundaries.yMax);
}
graphicalView = new GraphicalView(this.getActivity(), chart);
graphicalView.setBackgroundColor(this.bgColor);
graphicalView.addZoomListener(new ZoomListener() {
@Override
public void zoomApplied(ZoomEvent e) {
updateDataSets(chart);
}
@Override
public void zoomReset() {
updateDataSets(chart);
}
}, true, true);
graphicalView.addPanListener(new PanListener() {
@Override
public void panApplied() {
updateDataSets(chart);
}
});
graphContainer.addView(graphicalView);
updateDataSets(chart, 50);
} else {
graphicalView = null;
}
}
private double getMaxValue(@Nullable PlotBoundaries plotBoundaries) {
return plotBoundaries == null ? DEFAULT_MAX_NUMBER : plotBoundaries.xMax;
}
private double getMinValue(@Nullable PlotBoundaries plotBoundaries) {
return plotBoundaries == null ? DEFAULT_MIN_NUMBER : plotBoundaries.xMin;
}
private void updateDataSets(@NotNull final XYChart chart) {
updateDataSets(chart, EVAL_DELAY_MILLIS);
}
private void updateDataSets(@NotNull final XYChart chart, long millisToWait) {
final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this.getActivity());
final GraphLineColor imagLineColor = CalculatorPreferences.Graph.lineColorImag.getPreference(preferences);
final Generic expression = preparedInput.getExpression();
final Constant variable = preparedInput.getVariable();
if (expression != null && variable != null) {
pendingOperation.setObject(new Runnable() {
@Override
public void run() {
// allow only one runner at one time
synchronized (pendingOperation) {
//lock all operations with history
if (pendingOperation.getObject() == this) {
plotExecutor.execute(new Runnable() {
@Override
public void run() {
final XYMultipleSeriesRenderer dr = chart.getRenderer();
final MyXYSeries realSeries = (MyXYSeries) chart.getDataset().getSeriesAt(0);
final MyXYSeries imagSeries;
if (chart.getDataset().getSeriesCount() > 1) {
imagSeries = (MyXYSeries) chart.getDataset().getSeriesAt(1);
} else {
imagSeries = new MyXYSeries(PlotUtils.getImagFunctionName(variable), PlotUtils.DEFAULT_NUMBER_OF_STEPS * 2);
}
try {
if (PlotUtils.addXY(dr.getXAxisMin(), dr.getXAxisMax(), expression, variable, realSeries, imagSeries, true, PlotUtils.DEFAULT_NUMBER_OF_STEPS)) {
if (chart.getDataset().getSeriesCount() <= 1) {
chart.getDataset().addSeries(imagSeries);
chart.getRenderer().addSeriesRenderer(PlotUtils.createImagRenderer(imagLineColor.getColor()));
}
}
} catch (ArithmeticException e) {
PlotUtils.handleArithmeticException(e, CalculatorPlotFragment.this);
}
uiHandler.post(new Runnable() {
@Override
public void run() {
graphicalView.repaint();
}
});
}
});
}
}
}
});
}
uiHandler.postDelayed(pendingOperation.getObject(), millisToWait);
}
@NotNull
private final MutableObject<Runnable> pendingOperation = new MutableObject<Runnable>();
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable final Object data) {
if (calculatorEventType.isOfType(CalculatorEventType.display_state_changed)) {
if (!preparedInput.isFromInputArgs()) {
final CalculatorEventHolder.Result result = this.lastEventHolder.apply(calculatorEventData);
if (result.isNewAfter()) {
this.preparedInput = prepareInputFromDisplay(((CalculatorDisplayChangeEventData) data).getNewValue(), null);
createChart();
uiHandler.post(new Runnable() {
@Override
public void run() {
final View view = getView();
if (view != null) {
createGraphicalView(view, preparedInput.getPlotBoundaries());
}
}
});
}
}
}
}
/* public void zoomInClickHandler(@NotNull View v) {
this.graphicalView.zoomIn();
}
public void zoomOutClickHandler(@NotNull View v) {
this.graphicalView.zoomOut();
}*/
/*
**********************************************************************
*
* MENU
*
**********************************************************************
*/
@Override
public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
super.onCreateOptionsMenu(menu, inflater);
final FragmentActivity activity = this.getActivity();
if (activity != null) {
fragmentMenu.onCreateOptionsMenu(activity, menu);
}
}
@Override
public void onPrepareOptionsMenu(Menu menu) {
super.onPrepareOptionsMenu(menu);
final FragmentActivity activity = this.getActivity();
if (activity != null) {
fragmentMenu.onPrepareOptionsMenu(activity, menu);
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
return super.onOptionsItemSelected(item) || fragmentMenu.onOptionsItemSelected(this.getActivity(), item);
}
public void onError() {
this.chart = null;
}
/*
**********************************************************************
*
* STATIC
*
**********************************************************************
*/
private static enum PlotMenu implements IdentifiableMenuItem<MenuItem> {
preferences(R.id.menu_plot_settings) {
@Override
public void onClick(@NotNull MenuItem data, @NotNull Context context) {
context.startActivity(new Intent(context, CalculatorPlotPreferenceActivity.class));
}
};
private final int itemId;
private PlotMenu(int itemId) {
this.itemId = itemId;
}
@NotNull
@Override
public Integer getItemId() {
return itemId;
}
}
public static final class PlotBoundaries implements Serializable {
private double xMin;
private double xMax;
private double yMin;
private double yMax;
public PlotBoundaries() {
}
public PlotBoundaries(@NotNull XYMultipleSeriesRenderer renderer) {
this.xMin = renderer.getXAxisMin();
this.yMin = renderer.getYAxisMin();
this.xMax = renderer.getXAxisMax();
this.yMax = renderer.getYAxisMax();
}
@Override
public String toString() {
return "PlotBoundaries{" +
"yMax=" + yMax +
", yMin=" + yMin +
", xMax=" + xMax +
", xMin=" + xMin +
'}';
}
}
public static class PreparedInput {
@Nullable
private Input input;
@Nullable
private Generic expression;
@Nullable
private Constant variable;
private boolean fromInputArgs;
@Nullable
private PlotBoundaries plotBoundaries = null;
private PreparedInput() {
}
@NotNull
public static PreparedInput newInstance(@NotNull Input input, @NotNull Generic expression, @NotNull Constant variable, boolean fromInputArgs, @Nullable PlotBoundaries plotBoundaries) {
final PreparedInput result = new PreparedInput();
result.input = input;
result.expression = expression;
result.variable = variable;
result.fromInputArgs = fromInputArgs;
result.plotBoundaries = plotBoundaries;
return result;
}
@NotNull
public static PreparedInput newErrorInstance(boolean fromInputArgs) {
final PreparedInput result = new PreparedInput();
result.input = null;
result.expression = null;
result.variable = null;
result.fromInputArgs = fromInputArgs;
return result;
}
public boolean isFromInputArgs() {
return fromInputArgs;
}
@Nullable
public Input getInput() {
return input;
}
@Nullable
public Generic getExpression() {
return expression;
}
@Nullable
public PlotBoundaries getPlotBoundaries() {
return plotBoundaries;
}
@Nullable
public Constant getVariable() {
return variable;
}
public boolean isError() {
return input == null || expression == null || variable == null;
}
}
public static class Input implements Serializable {
@NotNull
private String expression;
@NotNull
private String variableName;
public Input(@NotNull String expression, @NotNull String variableName) {
this.expression = expression;
this.variableName = variableName;
}
@NotNull
public String getExpression() {
return expression;
}
@NotNull
public String getVariableName() {
return variableName;
}
}
}

View File

@@ -0,0 +1,21 @@
package org.solovyev.android.calculator.plot;
import android.os.Bundle;
import com.actionbarsherlock.app.SherlockPreferenceActivity;
import org.solovyev.android.calculator.R;
/**
* User: serso
* Date: 10/4/12
* Time: 9:01 PM
*/
public class CalculatorPlotPreferenceActivity extends SherlockPreferenceActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//noinspection deprecation
addPreferencesFromResource(R.xml.preferences_plot);
}
}

View File

@@ -0,0 +1,28 @@
package org.solovyev.android.calculator.plot;
import android.graphics.Color;
/**
* User: serso
* Date: 10/4/12
* Time: 10:08 PM
*/
public enum GraphLineColor {
white(Color.WHITE),
grey(Color.GRAY),
red(Color.RED),
blue(Color.rgb(16, 100, 140)),
green(Color.GREEN);
private final int color;
private GraphLineColor(int color) {
this.color = color;
}
public int getColor() {
return this.color;
}
}

View File

@@ -0,0 +1,258 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.plot;
import org.achartengine.model.XYSeries;
import org.achartengine.util.MathHelper;
import java.util.*;
/**
* User: serso
* Date: 12/5/11
* Time: 8:43 PM
*/
/**
* BEST SOLUTION IS TO MODIFY LIBRARY CLASS
* NOTE: this class is a copy of XYSeries with some modifications:
* 1. Possibility to insert point in th emiddle og the range
*/
public class MyXYSeries extends XYSeries {
/**
* The series title.
*/
private String mTitle;
/**
* A list to contain the values for the X axis.
*/
private List<Double> mX = new ArrayList<Double>();
/**
* A list to contain the values for the Y axis.
*/
private List<Double> mY = new ArrayList<Double>();
/**
* The minimum value for the X axis.
*/
private double mMinX = MathHelper.NULL_VALUE;
/**
* The maximum value for the X axis.
*/
private double mMaxX = -MathHelper.NULL_VALUE;
/**
* The minimum value for the Y axis.
*/
private double mMinY = MathHelper.NULL_VALUE;
/**
* The maximum value for the Y axis.
*/
private double mMaxY = -MathHelper.NULL_VALUE;
/**
* The scale number for this series.
*/
private int mScaleNumber;
/**
* Builds a new XY series.
*
* @param title the series title.
*/
public MyXYSeries(String title) {
this(title, 10);
}
public MyXYSeries(String title, int initialCapacity) {
super(title, 0);
this.mX = new ArrayList<Double>(initialCapacity);
this.mY = new ArrayList<Double>(initialCapacity);
mTitle = title;
mScaleNumber = 0;
initRange();
}
public int getScaleNumber() {
return mScaleNumber;
}
/**
* Initializes the range for both axes.
*/
private void initRange() {
mMinX = MathHelper.NULL_VALUE;
mMaxX = -MathHelper.NULL_VALUE;
mMinY = MathHelper.NULL_VALUE;
mMaxY = -MathHelper.NULL_VALUE;
int length = getItemCount();
for (int k = 0; k < length; k++) {
double x = getX(k);
double y = getY(k);
updateRange(x, y);
}
}
/**
* Updates the range on both axes.
*
* @param x the new x value
* @param y the new y value
*/
private void updateRange(double x, double y) {
mMinX = Math.min(mMinX, x);
mMaxX = Math.max(mMaxX, x);
mMinY = Math.min(mMinY, y);
mMaxY = Math.max(mMaxY, y);
}
/**
* Returns the series title.
*
* @return the series title
*/
public String getTitle() {
return mTitle;
}
/**
* Sets the series title.
*
* @param title the series title
*/
public void setTitle(String title) {
mTitle = title;
}
/**
* Adds a new value to the series.
*
* @param x the value for the X axis
* @param y the value for the Y axis
*/
public void add(double x, double y) {
boolean added = false;
for (int i = 0; i < mX.size(); i++ ) {
if ( mX.get(i) > x ) {
mX.add(i, x);
mY.add(i, y);
added = true;
break;
}
}
if ( !added ) {
mX.add(x);
mY.add(y);
}
updateRange(x, y);
}
public boolean needToAdd(double density, double x) {
boolean result = true;
for (Double x1 : mX) {
if (Math.abs(x - x1) < density) {
result = false;
break;
}
}
return result;
}
/**
* Removes an existing value from the series.
*
* @param index the index in the series of the value to remove
*/
public void remove(int index) {
double removedX = mX.remove(index);
double removedY = mY.remove(index);
if (removedX == mMinX || removedX == mMaxX || removedY == mMinY || removedY == mMaxY) {
initRange();
}
}
/**
* Removes all the existing values from the series.
*/
public void clear() {
mX.clear();
mY.clear();
initRange();
}
/**
* Returns the X axis value at the specified index.
*
* @param index the index
* @return the X value
*/
public double getX(int index) {
return mX.get(index);
}
/**
* Returns the Y axis value at the specified index.
*
* @param index the index
* @return the Y value
*/
public double getY(int index) {
return mY.get(index);
}
/**
* Returns the series item count.
*
* @return the series item count
*/
public int getItemCount() {
return mX == null ? 0 : mX.size();
}
/**
* Returns the minimum value on the X axis.
*
* @return the X axis minimum value
*/
public double getMinX() {
return mMinX;
}
/**
* Returns the minimum value on the Y axis.
*
* @return the Y axis minimum value
*/
public double getMinY() {
return mMinY;
}
/**
* Returns the maximum value on the X axis.
*
* @return the X axis maximum value
*/
public double getMaxX() {
return mMaxX;
}
/**
* Returns the maximum value on the Y axis.
*
* @return the Y axis maximum value
*/
public double getMaxY() {
return mMaxY;
}
}

View File

@@ -0,0 +1,440 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.plot;
import jscl.math.Expression;
import jscl.math.Generic;
import jscl.math.JsclInteger;
import jscl.math.NumericWrapper;
import jscl.math.function.Constant;
import jscl.math.numeric.Complex;
import jscl.math.numeric.Numeric;
import jscl.math.numeric.Real;
import org.achartengine.chart.CubicLineChart;
import org.achartengine.chart.PointStyle;
import org.achartengine.chart.ScatterChart;
import org.achartengine.chart.XYChart;
import org.achartengine.model.XYMultipleSeriesDataset;
import org.achartengine.renderer.BasicStroke;
import org.achartengine.renderer.XYMultipleSeriesRenderer;
import org.achartengine.renderer.XYSeriesRenderer;
import org.achartengine.util.MathHelper;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.common.msg.MessageType;
import org.solovyev.common.text.StringUtils;
import java.util.Arrays;
/**
* User: serso
* Date: 12/5/11
* Time: 8:58 PM
*/
public final class PlotUtils {
private static final double MAX_Y_DIFF = 1;
private static final double MAX_X_DIFF = 1;
static final int DEFAULT_NUMBER_OF_STEPS = 100;
// not intended for instantiation
private PlotUtils() {
throw new AssertionError();
}
public static boolean addXY(double minValue,
double maxValue,
@NotNull Generic expression,
@NotNull Constant variable,
@NotNull MyXYSeries realSeries,
@Nullable MyXYSeries imagSeries,
boolean addExtra,
int numberOfSteps) {
boolean imagExists = false;
double min = Math.min(minValue, maxValue);
double max = Math.max(minValue, maxValue);
double dist = max - min;
if (addExtra) {
min = min - dist;
max = max + dist;
}
final double eps = 0.000000001;
final double defaultStep = Math.max(dist / numberOfSteps, eps);
double step = defaultStep;
final Point real = new Point();
final Point imag = new Point();
double x = min;
while (x <= max) {
boolean needToCalculateRealY = realSeries.needToAdd(step, x);
if (needToCalculateRealY) {
final Complex c = calculatorExpression(expression, variable, x);
Double y = prepareY(c.realPart());
if (y != null) {
real.moveToNextPoint(x, y);
addSingularityPoint(realSeries, real);
realSeries.add(x, y);
}
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
if (needToCalculateImagY) {
y = prepareY(c.imaginaryPart());
if (y != null) {
imag.moveToNextPoint(x, y);
addSingularityPoint(imagSeries, imag);
imagSeries.add(x, y);
}
if (c.imaginaryPart() != 0d) {
imagExists = true;
}
}
} else {
boolean needToCalculateImagY = imagSeries != null && imagSeries.needToAdd(step, x);
if (needToCalculateImagY) {
final Complex c = calculatorExpression(expression, variable, x);
Double y = prepareY(c.imaginaryPart());
if (y != null) {
imag.moveToNextPoint(x, y);
addSingularityPoint(imagSeries, imag);
imagSeries.add(x, y);
}
if (c.imaginaryPart() != 0d) {
imagExists = true;
}
}
}
step = updateStep(real, step, defaultStep / 2);
x += step;
}
return imagExists;
}
@NotNull
static String getImagFunctionName(@NotNull Constant variable) {
return "g(" + variable.getName() + ")" + " = " + "Im(ƒ(" + variable.getName() + "))";
}
@NotNull
private static String getRealFunctionName(@NotNull Generic expression, @NotNull Constant variable) {
return "ƒ(" + variable.getName() + ")" + " = " + expression.toString();
}
@NotNull
static XYChart prepareChart(final double minValue,
final double maxValue,
@NotNull final Generic expression,
@NotNull final Constant variable,
int bgColor,
boolean interpolate,
int realLineColor,
int imagLineColor) {
final MyXYSeries realSeries = new MyXYSeries(getRealFunctionName(expression, variable), DEFAULT_NUMBER_OF_STEPS * 2);
final MyXYSeries imagSeries = new MyXYSeries(getImagFunctionName(variable), DEFAULT_NUMBER_OF_STEPS * 2);
boolean imagExists = addXY(minValue, maxValue, expression, variable, realSeries, imagSeries, false, DEFAULT_NUMBER_OF_STEPS);
final XYMultipleSeriesDataset data = new XYMultipleSeriesDataset();
data.addSeries(realSeries);
if (imagExists) {
data.addSeries(imagSeries);
}
final XYMultipleSeriesRenderer renderer = new XYMultipleSeriesRenderer();
renderer.setShowGrid(true);
renderer.setXTitle(variable.getName());
renderer.setYTitle("f(" + variable.getName() + ")");
renderer.setChartTitleTextSize(25);
renderer.setAxisTitleTextSize(25);
renderer.setLabelsTextSize(25);
renderer.setLegendTextSize(25);
renderer.setMargins(new int[]{25, 25, 25, 25});
renderer.setApplyBackgroundColor(true);
renderer.setBackgroundColor(bgColor);
renderer.setMarginsColor(bgColor);
renderer.setZoomEnabled(true);
renderer.setZoomButtonsVisible(true);
renderer.addSeriesRenderer(createCommonRenderer(realLineColor));
if (imagExists) {
renderer.addSeriesRenderer(createImagRenderer(imagLineColor));
}
if (interpolate) {
return new CubicLineChart(data, renderer, 0.1f);
} else {
return new ScatterChart(data, renderer);
}
}
static XYSeriesRenderer createImagRenderer(int color) {
final XYSeriesRenderer imagRenderer = createCommonRenderer(color);
imagRenderer.setStroke(BasicStroke.DASHED);
return imagRenderer;
}
@NotNull
private static XYSeriesRenderer createCommonRenderer(int color) {
final XYSeriesRenderer renderer = new XYSeriesRenderer();
renderer.setFillPoints(true);
renderer.setPointStyle(PointStyle.CIRCLE);
renderer.setLineWidth(3);
renderer.setColor(color);
renderer.setStroke(BasicStroke.SOLID);
return renderer;
}
static void handleArithmeticException(@NotNull ArithmeticException e, @NotNull CalculatorPlotFragment calculatorPlotFragment) {
String message = e.getLocalizedMessage();
if (StringUtils.isEmpty(message)) {
message = e.getMessage();
}
Locator.getInstance().getNotifier().showMessage(R.string.arithmetic_error_while_plot, MessageType.error, Arrays.asList(message));
calculatorPlotFragment.onError();
}
private static class Point {
private static final double DEFAULT = Double.MIN_VALUE;
private double x0 = DEFAULT;
private double x1 = DEFAULT;
private double x2 = DEFAULT;
private double y0 = DEFAULT;
private double y1 = DEFAULT;
private double y2 = DEFAULT;
private Point() {
}
public void moveToNextPoint(double x, double y) {
if ( this.x2 == x ) {
return;
}
this.x0 = this.x1;
this.x1 = this.x2;
this.x2 = x;
this.y0 = this.y1;
this.y1 = this.y2;
this.y2 = y;
}
public boolean isFullyDefined() {
return x0 != DEFAULT && x1 != DEFAULT && x2 != DEFAULT && y0 != DEFAULT && y1 != DEFAULT && y2 != DEFAULT;
}
public double getDx2() {
return x2 - x1;
}
public double getAbsDx2() {
if ( x2 > x1 ) {
return Math.abs(x2 - x1);
} else {
return Math.abs(x1 - x2);
}
}
public double getAbsDx1() {
if ( x1 > x0 ) {
return Math.abs(x1 - x0);
} else {
return Math.abs(x0 - x1);
}
}
public double getAbsDy1() {
if ( y1 > y0 ) {
return Math.abs(y1 - y0);
} else {
return Math.abs(y0 - y1);
}
}
public double getAbsDy2() {
if ( y2 > y1 ) {
return Math.abs(y2 - y1);
} else {
return Math.abs(y1 - y2);
}
}
public double getX0() {
return x0;
}
public double getX1() {
return x1;
}
public double getX2() {
return x2;
}
public boolean isX2Defined() {
return x2 != DEFAULT;
}
public double getY0() {
return y0;
}
public double getY1() {
return y1;
}
public double getY2() {
return y2;
}
public void clearHistory () {
this.x0 = DEFAULT;
this.x1 = DEFAULT;
this.y0 = DEFAULT;
this.y1 = DEFAULT;
}
public double getAbsDyDx2() {
double dx2 = this.getAbsDx2();
double dy2 = this.getAbsDy2();
return dy2 / dx2;
}
public double getAbsDyDx1() {
double dx1 = this.getAbsDx1();
double dy1 = this.getAbsDy1();
return dy1 / dx1;
}
public double getDyDx1() {
double result = getAbsDyDx1();
return y1 > y0 ? result : -result;
}
public double getDyDx2() {
double result = getAbsDyDx2();
return y2 > y1 ? result : -result;
}
@Override
public String toString() {
return "Point{" +
"x0=" + x0 +
", x1=" + x1 +
", x2=" + x2 +
", y0=" + y0 +
", y1=" + y1 +
", y2=" + y2 +
'}';
}
}
private static double updateStep(@NotNull Point real,
double step,
double eps) {
if ( !real.isFullyDefined() ) {
return step;
} else {
double dydx2 = real.getAbsDyDx2();
double dydx1 = real.getAbsDyDx1();
double k = dydx2 / dydx1;
if ( k > 1 ) {
step = step / k;
} else if ( k > 0 ) {
step = step * k;
}
return Math.max(step, eps);
}
}
@NotNull
public static Complex calculatorExpression(@NotNull Generic expression, @NotNull Constant variable, double x) {
try {
return unwrap(expression.substitute(variable, Expression.valueOf(x)).numeric());
} catch (RuntimeException e) {
return NaN;
}
}
public static void addSingularityPoint(@NotNull MyXYSeries series,
@NotNull Point point) {
if (point.isFullyDefined()) {
// y or prevY should be more than 1d because if they are too small false singularity may occur (e.g., 1/0.000000000000000001)
// double dy0 = y1 - y0;
// double dx0 = x1 - x0;
// double dydx0 = dy0 / dx0;
double dy2 = point.getAbsDy2();
double dx2 = point.getAbsDx2();
//double dx1 = x2 - x1;
// double dydx1 = dy2 / dx1;
if ( dy2 > MAX_Y_DIFF && dx2 < MAX_X_DIFF && isDifferentSign(point.getY2(), point.getY1()) && isDifferentSign(point.getDyDx1(), point.getDyDx2())) {
//Log.d(CalculatorPlotActivity.class.getName(), "Singularity: " + point);
//Log.d(CalculatorPlotActivity.class.getName(), String.valueOf(prevX + Math.abs(x - prevX) / 2) + ", null");
series.add(point.getX1() + point.getAbsDx2() / 2, MathHelper.NULL_VALUE);
point.clearHistory();
}
}
}
private static boolean isDifferentSign(@NotNull Double y0, @NotNull Double y1) {
return (y0 >= 0 && y1 < 0) || (y1 >= 0 && y0 < 0);
}
@Nullable
public static Double prepareY(double y) {
if (Double.isNaN(y)) {
return null;
} else {
return y;
}
}
private static final Complex NaN = Complex.valueOf(Double.NaN, 0d);
@NotNull
public static Complex unwrap(@Nullable Generic numeric) {
if (numeric instanceof JsclInteger) {
return Complex.valueOf(((JsclInteger) numeric).intValue(), 0d);
} else if (numeric instanceof NumericWrapper) {
return unwrap(((NumericWrapper) numeric).content());
} else {
return NaN;
}
}
@NotNull
public static Complex unwrap(@Nullable Numeric content) {
if (content instanceof Real) {
return Complex.valueOf(((Real) content).doubleValue(), 0d);
} else if (content instanceof Complex) {
return ((Complex) content);
} else {
throw new ArithmeticException();
}
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.view;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.view.drag.DirectionDragButton;
/**
* User: serso
* Date: 11/22/11
* Time: 2:42 PM
*/
public class AngleUnitsButton extends DirectionDragButton {
public AngleUnitsButton(Context context, @NotNull AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void initDirectionTextPaint(@NotNull Paint basePaint,
@NotNull DirectionTextData directionTextData,
@NotNull Resources resources) {
super.initDirectionTextPaint(basePaint, directionTextData, resources);
final TextPaint directionTextPaint = directionTextData.getPaint();
if (Locator.getInstance().getEngine().getAngleUnits().name().equals(directionTextData.getText())) {
directionTextPaint.setColor(resources.getColor(R.color.cpp_selected_angle_unit_text_color));
} else {
directionTextPaint.setColor(resources.getColor(R.color.cpp_default_text_color));
directionTextPaint.setAlpha(getDirectionTextAlpha());
}
}
}

View File

@@ -0,0 +1,47 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.view;
import android.content.Context;
import android.content.SharedPreferences;
import android.util.AttributeSet;
import android.widget.TextView;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.model.AndroidCalculatorEngine;
/**
* User: serso
* Date: 12/10/11
* Time: 10:34 PM
*/
public class CalculatorAdditionalTitle extends TextView implements SharedPreferences.OnSharedPreferenceChangeListener {
public CalculatorAdditionalTitle(Context context) {
super(context);
}
public CalculatorAdditionalTitle(Context context, AttributeSet attrs) {
super(context, attrs);
}
public CalculatorAdditionalTitle(Context context, AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
}
public void init(@NotNull SharedPreferences preferences) {
onSharedPreferenceChanged(preferences, null);
}
@Override
public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) {
setText(((AndroidCalculatorEngine) Locator.getInstance().getEngine()).getNumeralBaseFromPrefs(preferences)
+ " / " +
((AndroidCalculatorEngine) Locator.getInstance().getEngine()).getAngleUnitsFromPrefs(preferences));
}
}

View File

@@ -0,0 +1,98 @@
package org.solovyev.android.calculator.view;
import android.app.AlertDialog;
import android.content.Context;
import android.view.View;
import android.view.WindowManager;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.CalculatorParseException;
import org.solovyev.android.calculator.R;
import org.solovyev.android.calculator.ToJsclTextProcessor;
import org.solovyev.android.calculator.units.CalculatorNumeralBase;
import org.solovyev.common.MutableObject;
import org.solovyev.common.text.StringUtils;
import org.solovyev.math.units.Unit;
import org.solovyev.math.units.UnitImpl;
import java.util.Arrays;
/**
* User: serso
* Date: 4/22/12
* Time: 12:20 AM
*/
public class NumeralBaseConverterDialog {
@Nullable
private String initialFromValue;
public NumeralBaseConverterDialog(String initialFromValue) {
this.initialFromValue = initialFromValue;
}
public void show(@NotNull Context context) {
final UnitConverterViewBuilder b = new UnitConverterViewBuilder();
b.setFromUnitTypes(Arrays.asList(CalculatorNumeralBase.values()));
b.setToUnitTypes(Arrays.asList(CalculatorNumeralBase.values()));
if (!StringUtils.isEmpty(initialFromValue)) {
String value = initialFromValue;
try {
value = ToJsclTextProcessor.getInstance().process(value).getExpression();
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
} catch (CalculatorParseException e) {
b.setFromValue(UnitImpl.newInstance(value, CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
}
} else {
b.setFromValue(UnitImpl.newInstance("", CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase())));
}
b.setConverter(CalculatorNumeralBase.getConverter());
final MutableObject<AlertDialog> alertDialogHolder = new MutableObject<AlertDialog>();
b.setOkButtonOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
final AlertDialog alertDialog = alertDialogHolder.getObject();
if (alertDialog != null) {
alertDialog.dismiss();
}
}
});
b.setCustomButtonData(new UnitConverterViewBuilder.CustomButtonData(context.getString(R.string.c_use_short), new UnitConverterViewBuilder.CustomButtonOnClickListener() {
@Override
public void onClick(@NotNull Unit<String> fromUnits, @NotNull Unit<String> toUnits) {
String toUnitsValue = toUnits.getValue();
if (!toUnits.getUnitType().equals(CalculatorNumeralBase.valueOf(Locator.getInstance().getEngine().getNumeralBase()))) {
toUnitsValue = ((CalculatorNumeralBase) toUnits.getUnitType()).getNumeralBase().getJsclPrefix() + toUnitsValue;
}
Locator.getInstance().getKeyboard().buttonPressed(toUnitsValue);
final AlertDialog alertDialog = alertDialogHolder.getObject();
if (alertDialog != null) {
alertDialog.dismiss();
}
}
}));
final AlertDialog.Builder alertBuilder = new AlertDialog.Builder(context);
alertBuilder.setView(b.build(context));
alertBuilder.setTitle(R.string.c_conversion_tool);
final AlertDialog alertDialog = alertBuilder.create();
final WindowManager.LayoutParams lp = new WindowManager.LayoutParams();
lp.copyFrom(alertDialog.getWindow().getAttributes());
lp.width = WindowManager.LayoutParams.FILL_PARENT;
lp.height = WindowManager.LayoutParams.WRAP_CONTENT;
alertDialogHolder.setObject(alertDialog);
alertDialog.show();
alertDialog.getWindow().setAttributes(lp);
}
}

View File

@@ -0,0 +1,44 @@
/*
* Copyright (c) 2009-2011. Created by serso aka se.solovyev.
* For more information, please, contact se.solovyev@gmail.com
* or visit http://se.solovyev.org
*/
package org.solovyev.android.calculator.view;
import android.content.Context;
import android.content.res.Resources;
import android.graphics.Paint;
import android.text.TextPaint;
import android.util.AttributeSet;
import org.jetbrains.annotations.NotNull;
import org.solovyev.android.calculator.Locator;
import org.solovyev.android.calculator.R;
import org.solovyev.android.view.drag.DirectionDragButton;
/**
* User: serso
* Date: 12/8/11
* Time: 2:22 AM
*/
public class NumeralBasesButton extends DirectionDragButton {
public NumeralBasesButton(Context context, @NotNull AttributeSet attrs) {
super(context, attrs);
}
@Override
protected void initDirectionTextPaint(@NotNull Paint basePaint,
@NotNull DirectionTextData directionTextData,
@NotNull Resources resources) {
super.initDirectionTextPaint(basePaint, directionTextData, resources);
final TextPaint directionTextPaint = directionTextData.getPaint();
if (Locator.getInstance().getEngine().getNumeralBase().name().equals(directionTextData.getText())) {
directionTextPaint.setColor(resources.getColor(R.color.cpp_selected_angle_unit_text_color));
} else {
directionTextPaint.setColor(resources.getColor(R.color.cpp_default_text_color));
directionTextPaint.setAlpha(getDirectionTextAlpha());
}
}
}

View File

@@ -0,0 +1,41 @@
package org.solovyev.android.calculator.view;
import android.content.SharedPreferences;
import android.os.Vibrator;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;
import org.solovyev.android.view.VibratorContainer;
import org.solovyev.android.view.drag.DragButton;
import org.solovyev.android.view.drag.OnDragListener;
import org.solovyev.android.view.drag.OnDragListenerWrapper;
/**
* User: serso
* Date: 4/20/12
* Time: 3:27 PM
*/
public class OnDragListenerVibrator extends OnDragListenerWrapper {
private static final float VIBRATION_TIME_SCALE = 0.5f;
@NotNull
private final VibratorContainer vibrator;
public OnDragListenerVibrator(@NotNull OnDragListener onDragListener,
@Nullable Vibrator vibrator,
@NotNull SharedPreferences preferences) {
super(onDragListener);
this.vibrator = new VibratorContainer(vibrator, preferences, VIBRATION_TIME_SCALE);
}
@Override
public boolean onDrag(@NotNull DragButton dragButton, @NotNull org.solovyev.android.view.drag.DragEvent event) {
boolean result = super.onDrag(dragButton, event);
if (result) {
vibrator.vibrate();
}
return result;
}
}

Some files were not shown because too many files have changed in this diff Show More