├── .gitignore ├── .idea ├── checkstyle-idea.xml ├── compiler.xml ├── copyright │ └── profiles_settings.xml ├── gradle.xml ├── misc.xml ├── modules.xml └── vcs.xml ├── LICENSE ├── README.md ├── app ├── .gitignore ├── build.gradle ├── proguard-rules.pro └── src │ ├── androidTest │ └── java │ │ └── com │ │ └── github │ │ └── gianlucanitti │ │ └── expreval │ │ └── ApplicationTest.java │ ├── main │ ├── AndroidManifest.xml │ ├── ic_launcher-web.png │ ├── java │ │ └── com │ │ │ └── github │ │ │ └── gianlucanitti │ │ │ └── expreval │ │ │ ├── ContextDialogFragment.java │ │ │ ├── EditFunctionDialogFragment.java │ │ │ ├── EditVariableDialogFragment.java │ │ │ ├── ExprEval.java │ │ │ ├── ExprEvalDialog.java │ │ │ ├── LibraryLocalizer.java │ │ │ ├── TextViewExpressionContext.java │ │ │ └── TextViewWriter.java │ └── res │ │ ├── layout │ │ ├── activity_expr_eval.xml │ │ ├── activity_expr_eval_dialog.xml │ │ ├── dialog_edit_function.xml │ │ └── dialog_edit_variable.xml │ │ ├── menu │ │ └── mainmenu.xml │ │ ├── mipmap-hdpi │ │ └── ic_launcher.png │ │ ├── mipmap-mdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxhdpi │ │ └── ic_launcher.png │ │ ├── mipmap-xxxhdpi │ │ └── ic_launcher.png │ │ ├── values-it │ │ └── strings.xml │ │ ├── values-pt-rBR │ │ └── strings.xml │ │ ├── values-w820dp │ │ └── dimens.xml │ │ └── values │ │ ├── colors.xml │ │ ├── dimens.xml │ │ ├── strings.xml │ │ └── styles.xml │ └── test │ └── java │ └── com │ └── github │ └── gianlucanitti │ └── expreval │ └── ExampleUnitTest.java ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── icon.xcf └── settings.gradle /.gitignore: -------------------------------------------------------------------------------- 1 | *.iml 2 | .gradle 3 | /local.properties 4 | /.idea/workspace.xml 5 | /.idea/libraries 6 | .DS_Store 7 | /build 8 | /captures 9 | -------------------------------------------------------------------------------- /.idea/checkstyle-idea.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 14 | 15 | -------------------------------------------------------------------------------- /.idea/compiler.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | -------------------------------------------------------------------------------- /.idea/copyright/profiles_settings.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | -------------------------------------------------------------------------------- /.idea/gradle.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 17 | 18 | -------------------------------------------------------------------------------- /.idea/misc.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /.idea/modules.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /.idea/vcs.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2016 Gianluca Nitti 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # android-expr-eval 2 | Android application to solve math expressions. Based on [java-expr-eval](https://github.com/gianluca-nitti/java-expr-eval). 3 | 4 | ## New features 5 | Now supports [ACTION_PROCESS_TEXT](https://medium.com/google-developers/custom-text-selection-actions-with-action-process-text-191f792d2999#.ir1ubmtgz) intent on Android 6.0+, i.e. you can evaluate an expression from any application using ExprEval and replace it with the result. 6 | See this animation for an example: 7 | 8 | ![ACTION_PROCESS_TEXT example](http://i.imgur.com/r0xHgQH.png) 9 | 10 | A custom intent is also available: you can use `startActivityForResult()` with `com.github.gianlucanitti.expreval.ACTION_EVAL` Intent to have an expression evaluated. 11 | You have to `putExtra()` the expression with `"expression"` as the extra name, and the result will be returned in an extra named `"result"`. 12 | 13 | This feature was proposed in [issue #1](https://github.com/gianluca-nitti/android-expr-eval/issues/1). 14 | 15 | ## Download/compile 16 | See the releases page for prebuilt APK. 17 | To build from source, clone the repository, make sure you have the Android SDK installed (and you have the ANDROID_HOME environment variable correctly set), and run 18 | ``` 19 | ./gradlew assembleRelease 20 | ``` 21 | (you may need to `chmod +x gradlew` it if it's not already executable). 22 | 23 | ## Contributed translations 24 | * [@afmachado](https://github.com/afmachado): Brazilian Portuguese 25 | 26 | ## Screenshots ([Imgur album](http://imgur.com/a/wOJfT)) 27 | ![Main activity](http://i.imgur.com/DJSZBVH.png) 28 | ![Context dialog](http://i.imgur.com/uCcd0Ih.png) 29 | ![New variable dialog](http://i.imgur.com/RTTOVlu.png) 30 | ![New function dialog](http://i.imgur.com/cro2lgq.png) -------------------------------------------------------------------------------- /app/.gitignore: -------------------------------------------------------------------------------- 1 | /build 2 | -------------------------------------------------------------------------------- /app/build.gradle: -------------------------------------------------------------------------------- 1 | apply plugin: 'com.android.application' 2 | 3 | android { 4 | compileSdkVersion 25 5 | buildToolsVersion "25.0.2" 6 | 7 | defaultConfig { 8 | applicationId "com.github.gianlucanitti.expreval" 9 | minSdkVersion 15 10 | targetSdkVersion 25 11 | versionCode 3 12 | versionName "1.2" 13 | } 14 | buildTypes { 15 | release { 16 | minifyEnabled false 17 | proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro' 18 | } 19 | } 20 | } 21 | 22 | dependencies { 23 | compile fileTree(include: ['*.jar'], dir: 'libs') 24 | testCompile 'junit:junit:4.12' 25 | compile 'com.github.gianluca-nitti:java-expr-eval:v3.1' 26 | compile 'com.android.support:appcompat-v7:25+' 27 | compile 'com.android.support:support-v4:25+' 28 | } 29 | -------------------------------------------------------------------------------- /app/proguard-rules.pro: -------------------------------------------------------------------------------- 1 | # Add project specific ProGuard rules here. 2 | # By default, the flags in this file are appended to flags specified 3 | # in C:\Users\Gianluca\AppData\Local\Android\android-sdk/tools/proguard/proguard-android.txt 4 | # You can edit the include path and order by changing the proguardFiles 5 | # directive in build.gradle. 6 | # 7 | # For more details, see 8 | # http://developer.android.com/guide/developing/tools/proguard.html 9 | 10 | # Add any project specific keep options here: 11 | 12 | # If your project uses WebView with JS, uncomment the following 13 | # and specify the fully qualified class name to the JavaScript interface 14 | # class: 15 | #-keepclassmembers class fqcn.of.javascript.interface.for.webview { 16 | # public *; 17 | #} 18 | -------------------------------------------------------------------------------- /app/src/androidTest/java/com/github/gianlucanitti/expreval/ApplicationTest.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.app.Application; 4 | import android.test.ApplicationTestCase; 5 | 6 | /** 7 | * Testing Fundamentals 8 | */ 9 | public class ApplicationTest extends ApplicationTestCase { 10 | public ApplicationTest() { 11 | super(Application.class); 12 | } 13 | } -------------------------------------------------------------------------------- /app/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 11 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | -------------------------------------------------------------------------------- /app/src/main/ic_launcher-web.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gianluca-nitti/android-expr-eval/7270001bd9be435140623515bae2b20e28bc6d63/app/src/main/ic_launcher-web.png -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/ContextDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.app.*; 4 | import android.content.DialogInterface; 5 | import android.os.Bundle; 6 | import android.widget.Toast; 7 | import com.github.gianlucanitti.javaexpreval.*; 8 | 9 | import java.util.*; 10 | 11 | public class ContextDialogFragment extends DialogFragment implements Observer, DialogInterface.OnClickListener{ 12 | 13 | private static abstract class ContextItem{ 14 | @Override 15 | public abstract String toString(); 16 | } 17 | 18 | private static class VariableContextItem extends ContextItem{ 19 | private String name; 20 | private ExpressionContext.VariableValue data; 21 | 22 | private VariableContextItem(String name, ExpressionContext.VariableValue data){ 23 | this.name = name; 24 | this.data = data; 25 | } 26 | 27 | @Override 28 | public String toString(){ 29 | return (data.isReadOnly() ? "readonly " : "") + name + "=" + data.getValue(); 30 | } 31 | } 32 | 33 | private static class FunctionContextItem extends ContextItem{ 34 | private Function data; 35 | 36 | private FunctionContextItem(Function data){ 37 | this.data = data; 38 | } 39 | 40 | @Override 41 | public String toString(){ 42 | return (data.isReadOnly() ? "readonly " : "") + data.getName() + "(" + data.getArgCount() + " arguments)"; 43 | } 44 | } 45 | 46 | private class ListItems{ 47 | 48 | private ArrayList items = new ArrayList<>(); 49 | private ExpressionContext ctx; 50 | 51 | private void update(ExpressionContext ctx){ 52 | this.ctx = ctx; 53 | items.clear(); 54 | for(Map.Entry v: ctx.getVariables().entrySet()) 55 | items.add(new VariableContextItem(v.getKey(), v.getValue())); 56 | for(Function f: ctx.getFunctions()) 57 | items.add(new FunctionContextItem(f)); 58 | } 59 | 60 | private String[] getStrings(){ 61 | String[] result = new String[items.size()]; 62 | for(int i = 0; i < result.length; i++) 63 | result[i] = items.get(i).toString(); 64 | return result; 65 | } 66 | 67 | private ContextItem getItem(int i){ 68 | return items.get(i); 69 | } 70 | 71 | private ContextItem removeItem(int i) throws ReadonlyException{ 72 | ContextItem item = items.get(i); 73 | if (item instanceof VariableContextItem) { 74 | ctx.delVariable(((VariableContextItem) item).name); 75 | } else { 76 | Function f = ((FunctionContextItem) item).data; 77 | ctx.delFunction(f.getName(), f.getArgCount()); 78 | } 79 | update(ctx); 80 | return item; 81 | } 82 | } 83 | 84 | private FragmentManager fm; 85 | private EditVariableDialogFragment editVariable; 86 | private EditFunctionDialogFragment editFunction; 87 | 88 | private ListItems items = new ListItems(); 89 | 90 | @Override 91 | public void update(Observable observable, Object data) { 92 | items.update((ExpressionContext)observable); 93 | } 94 | 95 | @Override 96 | public Dialog onCreateDialog(Bundle savedInstanceState){ 97 | setRetainInstance(true); 98 | editVariable = new EditVariableDialogFragment(); 99 | editFunction = new EditFunctionDialogFragment(); 100 | fm = getFragmentManager(); 101 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 102 | builder.setItems(items.getStrings(), this); 103 | builder.setPositiveButton(R.string.newContextItem, this); 104 | return builder.create(); 105 | } 106 | 107 | @Override 108 | public void onClick(DialogInterface dialog, final int which) { 109 | final ExprEval activity = (ExprEval)getActivity(); 110 | AlertDialog.Builder innerDialog = new AlertDialog.Builder(activity); 111 | if (which == DialogInterface.BUTTON_POSITIVE) { 112 | innerDialog.setItems(R.array.addVariableFunction, new DialogInterface.OnClickListener() { 113 | @Override 114 | public void onClick(DialogInterface dialogInterface, int i) { 115 | if(i == 0) { 116 | editVariable.show(fm, "edit_variable"); 117 | }else if (i == 1){ 118 | editFunction.show(fm, "edit_function"); 119 | } 120 | } 121 | }); 122 | } else { 123 | innerDialog.setMessage(getString(R.string.contextItemDeletePrompt, items.getItem(which).toString())); 124 | final String deletedMsg = getString(R.string.contextItemDeleted, items.getItem(which).toString()); 125 | innerDialog.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener(){ 126 | @Override 127 | public void onClick(DialogInterface dialog_inner, int which_inner) { 128 | try{ 129 | items.removeItem(which); 130 | activity.writeOutput(deletedMsg); 131 | }catch(ExpressionException ex){ 132 | Toast.makeText(activity, ex.getMessage(), Toast.LENGTH_LONG).show(); 133 | activity.writeOutput(ex.getMessage()); 134 | } 135 | } 136 | }); 137 | innerDialog.setNegativeButton(android.R.string.no, null); 138 | } 139 | innerDialog.create().show(); 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/EditFunctionDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.app.DialogFragment; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.*; 10 | import com.github.gianlucanitti.javaexpreval.*; 11 | 12 | import java.util.ArrayList; 13 | 14 | public class EditFunctionDialogFragment extends DialogFragment implements DialogInterface.OnClickListener { 15 | 16 | private ExpressionContext ctx; 17 | private ArrayList argNames; 18 | 19 | private EditText nameText; 20 | private EditText exprText; 21 | private EditText newArgText; 22 | private ListView argsList; 23 | private CheckBox readonlyCheckbox; 24 | 25 | private void writeOutput(String s) { 26 | ((ExprEval) getActivity()).writeOutput(s); 27 | } 28 | 29 | @Override 30 | public Dialog onCreateDialog(Bundle savedInstanceState) { 31 | setRetainInstance(true); 32 | ctx = ((ExprEval) getActivity()).getContext(); 33 | argNames = new ArrayList<>(); 34 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 35 | View rootLayout = getActivity().getLayoutInflater().inflate(R.layout.dialog_edit_function, null); 36 | nameText = (EditText) rootLayout.findViewById(R.id.nameText); 37 | exprText = (EditText) rootLayout.findViewById(R.id.exprText); 38 | newArgText = (EditText) rootLayout.findViewById(R.id.newArgText); 39 | argsList = (ListView) rootLayout.findViewById(R.id.argsList); 40 | readonlyCheckbox = (CheckBox) rootLayout.findViewById(R.id.readonlyCheckbox); 41 | final ArrayAdapter argsArrayAdapter = new ArrayAdapter<>(getActivity(), android.R.layout.simple_list_item_1, argNames); 42 | argsList.setAdapter(argsArrayAdapter); 43 | argsList.setOnItemClickListener(new AdapterView.OnItemClickListener() { 44 | @Override 45 | public void onItemClick(AdapterView adapterView, View view, int i, long l) { 46 | argNames.remove(i); 47 | argsArrayAdapter.notifyDataSetChanged(); 48 | } 49 | }); 50 | rootLayout.findViewById(R.id.add_argument).setOnClickListener(new View.OnClickListener() { 51 | @Override 52 | public void onClick(View view) { 53 | if (NamedSymbolExpression.isValidSymbolName(newArgText.getText().toString())) { 54 | argNames.add(newArgText.getText().toString()); 55 | argsArrayAdapter.notifyDataSetChanged(); 56 | newArgText.getText().clear(); 57 | } else { 58 | newArgText.setError(getString(R.string.invalidArgName)); 59 | } 60 | } 61 | }); 62 | builder.setView(rootLayout); 63 | builder.setPositiveButton(android.R.string.ok, this); 64 | builder.setNegativeButton(android.R.string.cancel, this); 65 | return builder.create(); 66 | } 67 | 68 | @Override 69 | public void onClick(DialogInterface dialogInterface, int i) { 70 | if (i == DialogInterface.BUTTON_POSITIVE) { 71 | try { 72 | ctx.setFunction(nameText.getText().toString(), Expression.parse(exprText.getText().toString()), readonlyCheckbox.isChecked(), argNames.toArray(new String[argNames.size()])); 73 | writeOutput(getString(R.string.functionNewDef, ctx.getFunction(nameText.getText().toString(), argNames.size()).toString(), exprText.getText().toString())); 74 | } catch (ExpressionException ex) { 75 | writeOutput(ex.getMessage()); 76 | Toast.makeText(getActivity(), ex.getMessage(), Toast.LENGTH_LONG).show(); 77 | } 78 | } 79 | } 80 | 81 | } -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/EditVariableDialogFragment.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.app.AlertDialog; 4 | import android.app.Dialog; 5 | import android.app.DialogFragment; 6 | import android.content.DialogInterface; 7 | import android.os.Bundle; 8 | import android.view.View; 9 | import android.widget.CheckBox; 10 | import android.widget.EditText; 11 | import android.widget.Toast; 12 | import com.github.gianlucanitti.javaexpreval.Expression; 13 | import com.github.gianlucanitti.javaexpreval.ExpressionContext; 14 | import com.github.gianlucanitti.javaexpreval.ExpressionException; 15 | 16 | public class EditVariableDialogFragment extends DialogFragment implements DialogInterface.OnClickListener{ 17 | 18 | private ExpressionContext ctx; 19 | 20 | private EditText nameText; 21 | private EditText valueText; 22 | private CheckBox readonlyCheckbox; 23 | 24 | private void writeOutput(String s){ 25 | ((ExprEval) getActivity()).writeOutput(s); 26 | } 27 | 28 | @Override 29 | public Dialog onCreateDialog(Bundle savedInstanceState){ 30 | setRetainInstance(true); 31 | ctx = ((ExprEval) getActivity()).getContext(); 32 | AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); 33 | View rootLayout = getActivity().getLayoutInflater().inflate(R.layout.dialog_edit_variable, null); 34 | nameText = (EditText) rootLayout.findViewById(R.id.nameText); 35 | valueText = (EditText) rootLayout.findViewById(R.id.valueText); 36 | readonlyCheckbox = (CheckBox) rootLayout.findViewById(R.id.readonlyCheckbox); 37 | builder.setView(rootLayout); 38 | builder.setPositiveButton(android.R.string.ok, this); 39 | builder.setNegativeButton(android.R.string.cancel, this); 40 | return builder.create(); 41 | } 42 | 43 | @Override 44 | public void onClick(DialogInterface dialogInterface, int which) { 45 | if(which == DialogInterface.BUTTON_POSITIVE){ 46 | String varName = nameText.getText().toString(); 47 | try { 48 | ctx.setVariable(varName , readonlyCheckbox.isChecked(), Expression.parse(valueText.getText().toString())); 49 | writeOutput(getString(R.string.varNewValue, varName, Double.toString(ctx.getVariable(varName)))); 50 | }catch(ExpressionException ex){ 51 | writeOutput(ex.getMessage()); 52 | Toast.makeText(getActivity(), ex.getMessage(), Toast.LENGTH_LONG).show(); 53 | } 54 | } 55 | } 56 | 57 | } 58 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/ExprEval.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.app.AlertDialog; 4 | import android.content.DialogInterface; 5 | import android.support.v7.app.AppCompatActivity; 6 | import android.os.Bundle; 7 | import android.support.v7.widget.Toolbar; 8 | import android.text.Html; 9 | import android.text.method.LinkMovementMethod; 10 | import android.text.method.ScrollingMovementMethod; 11 | import android.view.Menu; 12 | import android.view.MenuItem; 13 | import android.view.View; 14 | import android.widget.Button; 15 | import android.widget.EditText; 16 | import android.widget.TextView; 17 | import com.github.gianlucanitti.javaexpreval.ExpressionContext; 18 | 19 | public class ExprEval extends AppCompatActivity implements View.OnClickListener{ 20 | 21 | private TextViewExpressionContext ctx; 22 | 23 | private EditText in; 24 | private TextView out; 25 | private Button evalBtn; 26 | 27 | private ContextDialogFragment ctxDialog; 28 | 29 | public ExpressionContext getContext(){ 30 | return ctx; 31 | } 32 | 33 | public void writeOutput(String s){ 34 | out.append(s + System.getProperty("line.separator")); 35 | } 36 | 37 | @Override 38 | protected void onCreate(Bundle savedInstanceState) { 39 | super.onCreate(savedInstanceState); 40 | setContentView(R.layout.activity_expr_eval); 41 | setSupportActionBar((Toolbar)findViewById(R.id.my_toolbar)); 42 | LibraryLocalizer.localize(this); 43 | in = (EditText) findViewById(R.id.inputText); 44 | out = (TextView) findViewById(R.id.outputText); 45 | out.setMovementMethod(new ScrollingMovementMethod()); 46 | evalBtn = (Button)findViewById(R.id.evalBtn); 47 | evalBtn.setOnClickListener(this); 48 | ctxDialog = new ContextDialogFragment(); 49 | ctx = new TextViewExpressionContext(out); 50 | ctx.addObserver(ctxDialog); 51 | ctxDialog.update(ctx, null); 52 | } 53 | 54 | @Override 55 | public boolean onCreateOptionsMenu(Menu menu) { 56 | getMenuInflater().inflate(R.menu.mainmenu, menu); 57 | return true; 58 | } 59 | 60 | @Override 61 | public boolean onOptionsItemSelected(MenuItem item){ 62 | switch(item.getItemId()){ 63 | case R.id.action_context: 64 | ctxDialog.show(getFragmentManager(), "context_dialog"); 65 | return true; 66 | case R.id.action_clearctx: 67 | AlertDialog.Builder prompt = new AlertDialog.Builder(this); 68 | prompt.setMessage(R.string.clearContextPrompt); 69 | prompt.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() { 70 | @Override 71 | public void onClick(DialogInterface dialogInterface, int i) { 72 | ctx.clear(); 73 | ctx.writeOutput(getString(R.string.contextCleared)); 74 | } 75 | }); 76 | prompt.setNegativeButton(android.R.string.no, null); 77 | prompt.show(); 78 | return true; 79 | case R.id.action_clearout: 80 | out.setText(""); 81 | return true; 82 | case R.id.action_verbose: 83 | item.setChecked(!item.isChecked()); 84 | ctx.setVerbose(item.isChecked()); 85 | return true; 86 | case R.id.action_input: 87 | item.setChecked(!item.isChecked()); 88 | ctx.setEchoInput(item.isChecked()); 89 | return true; 90 | case R.id.action_help: 91 | new AlertDialog.Builder(this).setMessage(Html.fromHtml(getString(R.string.helpMessage))).show(); 92 | return true; 93 | case R.id.action_about: 94 | AlertDialog d = new AlertDialog.Builder(this) 95 | .setMessage(Html.fromHtml(getString(R.string.aboutMessage))) 96 | .show(); 97 | ((TextView)d.findViewById(android.R.id.message)).setMovementMethod(LinkMovementMethod.getInstance()); 98 | return true; 99 | default: 100 | return super.onOptionsItemSelected(item); 101 | } 102 | } 103 | 104 | @Override 105 | public void onClick(View view){ 106 | if(ctx.update(in.getText().toString()) == TextViewExpressionContext.Status.EXIT) 107 | finish(); 108 | in.getText().clear(); 109 | } 110 | 111 | } 112 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/ExprEvalDialog.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.content.Intent; 4 | import android.support.v7.app.AppCompatActivity; 5 | import android.os.Bundle; 6 | import android.text.method.ScrollingMovementMethod; 7 | import android.view.View; 8 | import android.widget.Button; 9 | import android.widget.TextView; 10 | import com.github.gianlucanitti.javaexpreval.UndefinedException; 11 | 12 | public class ExprEvalDialog extends AppCompatActivity implements View.OnClickListener{ 13 | 14 | private boolean actionIsProcessText; 15 | private String result; 16 | 17 | @Override 18 | protected void onCreate(Bundle savedInstanceState) { 19 | super.onCreate(savedInstanceState); 20 | setContentView(R.layout.activity_expr_eval_dialog); 21 | LibraryLocalizer.localize(this); 22 | View okButton = findViewById(R.id.evalDialogOK); 23 | View cancelButton = findViewById(R.id.evalDialogCancel); 24 | okButton.setOnClickListener(this); 25 | cancelButton.setOnClickListener(this); 26 | actionIsProcessText = getIntent().getAction().equals(Intent.ACTION_PROCESS_TEXT); 27 | String expr = getIntent().getStringExtra(actionIsProcessText ? Intent.EXTRA_PROCESS_TEXT : "expression"); 28 | if(actionIsProcessText && getIntent().getBooleanExtra(Intent.EXTRA_PROCESS_TEXT_READONLY, false)){ 29 | okButton.setVisibility(View.INVISIBLE); 30 | ((Button)cancelButton).setText(R.string.close); 31 | findViewById(R.id.evalDialogOkToReplace).setVisibility(View.GONE); 32 | } 33 | ((TextView)findViewById(R.id.evalDialogExpr)).append(" " + expr); 34 | TextView evalDialogLog = (TextView)findViewById(R.id.evalDialogLog); 35 | evalDialogLog.setMovementMethod(new ScrollingMovementMethod()); 36 | evalDialogLog.append(System.getProperty("line.separator")); 37 | TextViewExpressionContext ctx = new TextViewExpressionContext(evalDialogLog); 38 | ctx.setStopOnError(true); 39 | boolean failed = ctx.update(expr) == TextViewExpressionContext.Status.ERROR; 40 | okButton.setEnabled(!failed); 41 | if(failed) 42 | result = getString(R.string.evalFailed); 43 | else 44 | try { 45 | result = Double.toString(ctx.getVariable("ans")); 46 | }catch(UndefinedException e){ 47 | result = getString(R.string.evalFailed); 48 | } 49 | ((TextView)findViewById(R.id.evalDialogResult)).append(" " + result); 50 | } 51 | 52 | @Override 53 | public void onClick(View view) { 54 | if(view.getId() == R.id.evalDialogOK) 55 | setResult(RESULT_OK, new Intent().putExtra(actionIsProcessText ? Intent.EXTRA_PROCESS_TEXT : "result", result)); 56 | else 57 | setResult(RESULT_CANCELED); 58 | finish(); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/LibraryLocalizer.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.content.Context; 4 | import static com.github.gianlucanitti.javaexpreval.LocalizationHelper.*; 5 | 6 | public class LibraryLocalizer { 7 | 8 | private static void setMessageResource(Context c, Message m, int id){ 9 | setMessage(m, c.getString(id)); 10 | } 11 | 12 | public static void localize(Context c){ 13 | setMessageResource(c, Message.CONTEXT_CLEARED, R.string.contextCleared); 14 | setMessageResource(c, Message.EMPTY_EXPR, R.string.emptyExpr); 15 | setMessageResource(c, Message.EMPTY_SYM_NAME, R.string.emptySymName); 16 | setMessageResource(c, Message.ERROR_PREFIX, R.string.errorPrefix); 17 | setMessageResource(c, Message.EVAL_STEP, R.string.evalStep); 18 | setMessageResource(c, Message.EXPR_END_REACHED, R.string.exprEndReached); 19 | setMessageResource(c, Message.FAILED_STORE_RESULT, R.string.failedStoreResult); 20 | setMessageResource(c, Message.FUNC_ASSIGNED, R.string.functionNewDef); 21 | setMessageResource(c, Message.INCORRECT_DELETE, R.string.incorrectDelete); 22 | setMessageResource(c, Message.INTERACTIVE_HELP, R.string.interactiveHelp); 23 | setMessageResource(c, Message.INVALID_OPERATOR, R.string.invalidOperator); 24 | setMessageResource(c, Message.INVALID_SYM_NAME, R.string.invalidSymName); 25 | setMessageResource(c, Message.ONLY_ONE_EQUALITY, R.string.onlyOneEquality); 26 | setMessageResource(c, Message.OPERATOR_EXPECTED, R.string.operatorExpected); 27 | setMessageResource(c, Message.OPERATOR_FOUND, R.string.operatorFound); 28 | setMessageResource(c, Message.PARENTHESIS_MISMATCH, R.string.parenthesisMismatch); 29 | setMessageResource(c, Message.READONLY_FUNC, R.string.readonlyFunc); 30 | setMessageResource(c, Message.READONLY_VAR, R.string.readonlyVar); 31 | setMessageResource(c, Message.RESERVED_WORD, R.string.reservedWord); 32 | setMessageResource(c, Message.REWRITE_STEP, R.string.rewriteStep); 33 | setMessageResource(c, Message.UNDEFINED_FUNC, R.string.undefinedFunc); 34 | setMessageResource(c, Message.UNDEFINED_VAR, R.string.undefinedVar); 35 | setMessageResource(c, Message.UNKNOWN_CHAR, R.string.unknownChar); 36 | setMessageResource(c, Message.VAR_ASSIGNED, R.string.varNewValue); 37 | setMessageResource(c, Message.VAR_DELETED, R.string.contextItemDeleted); 38 | } 39 | 40 | } 41 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/TextViewExpressionContext.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.graphics.Color; 4 | import android.widget.TextView; 5 | import com.github.gianlucanitti.javaexpreval.InteractiveExpressionContext; 6 | import com.github.gianlucanitti.javaexpreval.NullOutputStream; 7 | 8 | import java.io.IOException; 9 | import java.io.StringReader; 10 | 11 | public class TextViewExpressionContext extends InteractiveExpressionContext { 12 | 13 | private TextViewWriter inEchoWriter; 14 | private TextViewWriter outWriter; 15 | private TextViewWriter verboseWriter; 16 | private TextViewWriter errorWriter; 17 | boolean echoInput = true; 18 | 19 | public TextViewExpressionContext(TextView out){ 20 | super(); 21 | inEchoWriter = new TextViewWriter(out, Color.YELLOW); 22 | setOutputWriter(outWriter = new TextViewWriter(out, Color.GREEN), true); 23 | setVerboseOutputWriter(verboseWriter = new TextViewWriter(out, out.getTextColors().getDefaultColor()), true); 24 | setErrorOutputWriter(errorWriter = new TextViewWriter(out, Color.RED), true); 25 | } 26 | 27 | public void writeOutput(String s){ 28 | try { 29 | outWriter.write(s); 30 | outWriter.flush(); 31 | }catch (IOException e){} 32 | } 33 | 34 | public void setVerbose(boolean b){ 35 | if(b) 36 | setVerboseOutputWriter(verboseWriter, true); 37 | else 38 | setVerboseOutputWriter(NullOutputStream.getWriter(), true); 39 | } 40 | 41 | public void setEchoInput(boolean b){ 42 | echoInput = b; 43 | } 44 | 45 | public Status update(String s){ 46 | setInputReader(new StringReader(s)); 47 | InteractiveExpressionContext.Status status = null; 48 | try { 49 | if(echoInput) { 50 | inEchoWriter.write("> " + s + System.getProperty("line.separator")); 51 | inEchoWriter.flush(); 52 | } 53 | return update(); 54 | }catch(IOException ex){ 55 | return Status.ERROR; 56 | } 57 | } 58 | 59 | } 60 | -------------------------------------------------------------------------------- /app/src/main/java/com/github/gianlucanitti/expreval/TextViewWriter.java: -------------------------------------------------------------------------------- 1 | package com.github.gianlucanitti.expreval; 2 | 3 | import android.support.annotation.NonNull; 4 | import android.text.SpannableString; 5 | import android.text.style.ForegroundColorSpan; 6 | import android.widget.TextView; 7 | 8 | import java.io.IOException; 9 | import java.io.Writer; 10 | 11 | public class TextViewWriter extends Writer { 12 | 13 | private StringBuilder buffer; 14 | private TextView v; 15 | private int color; 16 | 17 | public TextViewWriter(TextView target, int c){ 18 | v = target; 19 | color = c; 20 | buffer = new StringBuilder(); 21 | } 22 | 23 | @Override 24 | public void close() throws IOException { 25 | flush(); 26 | } 27 | 28 | @Override 29 | public void flush() throws IOException { 30 | SpannableString s = new SpannableString(buffer.toString()); 31 | s.setSpan(new ForegroundColorSpan(color), 0, s.length(), 0); 32 | v.append(s); 33 | buffer = new StringBuilder(); 34 | } 35 | 36 | @Override 37 | public void write(@NonNull char[] buf, int offset, int count) throws IOException { 38 | for(int i = offset; i < count; i++) 39 | buffer.append(buf[i]); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /app/src/main/res/layout/activity_expr_eval.xml: -------------------------------------------------------------------------------- 1 | 2 | 13 | 14 | 22 | 23 | 27 | 35 |