From 712f94484f4da1fcb076f373e5f12ce01bb6f665 Mon Sep 17 00:00:00 2001 From: serso Date: Tue, 19 Jan 2016 12:59:57 +0100 Subject: [PATCH] KeyboardUi in EditFunctionDialog --- .../android/calculator/KeyboardUi.java | 408 ++++++++++++++++++ .../android/calculator/KeyboardWindow.java | 105 +++++ .../function/EditFunctionFragment.java | 231 +++++++++- .../function/FunctionParamsView.java | 9 +- .../history/EditHistoryFragment.java | 8 +- app/src/main/res/drawable-hdpi/app_icon.png | Bin 0 -> 5805 bytes .../drawable-hdpi/ic_backspace_white_24dp.png | Bin 0 -> 525 bytes .../res/drawable-hdpi/ic_done_white_24dp.png | Bin 0 -> 264 bytes .../drawable-hdpi/ic_keyboard_white_24dp.png | Bin 0 -> 343 bytes .../drawable-hdpi/ic_space_bar_white_24dp.png | Bin 0 -> 188 bytes app/src/main/res/drawable-mdpi/app_icon.png | Bin 0 -> 3037 bytes .../drawable-mdpi/ic_backspace_white_24dp.png | Bin 0 -> 371 bytes .../res/drawable-mdpi/ic_done_white_24dp.png | Bin 0 -> 207 bytes .../drawable-mdpi/ic_keyboard_white_24dp.png | Bin 0 -> 222 bytes .../drawable-mdpi/ic_space_bar_white_24dp.png | Bin 0 -> 147 bytes app/src/main/res/drawable-xhdpi/app_icon.png | Bin 0 -> 7510 bytes .../ic_backspace_white_24dp.png | Bin 0 -> 640 bytes .../res/drawable-xhdpi/ic_done_white_24dp.png | Bin 0 -> 323 bytes .../drawable-xhdpi/ic_keyboard_white_24dp.png | Bin 0 -> 316 bytes .../ic_space_bar_white_24dp.png | Bin 0 -> 176 bytes app/src/main/res/drawable-xxhdpi/app_icon.png | Bin 0 -> 14111 bytes .../ic_backspace_white_24dp.png | Bin 0 -> 838 bytes .../drawable-xxhdpi/ic_done_white_24dp.png | Bin 0 -> 455 bytes .../ic_keyboard_white_24dp.png | Bin 0 -> 389 bytes .../ic_space_bar_white_24dp.png | Bin 0 -> 186 bytes .../ic_backspace_white_24dp.png | Bin 0 -> 1132 bytes .../drawable-xxxhdpi/ic_done_white_24dp.png | Bin 0 -> 565 bytes .../ic_keyboard_white_24dp.png | Bin 0 -> 471 bytes .../ic_space_bar_white_24dp.png | Bin 0 -> 198 bytes app/src/main/res/values-small/dimens.xml | 1 + app/src/main/res/values/dimens.xml | 1 + app/src/main/res/values/ids.xml | 12 + app/src/main/res/values/text_strings.xml | 1 + 33 files changed, 768 insertions(+), 8 deletions(-) create mode 100644 app/src/main/java/org/solovyev/android/calculator/KeyboardUi.java create mode 100644 app/src/main/java/org/solovyev/android/calculator/KeyboardWindow.java create mode 100644 app/src/main/res/drawable-hdpi/app_icon.png create mode 100644 app/src/main/res/drawable-hdpi/ic_backspace_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_done_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png create mode 100644 app/src/main/res/drawable-hdpi/ic_space_bar_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/app_icon.png create mode 100644 app/src/main/res/drawable-mdpi/ic_backspace_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_done_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png create mode 100644 app/src/main/res/drawable-mdpi/ic_space_bar_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/app_icon.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_backspace_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png create mode 100644 app/src/main/res/drawable-xhdpi/ic_space_bar_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/app_icon.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_backspace_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_done_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png create mode 100644 app/src/main/res/drawable-xxhdpi/ic_space_bar_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_backspace_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_done_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_keyboard_white_24dp.png create mode 100644 app/src/main/res/drawable-xxxhdpi/ic_space_bar_white_24dp.png diff --git a/app/src/main/java/org/solovyev/android/calculator/KeyboardUi.java b/app/src/main/java/org/solovyev/android/calculator/KeyboardUi.java new file mode 100644 index 00000000..3c387b00 --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/KeyboardUi.java @@ -0,0 +1,408 @@ +package org.solovyev.android.calculator; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.Resources; +import android.graphics.Color; +import android.graphics.PointF; +import android.os.Build; +import android.support.annotation.DrawableRes; +import android.support.annotation.IdRes; +import android.support.annotation.NonNull; +import android.text.TextUtils; +import android.util.TypedValue; +import android.view.GestureDetector; +import android.view.HapticFeedbackConstants; +import android.view.MotionEvent; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Button; +import android.widget.EditText; +import android.widget.ImageButton; +import android.widget.ImageView; +import android.widget.LinearLayout; + +import org.solovyev.android.views.dragbutton.DirectionDragButton; +import org.solovyev.android.views.dragbutton.DragButton; +import org.solovyev.android.views.dragbutton.DragDirection; +import org.solovyev.android.views.dragbutton.SimpleDragListener; + +import java.util.List; + +import static org.solovyev.android.views.dragbutton.DirectionDragButton.Direction.down; +import static org.solovyev.android.views.dragbutton.DirectionDragButton.Direction.up; + + +public class KeyboardUi { + @NonNull + private final ButtonHandler buttonHandler = new ButtonHandler(); + @NonNull + private final User user; + @NonNull + private final List parameterNames; + @NonNull + private final SimpleDragListener dragListener; + private final int textColor; + private final int textColorSecondary; + private final int sidePadding; + + @SuppressWarnings("deprecation") + public KeyboardUi(@NonNull User user, @NonNull List parameterNames) { + this.user = user; + this.parameterNames = parameterNames; + this.dragListener = new SimpleDragListener(buttonHandler, user.getContext()); + final Resources resources = user.getResources(); + textColor = resources.getColor(R.color.cpp_button_text); + textColorSecondary = resources.getColor(R.color.cpp_button_text); + sidePadding = resources.getDimensionPixelSize(R.dimen.cpp_button_padding); + } + + public void makeView() { + LinearLayout row = makeRow(); + addButton(row, 0, "7"); + addButton(row, 0, "8"); + addButton(row, 0, "9").setText("π", up).setText("e", down); + addOperationButton(row, R.id.cpp_kb_button_multiply, Locator.getInstance().getEngine().getMultiplicationSign()).setText("^n", up).setText("^2", down); + addButton(row, R.id.cpp_kb_button_clear, "C"); + + row = makeRow(); + addButton(row, 0, "4"); + addButton(row, 0, "5"); + addButton(row, 0, "6"); + addOperationButton(row, R.id.cpp_kb_button_divide, "/").setText("%", up).setText("sqrt", down); + final View backspace = addImageButton(row, R.id.cpp_kb_button_backspace, R.drawable.ic_backspace_white_24dp); + LongClickEraser.createAndAttach(backspace, user.getEditor()); + + row = makeRow(); + addButton(row, 0, "1"); + addButton(row, 0, "2"); + addButton(row, 0, "3"); + addOperationButton(row, R.id.cpp_kb_button_plus, "+"); + addImageButton(row, R.id.cpp_kb_button_space, R.drawable.ic_space_bar_white_24dp); + + row = makeRow(); + addButton(row, R.id.cpp_kb_button_brackets, "( )").setText("(", up).setText(")", down); + addButton(row, 0, "0").setText("00", up).setText("000", down); + addButton(row, 0, ".").setText(",", up); + addOperationButton(row, R.id.cpp_kb_button_minus, "−"); + addImageButton(row, R.id.cpp_kb_button_keyboard, R.drawable.ic_keyboard_white_24dp); + + row = makeRow(); + final int parametersCount = parameterNames.size(); + addButton(row, 0, parametersCount > 0 ? parameterNames.get(0) : "x"); + addButton(row, 0, parametersCount > 1 ? parameterNames.get(1) : "y"); + addButton(row, R.id.cpp_kb_button_functions, "f(x)"); + addButton(row, R.id.cpp_kb_button_constants, "π"); + addImageButton(row, R.id.cpp_kb_button_close, R.drawable.ic_done_white_24dp); + } + + @NonNull + private View addImageButton(@NonNull LinearLayout row, @IdRes int id, @DrawableRes int icon) { + final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT); + lp.weight = 1f; + final View view = makeImageButton(id, icon); + row.addView(view, lp); + return view; + } + + @NonNull + private DirectionDragButton addOperationButton(@NonNull LinearLayout row, @IdRes int id, @NonNull String text) { + final DirectionDragButton button = addButton(row, id, text); + button.setBackgroundResource(R.drawable.material_button_light_primary); + button.setTextColor(Color.WHITE); + button.setDirectionTextColor(Color.WHITE); + return button; + } + + @NonNull + private DirectionDragButton addButton(@NonNull LinearLayout row, @IdRes int id, @NonNull String text) { + final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(0, ViewGroup.LayoutParams.MATCH_PARENT); + lp.weight = 1f; + final DirectionDragButton view = makeButton(id, text); + row.addView(view, lp); + return view; + } + + @NonNull + private LinearLayout makeRow() { + final LinearLayout row = new LinearLayout(user.getContext()); + row.setOrientation(LinearLayout.HORIZONTAL); + final LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, 0); + lp.weight = 1f; + user.getKeyboard().addView(row, lp); + return row; + } + + @NonNull + private DirectionDragButton makeButton(@IdRes int id, @NonNull String text) { + final DirectionDragButton button = new DirectionDragButton(user.getContext()); + fillButton(button, id); + button.setText(text); + button.setTextColor(textColor); + button.setDirectionTextColor(textColorSecondary); + button.setTextSize(TypedValue.COMPLEX_UNIT_DIP, 24); + button.setOnDragListener(dragListener); + return button; + } + + private void fillButton(@NonNull View button, @IdRes int id) { + button.setOnClickListener(buttonHandler); + button.setId(id); + button.setBackgroundResource(R.drawable.material_button_light); + button.setPadding(sidePadding, 1, sidePadding, 1); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + button.setStateListAnimator(null); + } + } + + @NonNull + private View makeImageButton(@IdRes int id, @DrawableRes int icon) { + final ImageButton button = new ImageButton(user.getContext()); + fillButton(button, id); + button.setImageResource(icon); + button.setScaleType(ImageView.ScaleType.CENTER_INSIDE); + return button; + } + + public interface User { + @NonNull + Context getContext(); + + @NonNull + Resources getResources(); + + @NonNull + EditText getEditor(); + + @NonNull + ViewGroup getKeyboard(); + + void insertOperator(char operator); + + void insertOperator(@NonNull String operator); + + void showFunctions(@NonNull View v); + + void showConstants(@NonNull View v); + + void insertText(@NonNull CharSequence text, int offset); + + void done(); + + void showIme(); + } + + private class ButtonHandler implements View.OnClickListener, SimpleDragListener.DragProcessor { + @Override + public void onClick(@NonNull View v) { + switch (v.getId()) { + case R.id.cpp_kb_button_divide: + user.insertOperator('/'); + break; + case R.id.cpp_kb_button_plus: + user.insertOperator('+'); + break; + case R.id.cpp_kb_button_minus: + user.insertOperator('-'); + break; + case R.id.cpp_kb_button_multiply: + user.insertOperator('*'); + break; + case R.id.cpp_kb_button_functions: + user.showFunctions(v); + break; + case R.id.cpp_kb_button_constants: + user.showConstants(v); + break; + case R.id.cpp_kb_button_space: + user.insertText(" ", 0); + break; + case R.id.cpp_kb_button_keyboard: + user.showIme(); + break; + case R.id.cpp_kb_button_clear: + user.getEditor().setText(""); + user.getEditor().setSelection(0); + break; + case R.id.cpp_kb_button_brackets: + user.insertText("()", -1); + break; + case R.id.cpp_kb_button_close: + user.done(); + break; + default: + onDefaultClick(v); + break; + } + user.getEditor().requestFocus(); + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + + private void onDefaultClick(@NonNull View v) { + user.insertText(((Button) v).getText(), 0); + } + + @Override + public boolean processDragEvent(@NonNull DragDirection direction, @NonNull DragButton button, @NonNull PointF startPoint, @NonNull MotionEvent e) { + switch (button.getId()) { + default: + return onDefaultDrag(button, direction); + } + } + + private boolean onDefaultDrag(@NonNull DragButton button, @NonNull DragDirection direction) { + final String text = ((DirectionDragButton) button).getText(direction); + if (TextUtils.isEmpty(text)) { + return false; + } + switch (text) { + case "sqrt": + user.insertText("sqrt()", -1); + break; + case ",": + user.insertText(", ", 0); + break; + case "^n": + user.insertOperator('^'); + break; + case "^2": + user.insertOperator("^ 2"); + break; + case "?": + case ">": + case "<": + case ">=": + case "<=": + case ":": + user.insertOperator(text); + break; + default: + user.insertText(text, 0); + break; + } + button.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + return true; + } + } + + public static final class LongClickEraser implements View.OnTouchListener, View.OnClickListener { + + @NonNull + private final View view; + + @NonNull + private final EditText editText; + + @NonNull + private final GestureDetector gestureDetector; + + @NonNull + private final Eraser eraser = new Eraser(); + + private LongClickEraser(@NonNull final View view, @NonNull EditText editText) { + this.view = view; + this.editText = editText; + this.gestureDetector = new GestureDetector(view.getContext(), new GestureDetector.SimpleOnGestureListener() { + public void onLongPress(MotionEvent e) { + if (eraser.isTracking()) { + eraser.start(); + } + } + }); + } + + public static void createAndAttach(@NonNull View view, @NonNull EditText editText) { + final LongClickEraser l = new LongClickEraser(view, editText); + view.setOnClickListener(l); + view.setOnTouchListener(l); + } + + private static void erase(@NonNull EditText editText) { + final int start = clampSelection(editText.getSelectionStart()); + final int end = clampSelection(editText.getSelectionEnd()); + if (start != end) { + editText.getText().delete(Math.min(start, end), Math.max(start, end)); + } else if (start > 0) { + editText.getText().delete(start - 1, start); + } + } + + public static int clampSelection(int selection) { + return selection < 0 ? 0 : selection; + } + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View v, MotionEvent event) { + switch (event.getAction()) { + case MotionEvent.ACTION_CANCEL: + case MotionEvent.ACTION_UP: + eraser.stopTracking(); + break; + default: + eraser.startTracking(); + gestureDetector.onTouchEvent(event); + break; + } + return false; + } + + @Override + public void onClick(View v) { + erase(editText); + v.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + } + + private class Eraser implements Runnable { + private static final int DELAY = 300; + private long delay; + private boolean erasing; + private boolean tracking = true; + + @Override + public void run() { + erase(editText); + if (editText.length() == 0 || clampSelection(editText.getSelectionStart()) == 0) { + stop(); + return; + } + delay = Math.max(50, 2 * delay / 3); + view.postDelayed(this, delay); + } + + void start() { + if (erasing) { + stop(); + } + erasing = true; + delay = DELAY; + view.removeCallbacks(this); + view.performHapticFeedback(HapticFeedbackConstants.KEYBOARD_TAP); + run(); + } + + void stop() { + view.removeCallbacks(this); + if (!erasing) { + return; + } + + erasing = false; + } + + public void stopTracking() { + stop(); + tracking = false; + } + + public boolean isTracking() { + return tracking; + } + + public void startTracking() { + tracking = true; + } + } + } + +} \ No newline at end of file diff --git a/app/src/main/java/org/solovyev/android/calculator/KeyboardWindow.java b/app/src/main/java/org/solovyev/android/calculator/KeyboardWindow.java new file mode 100644 index 00000000..a17d5632 --- /dev/null +++ b/app/src/main/java/org/solovyev/android/calculator/KeyboardWindow.java @@ -0,0 +1,105 @@ +package org.solovyev.android.calculator; + +import android.app.Dialog; +import android.content.Context; +import android.os.IBinder; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.view.Gravity; +import android.view.View; +import android.view.Window; +import android.view.WindowManager; +import android.view.inputmethod.InputMethodManager; +import android.widget.EditText; +import android.widget.LinearLayout; +import android.widget.PopupWindow; + +import java.util.List; + +public class KeyboardWindow { + + @Nullable + private PopupWindow window; + @Nullable + private Dialog dialog; + + private static void hideIme(@NonNull View view) { + final IBinder token = view.getWindowToken(); + if (token != null) { + InputMethodManager imm = (InputMethodManager) view.getContext().getSystemService( + Context.INPUT_METHOD_SERVICE); + imm.hideSoftInputFromWindow(token, 0); + } + } + + public void hide() { + if (!isShown()) { + return; + } + moveDialog(Gravity.CENTER); + window.dismiss(); + window = null; + dialog = null; + } + + public void show(@NonNull KeyboardUi.User user, @Nullable Dialog dialog, @NonNull List parameterNames) { + if (isShown()) { + return; + } + this.dialog = dialog; + moveDialog(Gravity.TOP); + final EditText editor = user.getEditor(); + hideIme(editor); + 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 keyboardSize = 5 * buttonSize; + window = new PopupWindow(view, keyboardSize, keyboardSize); + window.setClippingEnabled(false); + window.setOnDismissListener(new PopupWindow.OnDismissListener() { + @Override + public void onDismiss() { + window = null; + } + }); + // see http://stackoverflow.com/a/4713487/720489 + editor.post(new Runnable() { + @Override + public void run() { + if (window == null) { + return; + } + if (editor.getWindowToken() != null) { + hideIme(editor); + final int inputWidth = editor.getWidth(); + final int xOff = (inputWidth - keyboardSize) / 2; + window.setWidth(keyboardSize); + window.showAsDropDown(editor, xOff, 0); + } else { + editor.postDelayed(this, 50); + } + } + }); + new KeyboardUi(user, parameterNames).makeView(); + } + + public boolean isShown() { + return window != null; + } + + @SuppressWarnings("unchecked") + public V getContentView() { + return (V) window.getContentView(); + } + + public void moveDialog(int gravity) { + if (dialog == null) { + return; + } + final Window window = dialog.getWindow(); + final WindowManager.LayoutParams lp = window.getAttributes(); + lp.gravity = gravity; + window.setAttributes(lp); + } +} diff --git a/app/src/main/java/org/solovyev/android/calculator/function/EditFunctionFragment.java b/app/src/main/java/org/solovyev/android/calculator/function/EditFunctionFragment.java index 9d088bd0..ff4679f3 100644 --- a/app/src/main/java/org/solovyev/android/calculator/function/EditFunctionFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/function/EditFunctionFragment.java @@ -22,9 +22,11 @@ package org.solovyev.android.calculator.function; +import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; +import android.content.res.Resources; import android.os.Bundle; import android.os.Parcel; import android.os.Parcelable; @@ -33,8 +35,15 @@ import android.support.design.widget.TextInputLayout; import android.support.v4.app.FragmentManager; import android.support.v7.app.AlertDialog; import android.support.v7.app.AppCompatActivity; +import android.text.Editable; +import android.view.ContextMenu; +import android.view.KeyEvent; import android.view.LayoutInflater; +import android.view.Menu; +import android.view.MenuItem; import android.view.View; +import android.view.ViewGroup; +import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.EditText; @@ -45,6 +54,8 @@ import org.solovyev.android.calculator.CalculatorEventListener; import org.solovyev.android.calculator.CalculatorEventType; import org.solovyev.android.calculator.CalculatorUtils; import org.solovyev.android.calculator.DisplayState; +import org.solovyev.android.calculator.KeyboardUi; +import org.solovyev.android.calculator.KeyboardWindow; import org.solovyev.android.calculator.Locator; import org.solovyev.android.calculator.R; import org.solovyev.android.calculator.math.edit.CalculatorFunctionsActivity; @@ -52,8 +63,10 @@ import org.solovyev.android.calculator.math.edit.FunctionsFragment; import org.solovyev.android.calculator.math.edit.MathEntityRemover; import org.solovyev.android.calculator.math.edit.VarEditorSaver; import org.solovyev.android.calculator.model.AFunction; +import org.solovyev.common.math.MathRegistry; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Set; @@ -66,11 +79,23 @@ import jscl.math.Generic; import jscl.math.function.Constant; import jscl.math.function.CustomFunction; import jscl.math.function.Function; +import jscl.math.function.IConstant; import jscl.math.function.IFunction; -public class EditFunctionFragment extends BaseDialogFragment implements CalculatorEventListener { +public class EditFunctionFragment extends BaseDialogFragment implements CalculatorEventListener, View.OnClickListener, View.OnFocusChangeListener, View.OnKeyListener { private static final String ARG_INPUT = "input"; + private static final int MENU_FUNCTION = Menu.FIRST; + private static final int MENU_CONSTANT = Menu.FIRST + 1; + + @NonNull + private final MathRegistry functionsRegistry = Locator.getInstance().getEngine().getFunctionsRegistry(); + @NonNull + private final MathRegistry constantsRegistry = Locator.getInstance().getEngine().getVarsRegistry(); + @NonNull + private final KeyboardWindow keyboardWindow = new KeyboardWindow(); + @NonNull + private final KeyboardUser keyboardUser = new KeyboardUser(); @Bind(R.id.function_params) FunctionParamsView paramsView; @Bind(R.id.function_name_label) @@ -120,7 +145,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat builder.setPositiveButton(R.string.ok, null); final AFunction function = input.getFunction(); builder.setTitle(function == null ? R.string.function_create_function : R.string.function_edit_function); - if(function != null) { + if (function != null) { builder.setNeutralButton(R.string.c_remove, null); } } @@ -154,6 +179,34 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat return dialog; } + @Override + public void onFocusChange(View v, boolean hasFocus) { + if (v.getId() == R.id.function_body) { + if (hasFocus) { + keyboardWindow.show(keyboardUser, getDialog(), paramsView.getParams()); + } else { + keyboardWindow.hide(); + } + } + } + @Override + public void onClick(View v) { + if (v.getId() == R.id.function_body) { + keyboardWindow.show(keyboardUser, getDialog(), paramsView.getParams()); + } + } + + @Override + public boolean onKey(View v, int keyCode, KeyEvent event) { + if (v.getId() == R.id.function_body) { + if (event.getAction() == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK && keyboardWindow.isShown()) { + keyboardWindow.hide(); + return true; + } + } + return false; + } + private void tryClose() { if (validate()) { applyData(); @@ -169,6 +222,9 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat if (!validateName()) { return false; } + if (!validateBody()) { + return false; + } return true; } @@ -182,6 +238,11 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat return true; } + private boolean validateBody() { + return true; + } + + @SuppressLint("InflateParams") @NonNull @Override protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) { @@ -198,6 +259,9 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat descriptionView.setText(input.getDescription()); bodyView.setText(input.getContent()); } + bodyView.setOnClickListener(this); + bodyView.setOnFocusChangeListener(this); + bodyView.setOnKeyListener(this); return view; } @@ -275,7 +339,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat result.content = in.readString(); result.description = in.readString(); - final List parameterNames = new ArrayList(); + final List parameterNames = new ArrayList<>(); in.readTypedList(parameterNames, STRING_CREATOR); result.parameterNames = parameterNames; @@ -310,7 +374,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat result.name = name; result.content = value; result.description = description; - result.parameterNames = new ArrayList(parameterNames); + result.parameterNames = new ArrayList<>(parameterNames); return result; } @@ -323,7 +387,7 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat final Generic generic = viewState.getResult(); if (generic != null) { final Set constants = CalculatorUtils.getNotSystemConstants(generic); - final List parameterNames = new ArrayList(constants.size()); + final List parameterNames = new ArrayList<>(constants.size()); for (Constant constant : constants) { parameterNames.add(constant.getName()); } @@ -372,4 +436,161 @@ public class EditFunctionFragment extends BaseDialogFragment implements Calculat out.writeSerializable(function); } } + + private class KeyboardUser implements KeyboardUi.User, MenuItem.OnMenuItemClickListener { + @NonNull + @Override + public Context getContext() { + return getActivity(); + } + + @NonNull + @Override + public Resources getResources() { + return EditFunctionFragment.this.getResources(); + } + + @NonNull + @Override + public EditText getEditor() { + return bodyView; + } + + @NonNull + @Override + public ViewGroup getKeyboard() { + return keyboardWindow.getContentView(); + } + + @Override + public void insertOperator(char operator) { + insertOperator(String.valueOf(operator)); + } + + public int clampSelection(int selection) { + return selection < 0 ? 0 : selection; + } + + @Override + public void insertOperator(@NonNull String operator) { + final int start = clampSelection(bodyView.getSelectionStart()); + final int end = clampSelection(bodyView.getSelectionEnd()); + final Editable e = bodyView.getText(); + e.replace(start, end, getOperator(start, end, e, operator)); + } + + @NonNull + private String getOperator(int start, int end, @NonNull Editable e, @NonNull CharSequence operator) { + boolean spaceBefore = true; + boolean spaceAfter = true; + if (start > 0 && Character.isSpaceChar(e.charAt(start - 1))) { + spaceBefore = false; + } + if (end < e.length() && Character.isSpaceChar(e.charAt(end))) { + spaceAfter = false; + } + + if (spaceBefore && spaceAfter) { + return " " + operator + " "; + } + if (spaceBefore) { + return " " + operator; + } + if (spaceAfter) { + return operator + " "; + } + return String.valueOf(operator); + } + + @Override + public void showConstants(@NonNull View v) { + bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + final int id = v.getId(); + if (id == R.id.function_body) { + menu.clear(); + for (String constant : getNamesSorted(constantsRegistry)) { + menu.add(MENU_CONSTANT, Menu.NONE, Menu.NONE, constant).setOnMenuItemClickListener(KeyboardUser.this); + } + unregisterForContextMenu(bodyView); + } + } + }); + bodyView.showContextMenu(); + } + + @Nonnull + private List getNamesSorted(@NonNull MathRegistry registry) { + final List names = registry.getNames(); + Collections.sort(names); + return names; + } + + @Override + public void showFunctions(@NonNull View v) { + bodyView.setOnCreateContextMenuListener(new View.OnCreateContextMenuListener() { + @Override + public void onCreateContextMenu(ContextMenu menu, View v, ContextMenu.ContextMenuInfo menuInfo) { + final int id = v.getId(); + if (id == R.id.function_body) { + menu.clear(); + for (String function : getNamesSorted(functionsRegistry)) { + menu.add(MENU_FUNCTION, Menu.NONE, Menu.NONE, function).setOnMenuItemClickListener(KeyboardUser.this); + } + unregisterForContextMenu(bodyView); + } + } + }); + bodyView.showContextMenu(); + } + + @Override + public void insertText(@NonNull CharSequence text, int selectionOffset) { + final int start = clampSelection(bodyView.getSelectionStart()); + final int end = clampSelection(bodyView.getSelectionEnd()); + final Editable e = bodyView.getText(); + e.replace(start, end, text); + if (selectionOffset != 0) { + final int selection = clampSelection(bodyView.getSelectionEnd()); + final int newSelection = selection + selectionOffset; + if (newSelection >= 0 && newSelection < e.length()) { + bodyView.setSelection(newSelection); + } + } + } + + @Override + public void done() { + keyboardWindow.hide(); + validateBody(); + } + + @Override + public void showIme() { + final InputMethodManager keyboard = (InputMethodManager) + getContext().getSystemService(Context.INPUT_METHOD_SERVICE); + keyboard.showSoftInput(getEditor(), InputMethodManager.SHOW_FORCED); + keyboardWindow.hide(); + } + + @Override + public boolean onMenuItemClick(MenuItem item) { + final int groupId = item.getGroupId(); + final CharSequence title = item.getTitle(); + if (groupId == MENU_FUNCTION) { + final int argsListIndex = title.toString().indexOf("("); + if (argsListIndex < 0) { + keyboardUser.insertText(title + "()", -1); + } else { + keyboardUser.insertText(title.subSequence(0, argsListIndex) + "()", -1); + } + } else if (groupId == MENU_CONSTANT) { + keyboardUser.insertText(title.toString(), 0); + } else { + return false; + } + return true; + } + } } diff --git a/app/src/main/java/org/solovyev/android/calculator/function/FunctionParamsView.java b/app/src/main/java/org/solovyev/android/calculator/function/FunctionParamsView.java index 16392d4b..f7aee1bb 100644 --- a/app/src/main/java/org/solovyev/android/calculator/function/FunctionParamsView.java +++ b/app/src/main/java/org/solovyev/android/calculator/function/FunctionParamsView.java @@ -27,6 +27,8 @@ 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.util.AttributeSet; import android.view.View; import android.view.ViewGroup; @@ -38,6 +40,7 @@ import android.widget.TextView; import org.solovyev.android.Check; import org.solovyev.android.calculator.App; +import org.solovyev.android.calculator.R; import java.util.ArrayList; import java.util.List; @@ -146,6 +149,7 @@ public class FunctionParamsView extends LinearLayout { } paramView.setInputType(EditorInfo.TYPE_CLASS_TEXT); paramView.setId(id); + paramView.setHint(R.string.c_function_parameter); rowView.addView(paramView, new LayoutParams(0, WRAP_CONTENT, 3)); addView(rowView, new ViewGroup.LayoutParams(MATCH_PARENT, WRAP_CONTENT)); @@ -195,7 +199,10 @@ public class FunctionParamsView extends LinearLayout { for (int i = 1; i < getChildCount(); i++) { final ViewGroup row = getRowByIndex(i); final EditText paramView = (EditText) row.getChildAt(PARAM_VIEW_INDEX); - params.add(paramView.getText().toString()); + final Editable param = paramView.getText(); + if (!TextUtils.isEmpty(param)) { + params.add(param.toString()); + } } return params; diff --git a/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java b/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java index 49bc7050..fc46de4f 100644 --- a/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java +++ b/app/src/main/java/org/solovyev/android/calculator/history/EditHistoryFragment.java @@ -1,5 +1,6 @@ package org.solovyev.android.calculator.history; +import android.annotation.SuppressLint; import android.content.Context; import android.content.DialogInterface; import android.os.Bundle; @@ -11,14 +12,16 @@ import android.view.LayoutInflater; import android.view.View; import android.widget.EditText; import android.widget.TextView; -import butterknife.Bind; -import butterknife.ButterKnife; + import org.solovyev.android.calculator.AppComponent; import org.solovyev.android.calculator.BaseDialogFragment; import org.solovyev.android.calculator.R; import javax.inject.Inject; +import butterknife.Bind; +import butterknife.ButterKnife; + public class EditHistoryFragment extends BaseDialogFragment { public static final String ARG_STATE = "state"; @@ -81,6 +84,7 @@ public class EditHistoryFragment extends BaseDialogFragment { }); } + @SuppressLint("InflateParams") @NonNull @Override protected View onCreateDialogView(@NonNull Context context, @NonNull LayoutInflater inflater, @Nullable Bundle savedInstanceState) { diff --git a/app/src/main/res/drawable-hdpi/app_icon.png b/app/src/main/res/drawable-hdpi/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..39c8550bca7edb67a7ceb0ad27f417a769c74651 GIT binary patch literal 5805 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 IbxsLQ0M$fYtN;K2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-hdpi/ic_keyboard_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..d2b2ed4ddb683979f6a2024340d5c2636ee076bb GIT binary patch literal 343 zcmV-d0jU0oP)hfu{OrgiUnZgN0b#SdE0DW z*pHdVWPwh9ER}8akOlpQr!TDJm;>o4Yixr;BIdFKuu>y5!hlVfY%1FzO8OULQ*0kof0SnnjO<&280 zIobytNZwoT{3Pt3we~dQFMO}z9Eqwv@aoCdTZ<`F@c2gWbjyPdOg_t^q|k0wldT1B8JTQ1%T^vI^jwdHbur5vz;n6(Mac1YG z13<9gvO|RQGOv*4f5rv|1}_YR5(V14)UqmbC8UouX}f83|NFlrbD8Iwg;i?{P1^gK d0u7QF82+5#er=e@QwcPf!PC{xWt~$(69DPnI4A%B literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/app_icon.png b/app/src/main/res/drawable-mdpi/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..257ab38952b980d804db8f66bc823e96ca379d1d GIT binary patch literal 3037 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 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_backspace_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_backspace_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..99871fb22437cb09dad0e6f726518b8a8a2aff59 GIT binary patch literal 371 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zVGw3ym^BBap1s7=*OmP~had-!NlEq81O^617Ec$)5R21iCmZ@5F%WQ#FJkrJJNQQK zQ05Ck9YI@Ok2k4d7lIj2i<(t;e0{>z@kC&uf<@T|4~+>k4oOA-?@iyA^P_ws&q~{; z6aToEo-V!gb?37yGQM#amgv>aP;B4B{$HhS`DaU+o2zS2>wab57bm=lGk&0<9bWWVc0> z+>>H3pQr3xtGrdORMD(}$7AVJGre^8rQ91OW-U;?`ALdlskG_hAiTN(fTD4}6Xes!*ckk5YM!sq|z#+NsF6r7pcX8r&DpSBFeOK$^o w52{p%u4-K}A#=y)`7BQZba@xgzopr01FXB9{>OV literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_keyboard_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..b863d02e35fe8bf34b50b79c1b95c2830d555689 GIT binary patch literal 222 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zVGw3ym^BBap1s7=*OmP~had-!)QYt;)&qs|JzX3_EKWb|Kgiplz~d@^MD&Vk#4*Ji zCr$6Psx~OTI=G-hK|x*k-@?qv*Z)PXGV_ literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-mdpi/ic_space_bar_white_24dp.png b/app/src/main/res/drawable-mdpi/ic_space_bar_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..efe2f491f8702617e66a75118dec028499725199 GIT binary patch literal 147 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM0wlfaz7_*1rX+877l!}s{b%+Ad7K3vkw8&y zVGw3ym^BBap1s7=*OmP~haeZX^yK_k6M#Z0o-U3d7N?UFBv_rLV}8~@wKpvCSt8|A l&8xE_Z)qd%;)D(+2KkGeIt+gft_P}T@O1TaS?83{1OP??CBgsz literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/app_icon.png b/app/src/main/res/drawable-xhdpi/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1658a3f8e56ecd3add5f5537921d4e7b1999dea2 GIT binary patch literal 7510 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 aPVfhq$js3ZhY{xh0000LkJd literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_done_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..e27c938b9911f890967de0f4fbf0414bbe33c9c5 GIT binary patch literal 323 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+`=Ht$S`Y;1W=H@#M9T6{XRE04}+240;zRCp(~y)jv*0;-%hjSJ7gf>x<4}`LT{q= zhHHZ1iO}9HwE3Vz={^RxGfA{tMUCT9PGmdrIPhOb4b&+PAr96M(A>p-x?&9)t zn#amE^=EO+nIBu?p4Kibb9(oJ)Zjhqo0e!ia4Qyx=)n)FM;A%Qg=%it?A*&M zvT57h)vsbjx@0oyx4a4tY-N(o_v_tk;Pbv!ob`6GO5)LIR`ocgiC+}>OKTM>qYLv4 Qf&OFgboFyt=akR{0Aw$CRsaA1 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_keyboard_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..8041bc62fc70822e74c18dc7b7eb8d8ae9e6d164 GIT binary patch literal 316 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+`=Ht$S`Y;1W=H@#M9T6{XT~v2e;LoCcOnfp|hSYjv*0;-%hjTI%FVn{J-YiKQ^s; zmJZKkR^4-Wk$WU^*Y0$UI47>Rw{}S|A6v9i-|5NUW$ta?Pygm%Nsv&hZQ(vErfMP< zpY|qT;e@T~2GVmjs_iOcP8GfRxWIp!T*SgH3xzW-UCWN&pdxyvwy|Tg>(ABBmD6V` z+SI9a|K+r_+EQWAJqK Kb6Mw<&;$U(N_#s1 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xhdpi/ic_space_bar_white_24dp.png b/app/src/main/res/drawable-xhdpi/ic_space_bar_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..0c555e9c529aa6edbc0d88b5522a11503b1c4878 GIT binary patch literal 176 zcmeAS@N?(olHy`uVBq!ia0vp^1|ZDA0wn)(8}a}trX+877l!}s{b%+Ad7K3vk;M!Q z+`=Ht$S`Y;1W=H@#M9T6{XT~v7dO|>`=^qCLXMs;jv*0;-(GU$Vh|8u4R~Vlu3qSS zV&Za@lWy;Gmt8rNucpXwfdA2-fZHon=kZ2;U3y)bL!dza{lBfkw)_kY_9gq>Sl^xx SOPB*RjKR~@&t;ucLK6TY&o!q2 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/app_icon.png b/app/src/main/res/drawable-xxhdpi/app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..de6a60552cc154467bdb2d1db068d48c122ad86f GIT binary patch literal 14111 zcmV+)H{i&LP)@!(NLJ~-T5CXD?#g*l$h%D|XF3%O={Uk(1K~zu>LH74VL4Amz zAhNiiBB0NREGn`igluFZ$xLQndaJtU_s6|eb*s8(GBadg2B-YAWO}-*YO20<&i8!J za)C*>owC$gsqJkT7^%Yl5`18N`nhRwr?aETV?4PsjJbyBsyS>TD3ZSFkeO!HR{xG&^@zh6O-1L z&8=UQpPRYirH20Vu(ll~<&?~&vuFJ1;I||pP*b@ay$9a*iQ}r>w24Cc4{3O3YLB-zom|r_ zK0-o)2c0#=ZSQq0!;ayMtexiZ?H4Px!J)Qg6fgR#`RRwgFE`O#*>|qFWOm(L_ao^Bv6K{$?=JoFiQC(*ySL-I!O0xPPhJvj zHs`)gkBe=C-^sCC@})`-X3Hln_{z~I2NFd!5bf-nHI%R!Sd@>S{JHEd2WH=yZl+U| z@KNp;Pxh|CkG^@)`5Pu{_&%utWP{=>H(pk#*!;q~rbc%2ixKa%|=06|B7a>1cBGn~(gtY1qd*XXocZQ6Qq$i)yEe0X zu0iCSCMG`z^6NbIB!eMI)TG^XEip?FymGnJNdkVxP5XY)>ru?Tvd`)1TbJ+s(U*SP z_X7S>u6B->e)n5*<{sH_LCq|DoLIr@aqn7tTVeU<&s+B5YXLwGUL#xK?0-B}cl{Cn zQATKdd6h{ z__gxjzGf^C0DR%B<=eh>!{s-o-MS@(26e1T<6v)r^KE9KWKJC9OCLTd5PS%wK_Cdw zP*EU@6b9hYzt^{%K;~zTeue@G1~S~>WO1YeI4!V6b*m zpwJ$ZWtcUP*jOL^vJE&{)SB4rH_bVx)jK>h18-2Qz-8|9y0PsIA6eL#B;J#~l!GQK zaFWr_#aCW3SMJ+%ykAQvaSa{rJ6_Z3+FQc%&BZ%}%|CXsFJNyCqOMhGG*2jPL(*580{>5EGJqS|(dIhb{=imCN zni;1Z_=fUy@nNnJ@AOit9_&L1p-T+{t017kVn85Ba~(iw@C1M`+{XqBfk1-IcOn1@ z()$gpmN0xt*Z?98Vts4@F3_ezr~iwd^?ciZp74`Lfk(Ze9*QXdjWc+@ec7dR#p{|r zRBXVhepW43q^ujSEdrUFMhD-}#lWo%ETTpZy zkUn+*AYu(l9d%nCpt|J8)5YKOmcEQwW~OWk%5) ziRc&sf08Lxo66C_O*1ny-*DI)*WP{S{aXQD6lWKpO+{flY5VM3Ka^g0<#8wFcS~Ph z$og;e$cKQ4LT+O@H0#i=hGBCT+vWzqh+I?z1z2v!SQqm%h|PZ^@Pw`^03jv!q0EJj zM{&%6a8)|j;-9kch`LK|`1KWU4DQK9t7)2)0J5#msjojNHRn@@pOl~DUR=uf`)U!8 zDnn>vC0vq7vxLhcv=(Gs3QY7xfd*s|X9`81$c<3|$l!tzz^o$cV_TZQN|Au@9dL;} zNsb0H#RLF-J`9jllxg(-WBvh|i*LO9$~V}d{zL~6O$P9scJit2?jJhh#QwSNCFPXb zouEQD075KumPAMuoz>J`{*~{Q)+%NPC{*nIJ9JeDjg%{ z1b_^9h#)~m24M4jf+&gZ{C2*hTr57Jd{xfoQcG%@op+{cy?I)&BnT45Evk@<2?&&W zFY~-*IiRic?&|0qfJE^dIvYaDagNL z_28FFcX!-;?wMbD@R6JD`NzRaj(D_oTAfQNUd%$JBfb)a+Z5blCIw&733@1Fj_<^Olr|K9u9y!4@eZ=F-S)y>c%H_NR7Ne{po5^iG1 zH0I{&(z9NF_?w>m&F%NM0l;C0A2wn6kesO2=Wh=`g0s*1Jk8y2_eJvXU9Qik%8S9u zx{1d61lBq9CIXnbSlotNuYB|0zJ1eumjyy0KlHa1gL2b?)q5S;lFQUNZ-Mk9 z9Vw6iLOlU+GrUKt#&r(b@5m?qaQB~jCo-v?&>U#{hAoZm8|GcupDFFHIR{xBg+t2# z0wf7YmRqyy_>rVy4FKnV{k%0O@IT1sLKJ>e$ZtgWvb{Qo#K!(~=>I7q-GTvt^2L|j zKR;T!xuVMO=l5RIR~k@%_X=1i4EF&?8c~>EWq=%ohMD4HyB*N*f%DJ5sD2{zAu$oH z&(&94g;S1yx4ZX8j{b0Ou6%-0kw6%@t>E4U;km(7q4$}#&Y?)C!(u=HWm{Z?MWTb9 z6D)C%0>T{;alwH|8SKNDbB6EtQO{7v_k9fbzUn0#kVLV4001k3aQ#&4TxZ#W!!m~f z2Z)KPQcpMlU46w>IQ4@cqJ589x}<*=ebI+6smf^*>Bx&hTftE98*7f7zDG6kS%-AW zcgk}iQAc5f6-8wn#zsc?tB5c-_A^=UQ6vVXQ!~qZC;y)CVmqZ)$K_e}zKE*FG7LcG zO0#D!s5#?DcYJt$HLE%Wpeoh}0ML=o^6Og9EI8gAg!!tt%ObMXWCEBWQdrB97KbT%;(BCk9F68s>`+UUb2C7AOC< z3%_;IyxOMJyWAQzO$TvGTQ^wlD~TX5a;L7P2`u$Fo^dBDnu>^X5So)fB4rV|&&Z-FB1g4O-y^->v2QxD`-MBHeb?;%>976Z zlS>yJTYGMHhI*HjUZ%0*Evqw!i?$0Azy0 z@IFXo*f}rFhZsUcu8&A_A;r|iMcxL$b6m7IX*GicTtNbYqrYRv#fbzcfsIdgF2sEh(J53RAtiXlF)MJX67m3IPg(1Fxbfjuc<#?ZTj$h|wFh>aC z3J{1*DQw^>cepV~dT8Fo{lJ35+*uQi3r#2v^xl1rO3~t$50_BN+VnUU;>IWf>sOGP zu+j-^LN&5d2Ou_IwB&%~zBz7|G5H za3K;6i7US=EOjW#hXs};vXXHKnJOiw5tr5z0W3>so|QfBZD-7GnsQ-7xUZ%|=Iq~3 z`58DW0(h#}BqsA{8dMHU7++n#?|DFT22< zC~oXk2T<@7%|7<9X)pE4BcFW25k38p+d#c=9QU_U!~F6-!{oxQ9t6#0dyn6@}WpFe4@)g&}_sdD-U6c9)cJO~Ov5MiEpJ zu&Cm`0A(|_u^S-}vW@B>qzVmF07`7#>6-lqOJxZVln*@noQriYrcBU86+X%)vvHSV z@rw{tl!%tBUlS^#A_^0zLpUHB+O5V@6!!V>(9;=4x+$QhK7AlFT7dm%BJiLI0igQ< zTHW0A`%S|$u$B9b)%s8Oa^XCSW#Y(coiJej{tsIHb?;wu3zVQUfsgxjU z5m=p|?3JP%S;WU+>W0X&TpSDG!?Z?lZp@K6BaYxRoQS9>k-D|5nsAz$8|iHyT}=OT z{V!kjdp98f)b-W#NZj(%FC0)?^K}Ya^#Vu5D3%H9dn6RI9dDnYKpP6vidbrX3BmIM?0JL)f^m6aVt9tT3oDOl@_pZs@am&p$H=ciX>PJnL zB)!`AK78f1iFjpnuPG1>v+Z-LnDxo8IOkq_eFi6-E;0YTVrK%0#(L6$-+kinS@io; z&S}`B#GKy2{H&Jd<^}0=y1u8UXKT4!-dyiG>sz+1-*n!^m*%T|9{_He2CP{Aon3m_ znFaWA2oa>}mP!HhXDQUzGCV&bwx+c!mI$@Ob$gq2 z(E`B|v5fwXRG=N-3I^b~f_t{87yH`%?O$4U9uIL%>06gxTDPAwuq&5~vjotTYp8GT z>g;MyrBYo5;jVw6RJ-}AFMpvh)IAwD0NDq+_L6U;H`GpF(&c1M?k4BtenpE1J%zGT z$ViF$l#3d_v?5jL|7mk+;J#hfKeO@k-~Dk_@^|W0-~YlEN}mO=J5m5KfOML%Xg448 zW+}Li29B^JvoOiZG0i8E1rUvR2@A|rsA`KC2!qIj!!X@Wz)E7PLD58SJQDDX{C1E; zX3M|4(>Fh#zwNBgoKs3(%L6`nru+5%rtLFLc*o6h)G4*_mbj`6W(5`!o=lZHeX(MT zM?YKJukQQAIqNqAKN<7TD`%|W`PylpPOq+=dF-}S?rWQi{vmy(qHZ%bAx^2R&{Hgd zfj6~e>fYp~Yi<>L9(wsz=S+WM*~OQ7LBV;jzoO!4{lvR4k523F&l4V9L&#GZd(QKb zPU%REh)lGlZ#IWndybW_>WLb5tBx?BQejcOux24$4DUhpM%)x0G~twrjhO+|H3(Nk6~DQ@}&fWfjtKg5z$CNs~? z)jWAdQ@;K7r`NGvmbuSck3YJXoWpg8SE5yT2d}aZw0BCB$^y;xj7%zWHNtW`VH2U% ziY!S~Ed>ch$fQtda&J*lJ|eL&_POyX_ap!vn@Yd#-dcKU#r+TZ!9%#uJKrtNI%MIS z4$hW;SU1?QH^a126UFs<(#~8ekLt+Zzd4)Q?R9T?+vC6f!~N}J=|2;ru|C1}Wb1;x z+EO*kI~467wA>8scC=f_dmc8H`K?=I_M=bj_qLXuE2k5DpYXyu7mqxhMrV&q@Ex|e zO~N7&Cp-)fdE>(5Y$BB*ENi1DanSe{3tRgREckm4S~7e8oPS{^zkLo1u>;=4U=k3j zjK*U3F-O#U%TB)N%EqxM+am^0u;bg$|8jOKoMSsYb!^G^N1R=jAoLc?XjAgTU75Q5 zf4A&oa%W#thQj)n2!DOd#pX5vR$6%u^AT1bJrV2ibXM}p9(!lEFb9bQyALe(cL%-s z_V33FK?Epf_AT}EEvJ6MdE<=CNzLW1!yttz{iPkewEXU{CmTw=$Ntw`_o%TwyrbM! z&t0@@*>R5PE_qt!ABBsTMCkBTD`jhsUcL8`bvymq5!fq?`d$_jO!Tifc?g!ir?ldfBkDgGzeGj40- zFBS}kyg9S-?Qa1ViP47jqXy86#q!Jn#-g!aqmr-Cr*Poq%uh7`WcqPXCsZ+xzGv~FzF02(N(+M+@ujnVuH00X|7-difx3{5tM zV4+o?qQK=?F9Em?qf{b1`GSMLKkJ~YM?i?Erz@=I0jyDX$iUn3~bdfJg9JE&% zb6Pz(GM?xZmg$Hlkx`Td4Xs=yvk8msX?!9t76jq>Xk68e>CbdR`V0tWJDWeJsdgq6 zb(MbQ8S)M_CV~Qya%xe`j5~mc6o^zxNJ;L$2f3*zGtM@cW)a=Ks0j~d zp%VRE>vL09S47g8ieM2i|j^0W{wS4*qN5cDf(!7p&QMs zEbs)+WHR^-o<}j9juorZjkAnO9x#3YVq@1yT2`i-%d$*p@>vj~l5tY7PfM5KWZm;{ z$D(SwP@G=@fRI4R2cF%S!eE)ObYT%Q8~j*yQ0MIX&8 zJJGnN8`5X6Ly^@T3I_x*DFcKg3E;SBTJ_>3*8v$f4ixc~YM_l&kr3LIE~Sd8)mDul zLLWwn$6h5UGFNCoNfst7B5$9CW32U>ZD|xr0!tSbu=_OANXU_LEv&?=eYlwFyfX>2 zBsfJ6EiZJUaa}irQWkj3-@;Wihalf4jVtBIGTT>Tcww6X{}pjzWzXHm}mh~YHd%7xnA?&a`PU8v0RoX5P<*_#RxLC20&|fTJ$(#~g zVPQWQhJeIOAdU}+qMcxUlq9$X57S=gM8o~=;$XP|!4;f=0gw!GRszZ43e`AHEMB7_l11TPyAyx3vva%EP8+tVwNX==hz5rZh> zz9MZQm}9L6O7O6@lEZAh7=Hi-MKTr2Y*i9#&k&^0jSEpN3e!F#7>E#$u_$sQG=#fM zJf>tFGJ3l@2!#XWJ>bcgvgj|nc;mtWG-iFUsSApyEO@88EjRCB+Nw^}Z|Z?y4q8Vl zbsP@msMv*IGXN`yLPx6q#7~5++NE(0;~}a{Eu~5!FZMns)Z-+F5dg{f{EOJY1h)Q7 zLrK)txe#`ADM`Cf4-%`k3k;SCZ{DL1v+Gew z=p+mkB&x`}urTLVaZV#VM>V#;W%Mr0WOEUWss+VPddP5^th|yvUd+;gNG3s9#byi_ zu3Mgl6V0qNf=LI#_cr#X@cZW*@P^&;*n4`>@Hu5Bl92`Cu>*(&!5J*0bxj8vU+Mud zhi+>)J%+3qj+S()b6`9+J&MfrSM&-1NEjDHOo*)JP&bFFBpA#{II7&S9=0vw2Mfw| zNLA4kDY}5w)F`U%f~z~*=-e1UN8Z67*VLiAC~(Bw0qwRKBhZ{i1|B!>p>_3kG;HnB zf3c)+alGLxr>W{gM5ZwA<#DlB!~$t8MRIh!P}ri8)WJ}=6jJ=;eUK1wkt}A}s_iOT z(sEdn6bp9~qgd=HF`{^YAPjhfM>glsTNF5U!2qQ65sd<-F@i_Ct!b;<(YUo&AJuYO zimN>k_C02>VyJ{`g&-qzB&dO^LM5shbv86%+&Pf2i%zS1mRP#V4w?h;P3r&^Iu1z@ z&#^^S&02h{eoKbUy(KM-Jusot4FwiZnbu}T*(dyKdlo%K7sv0}gT{1u%<7L4gmk`y z)-~;@Z|l?14^bi%9p2Bf;W;!^RyR4_TE?WJn3ahKa$~Ea977mybesg|h%NwG950?j z3ZWz_2TTT4aukk3iTG7Dsem~d0mN75*s1@Y1Hakv9!h~y8(8zkK0^&}?l5u-xy>XEYFeJJP5_hd`#8JCS z>^FT7zTy!T0kI%Bg%YN%Ye)U|ey|{OuZFmO!$o)DVA1#~Gx=aKyu_e}_;;}UInyhW z%HU84q(oLcsj*kPw$Uiu%7sgWq+~J*!&TW3(U4CoB@#x-)e5VVevY}f3Z6rZ0pG#B zn;Nja=-|z}_C~V9P*y__+ zRrRJ2Quz{QtZhSGSAV$RZbd46G}oW26yBMlMHvVQbAhW5VzTpUqXA0wudp?_tbz<+SS2!$YT#f^pTupeTX5X`PE1P|;jxTy z(p0gCnd{n6+ug4<9;)t=6pyUsnJk_e=PRtqvf`0Eb5)NpMguf95*?(fs{x3r)Eb00 z*N`~kN)kk-!cUv{DEgwRDj`?+NZP^q+D{@?)jX&|QLlFWBmTr-5Vn@mxP4tKj+xbs zg$;#}7p00N%v!Srxt>9=pjadr2l6Otso6R{Rf&SBN?La``+h~DFrm4M&b{#HT?c7x zq8f=NNw6Kc?do`lWa&snJu%u5bEI96NKD6PsvLmc?>qSYmR1~5?!mz={h(qIGuCZG zu6HoZ8&>0pW0H)SEAnySd5Pimso~riQ`L&t@evXOEl7qpmMT-4G>i)zYB+PKF(n3E z5<;1ttU|NcF^Pt;+fi-O@F3^Pv;oJB5><&V8HADo9%`$HYR}_6h0REpieO8<9+gko zkwZ&(FcB0l`NQvj*n2WY0zUeRM3b|U!V0pD=@b>QOr;8{^#)PZ&Bbes;+qsvn@v*x z8a67gxN$qOUe(lZLiHeyj3d(m>C8>rvGn%)F<9$CzHJ7ahO{Zt;++DXxL`Q4AY=6C z9A^N911W{YT@v%(k$7TbDG9J%A}I+VNstb}6%ky*?IdoxsL-COxC!$ePE_DB?YN6{ zM^RyLDQ0bK!v}Bq9cl^%6#5AMui8+5^fb70(tuB6DwUWF+n8KfkPi#esg!tXc}Q{2 zfJ#<#VZl;Nv;&hnf*Cqg{cB`%DJz!;8cu7%u)iapF>n1#c;C_Wy%mI=c}L=^Maq(tQk$N~_O534+KYy5TA) z-X^XR#zzrF+%1W!z~N*RSWykYi5p+ZM`*+%WCHL?VYk)m@&23s0HMlII!|8V?^Q`5 z|7bTR_5DDy< z?}@7GEhl)|P*F1~tN(aY1hdJ20!w+1KPd=I40x)G8`_SBWu=h1!7R@5HZjLiHTNDx@@PQgyp9LN(J zK!So6oC+2b3wxOp8F`8XE8T{h*ssvlfQoDbGJO3hRdjCf5Km>L{u3`j;n=i}YB~fH zL3$n*J-r&o-*TTW^AIC%=wayAn^H#!y?^Y0I@CvIaXrW-kea+cZEPf3h;T*Yq4-Cv zufojb<6uBjHBryhTE~G!!yD(M!pCtDkjV7DCpeQ0G!CqloheD^5ljTP>|^m`D{kF52phqjyLo$6M;(lLW%QCtQW@B+@gS6_ORdIpT*I?_)EAag)vIEMsa1I zG4PKrs6HRHhc>}!a(0^5II#f))*T9x5;WLaOLQo5$9kk1Nf1?w!X)XUrbNlykolb| zYkCbO9YwKM#EGhZG_qza2&tlvB@g`zN8Ry&hRx1ugEyRI@L#>~2Rzg+Z9-~RMyGN{ z>C))cV$fp&kdy=|B?+0F!k)MRLvknr10{|Z#f76?s0wYNMEb!_3NpnnVW9ML()v_X zt`1Sw8hl0&(ghE%Tk#ZLf7io0X+b;RLXZQLUK~K*fREhLMr3xc1-YXGkBLm=q(|zmHNDjC&LtqNu%(8ad{T51Lls?FKuG4@ z2s>^xjEPb_bo&~y22ZxHg#Ca2G!D4uaj-J-0J9M^$7zWdm*D3;^gXm4s;`XP0gaH2 zu4!!_{&@F3ylUh7_Guh+RIzrn=ccO>9r&K?_~bP!H;>++jB=aaUVJHC@}F{q2%7qR@5g=|mMu!dwJ`7%)V zU5QLj0sH^8K46^3{ANF}Y^&Q_xG(360dfU|)GrGEOAwBTKx4fi1?me} zSLo>Z)1#-I)*rtL!5pQmyXMr z-vE`LLbq4HzGp>kSMld*MY>#*sLFWU`2RqJtdDZGt^a%VFLposgQTtHS1*=Ou0&18 zAP)TH(^&MU7XT0NNk9?l1Zh|S=V%MWWWY6kYZg;VqPS@g{VTR3|8x(ytgHc>@$Kn9 z-s1o8&)e_lDmLAvOC3gLiHHauW%6C+hP&SJson?1a-O4F>L38o7eDmr4dR8i@7DEr zzso2wY(E;C($I`Y#k%&w^@T^*{Os)iI;(fbq~(>_{Io=ETR#rC^I7cimo*>{P$Z$) zh@XM=F=D7PE=>PkjN>^o$Pw^*%IJHj6McX0g6j9dR9#&C>31*Q{+lP{S38TDKd?;g zU~Z8%R3)Sb+w!>^fA@UDb?EPX$U5c zUV`8gQa;}q{ zlv(>MzCId@6#7-o+AaOHm;c}DmP^h)>%Z0q`x)(GE2CcSsk^TyTn^~}ZvEv;C+~Ck zKdCOWUip4cS`lZIkgiYVOc@>SK>6uZU+MR)TZ-4!{P^LU&b#3JZT25+pX}kEde1TC zrtA@Th1Rn7(-Msvd$I4GE3w<(HtAuOLZHfAFw=G=!a2}ke&3qMGp@GNM6~6$0{osb zdNb1>?)02L-+A}FrQp5<%QXG=o)vAmh3|QIPP5v~EP6e5%N`{Al=X_7?i}!(r+UjZ zcW)WYU3bYt3vRybtDo66R`87Z?>=++GF)}}l{Sf`tG|BX4DZc*?VYbr&-MjUDWCh@ z&E0F|j~`rh)?L^4B)^|L+2wz~_2Plun!bQnU@3AhEz!845BuHmJa&C-3#9LZgjtOz zv^fd{U+X@^5ER)Wg0KIr7(^b3d~1#P@y$hWsf_IhFTG-YUH$nV`QkZ!!+rLQbFP^F zo`Y#ot?1mvk#0>HPXCqxTKm(-TG!n6olkWScR$7zK(^T&?SY3n*;ficep;e&T`!j0 zwF+|{-v;UXfKVVdK(W9hLJ)EIJ4woDLOui-f;|v?)8?eVJFXz|J+vKg$QA2r>(Bq_ z+2{6+fwTgJ(@Imi%H3WuG=) zK156yRwh#)#b<2NAZp0T`mdGkwF@8ue3#JtuU_nZ>qa!M?gz7kFZAd-m?bZn{m`0_ z&R>{HLp_I37zLW(8}%7rRvJ=)LT$o(#bgo7pbmj|$|UuDz!jJ8yU8|lS~-@t)NLr4P~6mG6KP$C^?)eF z3W6BS5=7>G1C+orDGms=LbUXpc6$`MI~6uR>RE#XW&y#{04D(7YtIr*AOP{pTlR(k zNJ8^7gV^)NcGPb13|TgW7Q!q*Vnzv@e}@i?gYaBI7;wN4VGu(p2|*t0auPO^M#5p7 z#?rAO5cyCLnrpES3W@j26dKLagA=8LLrfI-G$A{70nXHg)jyNR9yfF$w?*kms0hj~ z0+BEh0om9T71|eJNU-^LFhv5|nkWKP$aowunff4W5m?2fi_RzzAcZ-uCh5vlaE{#! z5lBjibhhMVb36qgYir(?f5I8dl{MjW{WB%(^@C2-Y%802MNyVK;3T@1C%Ul=3Nz{f z*t83aN;yoSwgEx_J`-DoVAQ=%%zFPE7NJ@D4PGqAG>=&me1>Rj!lvH72mdlyEKC8& z+BV*?;xA2uSWBY>4;W~8rig{tbtByloxx%YFbGOAG;nlw%dYhi@Hw&)J|aN*5Mdn< zg7-*&o>_odr!iCrqG*3G$5f^aL|Y{atyIN|J0l`^3Ik2ej}@8!b=~^y-V}fkL ztDwh-3DJCBQ-w$(bZ1(DP8 zsBgmN8MD5+GM&BUoVA;J@%TSu*YDrjg#}MhVPLSo{g)42zU$LnpKcz+Dp$k@*f9V_ z3|2tH<7Mpm-~A8+3?)=(0TdBDu(37--xSBQm0ee4TpJ5`B;Xkf;X}Zqh*0}1;j@5m ze&+-5$$<~ju8jozplV166JHrcnoahe15&QbVtq*X6qUsWGNfQ*yI4yO#0<#{nNFj) z%iPs#7cTnjQ}vDCyZWhTI`F^OO$h5VVZ!(zGo0IFHedd=i+aA0I_E|_Anu*JWZ|J| zk7;l63ANF<55OG&X8xlHxwqr^{r;lzT0S^zU}Qsb**Ch64&TWh4$TOKMBxjFSv6a@ zfgT}JJzxb7fOK?g5QCw7WkPYiAD>lCV^87jM3F=gO-y8?6`72^(VK!%s)`9L z(_EO=@??H`>k}#8)1stViuv$!J@Y0!KxB6_R7R; zq^Y@M{tb9wM{-Y1_L8HHIPe>vUb)s6RTpq#1*LqYeE$dkeeHw-&V)^6*ca#8?|)wb zC=K=5i>~=*iS(ncCxJ!GM7s#`!E6$uCS9Rd8S+D9C=diml@_A(r~m$C=}#j#$1g5l zUeZ!z&@QV}7Rq8z`ds{I8A{V=5SkbW2qq7o`CQ@p5iI>20B0hBXF>vKXBt&D;W9&+ zIY3e`olP()4T0pChLMX;6sAeEh)&v#5v(zNfuQIEOyTl0`uEcOZuAe<6oAGzA%ZAQ z(%eKDAyK#7`A{siyCa?8WTsJ1hT~WaViF*s6=aYu8i67og7xQABGTj6KxXVUTh0+J zZYjbV9}fg+-uIaxqs7+8KnH_fF;lI`92Te{aFwRwQISFfAs|}UVG2N#!kq;@?!>zJ zQ6`qBtz5ojBmfOk8UkRcwIvSb-ZPke&96+&kxt!;XbPH9vw@x|08M;%#?e#H3Vt}K zAt*u}{p2X-Aiu>%nP#i5Q5Vcocx)NkA7QEkqV!7p6o4j+I}5Z^Vl7AtMrcKNl$xrk zq_)7vros_g>w#WVRxufg)3{3l*Z@d_dI~_3%$=DKn{_d~;L2s-M$Ca&zXF{osHHXw zVVUj@0^pnd>!A(809k{!YYIS<%bi(e9_)uVna5z&RIu0#8*5`qU4+{ZV|ZwBjyv`qnMGP<)cDhn*Zs5BXZ zA_<>@1!M$>6nxfUyC5@lo+6PT>NgXn14Ni;ZHfa;YGO^M@ND!8&YE&p)5Z)qAo9cJ zXsz*(X-G^g5^Fj{^y-Y=QvjOmt}K`~HYPV{!gQE6W)3Y30P=OqP#B>Z_CuOKX|A+t z3P6+Fm5nWkhtYBKOmnaZ~m8^|cVhYkH!sKtKl<0rUO__Ed4){(WDzaWl zTG){nbcG@ru`vLw{(pB0K(Cqw3D(DanR*HnX!NNJJ=a4Ei9vUL5XOrr^Ug5IRC~~Z zvPLeHCN+S>WIp*!HVel!1&d=|Cjt;cD#vw4vxic-?018lFdWx7HtI49s|GvBnFYOM zrZYr1M6OG~iYWlST3dT&TS<|>U8Y|~Pc#7#YKy4Bz=*%XR4AzO>VO9+7GNdAvA`jS zEFcB*r8UWxP)@4EHQB+*K5=1E0x0N4xMcQsN~Lmf6*A}PK`v~&p)=Qeof~{ zza|~0=rz-$EQ0oCfoMFZ2t`UHdd0#yuS`&m)23Q$U!3e*a8d#&EblxToSv3?k#qh7 zx&vsGAW{rjPaS^Rf8xAPTrgmRbJ&ZP7Ug!g=Z{pM8el_{m05|JIMEIr^veg}wzUdZ9Dhb}w4} z8{B$n7HJ!Pod~dAn~a+Iv0`K4C%2t&nuvcm z4|~E9?h8&mR)6jBgc2dn1G6 zKKZxD{q6%wYZtff`ILfcOVy*jAyO_l4N!f$z0@G@@2}%4x@V<+a>n^* zuL1ymu#9`2yYE%+Z+|)4q;CsI-uP3$cS`DrZEv3IbUS-G3gc~((Xdgu1!siyOm4D!Jh7}l}<`- zEY$Jp@6UUB!(*Sip#%WGxb(;P>((bGwe@*T0LbPLv75obg+Kphc2|F1jbteYsK5AY z`HO+ye&Bv@M_?E$hyZ|owOF3t`H{?`+-|vn(x7+u;6MBC{Ph2nc6h#1+r$CVczu4N d9CIRV{~r$v(7N4SK0E*b002ovPDHLkV1klG_*?)0 literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_backspace_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_backspace_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..05ef4b1218de72010ac6a8bd009b416c8f4065fb GIT binary patch literal 838 zcmV-M1G)T(P)%1ZzM0@q1IK~!ko z?U}u96G0S(zgTD^3JU%LJi~|*rKCn{0%Q{)0h&aj015FfNEq6LLo)jN4Td%dB?wPsg%q)~uR#|10HBd9|N80Cj&Kfrfrbf}F z%?E0x3p2-#XXOGdZs*dRpWHFO+(5hB&rPo%+;RZPGd6NvLa(hras%OFu0Y=^)}PsDfRx-IxC-?pUl`|xl4M?7_P|&14PL@l!R18pZhu#&1lL#cM+*AI0 zk7Z(|TSXkC0+MUI(c_qg3aLwEc39LPRZ`2f$FhmR?6KrP>Jpg)FfohldjiRd(n@fr#Gn244KkcV6wrVqh_)Q8hU+VJU6jmt`)Z5gl?(;`Yh8o~itU`q`ok9nZp zodFH04j}E1ASL0{UAgmgFv=Vqb$|XOb161PR6XENn}efiIF7Wfm8vQK(=JhYN*1A!|m%VWrPzrlYna5`^#o?bXyY8 z0eQ>j3K%Jo22QRXvgz_paySX}lUgs^iYj0P!-irGSn;q9582iwP?;yRym!wF671Tsg2tn-CG(_G4Y4nJ za0`PlBg3pY5H=O_WRu2JPazkO4k2pU|@{!ba4#HxcBykVbLK2f!2rYs!JQC zSMVxYI?b5F9r(=8qV$%-ZubUb3s;GLN$*W5?@wucdp}`zzF7Yf!v&eMmH|;}f7rkD z(~=g^eN<`_2l%L_9~e(R*LM$4d>@9i11SjuuEIGWZZd7rYW;Xofl;EeaO+Utnh|ou;szm>~B{tD~-7r)N-$BY-P&#jQNU*r)ZbHBwd`kehsYoAAtD;%D2 zFJImo+i~RN$9>MntURPE{R-rjx*uOY#L8Yg`6*9MkF!eMxd(o-N}dHueSLB-yZDPI zKXRFyswX67H0Q+OtBy@P>c6(UpQv!yb4MMEN6(xkzbCXGIpw)$fu+ZpIbWm~=I*bb u`YCdqvsU9}#i@rXCwM)|00)WhOa5z&M{0ULUflu=b_P#ZKbLh*2~7Y7NwzHj literal 0 HcmV?d00001 diff --git a/app/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png b/app/src/main/res/drawable-xxhdpi/ic_keyboard_white_24dp.png new file mode 100644 index 0000000000000000000000000000000000000000..2a5dcefa350d45bd306fee322edd6a3d1b9ba002 GIT binary patch literal 389 zcmV;00eb$4P)@LSO{~4x`Na~Ep^B=B<;MG|J%*X zqF>&;_fJxQ5JCtc#3`lM4PC_)V>IDq{N)fD8!T0rD5M+MqT{e2XZR|x3h63#=sLcm z6ME|QT$YW)iE|yGjahtY3XMeicx)GHiU_srLY|0F-7Zv;&!KYOg0Yf(tYS6OQJOo2 zsSt$4nJ za0`PlBg3pY5H=O_WK-yT-=O#yuz=5Lf)P(jv*QM-d=U&Vh|8;2vj~^_;SPz2s9B5(kl>^eHGoU!@q(*ZXhHy2ofV9A*4tY$aGZD5XCNN zC;&Slh{|gzAgc3~DEBO?h&IM9e3np><|urIv%R-FGdp`XEB5t!`o4X?o1J$%2US&7 zRaI40RaI60->TVO(0P^xnw;gKF%duDPk!N={N5MnSzN0%KI7A|p$}G%Wj>O7wgOlA zAfWX8#+=-%Ti{;|3LjqJON$QZ({wly*f+Sx^X45`4h|n`yl2<}eV1>7Tt)vd640m6 z0PjD*{d#HRvtN%G23HYEVh)v^HPgs3i@L`5^J(gVQ(qyAJ{9^)o z-YGum3egY+5BNtS9b@QL3S(9>*si%*6^ z!jnG-{=t`csQ`Rvu%;8m;zJ$uEnY2XTxt-fE9Aw8TIV#%;|0!qiEEB8e5iA+Q2dlG z_`ruce^ctJbxxw3DshBlhxf;W?=(9ntwQn>-;V>ouh7h=u>T+{CPta~4FgG_b` zc>g$L%Qj?cYv&RGxycWsz$YF$c(!vy2tcP$o+uoD#%gM_8E9nNTa2>y`?j;Q=|mOhVdEjrD*4b~F7u;>^e0J+YpgTRW;I_)(R6trPZkos z)AqQ9ekC$!N%y0;o)!jj+;RW+%i9Ye+uPfVE8?rGd_MV zcXJN>MbuTfZSfBdS>cPI@PFlF(^cp|;R>JdAdv8U%{jSm`6nO%J<9^;c!r0^wQzEW yTP(|Miw9Y`_d=CGB~S@e0+m1|Pzh85L+c+Z6{WziKD=B20000*e5%+LVYMs5LelXw_oEZymTcJF4sms!z! zBj4#(BY+S>2qAkh~qYs~P5W%pg0zDK}OA3$hAPJL&xj+i2Hg6`CL|Hu?3P&w-JIYp4E0eto~^K#%7Omv>lpU)K7b zM~P_(n)=>nA^!wj{RxM;frNJ~P7}2CC!I#!3HtgIkJIA`7W$Ltf#C!+7Wx4L&0s=f zp&vAn4J5!qKX9b4nJ za0`PlBg3pY5H=O_WK-y92~}d#Yc}bFfe9#x;TbZ+!SA&z8~CYeO1rDciDAsUF$18k+j3pjYJ=c>@vP<`gy~FnVF4w5esso z7RZX7C<)opS3AX`s&Vp-);#U4HI*}-^ZF=uNiec#JX%to^k1~zY)QiJJqy?beteJA zNETCS6?mYM#xA82Ft>GIuD5A|*rE4nJ za0`PlBg3pY5H=O_WK-yTwI2M=Y`mTLgAh+jv*Dd-d=O$Vh|8;2z+Aku3qSS zVy>#^r0->3z4vokfQmq%zH>A8*52nr=j}2|Hr bs6F6p^~HgW^=hHXY>20sp 20sp + 40dp \ No newline at end of file diff --git a/app/src/main/res/values/dimens.xml b/app/src/main/res/values/dimens.xml index 48c42fcb..56d56c2e 100644 --- a/app/src/main/res/values/dimens.xml +++ b/app/src/main/res/values/dimens.xml @@ -39,4 +39,5 @@ 1dp 20dp + 50dp \ No newline at end of file diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml index 96c8994c..00356f42 100644 --- a/app/src/main/res/values/ids.xml +++ b/app/src/main/res/values/ids.xml @@ -60,5 +60,17 @@ + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/text_strings.xml b/app/src/main/res/values/text_strings.xml index b0a78fc9..d5bf268c 100644 --- a/app/src/main/res/values/text_strings.xml +++ b/app/src/main/res/values/text_strings.xml @@ -149,6 +149,7 @@ Value Description Parameters + Parameter Create function Edit function Name of function is not valid: name must start with a letter, can contain letters, digits and underscore.