example/ivy/android: initial check in.

This version is not yet released to Play store.
Compared to the version in the play store

- fixed the reappearing keyboard bug
- allows app install on SD card
- when demo is enabled, the demo mode message is displayed
- when demo is enabled, the submit action button appears next to the
  edit text.
- use robpike.io/ivy/mobile instead of the wrapper in my personal repo.
- the demo script read code was moved from go to java.
- use the up-to-date help message retrieved with mobile.Help.
- textview was replaced with webview with javascript.

TODO: the app built from this code doesn't work in old android devices.
Next cl will fix it.

TODO: build instruction - not very different from
golang.org/x/mobile/example/bind/android.

Change-Id: Ifb594b79f31091b6864ec3e0d8dbaff7986a5afc
Reviewed-on: https://go-review.googlesource.com/13870
Reviewed-by: David Crawshaw <crawshaw@golang.org>
This commit is contained in:
Hyang-Ah (Hana) Kim 2015-08-22 12:32:18 -04:00 committed by Hyang-Ah Hana Kim
parent 4a6caa5fd7
commit fee22db634
41 changed files with 1142 additions and 0 deletions

View File

@ -0,0 +1,27 @@
apply plugin: 'com.android.application'
android {
compileSdkVersion 22
buildToolsVersion "22.0.1"
defaultConfig {
applicationId 'org.golang.ivy'
minSdkVersion 15
targetSdkVersion 22
versionCode 2
versionName '1.0.1'
}
buildTypes {
release {
minifyEnabled true
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
productFlavors {
}
}
dependencies {
compile fileTree(include: ['*.jar'], dir: 'libs')
compile 'com.android.support:appcompat-v7:22.1.1'
compile project(':ivy')
}

View File

@ -0,0 +1,17 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /Users/hakim/Library/Android/sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}

View File

@ -0,0 +1,13 @@
package org.golang.ivy;
import android.app.Application;
import android.test.ApplicationTestCase;
/**
* <a href="http://d.android.com/tools/testing/testing_android.html">Testing Fundamentals</a>
*/
public class ApplicationTest extends ApplicationTestCase<Application> {
public ApplicationTest() {
super(Application.class);
}
}

View File

@ -0,0 +1,52 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="org.golang.ivy"
android:versionCode="2"
android:versionName="1.0.1"
android:installLocation="auto">
<!-- undo gradle's auto addition of unrequested permissions
http://stackoverflow.com/questions/27410382/unrequested-permissions-not-declared-in-androidmanifest-crashlytics-maybe -->
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" tools:node="remove" />
<uses-permission android:name="android.permission.READ_PHONE_STATE" tools:node="remove" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" tools:node="remove" />
<application
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher"
android:theme="@style/IvyAppTheme"
android:windowSoftInputMode="adjustResize|stateAlwaysVisible">
<activity
android:name=".MainActivity"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:logo="@mipmap/ic_launcher"
android:windowSoftInputMode="adjustResize|stateUnchanged|stateAlwaysVisible"
android:configChanges="orientation|screenSize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".Help"
android:label="@string/title_activity_help"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.golang.ivy.MainActivity" />
</activity>
<activity
android:name=".AboutIvy"
android:label="@string/title_activity_about"
android:parentActivityName=".MainActivity">
<meta-data
android:name="android.support.PARENT_ACTIVITY"
android:value="org.golang.ivy.MainActivity" />
</activity>
</application>
</manifest>

View File

@ -0,0 +1,21 @@
<!--
Copyright 2015 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>
<body>
<center>
<h2>Ivy</h2><p>
Based on <a href='https://robpike.io/ivy'>robpike.io/ivy</a>
<p>
Copyright 2015 The <a href='https://golang.org/LICENSE'>Go Authors</a>.
<br>
The Ivy mascot is designed by
<a href='https://reneefrench.blogspot.com/'>Renee French</a>
and licensed under the
<a href='https://creativecommons.org/licenses/by/3.0/us/'>Creative Commons Attribution 3.0 license</a>.
</center>
</body>
</html>

View File

@ -0,0 +1,229 @@
# This is ivy. Type a newline to advance to each new step. Type one now.
# Each step in the demo is one line of input followed by some output from ivy. Type a newline now to see.
2+2
# The first line you see above (2+2) is input; the next (4) is output from a running ivy.
# Comments start with # and produce no output.
# Whenever you like, you can type an expression yourself. Try typing 2*3 now, followed by two newlines:
# Keep typing newlines; the ivy demo is about to start.
# Arithmetic has the obvious operations: + - * etc. ** is exponentiation. mod is modulo.
23
23 + 45
23 * 45
23 - 45
7 ** 3
7 mod 3
# Operator precedence is unusual.
# Unary operators operate on everything to the right.
# Binary operators operate on the item immediately to the left, and everything to the right.
2*3+4 # Parsed as 2*(3+4), not the usual (2*3)+4.
2**2+3 # 2**5, not (2**2) + 3
2**(2+3) # Use parentheses if you need to group differently.
# Ivy can do rational arithmetic, so 1/3 is really 1/3, not 0.333....
1/3
1/3 + 4/5
1/3 ** 2 # We'll see non-integral exponents later.
# Even when a number is input in floating notation, it is still an exact rational number inside.
1.2
# In fact, ivy is a "bignum" calculator that can handle huge numbers and rationals made of huge numers.
1e10 # Still an integer.
1e100 # Still an integer.
1e10/3 # Not an integer, but an exact rational.
3/1e10 # Not an integer, but an exact rational.
2**64 # They can get big.
2**640 # They can get really big.
# They can get really really big. Type a newline to see 2**6400 scroll by.
2**6400
# Ivy also has characters, which represent a Unicode code point.
'x'
char 0x61 # char is an operator: character with given value.
char 0x1f4a9
code '💩' # char's inverse, the value of given character, here printed in decimal.
# Everything in ivy can be placed into a vector.
# Vectors are written and displayed with spaces between the elements.
1 2 3
1 4/3 5/3 (2+1/3)
# Vectors of characters print without quotes or spaces.
'h' 'e' 'l' 'l' 'o'
# This is a nicer way to write 'h' 'e' 'l' 'l' 'o'. It means the same.
'hello'
# Arithmetic works on vectors.
1 2 3 + 4 5 6
# Arithmetic between scalar and vector also works, either way.
23 + 1 2 3
1 2 3 + 23 # Note the grouping: vector is a single value.
# More fun with scalar and vector.
1 << 1 2 3 4 5
(1 << 1 2 3 4 5) == (2 ** 1 2 3 4 5) # Note: true is 1, false is 0.
# iota is an "index generator": It counts from 1.
iota 10
2 ** iota 5
(1 << iota 100) == 2 ** iota 100
2 ** -1 + iota 32 # Again, see how the precedence rules work.
# The take operator removes n items from the beginning of the vector.
3 take iota 10
-3 take iota 10 # Negative n takes from the end.
# Drop is the other half: it drops n from the vector.
3 drop iota 10
-3 drop iota 10 # Negative n drops from the end.
6 drop 'hello world'
# Reduction
iota 15
# Add them up:
1 + 2 + 3 + 4 + 5 + 6 + 7 + 8 + 9 + 10 + 11 + 12 + 13 + 14 + 15
# Automate this by reducing + over the vector, like this:
+/iota 15
# We can reduce using any binary operator. This is factorial:
1 * 2 * 3 * 4 * 5 * 6 * 7 * 8 * 9 * 10
*/iota 10
*/iota 100
# Type this: */iota 10000
# That printed using floating-point notation for manageability but it is still an integer inside.
# max and min are binary operators that do the obvious. (Use semicolons to separate expresssions.)
3 max 7; 'is max and'; 3 min 7; 'is min'
# Like all binary arithmetic operators, max applies elementwise.
2 3 4 max 4 3 2
# Reduce using max to find maxmimum element in vector.
max/2 34 42 233 2 2 521 14 1 4 1 55 133
# Ivy allows multidimensional arrays. The shape operator binary rho builds them.
# Dimension (which may be a vector) on the left, data on the right.
5 rho 1
5 5 rho 1
5 5 rho 25
5 5 rho iota 25
3 5 5 rho iota 125
# Unary rho tells us the shape of an item.
x = 3 5 rho iota 15; x
rho x
x = 3 5 5 rho iota 75; x
rho x
# Arithmetic on matrices works as you would expect by now.
x/2
x**2
x**3
x**10
# Inner product is written with a . between the operators.
# This gives dot product: multiply corresponding elements and add the result.
1 2 3 4 +.* 2 3 4 5
# Any operator works. How many items are the same?
(1 2 3) +.== (1 3 3)
# How many differ?
(1 2 3) +.!= (1 3 3)
# Outer product generates a matrix of all combinations applying the binary operator.
(iota 5) o.* -1 + iota 5
# That's a letter 'o', dot, star.
# Any operator works; here is how to make an identity matrix.
x = iota 5; x o.== x
# Random numbers: Use a unary ? to roll an n-sided die from 1 to n.
?100
?100
?20 rho 6 # 20 rolls of a 6-sided die.
x = ?20 rho 6 # Remember one set of rolls.
x
# Indexing is easy.
x[1]
x[1 19 3] # You can index with a vector.
# The up and down operators generate index vectors that would sort the input.
up x
x[up x]
x[down x]
'hello world'[up 'hello world']
'hello world'[down 'hello world']
# More rolls of a die.
?10 rho 6
# Remember a set of rolls.
x = ?10 rho 6; x
# The outer product of == and the integers puts 1 in each row where that value appeared.
# Compare the last row of the next result to the 6s in x.
(iota 6) o.== x
# Count the number of times each value appears by reducing the matrix horizontally.
+/(iota 6) o.== x
# Do it for a much larger set of rolls: is the die fair?
+/(iota 6) o.== ?60000 rho 6
# Remember that ivy is a big number calculator.
*/iota 100
2**64
2**iota 64
-1+2**63
# Settings are made and queried with a leading right paren. )help lists the settings
)help
# Use )base to switch input and output to base 16.
)base 16
)base # The input and output for settings is always base 10.
# _ is a variable that holds the most recently evaluated expression. It remembers our 63-bit number.
_
1<<iota 10 # 16 powers of two, base 16.
(2**40)-1 # The largest 64-bit number base 16.
)obase 10 # Output base 10, input base still 16.
)base
-1+2**63 # The largest 63-bit number base 10.
-1+2**64 # The largest 64-bit number base 10.
# Go back to base 10 input and output.
)base 10
# Rationals can be very big too.
(2**1e3)/(3**1e2)
# Such output can be unwieldy. Change the output format using a Printf string.
)format '%.12g'
_
# We need more precision.
)format "%.100g" # Double quotes work too; there's no difference.
_
)format '%#x'
_
)format '%.12g' # A nice format, easily available by running ivy -g.
_
(3 4 rho iota 12)/4
# Irrational functions cannot be represented precisely by rational numbers.
# Ivy stores irrational results in high-precision (default 256-bit) floating point numbers.
sqrt 2
# pi and e are built-in, high-precision constants.
pi
e
)format "%.100g"
pi
)format '%.12g'
pi
# Exponentials and logarithms.
2**1/2 # Note: Non-integral exponent generates irrational result.
e**1e6
log e**1e6
log e**1e8
log 1e1000000 # Yes, that is 10 to the millionth power.
# Transcendentals. (The low bits aren't always quite right...)
sin pi/2
cos .25*pi * -1 + iota 9
log iota 6
# Successive approximations to e. (We force the calculation to use float using the "float" unary operator. Why?)
(float 1+10**-iota 9) ** 10**iota 9
# Default precision is 256 bits of mantissa. We can go up to 10000.
)prec 3000 # Units are bits, not digits.
e
)format '%.1000g' # Units are digits. (Sorry for the inconsistency.)
e
pi
sqrt 2
e**1e6
log e**1e6 # Only 904 good digits; the log algorithm is numerically weak. (Ideas welcome.)
(2**1e3)/(3**1e2)
# User-defined operators are declared as unary or binary (or both). This one computes the (unary) average.
op avg x = (+/x)/rho x
avg iota 100
# Here is a binary operator.
op n largest x = n take x[down x]
3 largest ? 100 rho 1000
4 largest 'hello world'
# Population count. Use encode to turn the value into a string of bits. Use log to decide how many.
op a base b = ((ceil b log a) rho b) encode a
7 base 2
op popcount n = +/n base 2
popcount 7
popcount 1e6
popcount 1e100
# Here is one to sum the digits. The unary operator text turns its argument into text, like Sprintf.
op sumdigits x = t = text x; +/(code (t in '0123456789') sel t) - code '0'
# Break it down: The sel operator selects from the right based on the non-zero elements in the left.
# The in operator generates a selector by choosing only the bytes that are ASCII digits.
sumdigits 99
sumdigits iota 10
sumdigits '23 skidoo' # Note: It counts only the digits.
# That's it! Have fun.
# For more information visit https://godoc.org/robpike.io/ivy

View File

@ -0,0 +1,60 @@
<!--
Copyright 2015 The Go Authors. All rights reserved.
Use of this source code is governed by a BSD-style
license that can be found in the LICENSE file.
-->
<html>
<head>
<style>
body {
font-family: sans-serif;
}
div {
padding: 1;
}
.comment {
color: grey;
}
.flow-hide {
text-overflow: ellipsis;
word-break: break-all;
white-space: nowrap;
overflow: hidden;
}
.flow-show {
word-break: break-all;
}
</style>
<script>
function flowClick(el) {
el.classList.toggle("flow-hide");
el.classList.toggle("flow-show");
}
function appendDiv(txt, tag) {
var el = document.createElement("div");
el.innerHTML = txt;
if (tag == "comment") {
el.classList.add("comment");
} else if (tag == "expr") {
} else {
el.classList.add("flow-hide");
el.onclick = function() {
flowClick(el);
};
}
document.body.appendChild(el);
window.scrollBy(0, document.body.offsetHeight);
}
</script>
<body>
</body>
</html>

Binary file not shown.

After

Width:  |  Height:  |  Size: 77 KiB

View File

@ -0,0 +1,55 @@
/*
* Copyright 2015 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package org.golang.ivy;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.webkit.WebView;
import android.webkit.WebViewClient;
/*
* Handles About menu item.
*/
public class AboutIvy extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_about);
LayoutInflater inflater = getLayoutInflater();
View layout = inflater.inflate(R.layout.activity_about,
(ViewGroup) findViewById(R.id.about_layout));
WebView webView = (WebView) layout.findViewById(R.id.about_ivy);
webView.setWebViewClient(new WebViewClient(){
public boolean shouldOverrideUrlLoading(WebView view, String url) {
if (url != null && (url.startsWith("https://") || url.startsWith("http://"))) {
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
} else {
return false;
}
}
});
webView.getSettings().setDefaultTextEncodingName("utf-8");
webView.loadUrl("file:///android_asset/aboutivy.html");
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
onBackPressed(); // back to parent.
return true;
}
}

View File

@ -0,0 +1,51 @@
/**
* Copyright 2015 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package org.golang.ivy;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.view.MenuItem;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import go.mobile.Mobile;
/*
* Displays the help message for Ivy.
*/
public class Help extends AppCompatActivity {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_help);
WebView webView = (WebView) findViewById(R.id.help_webview);
webView.setWebViewClient(new WebViewClient() {
public boolean shouldOverrideUrlLoading(WebView view, String url) {
// we are not a browser; redirect the request to proper apps.
if (url != null) {
view.getContext().startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
}
return true;
}
});
webView.getSettings().setDefaultTextEncodingName("utf-8");
String helpMsg = Mobile.Help();
// loadData has a rendering bug: https://code.google.com/p/android/issues/detail?id=6965
webView.loadDataWithBaseURL("http://godoc.org/robpike.io/ivy", helpMsg, "text/html", "UTF-8", null);
webView.setBackgroundColor(getResources().getColor(R.color.body));
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
onBackPressed(); // back to parent.
return true;
}
}

View File

@ -0,0 +1,302 @@
/*
* Copyright 2015 The Go Authors. All rights reserved.
* Use of this source code is governed by a BSD-style
* license that can be found in the LICENSE file.
*/
package org.golang.ivy;
import android.content.Intent;
import android.content.pm.ApplicationInfo;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.text.TextUtils;
import android.util.Log;
import android.util.Pair;
import android.view.KeyEvent;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ScrollView;
import android.widget.Toast;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import go.mobile.Mobile; // gobind generated class name. golang.org/issues/9660.
/*
* Main activity that consists of an edit view to accept the expression
* and a web view to display output of the expression.
*/
public class MainActivity extends AppCompatActivity {
final String DEMO_SCRIPT = "demo.ivy";
final String DEBUG_TAG = "Ivy";
final String PROMPT = "> ";
private WebView mWebView;
private EditText mEditText;
private ScrollView mScroller;
private BufferedReader mDemo;
private ImageButton mOKButton; // enabled only in demo mode.
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mScroller = (ScrollView) findViewById(R.id.scroller);
mWebView = (WebView) findViewById(R.id.webView);
mEditText = (EditText) findViewById(R.id.editText);
mOKButton = (ImageButton) findViewById(R.id.imageButton);
mOKButton.setVisibility(View.GONE);
if (savedInstanceState != null) {
mWebView.restoreState(savedInstanceState);
} else {
clear();
}
mWebView.setWebViewClient(new WebViewClient() {
// Disallow arbitrary contents loaded into our own webview.
public boolean shouldOverrideUrlLoading(WebView view, String url) {
view.getContext().startActivity(
new Intent(Intent.ACTION_VIEW, Uri.parse(url)));
return true;
}
});
mWebView.getSettings().setJavaScriptEnabled(true);
mWebView.setFocusable(false);
mWebView.addOnLayoutChangeListener(new View.OnLayoutChangeListener() {
@Override
public void onLayoutChange(View v, int left, int top, int right, int bottom,
int oldLeft, int oldTop, int oldRight, int oldBottom) {
// It's possible that the layout is not complete.
// In that case we will get all zero values for the positions. Ignore this case.
if (left == 0 && top == 0 && right == 0 && bottom == 0) {
return;
}
scrollToBottom();
}
});
mEditText.requestFocus();
mEditText.setOnKeyListener(new View.OnKeyListener() {
public boolean onKey(View v, int keyCode, KeyEvent event) {
if ((event.getAction() == KeyEvent.ACTION_DOWN) && (keyCode == KeyEvent.KEYCODE_ENTER)) {
callIvy();
return true;
}
return false;
}
});
mOKButton.setOnClickListener(new View.OnClickListener() {
public void onClick(View v) {
callIvy();
}
});
/* For webview debugging - visit chrome://inspect/#devices */
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
if (0 != (getApplicationInfo().flags &= ApplicationInfo.FLAG_DEBUGGABLE))
{ WebView.setWebContentsDebuggingEnabled(true); }
}
}
@Override
protected void onSaveInstanceState(Bundle outState) {
mWebView.saveState(outState);
super.onSaveInstanceState(outState);
}
public void onRestoreInstanceState(Bundle savedInstanceState) {
super.onRestoreInstanceState(savedInstanceState);
mWebView.restoreState(savedInstanceState);
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
MenuInflater inflater = getMenuInflater();
inflater.inflate(R.menu.menu_main, menu);
return true;
}
private long mLastPress = 0;
@Override
public void onBackPressed() {
// TODO: store and restore the state across app restarts.
long currentTime = System.currentTimeMillis();
if(currentTime - mLastPress > 6000){
Toast.makeText(getBaseContext(), "Press back again to exit.\nAll app state will be lost upon exit.", Toast.LENGTH_LONG).show();
mLastPress = currentTime;
}else{
super.onBackPressed();
}
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
switch (id) {
case R.id.action_about:
startActivity(new Intent(this, AboutIvy.class));
return true;
case R.id.action_help:
startActivity(new Intent(this, Help.class));
return true;
case R.id.action_clear:
clear();
return true;
case R.id.action_demo:
loadDemo();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
private void clear() {
mWebView.loadUrl("file:///android_asset/tape.html");
mWebView.setBackgroundColor(getResources().getColor(R.color.body));
mEditText.setText("");
Mobile.Reset();
unloadDemo();
}
private void appendShowText(final String s, final String tag) {
mWebView.loadUrl("javascript:appendDiv('" + TextUtils.htmlEncode(s).replaceAll("(\r\n|\n)", "<br />") + "', '" + tag + "')");
mWebView.setBackgroundColor(getResources().getColor(R.color.body));
}
private void callIvy() {
String s = mEditText.getText().toString().trim();
if (s != null && !s.isEmpty()) {
appendShowText(PROMPT + s, "expr");
}
new IvyCallTask().execute(s); // where call to Ivy backend occurs.
}
private synchronized void loadDemo() {
try {
if (mDemo == null) {
mDemo = new BufferedReader(new InputStreamReader(getAssets().open(DEMO_SCRIPT), "UTF-8"));
}
mOKButton.setVisibility(View.VISIBLE);
new IvyCallTask().execute("");
} catch (IOException e) {
Toast.makeText(this, "Failed to load Demo script.\nContact the app author.", Toast.LENGTH_SHORT);
}
}
private synchronized void unloadDemo() {
if (mDemo == null) { return; }
try {
mDemo.close();
} catch (IOException e) {
Log.d(DEBUG_TAG, e.toString());
}
mDemo = null;
mOKButton.setVisibility(View.GONE);
}
private synchronized String readDemo() {
if (mDemo == null) { return null; }
try {
return mDemo.readLine();
} catch (IOException e) {
unloadDemo();
}
return null;
}
private void scrollToBottom() {
mScroller.post(new Runnable() {
public void run() {
mScroller.smoothScrollTo(0, mWebView.getBottom());
}
});
}
// AsyncTask that evaluates the expression (string), and returns the strings
// to display in the web view and the edit view respectively.
private class IvyCallTask extends AsyncTask<String, Void, Pair<String, String> > {
private String ivyEval(final String expr) {
try {
return Mobile.Eval(expr); // Gobind-generated method.
} catch (Exception e) {
return "error: "+e.getMessage();
}
}
// doInBackground checks the demo script (if the passed-in param is empty),
// or returns the ivy evaluation result.
@Override
protected Pair<String, String> doInBackground(String ...param) {
final String expr = param[0];
// TODO: cancel, timeout
if (expr == null || expr.isEmpty()) {
return checkDemo();
}
return Pair.create(ivyEval(expr), "");
}
// checkDemo reads the demo script and returns the comment, and the next expression.
protected Pair<String, String> checkDemo() {
String showText = null;
while (true) {
String s = readDemo();
if (s == null) { return Pair.create(showText, null); }
int sharp = s.indexOf("#");
if (sharp < 0) {
return Pair.create(showText, s);
}
s += "\n";
if (showText == null) {
showText = s.substring(sharp, s.length());
} else {
showText += s.substring(sharp, s.length());
}
if (sharp > 0) {
return Pair.create(s.substring(sharp, s.length()), s.substring(0, sharp));
}
}
}
@Override
protected void onPostExecute(final Pair<String, String> result) {
if (result == null || (result.first == null && result.second == null)) {
return;
}
runOnUiThread(new Runnable() {
@Override
public void run() {
String showText = result.first;
if (showText != null) {
if (showText.startsWith("#")) {
appendShowText(showText, "comment");
} else {
appendShowText(showText, "result");
}
}
String editText = result.second;
if (editText != null) {
mEditText.setText(editText);
}
}
});
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 188 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 158 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 139 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 271 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 358 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 255 B

View File

@ -0,0 +1,13 @@
<?xml version="1.0" encoding="utf-8"?>
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_pressed="false">
<shape android:shape="oval">
<solid android:color="@color/blue"/>
</shape>
</item>
<item android:state_pressed="true">
<shape android:shape="oval">
<solid android:color="@color/button_material_dark"/>
</shape>
</item>
</selector>

Binary file not shown.

After

Width:  |  Height:  |  Size: 277 KiB

View File

@ -0,0 +1,30 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@+id/about_layout"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="#EEFFFFFF"
android:gravity="center"
android:orientation="vertical"
android:padding="10dp"
android:weightSum="1"
tools:context="org.golang.ivy.About">
<ImageView
android:id="@+id/ivysketch"
android:src="@drawable/ivyabout"
android:layout_gravity="center_horizontal"
android:layout_width="256dp"
android:layout_height="256dp"
android:contentDescription="@string/ivysketch_desc" />
<WebView
android:id="@+id/about_ivy"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:text="@string/app_name"
android:textSize="@dimen/abc_text_size_headline_material" />
</LinearLayout>

View File

@ -0,0 +1,14 @@
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="fill_parent"
android:layout_height="match_parent"
tools:context="org.golang.ivy.Help"
android:background="@color/body">
<WebView
android:id="@+id/help_webview"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:background="@color/body" />
</RelativeLayout>

View File

@ -0,0 +1,69 @@
<RelativeLayout
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
tools:context=".MainActivity"
android:id="@+id/layout"
android:background="@color/body">
<ScrollView
android:id="@+id/scroller"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:scrollbars="vertical"
android:fillViewport="false"
android:layout_alignParentTop="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_above="@+id/layout_bottom"
android:padding="@dimen/abc_control_padding_material">
<WebView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/webView"
android:gravity="left|bottom"
android:textIsSelectable="true"
android:clickable="false"
android:background="@color/body" />
</ScrollView>
<LinearLayout
android:orientation="horizontal"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerHorizontal="true"
android:id="@+id/layout_bottom"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
android:layout_alignParentBottom="true">
<EditText
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_weight="1"
android:id="@+id/editText"
android:gravity="top|left|start"
android:textStyle="normal"
android:inputType="textVisiblePassword"
android:hint="@string/editTextHelp"/>
<ImageButton
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:id="@+id/imageButton"
android:background="@drawable/circle_shape"
android:src="@drawable/ic_done_white_24dp"
android:contentDescription="@string/ok"
android:onClick="onClick"
android:clickable="true"
android:padding="@dimen/abc_control_padding_material"
android:layout_margin="@dimen/abc_control_padding_material" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,10 @@
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
tools:context="org.golang.ivy.About">
<item
android:id="@+id/action_settings"
android:title="@string/action_settings"
android:orderInCategory="100"
app:showAsAction="never" />
</menu>

View File

@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:ivyapp="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_clear"
android:title="@string/action_clear"
ivyapp:showAsAction="always" />
<item
android:id="@+id/action_demo"
android:title="@string/action_demo"
ivyapp:showAsAction="collapseActionView" />
<item
android:id="@+id/action_help"
android:title="@string/action_help"
ivyapp:showAsAction="collapseActionView" />
<item
android:id="@+id/action_about"
android:title="@string/action_about"
ivyapp:showAsAction="collapseActionView" />
</menu>

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 22 KiB

View File

@ -0,0 +1,6 @@
<resources>
<!-- Example customization of dimensions originally defined in res/values/dimens.xml
(such as screen margins) for screens with more than 820dp of available width. This
would include 7" and 10" devices in landscape (~960dp and ~1280dp respectively). -->
<dimen name="activity_horizontal_margin">64dp</dimen>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="ok">OK</string>
</resources>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<color name="body">#ffffeb</color>
<color name="sky">#e2f6ff</color>
<color name="blue">#1997d4</color>
<color name="white">#f9f9f9</color>
<color name="black">#000000</color>
</resources>

View File

@ -0,0 +1,5 @@
<resources>
<!-- Default screen margins, per the Android Design guidelines. -->
<dimen name="activity_horizontal_margin">16dp</dimen>
<dimen name="activity_vertical_margin">16dp</dimen>
</resources>

View File

@ -0,0 +1,51 @@
<resources>
<string name="app_name">Ivy</string>
<string name="hello_world">Hello world!</string>
<string name="action_settings">Settings</string>
<string name="action_help">Help</string>
<string name="action_about">About</string>
<string name="action_clear">Clear</string>
<string name="action_demo">Demo</string>
<string name="about_details">by Go Authors</string>
<string name="title_activity_about">About Ivy</string>
<string name="title_activity_help">Help</string>
<string name="ivy_sketch">ivy_sketch</string>
<string name="editTextHelp">type an expression</string>
<string name="ivysketch_desc">ivy sketch</string>
<string name="ok">OK</string>
<string-array name="expression_array">
<item>ceil</item>
<item>floor</item>
<item>rho</item>
<item>abs</item>
<item>iota</item>
<item>sgn</item>
<item>rev</item>
<item>flip</item>
<item>up</item>
<item>down</item>
<item>div</item>
<item>idiv</item>
<item>max</item>
<item>min</item>
<item>rho</item>
<item>take</item>
<item>drop</item>
<item>mod</item>
<item>imod</item>
<item>or</item>
<item>and</item>
<item>nor</item>
<item>nand</item>
<item>xor</item>
<item>def</item>
<item>) format</item>
<item>) op</item>
<item>) base</item>
<item>) origin</item>
</string-array>
</resources>

View File

@ -0,0 +1,16 @@
<resources>
<style name="IvyAppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="colorControlActivated">@color/sky</item>
<item name="colorPrimary">@color/blue</item>
<item name="colorPrimaryDark">@color/blue</item>
<item name="actionBarStyle">@style/MyActionBarLogo</item>
</style>
<style name="MyActionBarLogo" parent="Theme.AppCompat.Light.DarkActionBar">
<item name="background">@color/blue</item>
<item name="logo">@mipmap/ic_launcher</item>
<item name="displayOptions">useLogo|showHome</item>
</style>
</resources>

View File

@ -0,0 +1,19 @@
// Top-level build file where you can add configuration options common to all sub-projects/modules.
buildscript {
repositories {
jcenter()
}
dependencies {
classpath 'com.android.tools.build:gradle:1.2.3'
// NOTE: Do not place your application dependencies here; they belong
// in the individual module build.gradle files
}
}
allprojects {
repositories {
jcenter()
}
}

View File

@ -0,0 +1,18 @@
# Project-wide Gradle settings.
# IDE (e.g. Android Studio) users:
# Gradle settings configured through the IDE *will override*
# any settings specified in this file.
# For more details on how to configure your build environment visit
# http://www.gradle.org/docs/current/userguide/build_environment.html
# Specifies the JVM arguments used for the daemon process.
# The setting is particularly useful for tweaking memory settings.
# Default value: -Xmx10248m -XX:MaxPermSize=256m
# org.gradle.jvmargs=-Xmx2048m -XX:MaxPermSize=512m -XX:+HeapDumpOnOutOfMemoryError -Dfile.encoding=UTF-8
# When configured, Gradle will run in incubating parallel mode.
# This option should only be used with decoupled projects. More details, visit
# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects
# org.gradle.parallel=true

View File

@ -0,0 +1,24 @@
// 2015 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
plugins {
id "org.golang.mobile.bind" version "0.2.2"
}
gobind {
pkg = "robpike.io/ivy/mobile"
/* Set the following variable(s):
GOPATH: where the Go package is; check `go env`. This is required.
ex) GOPATH="/my/go/path".
GO: if the Go binary is not in the usual place, specify
the absolute path to the go binary.
GOMOBILE: optionally, you can set the absolute path to the gomobile
binary if the gomobile binary is not located in the GOPATH's
bin directory.
*/
}

View File

@ -0,0 +1 @@
include ':app', ':ivy'