diff --git a/calculatorpp/pom.xml b/calculatorpp/pom.xml index 81d2b234..6ebba13e 100644 --- a/calculatorpp/pom.xml +++ b/calculatorpp/pom.xml @@ -1,317 +1,322 @@ - - - - - org.solovyev.android - calculatorpp-parent - 1.3.2 - - - 4.0.0 - - org.solovyev.android - calculatorpp - apk - Calculator++ Application - - - - - - - org.solovyev.android - calculatorpp-core - 1.3.2 - - - - org.solovyev - common-core - - - - org.solovyev - common-text - - - - org.solovyev.android - android-common-core - apklib - - - - org.solovyev.android - android-common-ads - apklib - - - - org.solovyev.android - android-common-view - apklib - - - - org.solovyev.android - android-common-preferences - apklib - - - - org.solovyev.android - android-common-other - apklib - - - - org.solovyev.android - android-common-menu - apklib - - - - org.solovyev - jscl - - - - - - com.google.android - android - provided - - - - net.sf.opencsv - opencsv - 2.0 - test - - - - org.simpleframework - simple-xml - - - - achartengine - achartengine - 0.7.0 - - - - admob - admob - 6.1.0 - - - - org.solovyev.android - billing - 0.1 - - - - - com.google.guava - guava - 11.0.2 - - - - junit - junit - test - - - - com.intellij - annotations - - - - - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - true - - - true - - - - - manifestUpdate - process-resources - - manifest-update - - - - alignApk - package - - zipalign - - - - - - - - - - - - - release - - - - performRelease - true - - - - - - - - org.codehaus.mojo - properties-maven-plugin - 1.0-alpha-2 - - - initialize - - read-project-properties - - - - ${project.basedir}/misc/env/jarsigner.properties - - - - - - - - org.apache.maven.plugins - maven-jarsigner-plugin - - - signing - - sign - verify - - package - true - - true - - - ${project.build.directory}/${project.artifactId}-${project.version}.apk - - ${sign.keystore} - ${sign.alias} - ${sign.storepass} - ${sign.keypass} - false - - - - - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - true - - - - false - - - - false - ${project.build.directory}/${project.artifactId}-${project.version}.apk - ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk - - - - false - true - - - - true - - - - - - manifestUpdate - process-resources - - manifest-update - - - - alignApk - package - - zipalign - - - - - - - org.codehaus.mojo - build-helper-maven-plugin - - - - ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk - apk - signed-aligned - - - ${project.build.directory}/proguard/mapping.txt - map - release - - - - - - attach-signed-aligned - package - - attach-artifact - - - - - - - - - - - + + + + + org.solovyev.android + calculatorpp-parent + 1.3.2 + + + 4.0.0 + + org.solovyev.android + calculatorpp + apk + Calculator++ Application + + + + + + + org.solovyev.android + calculatorpp-core + 1.3.2 + + + + org.solovyev + common-core + + + + org.solovyev + common-text + + + + org.solovyev.android + android-common-core + apklib + + + + org.solovyev.android + android-common-ads + apklib + + + + org.solovyev.android + android-common-view + apklib + + + + org.solovyev.android + android-common-preferences + apklib + + + + org.solovyev.android + android-common-other + apklib + + + + org.solovyev.android + android-common-menu + apklib + + + + org.solovyev + jscl + + + + + + com.google.android + android + provided + + + + com.google.android + support-v4 + + + + net.sf.opencsv + opencsv + 2.0 + test + + + + org.simpleframework + simple-xml + + + + achartengine + achartengine + 0.7.0 + + + + admob + admob + 6.1.0 + + + + org.solovyev.android + billing + 0.1 + + + + + com.google.guava + guava + 11.0.2 + + + + junit + junit + test + + + + com.intellij + annotations + + + + + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + true + + + true + + + + + manifestUpdate + process-resources + + manifest-update + + + + alignApk + package + + zipalign + + + + + + + + + + + + + release + + + + performRelease + true + + + + + + + + org.codehaus.mojo + properties-maven-plugin + 1.0-alpha-2 + + + initialize + + read-project-properties + + + + ${project.basedir}/misc/env/jarsigner.properties + + + + + + + + org.apache.maven.plugins + maven-jarsigner-plugin + + + signing + + sign + verify + + package + true + + true + + + ${project.build.directory}/${project.artifactId}-${project.version}.apk + + ${sign.keystore} + ${sign.alias} + ${sign.storepass} + ${sign.keypass} + false + + + + + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + true + + + + false + + + + false + ${project.build.directory}/${project.artifactId}-${project.version}.apk + ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk + + + + false + true + + + + true + + + + + + manifestUpdate + process-resources + + manifest-update + + + + alignApk + package + + zipalign + + + + + + + org.codehaus.mojo + build-helper-maven-plugin + + + + ${project.build.directory}/${project.artifactId}-${project.version}-signed-aligned.apk + apk + signed-aligned + + + ${project.build.directory}/proguard/mapping.txt + map + release + + + + + + attach-signed-aligned + package + + attach-artifact + + + + + + + + + + + \ No newline at end of file diff --git a/calculatorpp/res/layout-port/main_calculator.xml b/calculatorpp/res/layout-port/main_calculator.xml index 909f5c5e..8cc49b13 100644 --- a/calculatorpp/res/layout-port/main_calculator.xml +++ b/calculatorpp/res/layout-port/main_calculator.xml @@ -1,84 +1,87 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/calculatorpp/res/layout/calc_editor.xml b/calculatorpp/res/layout/calc_editor.xml index 124b7d0a..aed1c1c4 100644 --- a/calculatorpp/res/layout/calc_editor.xml +++ b/calculatorpp/res/layout/calc_editor.xml @@ -7,9 +7,8 @@ --> + a:layout_width="match_parent" + a:layout_height="wrap_content"> textHighlighter = new TextHighlighter(Color.WHITE, false); - - /* - ********************************************************************** - * - * FIELDS - * - ********************************************************************** - */ - - @NotNull - private volatile CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance(); - - private volatile boolean viewStateChange = false; - - @NotNull - private final Object lock = new Object(); - - @NotNull - private final Handler handler = new Handler(); - - /* - ********************************************************************** - * - * CONSTRUCTORS - * - ********************************************************************** - */ - - public AndroidCalculatorDisplayView(Context context) { - super(context); - this.addTextChangedListener(new TextWatcherImpl()); - } - - public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) { - super(context, attrs); - this.addTextChangedListener(new TextWatcherImpl()); - - } - - public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - this.addTextChangedListener(new TextWatcherImpl()); - } - - /* - ********************************************************************** - * - * METHODS - * - ********************************************************************** - */ - - - @Override - public void setState(@NotNull final CalculatorDisplayViewState state) { - final CharSequence text = prepareText(state.getStringResult(), state.isValid()); - - handler.post(new Runnable() { - @Override - public void run() { - synchronized (lock) { - try { - viewStateChange = true; - - AndroidCalculatorDisplayView.this.state = state; - if (state.isValid()) { - setTextColor(getResources().getColor(R.color.default_text_color)); - setText(text); - - adjustTextSize(); - - } else { - // update text in order to get rid of HTML tags - setText(getText().toString()); - setTextColor(getResources().getColor(R.color.display_error_text_color)); - - // error messages are never shown -> just greyed out text (error message will be shown on click) - //setText(state.getErrorMessage()); - //redraw(); - } - } finally { - viewStateChange = false; - } - } - } - }); - } - - @NotNull - @Override - public CalculatorDisplayViewState getState() { - synchronized (lock) { - return this.state; - } - } - - @Nullable - private static CharSequence prepareText(@Nullable String text, boolean valid) { - CharSequence result; - - if (valid && text != null) { - - //Log.d(this.getClass().getName(), text); - - try { - final TextHighlighter.Result processedText = textHighlighter.process(text); - text = processedText.toString(); - result = Html.fromHtml(text); - } catch (CalculatorParseException e) { - result = text; - } - } else { - result = text; - } - - return result; - } - - private void adjustTextSize() { - // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) - setAddEllipsis(false); - setMinTextSize(10); - resizeText(); - } - - - public void handleTextChange(Editable s) { - synchronized (lock) { - if (!viewStateChange) { - // external text change => need to notify display - // todo serso: implement - } - } - } - - private final class TextWatcherImpl implements TextWatcher { - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - handleTextChange(s); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.content.Context; +import android.graphics.Color; +import android.os.Handler; +import android.text.Editable; +import android.text.Html; +import android.text.TextWatcher; +import android.util.AttributeSet; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.TextHighlighter; +import org.solovyev.android.view.AutoResizeTextView; + +/** + * User: serso + * Date: 9/17/11 + * Time: 10:58 PM + */ +public class AndroidCalculatorDisplayView extends AutoResizeTextView implements CalculatorDisplayView { + + /* + ********************************************************************** + * + * STATIC FIELDS + * + ********************************************************************** + */ + + @NotNull + private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false); + + /* + ********************************************************************** + * + * FIELDS + * + ********************************************************************** + */ + + @NotNull + private volatile CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance(); + + private volatile boolean viewStateChange = false; + + @NotNull + private final Object lock = new Object(); + + @NotNull + private final Handler handler = new Handler(); + + private volatile boolean initialized = false; + + /* + ********************************************************************** + * + * CONSTRUCTORS + * + ********************************************************************** + */ + + public AndroidCalculatorDisplayView(Context context) { + super(context); + this.addTextChangedListener(new TextWatcherImpl()); + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs) { + super(context, attrs); + this.addTextChangedListener(new TextWatcherImpl()); + + } + + public AndroidCalculatorDisplayView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + this.addTextChangedListener(new TextWatcherImpl()); + } + + /* + ********************************************************************** + * + * METHODS + * + ********************************************************************** + */ + + + @Override + public void setState(@NotNull final CalculatorDisplayViewState state) { + final CharSequence text = prepareText(state.getStringResult(), state.isValid()); + + handler.post(new Runnable() { + @Override + public void run() { + synchronized (lock) { + try { + viewStateChange = true; + + AndroidCalculatorDisplayView.this.state = state; + if (state.isValid()) { + setTextColor(getResources().getColor(R.color.default_text_color)); + setText(text); + + adjustTextSize(); + + } else { + // update text in order to get rid of HTML tags + setText(getText().toString()); + setTextColor(getResources().getColor(R.color.display_error_text_color)); + + // error messages are never shown -> just greyed out text (error message will be shown on click) + //setText(state.getErrorMessage()); + //redraw(); + } + } finally { + viewStateChange = false; + } + } + } + }); + } + + @NotNull + @Override + public CalculatorDisplayViewState getState() { + synchronized (lock) { + return this.state; + } + } + + @Nullable + private static CharSequence prepareText(@Nullable String text, boolean valid) { + CharSequence result; + + if (valid && text != null) { + + //Log.d(this.getClass().getName(), text); + + try { + final TextHighlighter.Result processedText = textHighlighter.process(text); + text = processedText.toString(); + result = Html.fromHtml(text); + } catch (CalculatorParseException e) { + result = text; + } + } else { + result = text; + } + + return result; + } + + private void adjustTextSize() { + // todo serso: think where to move it (keep in mind org.solovyev.android.view.AutoResizeTextView.resetTextSize()) + setAddEllipsis(false); + setMinTextSize(10); + resizeText(); + } + + + public void handleTextChange(Editable s) { + synchronized (lock) { + if (!viewStateChange) { + // external text change => need to notify display + // todo serso: implement + } + } + } + + public synchronized void init(@NotNull Context context) { + if (!initialized) { + this.setOnClickListener(new CalculatorDisplayOnClickListener(context)); + + this.initialized = true; + } + } + + private final class TextWatcherImpl implements TextWatcher { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + handleTextChange(s); + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorEditorView.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorEditorView.java index 2b8ee8ed..9650cedf 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorEditorView.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/AndroidCalculatorEditorView.java @@ -1,204 +1,213 @@ -/* - * Copyright (c) 2009-2011. Created by serso aka se.solovyev. - * For more information, please, contact se.solovyev@gmail.com - */ - -package org.solovyev.android.calculator; - -import android.content.Context; -import android.content.SharedPreferences; -import android.graphics.Color; -import android.os.Build; -import android.os.Handler; -import android.text.Editable; -import android.text.Html; -import android.text.TextWatcher; -import android.util.AttributeSet; -import android.util.Log; -import android.view.ContextMenu; -import android.widget.EditText; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.calculator.text.TextProcessor; -import org.solovyev.android.calculator.view.TextHighlighter; -import org.solovyev.common.collections.CollectionsUtils; - -/** - * User: serso - * Date: 9/17/11 - * Time: 12:25 AM - */ -public class AndroidCalculatorEditorView extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEditorView { - - private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display"; - private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true; - - private boolean highlightText = true; - - @NotNull - private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false); - - @NotNull - private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance(); - - private volatile boolean viewStateChange = false; - - // NOTE: static because super constructor calls some overridden methods (like onSelectionChanged and current lock is not yet created) - @NotNull - private static final Object lock = new Object(); - - @NotNull - private final Handler handler = new Handler(); - - public AndroidCalculatorEditorView(Context context) { - super(context); - this.addTextChangedListener(new TextWatcherImpl()); - } - - public AndroidCalculatorEditorView(Context context, AttributeSet attrs) { - super(context, attrs); - this.addTextChangedListener(new TextWatcherImpl()); - } - - public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) { - super(context, attrs, defStyle); - this.addTextChangedListener(new TextWatcherImpl()); - } - - - @Override - public boolean onCheckIsTextEditor() { - // NOTE: code below can be used carefully and should not be copied without special intention - // The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc - - if (Build.VERSION.SDK_INT >= 11) { - // fix for missing cursor in android 3 and higher - try { - // IDEA: return false always except if method was called from TextView.isCursorVisible() method - for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) { - if ("isCursorVisible".equals(stackTraceElement.getMethodName())) { - return true; - } - } - } catch (RuntimeException e) { - // just in case... - } - - return false; - } else { - return false; - } - } - - @Override - protected void onCreateContextMenu(ContextMenu menu) { - super.onCreateContextMenu(menu); - - menu.removeItem(android.R.id.selectAll); - } - - @Nullable - private CharSequence prepareText(@NotNull String text, boolean highlightText) { - CharSequence result; - - if (highlightText) { - - try { - final TextHighlighter.Result processesText = textHighlighter.process(text); - - assert processesText.getOffset() == 0; - - result = Html.fromHtml(processesText.toString()); - } catch (CalculatorParseException e) { - // set raw text - result = text; - - Log.e(this.getClass().getName(), e.getMessage(), e); - } - } else { - result = text; - } - - return result; - } - - public boolean isHighlightText() { - return highlightText; - } - - public void setHighlightText(boolean highlightText) { - this.highlightText = highlightText; - CalculatorLocatorImpl.getInstance().getEditor().updateViewState(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { - if (CALC_COLOR_DISPLAY_KEY.equals(key)) { - this.setHighlightText(preferences.getBoolean(CALC_COLOR_DISPLAY_KEY, CALC_COLOR_DISPLAY_DEFAULT)); - } - } - - public void init(@NotNull SharedPreferences preferences) { - onSharedPreferenceChanged(preferences, CALC_COLOR_DISPLAY_KEY); - } - - @Override - public void setState(@NotNull final CalculatorEditorViewState viewState) { - - final CharSequence text = prepareText(viewState.getText(), highlightText); - - handler.post(new Runnable() { - @Override - public void run() { - final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this; - synchronized (lock) { - try { - editorView.viewStateChange = true; - editorView.viewState = viewState; - editorView.setText(text, BufferType.EDITABLE); - editorView.setSelection(viewState.getSelection()); - } finally { - editorView.viewStateChange = false; - } - } - } - }); - } - - @Override - protected void onSelectionChanged(int selStart, int selEnd) { - synchronized (lock) { - if (!viewStateChange) { - // external text change => need to notify editor - super.onSelectionChanged(selStart, selEnd); - CalculatorLocatorImpl.getInstance().getEditor().setSelection(selStart); - } - } - } - - public void handleTextChange(Editable s) { - synchronized (lock) { - if (!viewStateChange) { - // external text change => need to notify editor - CalculatorLocatorImpl.getInstance().getEditor().setText(String.valueOf(s)); - } - } - } - - private final class TextWatcherImpl implements TextWatcher { - - @Override - public void beforeTextChanged(CharSequence s, int start, int count, int after) { - - } - - @Override - public void onTextChanged(CharSequence s, int start, int before, int count) { - } - - @Override - public void afterTextChanged(Editable s) { - handleTextChange(s); - } - } -} +/* + * Copyright (c) 2009-2011. Created by serso aka se.solovyev. + * For more information, please, contact se.solovyev@gmail.com + */ + +package org.solovyev.android.calculator; + +import android.content.Context; +import android.content.SharedPreferences; +import android.graphics.Color; +import android.os.Build; +import android.os.Handler; +import android.preference.PreferenceManager; +import android.text.Editable; +import android.text.Html; +import android.text.TextWatcher; +import android.util.AttributeSet; +import android.util.Log; +import android.view.ContextMenu; +import android.widget.EditText; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.calculator.text.TextProcessor; +import org.solovyev.android.calculator.view.TextHighlighter; +import org.solovyev.common.collections.CollectionsUtils; + +/** + * User: serso + * Date: 9/17/11 + * Time: 12:25 AM + */ +public class AndroidCalculatorEditorView extends EditText implements SharedPreferences.OnSharedPreferenceChangeListener, CalculatorEditorView { + + private static final String CALC_COLOR_DISPLAY_KEY = "org.solovyev.android.calculator.CalculatorModel_color_display"; + private static final boolean CALC_COLOR_DISPLAY_DEFAULT = true; + + private volatile boolean initialized = false; + + private boolean highlightText = true; + + @NotNull + private final static TextProcessor textHighlighter = new TextHighlighter(Color.WHITE, false); + + @NotNull + private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance(); + + private volatile boolean viewStateChange = false; + + // NOTE: static because super constructor calls some overridden methods (like onSelectionChanged and current lock is not yet created) + @NotNull + private static final Object lock = new Object(); + + @NotNull + private final Handler handler = new Handler(); + + public AndroidCalculatorEditorView(Context context) { + super(context); + } + + public AndroidCalculatorEditorView(Context context, AttributeSet attrs) { + super(context, attrs); + } + + public AndroidCalculatorEditorView(Context context, AttributeSet attrs, int defStyle) { + super(context, attrs, defStyle); + } + + @Override + public boolean onCheckIsTextEditor() { + // NOTE: code below can be used carefully and should not be copied without special intention + // The main purpose of code is to disable soft input (virtual keyboard) but leave all the TextEdit functionality, like cursor, scrolling, copy/paste menu etc + + if (Build.VERSION.SDK_INT >= 11) { + // fix for missing cursor in android 3 and higher + try { + // IDEA: return false always except if method was called from TextView.isCursorVisible() method + for (StackTraceElement stackTraceElement : CollectionsUtils.asList(Thread.currentThread().getStackTrace())) { + if ("isCursorVisible".equals(stackTraceElement.getMethodName())) { + return true; + } + } + } catch (RuntimeException e) { + // just in case... + } + + return false; + } else { + return false; + } + } + + @Override + protected void onCreateContextMenu(ContextMenu menu) { + super.onCreateContextMenu(menu); + + menu.removeItem(android.R.id.selectAll); + } + + @Nullable + private CharSequence prepareText(@NotNull String text, boolean highlightText) { + CharSequence result; + + if (highlightText) { + + try { + final TextHighlighter.Result processesText = textHighlighter.process(text); + + assert processesText.getOffset() == 0; + + result = Html.fromHtml(processesText.toString()); + } catch (CalculatorParseException e) { + // set raw text + result = text; + + Log.e(this.getClass().getName(), e.getMessage(), e); + } + } else { + result = text; + } + + return result; + } + + public boolean isHighlightText() { + return highlightText; + } + + public void setHighlightText(boolean highlightText) { + this.highlightText = highlightText; + CalculatorLocatorImpl.getInstance().getEditor().updateViewState(); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, String key) { + if (CALC_COLOR_DISPLAY_KEY.equals(key)) { + this.setHighlightText(preferences.getBoolean(CALC_COLOR_DISPLAY_KEY, CALC_COLOR_DISPLAY_DEFAULT)); + } + } + + public synchronized void init(@NotNull Context context) { + if (!initialized) { + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + + preferences.registerOnSharedPreferenceChangeListener(this); + + this.addTextChangedListener(new TextWatcherImpl()); + + onSharedPreferenceChanged(preferences, CALC_COLOR_DISPLAY_KEY); + + initialized = true; + } + } + + @Override + public void setState(@NotNull final CalculatorEditorViewState viewState) { + + final CharSequence text = prepareText(viewState.getText(), highlightText); + + handler.post(new Runnable() { + @Override + public void run() { + final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this; + synchronized (lock) { + try { + editorView.viewStateChange = true; + editorView.viewState = viewState; + editorView.setText(text, BufferType.EDITABLE); + editorView.setSelection(viewState.getSelection()); + } finally { + editorView.viewStateChange = false; + } + } + } + }); + } + + @Override + protected void onSelectionChanged(int selStart, int selEnd) { + synchronized (lock) { + if (!viewStateChange) { + // external text change => need to notify editor + super.onSelectionChanged(selStart, selEnd); + CalculatorLocatorImpl.getInstance().getEditor().setSelection(selStart); + } + } + } + + public void handleTextChange(Editable s) { + synchronized (lock) { + if (!viewStateChange) { + // external text change => need to notify editor + CalculatorLocatorImpl.getInstance().getEditor().setText(String.valueOf(s)); + } + } + } + + private final class TextWatcherImpl implements TextWatcher { + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + + @Override + public void afterTextChanged(Editable s) { + handleTextChange(s); + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java index a99e7a4a..989d3dde 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorActivity.java @@ -1,736 +1,744 @@ -/* - * 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.Activity; -import android.app.AlertDialog; -import android.content.SharedPreferences; -import android.content.pm.ActivityInfo; -import android.content.res.Configuration; -import android.os.Bundle; -import android.os.Vibrator; -import android.preference.PreferenceManager; -import android.text.Html; -import android.text.method.LinkMovementMethod; -import android.util.DisplayMetrics; -import android.util.Log; -import android.util.TypedValue; -import android.view.*; -import android.widget.Button; -import android.widget.LinearLayout; -import android.widget.TextView; -import android.widget.Toast; -import jscl.AngleUnit; -import jscl.NumeralBase; -import net.robotmedia.billing.BillingController; -import net.robotmedia.billing.IBillingObserver; -import org.jetbrains.annotations.NotNull; -import org.jetbrains.annotations.Nullable; -import org.solovyev.android.AndroidUtils; -import org.solovyev.android.FontSizeAdjuster; -import org.solovyev.android.calculator.about.CalculatorReleaseNotesActivity; -import org.solovyev.android.calculator.history.CalculatorHistoryState; -import org.solovyev.android.calculator.model.AndroidCalculatorEngine; -import org.solovyev.android.calculator.view.AngleUnitsButton; -import org.solovyev.android.calculator.view.CalculatorAdditionalTitle; -import org.solovyev.android.calculator.view.NumeralBasesButton; -import org.solovyev.android.calculator.view.OnDragListenerVibrator; -import org.solovyev.android.history.HistoryDragProcessor; -import org.solovyev.android.menu.ActivityMenu; -import org.solovyev.android.menu.LayoutActivityMenu; -import org.solovyev.android.prefs.Preference; -import org.solovyev.android.view.ColorButton; -import org.solovyev.android.view.drag.*; -import org.solovyev.common.Announcer; -import org.solovyev.common.equals.EqualsTool; -import org.solovyev.common.history.HistoryAction; -import org.solovyev.common.math.Point2d; -import org.solovyev.common.text.StringUtils; - -import java.lang.reflect.Field; -import java.lang.reflect.Modifier; -import java.util.ArrayList; -import java.util.List; - -public class CalculatorActivity extends Activity implements FontSizeAdjuster, SharedPreferences.OnSharedPreferenceChangeListener { - - @NotNull - public static final String TAG = "Calculator++"; - - private static final int HVGA_WIDTH_PIXELS = 320; - - @Nullable - private IBillingObserver billingObserver; - - @NotNull - private final Announcer dpclRegister = new Announcer(DragPreferencesChangeListener.class); - - private volatile boolean initialized; - - @NotNull - private CalculatorPreferences.Gui.Theme theme; - - @NotNull - private CalculatorPreferences.Gui.Layout layout; - - @Nullable - private Vibrator vibrator; - - private boolean useBackAsPrev; - - @NotNull - private NumeralBaseButtons numeralBaseButtons = new NumeralBaseButtons(); - - @NotNull - private ActivityMenu menu = LayoutActivityMenu.newInstance(R.menu.main_menu, CalculatorMenu.class); - - /** - * Called when the activity is first created. - */ - @Override - public void onCreate(@Nullable Bundle savedInstanceState) { - - CalculatorApplication.registerOnRemoteStackTrace(); - - final boolean customTitleSupported = requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); - - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - - CalculatorPreferences.setDefaultValues(preferences); - - setTheme(preferences); - super.onCreate(savedInstanceState); - setLayout(preferences); - - if (customTitleSupported) { - try { - getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.calc_title); - final CalculatorAdditionalTitle additionalAdditionalTitleText = (CalculatorAdditionalTitle)findViewById(R.id.additional_title_text); - additionalAdditionalTitleText.init(preferences); - preferences.registerOnSharedPreferenceChangeListener(additionalAdditionalTitleText); - } catch (ClassCastException e) { - // super fix for issue with class cast in android.view.Window.setFeatureInt() (see app error reports) - Log.e(CalculatorActivity.class.getName(), e.getMessage(), e); - } - } - - billingObserver = new CalculatorBillingObserver(this); - BillingController.registerObserver(billingObserver); - - firstTimeInit(preferences); - - // init billing controller - BillingController.checkBillingSupported(this); - - vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE); - - getCalculator().init(this, preferences); - - dpclRegister.clear(); - - final SimpleOnDragListener.Preferences dragPreferences = SimpleOnDragListener.getPreferences(preferences, this); - - setOnDragListeners(dragPreferences, preferences); - - final OnDragListener historyOnDragListener = new OnDragListenerVibrator(newOnDragListener(new HistoryDragProcessor(getCalculator()), dragPreferences), vibrator, preferences); - ((DragButton) findViewById(R.id.historyButton)).setOnDragListener(historyOnDragListener); - - ((DragButton) findViewById(R.id.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) { - operatorsButtonClickHandler(dragButton); - return true; - } - return false; - } - }, dragPreferences), vibrator, preferences)); - - - final OnDragListener toPositionOnDragListener = new OnDragListenerVibrator(new SimpleOnDragListener(new CursorDragProcessor(), dragPreferences), vibrator, preferences); - ((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener); - ((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener); - - final DragButton equalsButton = (DragButton) findViewById(R.id.equalsButton); - if (equalsButton != null) { - equalsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new EvalDragProcessor(), dragPreferences), vibrator, preferences)); - } - - final AngleUnitsButton angleUnitsButton = (AngleUnitsButton) findViewById(R.id.sixDigitButton); - if (angleUnitsButton != null) { - angleUnitsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new AngleUnitsChanger(), dragPreferences), vibrator, preferences)); - } - - final NumeralBasesButton clearButton = (NumeralBasesButton) findViewById(R.id.clearButton); - if (clearButton != null) { - clearButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new NumeralBasesChanger(), dragPreferences), vibrator, preferences)); - } - - final DragButton varsButton = (DragButton) findViewById(R.id.varsButton); - if (varsButton != null) { - varsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new VarsDragProcessor(), dragPreferences), vibrator, preferences)); - } - - final DragButton roundBracketsButton = (DragButton) findViewById(R.id.roundBracketsButton); - if ( roundBracketsButton != null ) { - roundBracketsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new RoundBracketsDragProcessor(), dragPreferences), vibrator, preferences)); - } - - initMultiplicationButton(); - - fixThemeParameters(true); - - if (layout == CalculatorPreferences.Gui.Layout.simple) { - toggleButtonDirectionText(R.id.oneDigitButton, false, DragDirection.up, DragDirection.down); - toggleButtonDirectionText(R.id.twoDigitButton, false, DragDirection.up, DragDirection.down); - toggleButtonDirectionText(R.id.threeDigitButton, false, DragDirection.up, DragDirection.down); - - toggleButtonDirectionText(R.id.sixDigitButton, false, DragDirection.up, DragDirection.down); - toggleButtonDirectionText(R.id.sevenDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down); - toggleButtonDirectionText(R.id.eightDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down); - - toggleButtonDirectionText(R.id.clearButton, false, DragDirection.left, DragDirection.up, DragDirection.down); - - toggleButtonDirectionText(R.id.fourDigitButton, false, DragDirection.down); - toggleButtonDirectionText(R.id.fiveDigitButton, false, DragDirection.down); - - toggleButtonDirectionText(R.id.nineDigitButton, false, DragDirection.left); - - toggleButtonDirectionText(R.id.multiplicationButton, false, DragDirection.left); - toggleButtonDirectionText(R.id.plusButton, false, DragDirection.down, DragDirection.up); - } - - numeralBaseButtons.toggleNumericDigits(this, preferences); - - toggleOrientationChange(preferences); - - toggleEqualsButton(preferences); - - preferences.registerOnSharedPreferenceChangeListener(this); - } - - @NotNull - private AndroidCalculatorEngine getEngine() { - return ((AndroidCalculatorEngine) CalculatorLocatorImpl.getInstance().getEngine()); - } - - @NotNull - private AndroidCalculator getCalculator() { - return ((AndroidCalculator) CalculatorLocatorImpl.getInstance().getCalculator()); - } - - private void fixThemeParameters(boolean fixMagicFlames) { - if (theme.getThemeType() == CalculatorPreferences.Gui.ThemeType.metro) { - - if (fixMagicFlames) { - // for metro themes we should turn off magic flames - AndroidUtils.processViewsOfType(this.getWindow().getDecorView(), ColorButton.class, new AndroidUtils.ViewProcessor() { - @Override - public void process(@NotNull ColorButton colorButton) { - colorButton.setDrawMagicFlame(false); - } - }); - } - - fixMargins(2, 2); - } else { - fixMargins(1, 1); - } - } - - private void toggleButtonDirectionText(int id, boolean showDirectionText, @NotNull DragDirection... dragDirections) { - final View v = findViewById(id); - if (v instanceof DirectionDragButton ) { - final DirectionDragButton button = (DirectionDragButton)v; - for (DragDirection dragDirection : dragDirections) { - button.showDirectionText(showDirectionText, dragDirection); - } - } - } - - private void fixMargins(int marginLeft, int marginBottom) { - // sad but true - - final View equalsButton = findViewById(R.id.equalsButton); - final View rightButton = findViewById(R.id.rightButton); - final View leftButton = findViewById(R.id.leftButton); - final View clearButton = findViewById(R.id.clearButton); - final View eraseButton = findViewById(R.id.eraseButton); - - int orientation = AndroidUtils.getScreenOrientation(this); - if (orientation == Configuration.ORIENTATION_PORTRAIT) { - setMarginsForView(equalsButton, marginLeft, marginBottom); - setMarginsForView(getCalculatorDisplayView(), marginLeft, marginBottom); - } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { - setMarginsForView(leftButton, marginLeft, marginBottom); - setMarginsForView(eraseButton, marginLeft, marginBottom); - setMarginsForView(clearButton, marginLeft, marginBottom); - setMarginsForView(rightButton, marginLeft, marginBottom); - // magic magic magic - setMarginsForView(getCalculatorDisplayView(), 3 * marginLeft, marginBottom); - } - } - - private void setMarginsForView(@NotNull View view, int marginLeft, int marginBottom) { - // 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 - - final DisplayMetrics dm = 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); - } - } - - private class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor { - - private final DigitButtonDragProcessor processor = new DigitButtonDragProcessor(getKeyboard()); - - @Override - public boolean processDragEvent(@NotNull DragDirection dragDirection, - @NotNull DragButton dragButton, - @NotNull Point2d startPoint2d, - @NotNull MotionEvent motionEvent) { - boolean result = false; - - if (dragButton instanceof AngleUnitsButton) { - if (dragDirection != DragDirection.left ) { - final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection); - if ( directionText != null ) { - try { - - final AngleUnit angleUnits = AngleUnit.valueOf(directionText); - - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this); - - AndroidCalculatorEngine.Preferences.angleUnit.putPreference(preferences, angleUnits); - - Toast.makeText(CalculatorActivity.this, CalculatorActivity.this.getString(R.string.c_angle_units_changed_to, angleUnits.name()), Toast.LENGTH_LONG).show(); - - result = true; - } catch (IllegalArgumentException e) { - Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText); - } - } - } else if ( dragDirection == DragDirection.left ) { - result = processor.processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent); - } - } - - return result; - } - } - - private class NumeralBasesChanger implements SimpleOnDragListener.DragProcessor { - - @Override - public boolean processDragEvent(@NotNull DragDirection dragDirection, - @NotNull DragButton dragButton, - @NotNull Point2d startPoint2d, - @NotNull MotionEvent motionEvent) { - boolean result = false; - - if ( dragButton instanceof NumeralBasesButton ) { - final String directionText = ((NumeralBasesButton) dragButton).getText(dragDirection); - if ( directionText != null ) { - try { - - final NumeralBase numeralBase = NumeralBase.valueOf(directionText); - - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this); - AndroidCalculatorEngine.Preferences.numeralBase.putPreference(preferences, numeralBase); - - Toast.makeText(CalculatorActivity.this, CalculatorActivity.this.getString(R.string.c_numeral_base_changed_to, numeralBase.name()), Toast.LENGTH_LONG).show(); - - result = true; - } catch (IllegalArgumentException e) { - Log.d(this.getClass().getName(), "Unsupported numeral base: " + directionText); - } - } - } - - return result; - } - } - - - private class VarsDragProcessor implements SimpleOnDragListener.DragProcessor { - - @Override - public boolean processDragEvent(@NotNull DragDirection dragDirection, - @NotNull DragButton dragButton, - @NotNull Point2d startPoint2d, - @NotNull MotionEvent motionEvent) { - boolean result = false; - - if (dragDirection == DragDirection.up) { - CalculatorActivityLauncher.createVar(CalculatorActivity.this, CalculatorLocatorImpl.getInstance().getDisplay()); - result = true; - } - - return result; - } - } - - private synchronized void setOnDragListeners(@NotNull SimpleOnDragListener.Preferences dragPreferences, @NotNull SharedPreferences preferences) { - final OnDragListener onDragListener = new OnDragListenerVibrator(newOnDragListener(new DigitButtonDragProcessor(getKeyboard()), dragPreferences), vibrator, preferences); - - final List dragButtonIds = new ArrayList(); - final List buttonIds = new ArrayList(); - - 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 = this.findViewById(viewId); - if (view instanceof DragButton) { - dragButtonIds.add(viewId); - } - if (view instanceof Button) { - buttonIds.add(viewId); - } - } catch (IllegalAccessException e) { - Log.e(R.id.class.getName(), e.getMessage()); - } - } - } - - for (Integer dragButtonId : dragButtonIds) { - ((DragButton) findViewById(dragButtonId)).setOnDragListener(onDragListener); - } - } - - @NotNull - private SimpleOnDragListener newOnDragListener(@NotNull SimpleOnDragListener.DragProcessor dragProcessor, - @NotNull SimpleOnDragListener.Preferences dragPreferences) { - final SimpleOnDragListener onDragListener = new SimpleOnDragListener(dragProcessor, dragPreferences); - dpclRegister.addListener(onDragListener); - return onDragListener; - } - - - private synchronized void setLayout(@NotNull SharedPreferences preferences) { - layout = CalculatorPreferences.Gui.layout.getPreferenceNoError(preferences); - - setContentView(layout.getLayoutId()); - } - - private synchronized void setTheme(@NotNull SharedPreferences preferences) { - theme = CalculatorPreferences.Gui.theme.getPreferenceNoError(preferences); - - setTheme(theme.getThemeId()); - } - - private synchronized void firstTimeInit(@NotNull SharedPreferences preferences) { - if (!initialized) { - this.useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences); - - 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(this, CalculatorActivity.class.getPackage().getName()); - - CalculatorPreferences.appVersion.putPreference(preferences, appVersion); - - boolean dialogShown = false; - if (EqualsTool.areEqual(savedVersion, CalculatorPreferences.appVersion.getDefaultValue())) { - // new start - final AlertDialog.Builder builder = new AlertDialog.Builder(this).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 = CalculatorReleaseNotesActivity.getReleaseNotes(this, savedVersion + 1); - if (!StringUtils.isEmpty(releaseNotes)) { - final AlertDialog.Builder builder = new AlertDialog.Builder(this).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); - } - } - - if ( !dialogShown ) { - dialogShown = showSpecialWindow(preferences, CalculatorPreferences.Gui.notesppAnnounceShown, R.layout.notespp_announce, R.id.notespp_announce); - } - - initialized = true; - } - } - - private boolean showSpecialWindow(@NotNull SharedPreferences preferences, @NotNull Preference specialWindowShownPref, int layoutId, int textViewId) { - boolean result = false; - - final Boolean specialWindowShown = specialWindowShownPref.getPreference(preferences); - if ( specialWindowShown != null && !specialWindowShown ) { - final LayoutInflater layoutInflater = (LayoutInflater) this.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(this).setView(view); - builder.setPositiveButton(android.R.string.ok, null); - builder.create().show(); - - result = true; - specialWindowShownPref.putPreference(preferences, true); - } - - return result; - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void elementaryButtonClickHandler(@NotNull View v) { - throw new UnsupportedOperationException("Not implemented yet!"); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void equalsButtonClickHandler(@NotNull View v) { - getCalculator().evaluate(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void historyButtonClickHandler(@NotNull View v) { - CalculatorActivityLauncher.showHistory(this); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void eraseButtonClickHandler(@NotNull View v) { - CalculatorLocatorImpl.getInstance().getEditor().erase(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void simplifyButtonClickHandler(@NotNull View v) { - throw new UnsupportedOperationException("Not implemented yet!"); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void moveLeftButtonClickHandler(@NotNull View v) { - getKeyboard().moveCursorLeft(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void moveRightButtonClickHandler(@NotNull View v) { - getKeyboard().moveCursorRight(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void pasteButtonClickHandler(@NotNull View v) { - getKeyboard().pasteButtonPressed(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void copyButtonClickHandler(@NotNull View v) { - getKeyboard().copyButtonPressed(); - } - - @NotNull - private static CalculatorKeyboard getKeyboard() { - return CalculatorLocatorImpl.getInstance().getKeyboard(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void clearButtonClickHandler(@NotNull View v) { - getKeyboard().clearButtonPressed(); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void digitButtonClickHandler(@NotNull View v) { - Log.d(String.valueOf(v.getId()), "digitButtonClickHandler() for: " + v.getId() + ". Pressed: " + v.isPressed()); - if (((ColorButton) v).isShowText()) { - getKeyboard().digitButtonPressed(((ColorButton) v).getText().toString()); - } - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void functionsButtonClickHandler(@NotNull View v) { - CalculatorActivityLauncher.showFunctions(this); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void operatorsButtonClickHandler(@NotNull View v) { - CalculatorActivityLauncher.showOperators(this); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void varsButtonClickHandler(@NotNull View v) { - CalculatorActivityLauncher.showVars(this); - } - - @SuppressWarnings({"UnusedDeclaration"}) - public void donateButtonClickHandler(@NotNull View v) { - CalculatorApplication.showDonationDialog(this); - } - - @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); - } - - - @Override - public boolean onCreateOptionsMenu(Menu menu) { - return this.menu.onCreateOptionsMenu(this, menu); - } - - @Override - public boolean onOptionsItemSelected(MenuItem item) { - return menu.onOptionsItemSelected(this, item); - } - - /** - * The font sizes in the layout files are specified for a HVGA display. - * Adjust the font sizes accordingly if we are running on a different - * display. - */ - @Override - public void adjustFontSize(@NotNull TextView view) { - float fontPixelSize = view.getTextSize(); - Display display = getWindowManager().getDefaultDisplay(); - int h = Math.min(display.getWidth(), display.getHeight()); - float ratio = (float) h / HVGA_WIDTH_PIXELS; - view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontPixelSize * ratio); - } - - @Override - protected void onResume() { - super.onResume(); - - final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); - - final CalculatorPreferences.Gui.Layout newLayout = CalculatorPreferences.Gui.layout.getPreference(preferences); - final CalculatorPreferences.Gui.Theme newTheme = CalculatorPreferences.Gui.theme.getPreference(preferences); - if (!theme.equals(newTheme) || !layout.equals(newLayout)) { - AndroidUtils.restartActivity(this); - } - } - - @Override - protected void onDestroy() { - if (billingObserver != null) { - BillingController.unregisterObserver(billingObserver); - } - - super.onDestroy(); - } - - @Override - public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) { - if (key != null && key.startsWith("org.solovyev.android.calculator.DragButtonCalibrationActivity")) { - dpclRegister.announce().onDragPreferencesChange(SimpleOnDragListener.getPreferences(preferences, this)); - } - - if ( CalculatorPreferences.Gui.usePrevAsBack.getKey().equals(key) ) { - useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences); - } - - if (AndroidCalculatorEngine.Preferences.numeralBase.getKey().equals(key)) { - numeralBaseButtons.toggleNumericDigits(this, preferences); - } - - if ( AndroidCalculatorEngine.Preferences.multiplicationSign.getKey().equals(key) ) { - initMultiplicationButton(); - } - - if ( CalculatorPreferences.Gui.autoOrientation.getKey().equals(key) ) { - toggleOrientationChange(preferences); - } - - if ( CalculatorPreferences.Gui.showEqualsButton.getKey().equals(key) ) { - toggleEqualsButton(preferences); - } - } - - private void toggleEqualsButton(@Nullable SharedPreferences preferences) { - preferences = preferences == null ? PreferenceManager.getDefaultSharedPreferences(this) : preferences; - - - if (AndroidUtils.getScreenOrientation(this) == Configuration.ORIENTATION_PORTRAIT || !CalculatorPreferences.Gui.autoOrientation.getPreference(preferences)) { - final Display display = this.getWindowManager().getDefaultDisplay(); - - final DragButton button = (DragButton)findViewById(R.id.equalsButton); - if (CalculatorPreferences.Gui.showEqualsButton.getPreference(preferences)) { - button.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.FILL_PARENT, 1f)); - if (display.getWidth() <= 480) { - // mobile phones - getCalculatorDisplayView().setBackgroundDrawable(null); - } - } else { - button.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.FILL_PARENT, 0f)); - if (display.getWidth() <= 480) { - // mobile phones - getCalculatorDisplayView().setBackgroundDrawable(this.getResources().getDrawable(R.drawable.equals9)); - } - } - fixThemeParameters(false); - } - } - - @NotNull - private AndroidCalculatorDisplayView getCalculatorDisplayView() { - return (AndroidCalculatorDisplayView) CalculatorLocatorImpl.getInstance().getDisplay().getView(); - } - - 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); - } - } - - private void initMultiplicationButton() { - final View multiplicationButton = findViewById(R.id.multiplicationButton); - if ( multiplicationButton instanceof Button) { - ((Button) multiplicationButton).setText(CalculatorLocatorImpl.getInstance().getEngine().getMultiplicationSign()); - } - } - - private static class RoundBracketsDragProcessor implements SimpleOnDragListener.DragProcessor { - @Override - public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) { - final boolean result; - - if ( dragDirection == DragDirection.left ) { - getKeyboard().roundBracketsButtonPressed(); - result = true; - } else { - result = new DigitButtonDragProcessor(getKeyboard()).processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent); - } - - return result; - } - } +/* + * 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.Activity; +import android.app.AlertDialog; +import android.app.FragmentManager; +import android.app.FragmentTransaction; +import android.content.Context; +import android.content.SharedPreferences; +import android.content.pm.ActivityInfo; +import android.content.res.Configuration; +import android.os.Bundle; +import android.os.Vibrator; +import android.preference.PreferenceManager; +import android.text.Html; +import android.text.method.LinkMovementMethod; +import android.util.DisplayMetrics; +import android.util.Log; +import android.util.TypedValue; +import android.view.*; +import android.widget.Button; +import android.widget.LinearLayout; +import android.widget.TextView; +import android.widget.Toast; +import jscl.AngleUnit; +import jscl.NumeralBase; +import net.robotmedia.billing.BillingController; +import net.robotmedia.billing.IBillingObserver; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.solovyev.android.AndroidUtils; +import org.solovyev.android.FontSizeAdjuster; +import org.solovyev.android.calculator.about.CalculatorReleaseNotesActivity; +import org.solovyev.android.calculator.history.CalculatorHistoryState; +import org.solovyev.android.calculator.model.AndroidCalculatorEngine; +import org.solovyev.android.calculator.view.AngleUnitsButton; +import org.solovyev.android.calculator.view.CalculatorAdditionalTitle; +import org.solovyev.android.calculator.view.NumeralBasesButton; +import org.solovyev.android.calculator.view.OnDragListenerVibrator; +import org.solovyev.android.history.HistoryDragProcessor; +import org.solovyev.android.menu.ActivityMenu; +import org.solovyev.android.menu.LayoutActivityMenu; +import org.solovyev.android.prefs.Preference; +import org.solovyev.android.view.ColorButton; +import org.solovyev.android.view.drag.*; +import org.solovyev.common.Announcer; +import org.solovyev.common.equals.EqualsTool; +import org.solovyev.common.history.HistoryAction; +import org.solovyev.common.math.Point2d; +import org.solovyev.common.text.StringUtils; + +import java.lang.reflect.Field; +import java.lang.reflect.Modifier; +import java.util.ArrayList; +import java.util.List; + +public class CalculatorActivity extends Activity implements FontSizeAdjuster, SharedPreferences.OnSharedPreferenceChangeListener { + + @NotNull + public static final String TAG = "Calculator++"; + + private static final int HVGA_WIDTH_PIXELS = 320; + + @Nullable + private IBillingObserver billingObserver; + + @NotNull + private final Announcer dpclRegister = new Announcer(DragPreferencesChangeListener.class); + + @NotNull + private CalculatorPreferences.Gui.Theme theme; + + @NotNull + private CalculatorPreferences.Gui.Layout layout; + + @Nullable + private Vibrator vibrator; + + private boolean useBackAsPrev; + + @NotNull + private NumeralBaseButtons numeralBaseButtons = new NumeralBaseButtons(); + + @NotNull + private ActivityMenu menu = LayoutActivityMenu.newInstance(R.menu.main_menu, CalculatorMenu.class); + + /** + * Called when the activity is first created. + */ + @Override + public void onCreate(@Nullable Bundle savedInstanceState) { + + CalculatorApplication.registerOnRemoteStackTrace(); + + final boolean customTitleSupported = requestWindowFeature(Window.FEATURE_CUSTOM_TITLE); + + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + + CalculatorPreferences.setDefaultValues(preferences); + + setTheme(preferences); + super.onCreate(savedInstanceState); + setLayout(preferences); + + final FragmentManager fragmentManager = getFragmentManager(); + + FragmentTransaction fragmentTransaction = fragmentManager.beginTransaction(); + final CalculatorEditorFragment editorFragment = new CalculatorEditorFragment(); + fragmentTransaction.add(R.id.editorContainer, editorFragment); + fragmentTransaction.commit(); + + fragmentTransaction = fragmentManager.beginTransaction(); + final CalculatorDisplayFragment displayFragment = new CalculatorDisplayFragment(); + fragmentTransaction.add(R.id.displayContainer, displayFragment); + fragmentTransaction.commit(); + + if (customTitleSupported) { + try { + getWindow().setFeatureInt(Window.FEATURE_CUSTOM_TITLE, R.layout.calc_title); + final CalculatorAdditionalTitle additionalAdditionalTitleText = (CalculatorAdditionalTitle)findViewById(R.id.additional_title_text); + additionalAdditionalTitleText.init(preferences); + preferences.registerOnSharedPreferenceChangeListener(additionalAdditionalTitleText); + } catch (ClassCastException e) { + // super fix for issue with class cast in android.view.Window.setFeatureInt() (see app error reports) + Log.e(CalculatorActivity.class.getName(), e.getMessage(), e); + } + } + + billingObserver = new CalculatorBillingObserver(this); + BillingController.registerObserver(billingObserver); + + this.useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences); + firstTimeInit(preferences, this); + + // init billing controller + BillingController.checkBillingSupported(this); + + vibrator = (Vibrator) this.getSystemService(VIBRATOR_SERVICE); + + dpclRegister.clear(); + + final SimpleOnDragListener.Preferences dragPreferences = SimpleOnDragListener.getPreferences(preferences, this); + + setOnDragListeners(dragPreferences, preferences); + + final OnDragListener historyOnDragListener = new OnDragListenerVibrator(newOnDragListener(new HistoryDragProcessor(getCalculator()), dragPreferences), vibrator, preferences); + ((DragButton) findViewById(R.id.historyButton)).setOnDragListener(historyOnDragListener); + + ((DragButton) findViewById(R.id.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) { + operatorsButtonClickHandler(dragButton); + return true; + } + return false; + } + }, dragPreferences), vibrator, preferences)); + + + final OnDragListener toPositionOnDragListener = new OnDragListenerVibrator(new SimpleOnDragListener(new CursorDragProcessor(), dragPreferences), vibrator, preferences); + ((DragButton) findViewById(R.id.rightButton)).setOnDragListener(toPositionOnDragListener); + ((DragButton) findViewById(R.id.leftButton)).setOnDragListener(toPositionOnDragListener); + + final DragButton equalsButton = (DragButton) findViewById(R.id.equalsButton); + if (equalsButton != null) { + equalsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new EvalDragProcessor(), dragPreferences), vibrator, preferences)); + } + + final AngleUnitsButton angleUnitsButton = (AngleUnitsButton) findViewById(R.id.sixDigitButton); + if (angleUnitsButton != null) { + angleUnitsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new AngleUnitsChanger(), dragPreferences), vibrator, preferences)); + } + + final NumeralBasesButton clearButton = (NumeralBasesButton) findViewById(R.id.clearButton); + if (clearButton != null) { + clearButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new NumeralBasesChanger(), dragPreferences), vibrator, preferences)); + } + + final DragButton varsButton = (DragButton) findViewById(R.id.varsButton); + if (varsButton != null) { + varsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new VarsDragProcessor(), dragPreferences), vibrator, preferences)); + } + + final DragButton roundBracketsButton = (DragButton) findViewById(R.id.roundBracketsButton); + if ( roundBracketsButton != null ) { + roundBracketsButton.setOnDragListener(new OnDragListenerVibrator(newOnDragListener(new RoundBracketsDragProcessor(), dragPreferences), vibrator, preferences)); + } + + initMultiplicationButton(); + + fixThemeParameters(true); + + if (layout == CalculatorPreferences.Gui.Layout.simple) { + toggleButtonDirectionText(R.id.oneDigitButton, false, DragDirection.up, DragDirection.down); + toggleButtonDirectionText(R.id.twoDigitButton, false, DragDirection.up, DragDirection.down); + toggleButtonDirectionText(R.id.threeDigitButton, false, DragDirection.up, DragDirection.down); + + toggleButtonDirectionText(R.id.sixDigitButton, false, DragDirection.up, DragDirection.down); + toggleButtonDirectionText(R.id.sevenDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down); + toggleButtonDirectionText(R.id.eightDigitButton, false, DragDirection.left, DragDirection.up, DragDirection.down); + + toggleButtonDirectionText(R.id.clearButton, false, DragDirection.left, DragDirection.up, DragDirection.down); + + toggleButtonDirectionText(R.id.fourDigitButton, false, DragDirection.down); + toggleButtonDirectionText(R.id.fiveDigitButton, false, DragDirection.down); + + toggleButtonDirectionText(R.id.nineDigitButton, false, DragDirection.left); + + toggleButtonDirectionText(R.id.multiplicationButton, false, DragDirection.left); + toggleButtonDirectionText(R.id.plusButton, false, DragDirection.down, DragDirection.up); + } + + numeralBaseButtons.toggleNumericDigits(this, preferences); + + toggleOrientationChange(preferences); + + // todo serso: continue + //toggleEqualsButton(preferences); + + preferences.registerOnSharedPreferenceChangeListener(this); + } + + @NotNull + private AndroidCalculatorEngine getEngine() { + return ((AndroidCalculatorEngine) CalculatorLocatorImpl.getInstance().getEngine()); + } + + @NotNull + private AndroidCalculator getCalculator() { + return ((AndroidCalculator) CalculatorLocatorImpl.getInstance().getCalculator()); + } + + private void fixThemeParameters(boolean fixMagicFlames) { + if (theme.getThemeType() == CalculatorPreferences.Gui.ThemeType.metro) { + + if (fixMagicFlames) { + // for metro themes we should turn off magic flames + AndroidUtils.processViewsOfType(this.getWindow().getDecorView(), ColorButton.class, new AndroidUtils.ViewProcessor() { + @Override + public void process(@NotNull ColorButton colorButton) { + colorButton.setDrawMagicFlame(false); + } + }); + } + + fixMargins(2, 2); + } else { + fixMargins(1, 1); + } + } + + private void toggleButtonDirectionText(int id, boolean showDirectionText, @NotNull DragDirection... dragDirections) { + final View v = findViewById(id); + if (v instanceof DirectionDragButton ) { + final DirectionDragButton button = (DirectionDragButton)v; + for (DragDirection dragDirection : dragDirections) { + button.showDirectionText(showDirectionText, dragDirection); + } + } + } + + private void fixMargins(int marginLeft, int marginBottom) { + // sad but true + + final View equalsButton = findViewById(R.id.equalsButton); + final View rightButton = findViewById(R.id.rightButton); + final View leftButton = findViewById(R.id.leftButton); + final View clearButton = findViewById(R.id.clearButton); + final View eraseButton = findViewById(R.id.eraseButton); + + int orientation = AndroidUtils.getScreenOrientation(this); + if (orientation == Configuration.ORIENTATION_PORTRAIT) { + setMarginsForView(equalsButton, marginLeft, marginBottom); + // todo serso: continue + //setMarginsForView(getCalculatorDisplayView(), marginLeft, marginBottom); + } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) { + setMarginsForView(leftButton, marginLeft, marginBottom); + setMarginsForView(eraseButton, marginLeft, marginBottom); + setMarginsForView(clearButton, marginLeft, marginBottom); + setMarginsForView(rightButton, marginLeft, marginBottom); + // magic magic magic + // todo serso: continue + //setMarginsForView(getCalculatorDisplayView(), 3 * marginLeft, marginBottom); + } + } + + private void setMarginsForView(@NotNull View view, int marginLeft, int marginBottom) { + // 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 + + final DisplayMetrics dm = 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); + } + } + + private class AngleUnitsChanger implements SimpleOnDragListener.DragProcessor { + + private final DigitButtonDragProcessor processor = new DigitButtonDragProcessor(getKeyboard()); + + @Override + public boolean processDragEvent(@NotNull DragDirection dragDirection, + @NotNull DragButton dragButton, + @NotNull Point2d startPoint2d, + @NotNull MotionEvent motionEvent) { + boolean result = false; + + if (dragButton instanceof AngleUnitsButton) { + if (dragDirection != DragDirection.left ) { + final String directionText = ((AngleUnitsButton) dragButton).getText(dragDirection); + if ( directionText != null ) { + try { + + final AngleUnit angleUnits = AngleUnit.valueOf(directionText); + + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this); + + AndroidCalculatorEngine.Preferences.angleUnit.putPreference(preferences, angleUnits); + + Toast.makeText(CalculatorActivity.this, CalculatorActivity.this.getString(R.string.c_angle_units_changed_to, angleUnits.name()), Toast.LENGTH_LONG).show(); + + result = true; + } catch (IllegalArgumentException e) { + Log.d(this.getClass().getName(), "Unsupported angle units: " + directionText); + } + } + } else if ( dragDirection == DragDirection.left ) { + result = processor.processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent); + } + } + + return result; + } + } + + private class NumeralBasesChanger implements SimpleOnDragListener.DragProcessor { + + @Override + public boolean processDragEvent(@NotNull DragDirection dragDirection, + @NotNull DragButton dragButton, + @NotNull Point2d startPoint2d, + @NotNull MotionEvent motionEvent) { + boolean result = false; + + if ( dragButton instanceof NumeralBasesButton ) { + final String directionText = ((NumeralBasesButton) dragButton).getText(dragDirection); + if ( directionText != null ) { + try { + + final NumeralBase numeralBase = NumeralBase.valueOf(directionText); + + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(CalculatorActivity.this); + AndroidCalculatorEngine.Preferences.numeralBase.putPreference(preferences, numeralBase); + + Toast.makeText(CalculatorActivity.this, CalculatorActivity.this.getString(R.string.c_numeral_base_changed_to, numeralBase.name()), Toast.LENGTH_LONG).show(); + + result = true; + } catch (IllegalArgumentException e) { + Log.d(this.getClass().getName(), "Unsupported numeral base: " + directionText); + } + } + } + + return result; + } + } + + + private class VarsDragProcessor implements SimpleOnDragListener.DragProcessor { + + @Override + public boolean processDragEvent(@NotNull DragDirection dragDirection, + @NotNull DragButton dragButton, + @NotNull Point2d startPoint2d, + @NotNull MotionEvent motionEvent) { + boolean result = false; + + if (dragDirection == DragDirection.up) { + CalculatorActivityLauncher.createVar(CalculatorActivity.this, CalculatorLocatorImpl.getInstance().getDisplay()); + result = true; + } + + return result; + } + } + + private synchronized void setOnDragListeners(@NotNull SimpleOnDragListener.Preferences dragPreferences, @NotNull SharedPreferences preferences) { + final OnDragListener onDragListener = new OnDragListenerVibrator(newOnDragListener(new DigitButtonDragProcessor(getKeyboard()), dragPreferences), vibrator, preferences); + + final List dragButtonIds = new ArrayList(); + final List buttonIds = new ArrayList(); + + 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 = this.findViewById(viewId); + if (view instanceof DragButton) { + dragButtonIds.add(viewId); + } + if (view instanceof Button) { + buttonIds.add(viewId); + } + } catch (IllegalAccessException e) { + Log.e(R.id.class.getName(), e.getMessage()); + } + } + } + + for (Integer dragButtonId : dragButtonIds) { + ((DragButton) findViewById(dragButtonId)).setOnDragListener(onDragListener); + } + } + + @NotNull + private SimpleOnDragListener newOnDragListener(@NotNull SimpleOnDragListener.DragProcessor dragProcessor, + @NotNull SimpleOnDragListener.Preferences dragPreferences) { + final SimpleOnDragListener onDragListener = new SimpleOnDragListener(dragProcessor, dragPreferences); + dpclRegister.addListener(onDragListener); + return onDragListener; + } + + + private synchronized void setLayout(@NotNull SharedPreferences preferences) { + layout = CalculatorPreferences.Gui.layout.getPreferenceNoError(preferences); + + setContentView(layout.getLayoutId()); + } + + private synchronized void setTheme(@NotNull SharedPreferences preferences) { + theme = CalculatorPreferences.Gui.theme.getPreferenceNoError(preferences); + setTheme(theme.getThemeId()); + } + + 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); + + 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 = CalculatorReleaseNotesActivity.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); + } + } + + if (!dialogShown) { + dialogShown = showSpecialWindow(preferences, CalculatorPreferences.Gui.notesppAnnounceShown, R.layout.notespp_announce, R.id.notespp_announce, context); + } + } + + private static boolean showSpecialWindow(@NotNull SharedPreferences preferences, @NotNull Preference 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; + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void elementaryButtonClickHandler(@NotNull View v) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void equalsButtonClickHandler(@NotNull View v) { + getCalculator().evaluate(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void historyButtonClickHandler(@NotNull View v) { + CalculatorActivityLauncher.showHistory(this); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void eraseButtonClickHandler(@NotNull View v) { + CalculatorLocatorImpl.getInstance().getEditor().erase(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void simplifyButtonClickHandler(@NotNull View v) { + throw new UnsupportedOperationException("Not implemented yet!"); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void moveLeftButtonClickHandler(@NotNull View v) { + getKeyboard().moveCursorLeft(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void moveRightButtonClickHandler(@NotNull View v) { + getKeyboard().moveCursorRight(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void pasteButtonClickHandler(@NotNull View v) { + getKeyboard().pasteButtonPressed(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void copyButtonClickHandler(@NotNull View v) { + getKeyboard().copyButtonPressed(); + } + + @NotNull + private static CalculatorKeyboard getKeyboard() { + return CalculatorLocatorImpl.getInstance().getKeyboard(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void clearButtonClickHandler(@NotNull View v) { + getKeyboard().clearButtonPressed(); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void digitButtonClickHandler(@NotNull View v) { + Log.d(String.valueOf(v.getId()), "digitButtonClickHandler() for: " + v.getId() + ". Pressed: " + v.isPressed()); + if (((ColorButton) v).isShowText()) { + getKeyboard().digitButtonPressed(((ColorButton) v).getText().toString()); + } + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void functionsButtonClickHandler(@NotNull View v) { + CalculatorActivityLauncher.showFunctions(this); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void operatorsButtonClickHandler(@NotNull View v) { + CalculatorActivityLauncher.showOperators(this); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void varsButtonClickHandler(@NotNull View v) { + CalculatorActivityLauncher.showVars(this); + } + + @SuppressWarnings({"UnusedDeclaration"}) + public void donateButtonClickHandler(@NotNull View v) { + CalculatorApplication.showDonationDialog(this); + } + + @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); + } + + + @Override + public boolean onCreateOptionsMenu(Menu menu) { + return this.menu.onCreateOptionsMenu(this, menu); + } + + @Override + public boolean onOptionsItemSelected(MenuItem item) { + return menu.onOptionsItemSelected(this, item); + } + + /** + * The font sizes in the layout files are specified for a HVGA display. + * Adjust the font sizes accordingly if we are running on a different + * display. + */ + @Override + public void adjustFontSize(@NotNull TextView view) { + float fontPixelSize = view.getTextSize(); + Display display = getWindowManager().getDefaultDisplay(); + int h = Math.min(display.getWidth(), display.getHeight()); + float ratio = (float) h / HVGA_WIDTH_PIXELS; + view.setTextSize(TypedValue.COMPLEX_UNIT_PX, fontPixelSize * ratio); + } + + @Override + protected void onResume() { + super.onResume(); + + final SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this); + + final CalculatorPreferences.Gui.Layout newLayout = CalculatorPreferences.Gui.layout.getPreference(preferences); + final CalculatorPreferences.Gui.Theme newTheme = CalculatorPreferences.Gui.theme.getPreference(preferences); + if (!theme.equals(newTheme) || !layout.equals(newLayout)) { + AndroidUtils.restartActivity(this); + } + } + + @Override + protected void onDestroy() { + if (billingObserver != null) { + BillingController.unregisterObserver(billingObserver); + } + + super.onDestroy(); + } + + @Override + public void onSharedPreferenceChanged(SharedPreferences preferences, @Nullable String key) { + if (key != null && key.startsWith("org.solovyev.android.calculator.DragButtonCalibrationActivity")) { + dpclRegister.announce().onDragPreferencesChange(SimpleOnDragListener.getPreferences(preferences, this)); + } + + if ( CalculatorPreferences.Gui.usePrevAsBack.getKey().equals(key) ) { + useBackAsPrev = CalculatorPreferences.Gui.usePrevAsBack.getPreference(preferences); + } + + if (AndroidCalculatorEngine.Preferences.numeralBase.getKey().equals(key)) { + numeralBaseButtons.toggleNumericDigits(this, preferences); + } + + if ( AndroidCalculatorEngine.Preferences.multiplicationSign.getKey().equals(key) ) { + initMultiplicationButton(); + } + + if ( CalculatorPreferences.Gui.autoOrientation.getKey().equals(key) ) { + toggleOrientationChange(preferences); + } + + if ( CalculatorPreferences.Gui.showEqualsButton.getKey().equals(key) ) { + toggleEqualsButton(preferences); + } + } + + private void toggleEqualsButton(@Nullable SharedPreferences preferences) { + preferences = preferences == null ? PreferenceManager.getDefaultSharedPreferences(this) : preferences; + + + if (AndroidUtils.getScreenOrientation(this) == Configuration.ORIENTATION_PORTRAIT || !CalculatorPreferences.Gui.autoOrientation.getPreference(preferences)) { + final Display display = this.getWindowManager().getDefaultDisplay(); + + final DragButton button = (DragButton)findViewById(R.id.equalsButton); + if (CalculatorPreferences.Gui.showEqualsButton.getPreference(preferences)) { + button.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.FILL_PARENT, 1f)); + if (display.getWidth() <= 480) { + // mobile phones + getCalculatorDisplayView().setBackgroundDrawable(null); + } + } else { + button.setLayoutParams(new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.FILL_PARENT, 0f)); + if (display.getWidth() <= 480) { + // mobile phones + getCalculatorDisplayView().setBackgroundDrawable(this.getResources().getDrawable(R.drawable.equals9)); + } + } + fixThemeParameters(false); + } + } + + @NotNull + private AndroidCalculatorDisplayView getCalculatorDisplayView() { + return (AndroidCalculatorDisplayView) CalculatorLocatorImpl.getInstance().getDisplay().getView(); + } + + 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); + } + } + + private void initMultiplicationButton() { + final View multiplicationButton = findViewById(R.id.multiplicationButton); + if ( multiplicationButton instanceof Button) { + ((Button) multiplicationButton).setText(CalculatorLocatorImpl.getInstance().getEngine().getMultiplicationSign()); + } + } + + private static class RoundBracketsDragProcessor implements SimpleOnDragListener.DragProcessor { + @Override + public boolean processDragEvent(@NotNull DragDirection dragDirection, @NotNull DragButton dragButton, @NotNull Point2d startPoint2d, @NotNull MotionEvent motionEvent) { + final boolean result; + + if ( dragDirection == DragDirection.left ) { + getKeyboard().roundBracketsButtonPressed(); + result = true; + } else { + result = new DigitButtonDragProcessor(getKeyboard()).processDragEvent(dragDirection, dragButton, startPoint2d, motionEvent); + } + + return result; + } + } } \ No newline at end of file diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayFragment.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayFragment.java new file mode 100644 index 00000000..9dfa9a40 --- /dev/null +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayFragment.java @@ -0,0 +1,32 @@ +package org.solovyev.android.calculator; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * User: Solovyev_S + * Date: 25.09.12 + * Time: 12:03 + */ +public class CalculatorDisplayFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.calc_display, null); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + ((AndroidCalculator) CalculatorLocatorImpl.getInstance().getCalculator()).setDisplay(getActivity()); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayOnClickListener.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayOnClickListener.java index 213f34d2..7d75681e 100644 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayOnClickListener.java +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorDisplayOnClickListener.java @@ -1,53 +1,53 @@ -package org.solovyev.android.calculator; - -import android.app.Activity; -import android.view.View; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.menu.AMenuBuilder; -import org.solovyev.android.menu.MenuImpl; - -import java.util.ArrayList; -import java.util.List; - -/** - * User: Solovyev_S - * Date: 21.09.12 - * Time: 10:58 - */ -public class CalculatorDisplayOnClickListener implements View.OnClickListener { - - @NotNull - private final Activity activity; - - public CalculatorDisplayOnClickListener(@NotNull Activity activity) { - this.activity = activity; - } - - @Override - public void onClick(View v) { - if (v instanceof CalculatorDisplayView) { - final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getDisplay(); - - final CalculatorDisplayViewState displayViewState = cd.getViewState(); - - if (displayViewState.isValid()) { - final List filteredMenuItems = new ArrayList(CalculatorDisplayMenuItem.values().length); - for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) { - if (menuItem.isItemVisible(displayViewState)) { - filteredMenuItems.add(menuItem); - } - } - - if (!filteredMenuItems.isEmpty()) { - AMenuBuilder.newInstance(activity, MenuImpl.newInstance(filteredMenuItems)).create(displayViewState).show(); - } - - } else { - final String errorMessage = displayViewState.getErrorMessage(); - if (errorMessage != null) { - CalculatorModel.showEvaluationError(activity, errorMessage); - } - } - } - } -} +package org.solovyev.android.calculator; + +import android.content.Context; +import android.view.View; +import org.jetbrains.annotations.NotNull; +import org.solovyev.android.menu.AMenuBuilder; +import org.solovyev.android.menu.MenuImpl; + +import java.util.ArrayList; +import java.util.List; + +/** + * User: Solovyev_S + * Date: 21.09.12 + * Time: 10:58 + */ +public class CalculatorDisplayOnClickListener implements View.OnClickListener { + + @NotNull + private final Context context; + + public CalculatorDisplayOnClickListener(@NotNull Context context) { + this.context = context; + } + + @Override + public void onClick(View v) { + if (v instanceof CalculatorDisplayView) { + final CalculatorDisplay cd = CalculatorLocatorImpl.getInstance().getDisplay(); + + final CalculatorDisplayViewState displayViewState = cd.getViewState(); + + if (displayViewState.isValid()) { + final List filteredMenuItems = new ArrayList(CalculatorDisplayMenuItem.values().length); + for (CalculatorDisplayMenuItem menuItem : CalculatorDisplayMenuItem.values()) { + if (menuItem.isItemVisible(displayViewState)) { + filteredMenuItems.add(menuItem); + } + } + + if (!filteredMenuItems.isEmpty()) { + AMenuBuilder.newInstance(context, MenuImpl.newInstance(filteredMenuItems)).create(displayViewState).show(); + } + + } else { + final String errorMessage = displayViewState.getErrorMessage(); + if (errorMessage != null) { + AndroidCalculator.showEvaluationError(context, errorMessage); + } + } + } + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditorFragment.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditorFragment.java new file mode 100644 index 00000000..8aeff57f --- /dev/null +++ b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorEditorFragment.java @@ -0,0 +1,32 @@ +package org.solovyev.android.calculator; + +import android.app.Fragment; +import android.os.Bundle; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; + +/** + * User: Solovyev_S + * Date: 25.09.12 + * Time: 10:49 + */ +public class CalculatorEditorFragment extends Fragment { + + @Override + public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { + return inflater.inflate(R.layout.calc_editor, null); + } + + @Override + public void onViewCreated(View view, Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + + ((AndroidCalculator) CalculatorLocatorImpl.getInstance().getCalculator()).setEditor(getActivity()); + } + + @Override + public void onActivityCreated(Bundle savedInstanceState) { + super.onActivityCreated(savedInstanceState); + } +} diff --git a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java b/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java deleted file mode 100644 index 5c06ae0c..00000000 --- a/calculatorpp/src/main/java/org/solovyev/android/calculator/CalculatorModel.java +++ /dev/null @@ -1,103 +0,0 @@ -/* - * 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.Activity; -import android.app.AlertDialog; -import android.content.SharedPreferences; -import android.util.Log; -import android.view.LayoutInflater; -import android.view.View; -import android.widget.TextView; -import org.jetbrains.annotations.NotNull; -import org.solovyev.android.calculator.jscl.JsclOperation; -import org.solovyev.common.gui.CursorControl; - -/** - * User: serso - * Date: 9/12/11 - * Time: 11:15 PM - */ -public enum CalculatorModel implements CalculatorEngineControl, CursorControl { - - instance; - - @NotNull - private final CalculatorEditor editor; - - @NotNull - private final CalculatorDisplay display; - - private CalculatorModel() { - display = CalculatorLocatorImpl.getInstance().getDisplay(); - editor = CalculatorLocatorImpl.getInstance().getEditor(); - } - - public CalculatorModel attachViews(@NotNull final Activity activity, @NotNull SharedPreferences preferences) { - Log.d(this.getClass().getName(), "CalculatorModel initialization with activity: " + activity); - - final AndroidCalculatorEditorView editorView = (AndroidCalculatorEditorView) activity.findViewById(R.id.calculatorEditor); - editorView.init(preferences); - preferences.registerOnSharedPreferenceChangeListener(editorView); - editor.setView(editorView); - - final AndroidCalculatorDisplayView displayView = (AndroidCalculatorDisplayView) activity.findViewById(R.id.calculatorDisplay); - displayView.setOnClickListener(new CalculatorDisplayOnClickListener(activity)); - display.setView(displayView); - - return this; - } - - public static void showEvaluationError(@NotNull Activity activity, @NotNull final String errorMessage) { - final LayoutInflater layoutInflater = (LayoutInflater) activity.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(activity) - .setPositiveButton(R.string.c_cancel, null) - .setView(errorMessageView); - - builder.create().show(); - } - - @Override - public void setCursorOnStart() { - this.editor.setCursorOnStart(); - } - - @Override - public void setCursorOnEnd() { - this.editor.setCursorOnEnd(); - } - - @Override - public void moveCursorLeft() { - this.editor.moveCursorLeft(); - } - - @Override - public void moveCursorRight() { - this.editor.moveCursorRight(); - } - - @Override - public void evaluate() { - CalculatorLocatorImpl.getInstance().getCalculator().evaluate(JsclOperation.numeric, this.editor.getViewState().getText()); - } - - @Override - public void simplify() { - CalculatorLocatorImpl.getInstance().getCalculator().evaluate(JsclOperation.simplify, this.editor.getViewState().getText()); - } - - - @NotNull - public CalculatorDisplay getDisplay() { - return display; - } - -} diff --git a/pom.xml b/pom.xml index eff76609..1874cdfc 100644 --- a/pom.xml +++ b/pom.xml @@ -1,245 +1,251 @@ - - - - 4.0.0 - - org.solovyev.android - calculatorpp-parent - pom - 1.3.2 - Calculator++ - - - calculatorpp - calculatorpp-test - calculatorpp-core - - - - UTF-8 - - - - - - - org.solovyev - common-core - 1.0.0 - - - - org.solovyev - common-text - 1.0.1 - - - - org.solovyev.android - android-common-core - apklib - 1.0.0 - - - - org.solovyev.android - android-common-ads - apklib - 1.0.0 - - - - org.solovyev.android - android-common-view - apklib - 1.0.0 - - - - org.solovyev.android - android-common-preferences - apklib - 1.0.0 - - - - org.solovyev.android - android-common-menu - apklib - 1.0.0 - - - - org.solovyev - jscl - 0.0.2 - - - xercesImpl - xerces - - - - - - org.solovyev.android - android-common-other - apklib - 1.0.0 - - - - junit - junit - 4.8.2 - - - - com.intellij - annotations - 7.0.3 - - - - com.google.android - android - 4.0.1.2 - provided - - - - com.google.android - android-test - 2.3.1 - - - - com.google.guava - guava - 11.0.2 - - - - org.simpleframework - simple-xml - 2.6.1 - - - stax-api - stax - - - xpp3 - xpp3 - - - - - - - - - - - - - com.electriccloud - javac2-maven-plugin - 1.0.1 - - - @NotNull Instrumentation - - instrument - - - compile - - - - - - - - - - - org.apache.maven.plugins - maven-jarsigner-plugin - 1.2 - - - - com.jayway.maven.plugins.android.generation2 - android-maven-plugin - 3.1.1 - - - - ${project.basedir}/src/main/java - - - - 15 - - - - 23 - 10000 - - - - - true - - - true - - - - - - - com.pyx4me - proguard-maven-plugin - 2.0.4 - - - - org.codehaus.mojo - build-helper-maven-plugin - 1.5 - - - - - - - - - - - - standard - - - - - release - - - - performRelease - true - - - - - - - + + + + 4.0.0 + + org.solovyev.android + calculatorpp-parent + pom + 1.3.2 + Calculator++ + + + calculatorpp + calculatorpp-test + calculatorpp-core + + + + UTF-8 + + + + + + + org.solovyev + common-core + 1.0.0 + + + + org.solovyev + common-text + 1.0.1 + + + + org.solovyev.android + android-common-core + apklib + 1.0.0 + + + + org.solovyev.android + android-common-ads + apklib + 1.0.0 + + + + org.solovyev.android + android-common-view + apklib + 1.0.0 + + + + org.solovyev.android + android-common-preferences + apklib + 1.0.0 + + + + org.solovyev.android + android-common-menu + apklib + 1.0.0 + + + + org.solovyev + jscl + 0.0.2 + + + xercesImpl + xerces + + + + + + org.solovyev.android + android-common-other + apklib + 1.0.0 + + + + junit + junit + 4.8.2 + + + + com.intellij + annotations + 7.0.3 + + + + com.google.android + android + 4.0.1.2 + provided + + + + com.google.android + support-v4 + r7 + + + + com.google.android + android-test + 2.3.1 + + + + com.google.guava + guava + 11.0.2 + + + + org.simpleframework + simple-xml + 2.6.1 + + + stax-api + stax + + + xpp3 + xpp3 + + + + + + + + + + + + + com.electriccloud + javac2-maven-plugin + 1.0.1 + + + @NotNull Instrumentation + + instrument + + + compile + + + + + + + + + + + org.apache.maven.plugins + maven-jarsigner-plugin + 1.2 + + + + com.jayway.maven.plugins.android.generation2 + android-maven-plugin + 3.1.1 + + + + ${project.basedir}/src/main/java + + + + 15 + + + + 23 + 10000 + + + + + true + + + true + + + + + + + com.pyx4me + proguard-maven-plugin + 2.0.4 + + + + org.codehaus.mojo + build-helper-maven-plugin + 1.5 + + + + + + + + + + + + standard + + + + + release + + + + performRelease + true + + + + + + + \ No newline at end of file