EditFunctionFragment changes

This commit is contained in:
serso 2016-01-22 17:13:37 +01:00
parent 2a8c559a01
commit d0cd2b8420
10 changed files with 209 additions and 103 deletions

View File

@ -1,6 +1,7 @@
package org.solovyev.android.calculator;
import android.content.Context;
import android.content.DialogInterface;
import android.content.SharedPreferences;
import android.os.Bundle;
import android.support.annotation.NonNull;
@ -11,6 +12,8 @@ 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;
@ -41,7 +44,17 @@ public abstract class BaseDialogFragment extends DialogFragment {
final AlertDialog.Builder b = new AlertDialog.Builder(context, theme.alertDialogTheme);
b.setView(view, spacing, spacing, spacing, spacing);
onPrepareDialog(b);
return b.create();
final AlertDialog dialog = b.create();
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
@Override
public void onShow(DialogInterface d) {
onShowDialog(dialog);
}
});
return dialog;
}
protected void onShowDialog(@NonNull AlertDialog dialog) {
}
protected abstract void onPrepareDialog(@NonNull AlertDialog.Builder builder);

View File

@ -6,6 +6,7 @@ import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.PointF;
import android.os.Build;
import android.support.annotation.ColorInt;
import android.support.annotation.DrawableRes;
import android.support.annotation.IdRes;
import android.support.annotation.NonNull;
@ -42,9 +43,13 @@ public class KeyboardUi {
private final List<String> parameterNames;
@NonNull
private final SimpleDragListener dragListener;
@ColorInt
private final int textColor;
@ColorInt
private final int textColorSecondary;
private final int sidePadding;
@DrawableRes
private final int buttonBackground;
@SuppressWarnings("deprecation")
public KeyboardUi(@NonNull User user, @NonNull List<String> parameterNames) {
@ -55,6 +60,7 @@ public class KeyboardUi {
textColor = resources.getColor(R.color.cpp_button_text);
textColorSecondary = resources.getColor(R.color.cpp_button_text);
sidePadding = resources.getDimensionPixelSize(R.dimen.cpp_button_padding);
buttonBackground = App.getTheme().light ? R.drawable.material_button_light : R.drawable.material_button_dark;
}
public void makeView() {
@ -148,7 +154,7 @@ public class KeyboardUi {
private void fillButton(@NonNull View button, @IdRes int id) {
button.setOnClickListener(buttonHandler);
button.setId(id);
button.setBackgroundResource(R.drawable.material_button_light);
button.setBackgroundResource(buttonBackground);
button.setPadding(sidePadding, 1, sidePadding, 1);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
button.setStateListAnimator(null);

View File

@ -53,7 +53,7 @@ public class KeyboardWindow {
final Context context = editor.getContext();
final LinearLayout view = new LinearLayout(context);
view.setOrientation(LinearLayout.VERTICAL);
final int buttonSize = context.getResources().getDimensionPixelSize(R.dimen.cpp_kb_button_size);
final int buttonSize = context.getResources().getDimensionPixelSize(R.dimen.cpp_clickable_area_size);
final int keyboardSize = 5 * buttonSize;
window = new PopupWindow(view, keyboardSize, keyboardSize);
window.setClippingEnabled(false);
@ -98,6 +98,9 @@ public class KeyboardWindow {
return;
}
final Window window = dialog.getWindow();
if (window == null) {
return;
}
final WindowManager.LayoutParams lp = window.getAttributes();
lp.gravity = gravity;
window.setAttributes(lp);

View File

@ -47,9 +47,9 @@ public class CppFunction implements Jsonable, Parcelable {
private static final String JSON_BODY = "b";
private static final String JSON_PARAMETERS = "ps";
private static final String JSON_DESCRIPTION = "d";
protected final int id;
@Nonnull
protected final List<String> parameters = new ArrayList<>();
protected int id;
@Nonnull
protected String name;
@Nonnull
@ -230,6 +230,13 @@ public class CppFunction implements Jsonable, Parcelable {
return this;
}
@Nonnull
public Builder withId(int id) {
Check.isTrue(!built);
function.id = id;
return this;
}
@Nonnull
public CppFunction build() {
built = true;

View File

@ -43,10 +43,12 @@ import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewParent;
import android.view.inputmethod.InputMethodManager;
import android.widget.Button;
import android.widget.EditText;
import org.solovyev.android.Check;
import org.solovyev.android.calculator.App;
import org.solovyev.android.calculator.AppComponent;
import org.solovyev.android.calculator.BaseDialogFragment;
@ -62,8 +64,11 @@ import org.solovyev.android.calculator.math.edit.FunctionsFragment;
import org.solovyev.android.calculator.math.edit.VarEditorSaver;
import org.solovyev.common.math.MathRegistry;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@ -75,6 +80,8 @@ import jscl.math.function.CustomFunction;
import jscl.math.function.Function;
import jscl.math.function.IConstant;
import static org.solovyev.android.calculator.function.CppFunction.NO_ID;
public class EditFunctionFragment extends BaseDialogFragment implements View.OnClickListener, View.OnFocusChangeListener, View.OnKeyListener {
private static final String ARG_FUNCTION = "function";
@ -101,12 +108,12 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
EditText bodyView;
@Bind(R.id.function_description)
EditText descriptionView;
private CppFunction function;
@Inject
Calculator calculator;
@Inject
FunctionsRegistry registry;
@Nullable
private CppFunction function;
@Nonnull
private static EditFunctionFragment create(@Nullable CppFunction function) {
@ -141,7 +148,10 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
function = getArguments().getParcelable(ARG_FUNCTION);
final Bundle arguments = getArguments();
if (arguments != null) {
function = arguments.getParcelable(ARG_FUNCTION);
}
}
@Override
@ -165,9 +175,13 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
public AlertDialog onCreateDialog(Bundle savedInstanceState) {
final AlertDialog dialog = super.onCreateDialog(savedInstanceState);
dialog.setCanceledOnTouchOutside(false);
dialog.setOnShowListener(new DialogInterface.OnShowListener() {
return dialog;
}
@Override
public void onShow(DialogInterface d) {
protected void onShowDialog(@NonNull AlertDialog dialog) {
super.onShowDialog(dialog);
nameView.selectAll();
showIme(nameView);
@ -188,9 +202,6 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
});
}
}
});
return dialog;
}
private void tryRemoveFunction(@NonNull CppFunction function, boolean confirmed) {
if (!confirmed) {
@ -220,19 +231,58 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (v.getId() == R.id.function_body) {
if (v instanceof EditText && FunctionParamsView.PARAM_VIEW_TAG.equals(v.getTag())) {
final ViewParent parentView = v.getParent();
if (parentView instanceof TextInputLayout) {
if (hasFocus) {
keyboardWindow.show(keyboardUser, getDialog(), paramsView.getParams());
clearError((TextInputLayout) parentView);
} else {
validateParameters();
}
}
return;
}
final int id = v.getId();
switch (id) {
case R.id.function_name:
if (hasFocus) {
clearError(nameLabel);
} else {
validateName();
}
break;
case R.id.function_body:
if (hasFocus) {
clearError(bodyLabel);
showKeyboard();
} else {
keyboardWindow.hide();
validateBody();
}
break;
}
}
private void showKeyboard() {
keyboardWindow.show(keyboardUser, getDialog(), collectParameters());
}
@Nonnull
private List<String> collectParameters() {
final List<String> parameters = new ArrayList<>();
for (String parameter : paramsView.getParams()) {
if (!TextUtils.isEmpty(parameter)) {
parameters.add(parameter);
}
}
return parameters;
}
@Override
public void onClick(View v) {
if (v.getId() == R.id.function_body) {
keyboardWindow.show(keyboardUser, getDialog(), paramsView.getParams());
showKeyboard();
}
}
@ -255,38 +305,16 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
}
private void applyData() {
final CppFunction newFunction = CppFunction.builder(nameView.getText().toString(), bodyView.getText().toString())
.withId(function == null ? NO_ID : function.id)
.withParameters(collectParameters())
.withDescription(descriptionView.getText().toString()).build();
final Function oldFunction = (function == null || function.id == NO_ID) ? null : registry.getById(function.id);
registry.add(newFunction.toCustomFunctionBuilder(), oldFunction);
}
private boolean validate() {
if (!validateName()) {
return false;
}
if (!validateParameters()) {
return false;
}
if (!validateBody()) {
return false;
}
final CppFunction newFunction = CppFunction.builder(nameView.getText().toString(), bodyView.getText().toString())
.withParameters(paramsView.getParams())
.withDescription(descriptionView.getText().toString()).build();
final Function oldFunction = function.id == CppFunction.NO_ID ? null : registry.getById(function.id);
registry.add(newFunction.toCustomFunctionBuilder(), oldFunction);
return true;
}
private boolean validateParameters(@Nonnull List<String> parameterNames) {
for (String parameterName : parameterNames) {
if (!VarEditorSaver.isValidName(parameterName)) {
return false;
}
}
// error = R.string.function_param_not_empty;
return true;
return validateName() & validateParameters() & validateBody();
}
private boolean validateName() {
@ -296,10 +324,17 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
return false;
}
final Function existingFunction = registry.get(name);
if (existingFunction != null && (!existingFunction.isIdDefined() || !existingFunction.getId().equals(function.getId()))) {
if (existingFunction != null) {
if (!existingFunction.isIdDefined()) {
Check.shouldNotHappen();
setError(nameLabel, getString(R.string.function_already_exists));
return false;
}
if (function != null && !existingFunction.getId().equals(function.getId())) {
setError(nameLabel, getString(R.string.function_already_exists));
return false;
}
}
clearError(nameLabel);
return true;
}
@ -315,13 +350,26 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
}
private boolean validateParameters() {
boolean valid = true;
final List<String> parameters = paramsView.getParams();
/*if (TextUtils.isEmpty(body)) {
setError(bodyLabel, getString(R.string.function_is_empty));
return false;
}*/
//clearError(bodyLabel);
return true;
final Set<String> usedParameters = new HashSet<>();
for (int i = 0; i < parameters.size(); i++) {
final String parameter = parameters.get(i);
final TextInputLayout paramLabel = paramsView.getParamLabel(i);
if (TextUtils.isEmpty(parameter)) {
clearError(paramLabel);
} else if (!VarEditorSaver.isValidName(parameter)) {
valid = false;
setError(paramLabel, getString(R.string.invalid_name));
} else if (usedParameters.contains(parameter)) {
valid = false;
setError(paramLabel, getString(R.string.function_duplicate_parameter));
} else {
usedParameters.add(parameter);
clearError(paramLabel);
}
}
return valid;
}
@SuppressLint("InflateParams")
@ -331,15 +379,18 @@ public class EditFunctionFragment extends BaseDialogFragment implements View.OnC
final View view = inflater.inflate(R.layout.fragment_function_edit, null);
ButterKnife.bind(this, view);
if (savedInstanceState == null) {
if (savedInstanceState == null && function != null) {
paramsView.addParams(function.getParameters());
nameView.setText(function.getName());
descriptionView.setText(function.getDescription());
bodyView.setText(function.getBody());
}
nameView.setOnFocusChangeListener(this);
paramsView.setOnFocusChangeListener(this);
bodyView.setOnClickListener(this);
bodyView.setOnFocusChangeListener(this);
bodyView.setOnKeyListener(this);
descriptionView.setOnFocusChangeListener(this);
return view;
}

View File

@ -27,9 +27,9 @@ import android.content.Context;
import android.os.Build;
import android.os.Parcel;
import android.os.Parcelable;
import android.text.Editable;
import android.text.TextUtils;
import android.support.design.widget.TextInputLayout;
import android.util.AttributeSet;
import android.view.Gravity;
import android.view.View;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
@ -53,9 +53,13 @@ import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
public class FunctionParamsView extends LinearLayout {
@Nonnull
public static final String PARAM_VIEW_TAG = "param-view";
private static final int HEADERS = 1;
private static final int PARAM_VIEW_INDEX = 3;
private static final int START_ROW_ID = App.generateViewId();
private int maxRowId = START_ROW_ID;
private final int clickableAreaSize = getResources().getDimensionPixelSize(R.dimen.cpp_clickable_area_size);
public FunctionParamsView(Context context) {
super(context);
@ -86,8 +90,8 @@ public class FunctionParamsView extends LinearLayout {
addParam(null);
}
});
headerView.addView(addButton, new LayoutParams(0, WRAP_CONTENT, 1));
headerView.addView(new View(context), new LayoutParams(0, WRAP_CONTENT, 5));
headerView.addView(addButton, new LayoutParams(clickableAreaSize, WRAP_CONTENT));
headerView.addView(new View(context), new LayoutParams(0, WRAP_CONTENT, 1));
addView(headerView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
@ -95,10 +99,11 @@ public class FunctionParamsView extends LinearLayout {
private LinearLayout makeRowView(@Nonnull Context context) {
final LinearLayout rowView = new LinearLayout(context);
rowView.setOrientation(HORIZONTAL);
rowView.setMinimumHeight(clickableAreaSize);
rowView.setGravity(Gravity.CENTER_VERTICAL);
return rowView;
}
public void addParams(@Nonnull List<String> params) {
for (String param : params) {
addParam(param);
@ -121,7 +126,7 @@ public class FunctionParamsView extends LinearLayout {
}
});
removeButton.setText("");
rowView.addView(removeButton, new LayoutParams(0, WRAP_CONTENT, 1));
rowView.addView(removeButton, new LayoutParams(clickableAreaSize, WRAP_CONTENT));
final Button upButton = new Button(context);
upButton.setOnClickListener(new OnClickListener() {
@ -131,7 +136,7 @@ public class FunctionParamsView extends LinearLayout {
}
});
upButton.setText("");
rowView.addView(upButton, new LayoutParams(0, WRAP_CONTENT, 1));
rowView.addView(upButton, new LayoutParams(clickableAreaSize, WRAP_CONTENT));
final Button downButton = new Button(context);
downButton.setOnClickListener(new OnClickListener() {
@ -141,16 +146,21 @@ public class FunctionParamsView extends LinearLayout {
}
});
downButton.setText("");
rowView.addView(downButton, new LayoutParams(0, WRAP_CONTENT, 1));
rowView.addView(downButton, new LayoutParams(clickableAreaSize, WRAP_CONTENT));
final TextInputLayout paramLabel = new TextInputLayout(context);
final EditText paramView = new EditText(context);
if (param != null) {
paramView.setText(param);
}
paramView.setOnFocusChangeListener(getOnFocusChangeListener());
paramView.setInputType(EditorInfo.TYPE_CLASS_TEXT);
paramView.setId(id);
paramView.setTag(PARAM_VIEW_TAG);
paramView.setHint(R.string.c_function_parameter);
rowView.addView(paramView, new LayoutParams(0, WRAP_CONTENT, 3));
paramLabel.addView(paramView, new TextInputLayout.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
rowView.addView(paramLabel, new LayoutParams(0, WRAP_CONTENT, 1));
addView(rowView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT));
}
@ -158,20 +168,20 @@ public class FunctionParamsView extends LinearLayout {
private void downRow(@Nonnull ViewGroup row) {
final int index = indexOfChild(row);
if (index < getChildCount() - 1) {
swap(row, getRowByIndex(index + 1));
swap(row, getRow(index + 1));
}
}
private void upRow(@Nonnull ViewGroup row) {
final int index = indexOfChild(row);
if (index > 1) {
swap(row, getRowByIndex(index - 1));
swap(row, getRow(index - 1));
}
}
private void swap(@Nonnull ViewGroup l, @Nonnull ViewGroup r) {
final EditText lParam = (EditText) l.getChildAt(PARAM_VIEW_INDEX);
final EditText rParam = (EditText) r.getChildAt(PARAM_VIEW_INDEX);
final EditText lParam = getParamView(l);
final EditText rParam = getParamView(r);
swap(lParam, rParam);
}
@ -183,7 +193,7 @@ public class FunctionParamsView extends LinearLayout {
}
@Nonnull
private ViewGroup getRowByIndex(int index) {
private ViewGroup getRow(int index) {
Check.isTrue(index >= 0 && index < getChildCount());
return (ViewGroup) getChildAt(index);
}
@ -196,18 +206,26 @@ public class FunctionParamsView extends LinearLayout {
public List<String> getParams() {
final List<String> params = new ArrayList<>(getChildCount());
for (int i = 1; i < getChildCount(); i++) {
final ViewGroup row = getRowByIndex(i);
final EditText paramView = (EditText) row.getChildAt(PARAM_VIEW_INDEX);
final Editable param = paramView.getText();
if (!TextUtils.isEmpty(param)) {
params.add(param.toString());
}
for (int i = HEADERS; i < getChildCount(); i++) {
final ViewGroup row = getRow(i);
final EditText paramView = getParamView(row);
params.add(paramView.getText().toString());
}
return params;
}
@Nonnull
private EditText getParamView(@Nonnull ViewGroup row) {
final TextInputLayout paramLabel = getParamLabel(row);
return (EditText) paramLabel.getChildAt(0);
}
@Nonnull
private TextInputLayout getParamLabel(@Nonnull ViewGroup row) {
return (TextInputLayout) row.getChildAt(PARAM_VIEW_INDEX);
}
@Override
protected Parcelable onSaveInstanceState() {
final Parcelable superState = super.onSaveInstanceState();
@ -218,9 +236,9 @@ public class FunctionParamsView extends LinearLayout {
private int[] getRowIds() {
final int childCount = getChildCount();
final int[] rowIds = new int[childCount - 1];
for (int i = 1; i < childCount; i++) {
final ViewGroup row = getRowByIndex(i);
final EditText paramView = (EditText) row.getChildAt(PARAM_VIEW_INDEX);
for (int i = HEADERS; i < childCount; i++) {
final ViewGroup row = getRow(i);
final EditText paramView = getParamView(row);
rowIds[i - 1] = paramView.getId();
}
return rowIds;
@ -243,6 +261,11 @@ public class FunctionParamsView extends LinearLayout {
super.onRestoreInstanceState(state.getSuperState());
}
@Nonnull
public TextInputLayout getParamLabel(int param) {
return getParamLabel(getRow(param + HEADERS));
}
public static final class SavedState extends BaseSavedState {
public static final Parcelable.Creator<SavedState> CREATOR =

View File

@ -23,7 +23,7 @@
-->
<ScrollView xmlns:a="http://schemas.android.com/apk/res/android"
a:layout_width="match_parent"
a:layout_width="wrap_content"
a:layout_height="wrap_content">
<LinearLayout
@ -47,7 +47,8 @@
<org.solovyev.android.calculator.function.FunctionParamsView
a:id="@+id/function_params"
a:layout_width="match_parent"
a:layout_height="wrap_content" />
a:layout_height="wrap_content"
a:orientation="vertical" />
<android.support.design.widget.TextInputLayout
a:id="@+id/function_body_label"

View File

@ -29,5 +29,5 @@
<dimen name="cpp_display_text_size">20sp</dimen>
<dimen name="cpp_display_text_size_mobile">20sp</dimen>
<dimen name="cpp_kb_button_size">40dp</dimen>
<dimen name="cpp_clickable_area_size">40dp</dimen>
</resources>

View File

@ -39,5 +39,6 @@
<dimen name="cpp_onscreen_main_padding">1dp</dimen>
<dimen name="cpp_dialog_spacing">20dp</dimen>
<dimen name="cpp_kb_button_size">50dp</dimen>
<dimen name="cpp_clickable_area_size">50dp</dimen>
<dimen name="cpp_dialog_width_max">400dp</dimen>
</resources>

View File

@ -155,7 +155,8 @@
<string name="function_name_is_not_valid">Name of function is not valid: name must start with a letter, can contain letters, digits and underscore.</string>
<string name="function_already_exists">Function with the same name already exists!</string>
<string name="function_is_empty">Function body could not be empty!</string>
<string name="function_param_not_empty">Function parameter should not be empty!</string>
<string name="invalid_name">Invalid name</string>
<string name="function_duplicate_parameter">There is already a parameter with the same name</string>
<string name="function_removal_confirmation_question">Do you really want to delete \'%s\' function?</string>
<string name="empty_function_error">Unable to create empty function!</string>
<string name="do_not_show_messages_in_session">Do not show this message until next session</string>