Variable validation

This commit is contained in:
serso 2016-01-30 21:12:24 +01:00
parent 4a9e8bf78a
commit 0170c3bb6a
11 changed files with 130 additions and 72 deletions

View File

@ -1,16 +1,16 @@
package org.solovyev.android.calculator;
import dagger.Component;
import org.solovyev.android.calculator.functions.EditFunctionFragment;
import org.solovyev.android.calculator.history.BaseHistoryFragment;
import org.solovyev.android.calculator.history.EditHistoryFragment;
import org.solovyev.android.calculator.math.edit.FunctionsFragment;
import org.solovyev.android.calculator.variables.VariablesFragment;
import org.solovyev.android.calculator.onscreen.CalculatorOnscreenService;
import org.solovyev.android.calculator.variables.EditVariableFragment;
import org.solovyev.android.calculator.variables.VariablesFragment;
import javax.inject.Singleton;
import dagger.Component;
@Singleton
@Component(modules = AppModule.class)
public interface AppComponent {
@ -21,6 +21,7 @@ public interface AppComponent {
void inject(BaseHistoryFragment fragment);
void inject(BaseDialogFragment fragment);
void inject(EditFunctionFragment fragment);
void inject(EditVariableFragment fragment);
void inject(EditHistoryFragment fragment);
void inject(FunctionsFragment fragment);
void inject(VariablesFragment fragment);

View File

@ -6,14 +6,13 @@ import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringRes;
import android.support.design.widget.TextInputLayout;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.FragmentActivity;
import android.support.v7.app.AlertDialog;
import android.view.LayoutInflater;
import android.view.View;
import android.view.Window;
import android.view.WindowManager;
import android.view.inputmethod.InputMethodManager;
import javax.inject.Inject;
@ -62,12 +61,15 @@ public abstract class BaseDialogFragment extends DialogFragment {
@NonNull
protected abstract View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState);
protected static void setError(@NonNull TextInputLayout textInput, @NonNull String error) {
protected void setError(@NonNull TextInputLayout textInput, @StringRes int error, Object... errorArgs) {
setError(textInput, getString(error, errorArgs));
}
protected void setError(@NonNull TextInputLayout textInput, @NonNull String error) {
textInput.setError(error);
textInput.setErrorEnabled(true);
}
protected static void clearError(@NonNull TextInputLayout textInput) {
protected void clearError(@NonNull TextInputLayout textInput) {
textInput.setError(null);
textInput.setErrorEnabled(false);
}

View File

@ -23,9 +23,12 @@
package org.solovyev.android.calculator;
import android.content.SharedPreferences;
import com.squareup.otto.Bus;
import jscl.AngleUnit;
import jscl.JsclMathEngine;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.math.operator.Operator;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.functions.FunctionsRegistry;
import org.solovyev.android.prefs.BooleanPreference;
@ -36,21 +39,14 @@ import org.solovyev.common.text.EnumMapper;
import org.solovyev.common.text.NumberMapper;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.Executor;
import javax.annotation.Nonnull;
import javax.inject.Inject;
import javax.inject.Singleton;
import jscl.AngleUnit;
import jscl.JsclMathEngine;
import jscl.MathEngine;
import jscl.NumeralBase;
import jscl.math.operator.Operator;
@Singleton
public class Engine implements SharedPreferences.OnSharedPreferenceChangeListener {
@ -82,7 +78,7 @@ public class Engine implements SharedPreferences.OnSharedPreferenceChangeListene
}
@Inject
public Engine(@Nonnull SharedPreferences preferences, @Nonnull JsclMathEngine mathEngine) {
public Engine(@Nonnull JsclMathEngine mathEngine) {
this.mathEngine = mathEngine;
this.mathEngine.setRoundResult(true);

View File

@ -65,9 +65,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
private static final int MENU_CATEGORY = Menu.FIRST + 2;
@NonNull
private final VariablesRegistry constantsRegistry = Locator.getInstance().getEngine().getVariablesRegistry();
@NonNull
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow();
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow(null);
@NonNull
private final KeyboardUser keyboardUser = new KeyboardUser();
@Bind(R.id.function_params)
@ -86,6 +84,8 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
Calculator calculator;
@Inject
FunctionsRegistry functionsRegistry;
@Inject
VariablesRegistry variablesRegistry;
@Nullable
private CppFunction function;
@ -453,7 +453,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
final int id = v.getId();
if (id == R.id.function_body) {
menu.clear();
addEntities(menu, getNamesSorted(constantsRegistry), MENU_CONSTANT);
addEntities(menu, getNamesSorted(variablesRegistry), MENU_CONSTANT);
unregisterForContextMenu(bodyView);
}
}

View File

@ -19,11 +19,18 @@ import org.solovyev.android.calculator.R;
public class FloatingKeyboardWindow {
@javax.annotation.Nullable
private final PopupWindow.OnDismissListener dismissListener;
@Nullable
private PopupWindow window;
@Nullable
private Dialog dialog;
public FloatingKeyboardWindow(@javax.annotation.Nullable PopupWindow.OnDismissListener dismissListener) {
this.dismissListener = dismissListener;
}
private static void hideIme(@NonNull View view) {
final IBinder token = view.getWindowToken();
if (token != null) {
@ -67,6 +74,9 @@ public class FloatingKeyboardWindow {
@Override
public void onDismiss() {
window = null;
if (dismissListener != null) {
dismissListener.onDismiss();
}
}
});
// see http://stackoverflow.com/a/4713487/720489

View File

@ -31,8 +31,6 @@ import android.support.design.widget.TextInputLayout;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.FragmentManager;
import android.support.v7.app.AlertDialog;
import android.text.Editable;
import android.text.TextWatcher;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.View;
@ -40,19 +38,24 @@ import android.view.ViewGroup;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import android.widget.Toast;
import android.widget.PopupWindow;
import butterknife.Bind;
import butterknife.ButterKnife;
import jscl.math.function.IConstant;
import org.solovyev.android.Activities;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.*;
import org.solovyev.android.calculator.functions.FunctionsRegistry;
import org.solovyev.android.calculator.keyboard.FloatingKeyboard;
import org.solovyev.android.calculator.keyboard.FloatingKeyboardWindow;
import org.solovyev.android.calculator.math.MathType;
import org.solovyev.android.calculator.math.edit.VarEditorSaver;
import org.solovyev.android.calculator.view.EditTextCompat;
import org.solovyev.common.text.Strings;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.inject.Inject;
import java.util.Arrays;
import java.util.List;
@ -61,10 +64,14 @@ import static org.solovyev.android.calculator.functions.CppFunction.NO_ID;
public class EditVariableFragment extends BaseDialogFragment implements CalculatorEventListener, View.OnFocusChangeListener, View.OnKeyListener, View.OnClickListener {
private static final String ARG_VARIABLE = "variable";
private final static String greekAlphabet = "αβγδεζηθικλμνξοπρστυφχψω";
private final static List<Character> acceptableChars = Arrays.asList(Strings.toObjects(("1234567890abcdefghijklmnopqrstuvwxyzйцукенгшщзхъфывапролджэячсмитьбюё_" + greekAlphabet).toCharArray()));
private final static List<Character> ACCEPTABLE_CHARACTERS = Arrays.asList(Strings.toObjects(("1234567890abcdefghijklmnopqrstuvwxyzйцукенгшщзхъфывапролджэячсмитьбюё_" + GreekFloatingKeyboard.ALPHABET).toCharArray()));
@NonNull
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow();
private final FloatingKeyboardWindow keyboardWindow = new FloatingKeyboardWindow(new PopupWindow.OnDismissListener() {
@Override
public void onDismiss() {
nameView.setShowSoftInputOnFocusCompat(true);
}
});
@NonNull
private final KeyboardUser keyboardUser = new KeyboardUser();
@Bind(R.id.variable_name_label)
@ -79,6 +86,12 @@ public class EditVariableFragment extends BaseDialogFragment implements Calculat
EditText valueView;
@Bind(R.id.variable_description)
EditText descriptionView;
@Inject
Calculator calculator;
@Inject
FunctionsRegistry functionsRegistry;
@Inject
VariablesRegistry variablesRegistry;
@Nullable
private CppVariable variable;
@ -124,6 +137,12 @@ public class EditVariableFragment extends BaseDialogFragment implements Calculat
}
}
@Override
protected void inject(@NonNull AppComponent component) {
super.inject(component);
component.inject(this);
}
@Override
protected void onPrepareDialog(@NonNull AlertDialog.Builder builder) {
builder.setNegativeButton(R.string.c_cancel, null);
@ -189,13 +208,61 @@ public class EditVariableFragment extends BaseDialogFragment implements Calculat
}
private boolean validateValue() {
final String value = valueView.getText().toString();
if (!Strings.isEmpty(value)) {
// value is not empty => must be a number
if (!VariablesFragment.isValidValue(value)) {
setError(valueLabel, R.string.c_value_is_not_a_number);
return false;
}
}
clearError(valueLabel);
return true;
}
private boolean validateName() {
final String name = nameView.getText().toString();
if (!VarEditorSaver.isValidName(name)) {
setError(nameLabel, getString(R.string.c_name_is_not_valid));
return false;
}
for (int i = 0; i < name.length(); i++) {
final char c = name.charAt(i);
if (!ACCEPTABLE_CHARACTERS.contains(Character.toLowerCase(c))) {
setError(nameLabel, getString(R.string.c_char_is_not_accepted, c));
return false;
}
}
final IConstant existingVariable = variablesRegistry.get(name);
if (existingVariable != null) {
if (!existingVariable.isIdDefined()) {
Check.shouldNotHappen();
setError(nameLabel, getString(R.string.c_var_already_exists));
return false;
}
if (isNewVariable()) {
// trying to create a new variable with existing name
setError(nameLabel, getString(R.string.c_var_already_exists));
return false;
}
Check.isNotNull(variable);
if (!existingVariable.getId().equals(variable.id)) {
// trying to change the name of existing variable to some other variable's name
setError(nameLabel, getString(R.string.c_var_already_exists));
return false;
}
}
final MathType.Result type = MathType.getType(name, 0, false);
if (type.type != MathType.text && type.type != MathType.constant) {
setError(nameLabel, getString(R.string.c_var_name_clashes));
return false;
}
clearError(nameLabel);
return true;
}
@Override
public void onResume() {
@ -295,28 +362,6 @@ public class EditVariableFragment extends BaseDialogFragment implements Calculat
keyboardWindow.show(new GreekFloatingKeyboard(keyboardUser), getDialog());
}
private class NameWatcher 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) {
for (int i = 0; i < s.length(); i++) {
char c = s.charAt(i);
if (!acceptableChars.contains(Character.toLowerCase(c))) {
s.delete(i, i + 1);
Toast.makeText(getActivity(), String.format(getString(R.string.c_char_is_not_accepted), c), Toast.LENGTH_SHORT).show();
}
}
}
}
private class KeyboardUser implements FloatingKeyboard.User {
@NonNull
@Override

View File

@ -19,7 +19,7 @@ import java.util.Locale;
public class GreekFloatingKeyboard extends BaseFloatingKeyboard implements View.OnClickListener {
private final static String GREEK_ALPHABET = "αβγδεζηθικλμνξοπρστυφχψω";
final static String ALPHABET = "αβγδεζηθικλμνξοπρστυφχψω";
public GreekFloatingKeyboard(@NonNull User user) {
super(user);
@ -41,8 +41,8 @@ public class GreekFloatingKeyboard extends BaseFloatingKeyboard implements View.
} else {
makeLastColumnLand(rowView, row);
}
} else if (letter < GREEK_ALPHABET.length()) {
final Button button = addButton(rowView, View.NO_ID, String.valueOf(GREEK_ALPHABET.charAt(letter)));
} else if (letter < ALPHABET.length()) {
final Button button = addButton(rowView, View.NO_ID, String.valueOf(ALPHABET.charAt(letter)));
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
fixCapitalization(button);
}
@ -146,7 +146,7 @@ public class GreekFloatingKeyboard extends BaseFloatingKeyboard implements View.
@Override
public void process(@Nonnull Button key) {
final String letter = key.getText().toString();
if (!GREEK_ALPHABET.contains(letter.toLowerCase(Locale.US))) {
if (!ALPHABET.contains(letter.toLowerCase(Locale.US))) {
return;
}
if (upperCase) {

View File

@ -66,7 +66,7 @@ public class VariablesFragment extends BaseEntitiesFragment<IConstant> implement
final List<IConstant> constants = expression.getUndefinedVars();
return constants.isEmpty();
} catch (RuntimeException e) {
return true;
return false;
}
}

View File

@ -7,12 +7,10 @@ import android.text.InputType;
import android.util.AttributeSet;
import android.util.Log;
import android.widget.EditText;
import org.solovyev.android.Check;
import java.lang.reflect.Method;
import javax.annotation.Nullable;
import java.lang.reflect.Method;
public class EditTextCompat extends EditText {
@ -38,23 +36,27 @@ public class EditTextCompat extends EditText {
}
public void dontShowSoftInputOnFocusCompat() {
setShowSoftInputOnFocusCompat(false);
}
public void setShowSoftInputOnFocusCompat(boolean show) {
Check.isMainThread();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
setShowSoftInputOnFocus(false);
setShowSoftInputOnFocus(show);
} else {
dontShowSoftInputOnFocusPreLollipop();
dontShowSoftInputOnFocusPreLollipop(show);
}
}
private void dontShowSoftInputOnFocusPreLollipop() {
private void dontShowSoftInputOnFocusPreLollipop(boolean show) {
final Method method = getSetShowSoftInputOnFocusMethod();
if (method == null) {
disableSoftInputFromAppearing();
return;
}
try {
method.invoke(this, false);
method.invoke(this, show);
} catch (Exception e) {
Log.w("EditTextCompat", e.getMessage(), e);
}

View File

@ -73,7 +73,8 @@
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:hint="@string/c_function_description"
a:inputType="text" />
a:inputType="textMultiLine"
a:maxLines="4" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>

View File

@ -55,12 +55,12 @@
a:id="@+id/variable_keyboard_button"
a:layout_width="wrap_content"
a:layout_height="wrap_content"
a:layout_gravity="end|center_vertical"
a:layout_gravity="end|top"
a:background="?attr/selectableItemBackgroundBorderless"
a:minWidth="0dp"
a:padding="@dimen/cpp_image_button_padding"
a:text="@string/cpp_show_greek_keyboard"
a:textAllCaps="false"
a:padding="@dimen/cpp_image_button_padding"
a:minWidth="0dp"
a:textAppearance="?android:attr/textAppearanceSmall"
tools:ignore="UnusedAttribute" />
</FrameLayout>
@ -87,7 +87,8 @@
a:layout_width="match_parent"
a:layout_height="wrap_content"
a:hint="@string/c_var_description"
a:inputType="text" />
a:inputType="textMultiLine"
a:maxLines="4" />
</android.support.design.widget.TextInputLayout>
</LinearLayout>