Merge branch 'dev-app-structure' into dev

Conflicts:
	android-app-core/src/main/java/org/solovyev/android/calculator/CalculatorButtons.java
	android-app/AndroidManifest.xml
	android-app/src/main/java/org/solovyev/android/calculator/about/CalculatorFragmentType.java
This commit is contained in:
Sergey Solovyev
2012-12-01 21:30:47 +04:00
698 changed files with 1528 additions and 1036 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.cpp_button_history);
if (historyButton != null) {
historyButton.setOnDragListener(historyOnDragListener);
}
final DragButton subtractionButton = getButton(root, R.id.cpp_button_subtraction);
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.cpp_button_right);
if (rightButton != null) {
rightButton.setOnDragListener(toPositionOnDragListener);
}
final DragButton leftButton = getButton(root, R.id.cpp_button_left);
if (leftButton != null) {
leftButton.setOnDragListener(toPositionOnDragListener);
}
final DragButton equalsButton = getButton(root, R.id.cpp_button_equals);
if (equalsButton != null) {
equalsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new EvalDragProcessor(), dragPreferences), vibrator, preferences));
}
final AngleUnitsButton angleUnitsButton = (AngleUnitsButton) getButton(root, R.id.cpp_button_6);
if (angleUnitsButton != null) {
angleUnitsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.AngleUnitsChanger(activity), dragPreferences), vibrator, preferences));
}
final NumeralBasesButton clearButton = (NumeralBasesButton) getButton(root, R.id.cpp_button_clear);
if (clearButton != null) {
clearButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.NumeralBasesChanger(activity), dragPreferences), vibrator, preferences));
}
final DragButton varsButton = getButton(root, R.id.cpp_button_vars);
if (varsButton != null) {
varsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.VarsDragProcessor(activity), dragPreferences), vibrator, preferences));
}
final DragButton functionsButton = getButton(root, R.id.cpp_button_functions);
if (functionsButton != null) {
functionsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.FunctionsDragProcessor(activity), dragPreferences), vibrator, preferences));
}
final DragButton roundBracketsButton = getButton(root, R.id.cpp_button_round_brackets);
if (roundBracketsButton != null) {
roundBracketsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new CalculatorButtons.RoundBracketsDragProcessor(), dragPreferences), vibrator, preferences));
}
if (layout == CalculatorPreferences.Gui.Layout.simple) {
toggleButtonDirectionText(root, R.id.cpp_button_1, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_2, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_3, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_6, false, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_7, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_8, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_clear, false, DragDirection.left, DragDirection.up, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_4, false, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_5, false, DragDirection.down);
toggleButtonDirectionText(root, R.id.cpp_button_9, false, DragDirection.left);
toggleButtonDirectionText(root, R.id.cpp_button_multiplication, false, DragDirection.left);
toggleButtonDirectionText(root, R.id.cpp_button_plus, 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,230 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.Application;
import android.content.Context;
import android.content.SharedPreferences;
import android.preference.PreferenceManager;
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 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,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.cpp_button_0, R.id.cpp_button_1);
}
},
oct(CalculatorNumeralBase.oct) {
@NotNull
@Override
public List<Integer> getButtonIds() {
final List<Integer> result = new ArrayList<Integer>(bin.getButtonIds());
result.addAll(Arrays.asList(R.id.cpp_button_2, R.id.cpp_button_3, R.id.cpp_button_4, R.id.cpp_button_5, R.id.cpp_button_6, R.id.cpp_button_7));
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.cpp_button_8, R.id.cpp_button_9));
return result;
}
},
hex(CalculatorNumeralBase.hex) {
@NotNull
private List<Integer> specialHexButtonIds = Arrays.asList(R.id.cpp_button_1, R.id.cpp_button_2, R.id.cpp_button_3, R.id.cpp_button_4, R.id.cpp_button_5, R.id.cpp_button_6);
@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,235 @@
package org.solovyev.android.calculator;
import android.app.Activity;
import android.app.AlertDialog;
import android.content.Context;
import android.content.Intent;
import android.content.SharedPreferences;
import android.net.Uri;
import android.preference.PreferenceManager;
import android.view.LayoutInflater;
import android.view.View;
import android.widget.TextView;
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.jetbrains.annotations.Nullable;
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.android.calculator.plot.PlotInput;
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 final class CalculatorActivityLauncher implements CalculatorEventListener {
public 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);
}
}
@Override
public void onCalculatorEvent(@NotNull CalculatorEventData calculatorEventData, @NotNull CalculatorEventType calculatorEventType, @Nullable Object data) {
switch (calculatorEventType){
case show_create_var_dialog:
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
@Override
public void run() {
CalculatorActivityLauncher.createVar(App.getInstance().getApplication(), Locator.getInstance().getDisplay());
}
});
break;
case show_create_function_dialog:
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
@Override
public void run() {
CalculatorActivityLauncher.createFunction(App.getInstance().getApplication(), Locator.getInstance().getDisplay());
}
});
break;
case plot_graph:
final PlotInput plotInput = (PlotInput) data;
assert plotInput != null;
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
@Override
public void run() {
plotGraph(App.getInstance().getApplication(), plotInput.getFunction(), plotInput.getConstant());
}
});
break;
case show_evaluation_error:
final String errorMessage = (String) data;
if (errorMessage != null) {
App.getInstance().getUiThreadExecutor().execute(new Runnable() {
@Override
public void run() {
showEvaluationError(App.getInstance().getApplication(), errorMessage);
}
});
}
break;
}
}
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();
}
}

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,181 @@
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);
App.getInstance().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,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.cpp_app_display_mobile, R.string.result);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.cpp_app_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,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.cpp_app_editor_mobile, R.string.editor);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.cpp_app_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.cpp_app_keyboard_mobile);
} else {
fragmentHelper = CalculatorApplication.getInstance().createFragmentHelper(R.layout.cpp_app_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,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,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,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,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,80 @@
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.matrix.CalculatorMatrixEditFragment;
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.cpp_app_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),
// todo serso: strings
matrix_edit(CalculatorMatrixEditFragment.class, R.layout.matrix_edit_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,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,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,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,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,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,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;
}
}

View File

@@ -0,0 +1,85 @@
package org.solovyev.android.fragments;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v4.app.FragmentTransaction;
import com.actionbarsherlock.app.SherlockFragmentActivity;
import org.jetbrains.annotations.NotNull;
import org.solovyev.common.collections.CollectionsUtils;
import java.util.Collections;
import java.util.List;
/**
* User: serso
* Date: 9/25/12
* Time: 9:29 PM
*/
public class FragmentUtils {
public static void createFragment(@NotNull FragmentActivity activity,
@NotNull Class<? extends Fragment> fragmentClass,
int parentViewId,
@NotNull String tag) {
final FragmentManager fm = activity.getSupportFragmentManager();
Fragment messagesFragment = fm.findFragmentByTag(tag);
final FragmentTransaction ft = fm.beginTransaction();
try {
if (messagesFragment == null) {
messagesFragment = Fragment.instantiate(activity, fragmentClass.getName(), null);
ft.add(parentViewId, messagesFragment, tag);
} else {
if (messagesFragment.isDetached()) {
ft.attach(messagesFragment);
}
}
} finally {
ft.commit();
}
}
public static void removeFragments(@NotNull SherlockFragmentActivity activity, @NotNull String... fragmentTags) {
removeFragments(activity, CollectionsUtils.asList(fragmentTags));
}
public static void removeFragments(@NotNull SherlockFragmentActivity activity, @NotNull List<String> fragmentTags) {
for (String fragmentTag : fragmentTags) {
removeFragment(activity, fragmentTag);
}
}
public static void detachFragments(@NotNull SherlockFragmentActivity activity, @NotNull String... fragmentTags) {
detachFragments(activity, CollectionsUtils.asList(fragmentTags));
}
public static void detachFragments(@NotNull SherlockFragmentActivity activity, @NotNull List<String> fragmentTags) {
for (String fragmentTag : fragmentTags) {
detachFragment(activity, fragmentTag);
}
}
public static void detachFragment(@NotNull SherlockFragmentActivity activity, @NotNull String fragmentTag) {
final Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(fragmentTag);
if ( fragment != null ) {
if ( !fragment.isDetached() ) {
FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
ft.detach(fragment);
ft.commit();
}
}
}
public static void removeFragment(@NotNull SherlockFragmentActivity activity, @NotNull String fragmentTag) {
final Fragment fragment = activity.getSupportFragmentManager().findFragmentByTag(fragmentTag);
if ( fragment != null ) {
if ( fragment.isAdded()) {
FragmentTransaction ft = activity.getSupportFragmentManager().beginTransaction();
ft.remove(fragment);
ft.commit();
}
}
}
}