Editor/display changes

This commit is contained in:
Sergey Solovyev
2012-09-24 23:20:19 +04:00
parent 4b08fa133e
commit 4e42f6ad6b
6 changed files with 813 additions and 699 deletions

View File

@@ -8,10 +8,12 @@ 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 android.util.Log;
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;
@@ -43,7 +45,12 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
*/
@NotNull
private CalculatorDisplayViewState state = CalculatorDisplayViewStateImpl.newDefaultInstance();
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();
@@ -58,14 +65,18 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
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());
}
/*
@@ -76,63 +87,103 @@ public class AndroidCalculatorDisplayView extends AutoResizeTextView implements
**********************************************************************
*/
public boolean isValid() {
synchronized (this) {
return this.state.isValid();
}
}
@Override
public void setState(@NotNull final CalculatorDisplayViewState state) {
handler.postDelayed(new Runnable() {
final CharSequence text = prepareText(state.getStringResult(), state.isValid());
handler.post(new Runnable() {
@Override
public void run() {
synchronized (AndroidCalculatorDisplayView.this) {
AndroidCalculatorDisplayView.this.state = state;
if ( state.isValid() ) {
setTextColor(getResources().getColor(R.color.default_text_color));
setText(state.getStringResult());
redraw();
} else {
setTextColor(getResources().getColor(R.color.display_error_text_color));
synchronized (lock) {
try {
viewStateChange = true;
// error messages are never shown -> just greyed out text (error message will be shown on click)
//setText(state.getErrorMessage());
//redraw();
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;
}
}
}
}, 1);
});
}
@NotNull
@Override
public CalculatorDisplayViewState getState() {
synchronized (this) {
synchronized (lock) {
return this.state;
}
}
private synchronized void redraw() {
if (isValid()) {
String text = getText().toString();
@Nullable
private static CharSequence prepareText(@Nullable String text, boolean valid) {
CharSequence result;
Log.d(this.getClass().getName(), text);
if (valid && text != null) {
//Log.d(this.getClass().getName(), text);
try {
TextHighlighter.Result result = textHighlighter.process(text);
text = result.toString();
final TextHighlighter.Result processedText = textHighlighter.process(text);
text = processedText.toString();
result = Html.fromHtml(text);
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
result = text;
}
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
} 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);
}
}
}

View File

@@ -10,10 +10,15 @@ 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;
@@ -25,32 +30,39 @@ import org.solovyev.common.collections.CollectionsUtils;
*/
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 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;
private boolean highlightText = true;
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, true);
@NotNull
private final static TextProcessor<TextHighlighter.Result, String> textHighlighter = new TextHighlighter(Color.WHITE, false);
@NotNull
private volatile CalculatorEditorViewState viewState = CalculatorEditorViewStateImpl.newDefaultInstance();
private volatile boolean viewStateChange = false;
// 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);
}
super(context);
this.addTextChangedListener(new TextWatcherImpl());
}
public AndroidCalculatorEditorView(Context context, AttributeSet attrs) {
super(context, attrs);
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());
}
@@ -59,12 +71,12 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
// 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 ) {
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()) ) {
if ("isCursorVisible".equals(stackTraceElement.getMethodName())) {
return true;
}
}
@@ -78,94 +90,115 @@ public class AndroidCalculatorEditorView extends EditText implements SharedPrefe
}
}
@Override
protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
@Override
protected void onCreateContextMenu(ContextMenu menu) {
super.onCreateContextMenu(menu);
menu.removeItem(android.R.id.selectAll);
}
menu.removeItem(android.R.id.selectAll);
}
// todo serso: fix redraw
// Now problem is that calculator editor cursor position might be different than position of cursor in view (as some extra spaces can be inserted fur to number formatting)
/*private synchronized void redraw() {
String text = getText().toString();
@Nullable
private CharSequence prepareText(@NotNull String text, boolean highlightText) {
CharSequence result;
int selectionStart = getSelectionStart();
int selectionEnd = getSelectionEnd();
if (highlightText) {
if (highlightText) {
try {
final TextHighlighter.Result processesText = textHighlighter.process(text);
Log.d(this.getClass().getName(), text);
assert processesText.getOffset() == 0;
try {
final TextHighlighter.Result result = textHighlighter.process(text);
selectionStart += result.getOffset();
selectionEnd += result.getOffset();
text = result.toString();
} catch (CalculatorParseException e) {
Log.e(this.getClass().getName(), e.getMessage(), e);
}
result = Html.fromHtml(processesText.toString());
} catch (CalculatorParseException e) {
// set raw text
result = text;
Log.d(this.getClass().getName(), text);
super.setText(Html.fromHtml(text), BufferType.EDITABLE);
} else {
super.setText(text, BufferType.EDITABLE);
}
Log.e(this.getClass().getName(), e.getMessage(), e);
}
} else {
result = text;
}
Log.d(this.getClass().getName(), getText().toString());
return result;
}
int length = getText().length();
setSelection(Math.max(Math.min(length, selectionStart), 0), Math.max(Math.min(length, selectionEnd), 0));
}*/
public boolean isHighlightText() {
return highlightText;
}
public boolean isHighlightText() {
return highlightText;
}
public void setHighlightText(boolean highlightText) {
this.highlightText = highlightText;
CalculatorLocatorImpl.getInstance().getEditor().updateViewState();
}
public void setHighlightText(boolean highlightText) {
this.highlightText = highlightText;
//redraw();
}
@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));
}
}
@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);
}
public void init(@NotNull SharedPreferences preferences) {
onSharedPreferenceChanged(preferences, CALC_COLOR_DISPLAY_KEY);
}
@Override
public void setState(@NotNull final CalculatorEditorViewState viewState) {
handler.postDelayed(new Runnable() {
final CharSequence text = prepareText(viewState.getText(), highlightText);
handler.post(new Runnable() {
@Override
public void run() {
final AndroidCalculatorEditorView editorView = AndroidCalculatorEditorView.this;
synchronized (editorView) {
synchronized (lock) {
try {
editorView.viewStateChange = true;
editorView.viewState = viewState;
editorView.setText(viewState.getText());
editorView.setText(text, BufferType.EDITABLE);
editorView.setSelection(viewState.getSelection());
//redraw();
} finally {
editorView.viewStateChange = false;
}
}
}
}, 1);
});
}
@Override
protected void onSelectionChanged(int selStart, int selEnd) {
synchronized (this) {
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);
}
}
}