Java, Python — Play Store
MathStep is a symbolic pocket calculator for Android with a complete CAS (Computer Algebra System) built-in. I built it because I felt the interfaces provided by other apps were difficult to use and ugly. It's designed for quickly entering expressions and equations and applying operations without cluttering the screen with buttons.
The latest version supports the following operations:
- Calculating limits
- Calculating sums
- Determining series expansions
- Solving equations
It has been installed 50 000+ times by people all over the world, whom are using it for their school exercises and work. It was the first application I developed with ads and an in-app purchase to remove them. I found that the best way to monetize an app is by simply making it paid, because ads are not worth it unless millions of people use your app every day.
The base UI of the app is not very special, it uses ActionBarSherlock to provide an Action Bar on versions of Android older than 4.0. These older versions are becoming less common in the wild, but are still required for 15% of the current install base. There is no reason not to use it, because it adds very little complexity to your project for the immense benefit of supporting versions of Android as low as 1.5.
The mathematical expressions are represented by an AST internally, which made them easy to reason about and manipulate. For the drawing I treat expressions as mostly 1-dimensional entities. In practice, only elements like roots and fractions need special care to render correctly. The code walks through the expression tree and calculates what the pixel dimensions would be if it was rendered at a standardized font size. When Android tells one of these views to actually draw itself, the view calculates what font size the expression should actually use to make the expression fit inside the view. It then scales all of its original drawing operations by this size and renders the expression. This allows the app to very quickly redraw expressions without recalculating anything.
The backend that actually performs the calculations is far more interesting. Since developing my own CAS would be a waste of time when there are many mature libraries around, I started looking around for open-source libraries. The most mature one I found was the SymPy project. It is very feature-complete, it's actively being developed and it's written in a language I was familiar in, Python.
Unfortunately my Android application was written in Java, so there were a couple of things I could do at that point:
- Port the Python library to Java
- Embed Python in the app
- Build a REST API in Python that talks to the app
Porting was simply not a reasonable solution. SymPy uses many features specific to Python, that would be rather difficult to translate to Java code. Embedding the entire Python interpreter in my app also seemed a little bit complex. Since I already had previous experience with writing a REST API server in Python, that seemed like the most reasonable solution.
I used Bottle to develop an API server that interfaced with SymPy on behalve of the app. This was very
easy to do and I released the first version of the app using this. The app sends a request like
/derive/x^2 to the server
and it responds with a JSON encoded result. The app then translates this result into Android views and displays it.
For example, in the case of the result displayed on the right, the server tells the app to render a header titled INPUT, followed by a rendered expression, followed by the center text REARRANGED TO, etc. This way the server has a lot of control over the information is displayed and how it is displayed, which allows for improving the backend without requiring users to update the app for every little change.
A lot of users told me that they would really like it if the app supported offline calculations. One reviewer rightfully stated that without offline calculations, the app offered little more over commercial alternatives like WolframAlpha. Offline calculations would only be possible if I somehow managed to find a way to embed the API inside the app. I initially thought this would be infeasible, but started looking into it anyway.
I started developing a sample project using Py4A and managed to embed the Python interpreter along with the full CAS library and backend code into an APK. The calculations were definitely slower than on the server, but that didn't matter much with the amount of calculations done in practice. I added this functionality to the main app and so far it has worked flawlessly.
The app will always use a JSON API to talk to an API server to get results. When offline calculations are enabled, the app will simply
talk to a local server instead of the remote server I run. This allowed me to implement offline calculations without making many changes
to the existing code. The Python interpreter and server process is ran in an Android
that is alive as long as the main app is running.