Java tutorial
// -------------------------------------------------------------------------- // Copyright (c) 2012 Henry Strickland & Thomas Shanks // // Permission is hereby granted, free of charge, to any person obtaining a // copy of this software and associated documentation files (the "Software"), // to deal in the Software without restriction, including without limitation // the rights to use, copy, modify, merge, publish, distribute, sublicense, // and/or sell copies of the Software, and to permit persons to whom the // Software is furnished to do so, subject to the following conditions: // // The above copyright notice and this permission notice shall be included // in all copies or substantial portions of the Software. // // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL // THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR // OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, // ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR // OTHER DEALINGS IN THE SOFTWARE. // -------------------------------------------------------------------------- package terse.a1; import java.io.BufferedReader; import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.FileOutputStream; import java.io.FileReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.UnsupportedEncodingException; import java.net.URI; import java.net.URLDecoder; import java.nio.ByteBuffer; import java.nio.ByteOrder; import java.nio.FloatBuffer; import java.util.ArrayList; import java.util.HashMap; import java.util.List; import java.util.concurrent.ArrayBlockingQueue; import java.util.concurrent.BlockingQueue; import java.util.regex.Matcher; import java.util.regex.Pattern; import org.apache.http.HttpResponse; import org.apache.http.NameValuePair; import org.apache.http.client.entity.UrlEncodedFormEntity; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.message.BasicNameValuePair; import org.apache.http.params.HttpParams; import terse.vm.Cls; import terse.vm.Parser; import terse.vm.Wrap; import terse.vm.Terp.ICanv; import terse.vm.Terp.IInk; import terse.vm.Ur; import terse.vm.Ur.Blk; import terse.vm.Ur.Num; import terse.vm.Static; import terse.vm.Terp; import terse.vm.Ur.Dict; import terse.vm.Ur.Obj; import terse.vm.Ur.Str; import terse.vm.Ur.Vec; import terse.vm.Usr; import android.app.Activity; import android.app.ListActivity; import android.content.Context; import android.content.Intent; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Canvas; import android.graphics.Color; import android.graphics.Paint; import android.net.Uri; import android.opengl.GLSurfaceView; import android.opengl.GLU; import android.os.Bundle; import android.os.Handler; import android.os.SystemClock; import android.text.InputType; import android.util.Log; import android.util.TypedValue; import android.view.Gravity; import android.view.KeyEvent; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceHolder.Callback; import android.view.SurfaceView; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnKeyListener; import android.view.ViewGroup; import android.view.Window; import android.view.WindowManager; import android.webkit.WebChromeClient; import android.webkit.WebView; import android.webkit.WebViewClient; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.LinearLayout.LayoutParams; import android.widget.ListView; import android.widget.ScrollView; import android.widget.TextView; import android.widget.Toast; import javax.microedition.khronos.egl.EGLConfig; import javax.microedition.khronos.opengles.GL10; public class TerseActivity extends Activity { public static Thread singleWorkThread; public static AndyTerp terp; public static String terp_error; public static String world = "tmp0"; private static int nextWorkThreadSerial = 0; protected String taSaveMe; String taPath; String taQueryStr; HashMap<String, String> taQuery; boolean taGotLongClick = false; GLSurfaceView glSurfaceView; static Pattern LINK_P = Pattern.compile("[|]link[|](/[-A-Za-z_0-9.:]*)[|]([^|]+)[|](.*)"); public Context context() { return this; } @Override protected void onPause() { super.onPause(); // The following call pauses the rendering thread. // If your OpenGL application is memory intensive, // you should consider de-allocating objects that // consume significant memory here. if (glSurfaceView != null) { glSurfaceView.onPause(); } } @Override protected void onResume() { super.onResume(); // The following call resumes a paused rendering thread. // If you de-allocated graphic objects for onPause() // this is a good place to re-allocate them. if (glSurfaceView != null) { glSurfaceView.onResume(); } } @Override public void onCreate(final Bundle savedInstanceState) { super.onCreate(savedInstanceState); glSurfaceView = null; // Forget gl on new activity. requestWindowFeature(Window.FEATURE_NO_TITLE); getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN, WindowManager.LayoutParams.FLAG_FULLSCREEN); if (savedInstanceState != null && savedInstanceState.containsKey("TerseActivity")) { taSaveMe = savedInstanceState.getString("TerseActivity"); } else { taSaveMe = null; } Runnable bg = new Runnable() { @Override public void run() { resetTerp(); } }; Runnable fg = new Runnable() { @Override public void run() { Intent intent = getIntent(); Uri uri = intent.getData(); Bundle extras = intent.getExtras(); String path = uri == null ? "/" : uri.getPath(); String query = uri == null ? "" : uri.getQuery(); viewPath(path, query, extras, savedInstanceState); } }; if (terp == null) { TextView tv = new TextView(TerseActivity.this); tv.setText(Static.fmt("Building new TerseTalk VM for world <%s>...", world)); tv.setTextAppearance(this, R.style.teletype); tv.setBackgroundColor(Color.BLACK); tv.setTextColor(Color.DKGRAY); tv.setTextSize(24); setContentView(tv); setContentViewThenBgThenFg("ResetSplash", tv, bg, fg); } else { fg.run(); } } void setContentViewThenBgThenFg(final String threadName, final View v, final Runnable bg, final Runnable fg) { setContentView(v); class BgThread extends Thread { BgThread() { super(threadName); } @Override public void run() { // terp.say("ThenBgThenFg: Running BG for %s", name); if (bg != null) bg.run(); // terp.say("ThenBgThenFg: Scheduling FG in UI for %s", name); runOnUiThread(fg); } } // terp.say("ThenBgThenFg: Starting BG thread for %s", name); new BgThread().start(); } void resetTerp() { try { terp = new AndyTerp(true, world); // Let's explore the disk. File theDir = getFilesDir(); terp.say("DIR: %s", theDir); String[] names = theDir.list(); for (int i = 0; i < names.length; i++) { File theFile = new File(theDir, names[i]); terp.say("FILE[%d]: <%s> time %d len %d", i, theFile.getAbsoluteFile(), theFile.lastModified(), theFile.length()); } } catch (IOException e) { e.printStackTrace(); terp_error = e.toString(); } } @Override protected void onSaveInstanceState(Bundle outState) { super.onSaveInstanceState(outState); outState.putString("TerseActivity", taSaveMe); } public static class Motion extends Obj { private View v; private MotionEvent ev; private Blk block; // =cls "Android" Motion Obj public Motion(AndyTerp t, View v, MotionEvent ev, Blk block) { super(t.wrapandy.clsMotion); this.v = v; this.ev = ev; this.block = block; } // =meth Motion "access" x public double _x() { // return ev.getX(); return ev.getRawX() - v.getLeft(); } // =meth Motion "access" y public double _y() { // return ev.getY(); return ev.getRawY() - v.getTop(); } // =meth Motion "access" action public int _action() { return ev.getAction(); } } public class AndyTerp extends Terp { final public WrapAndy wrapandy; final BlockingQueue<Motion> eventQueue = new ArrayBlockingQueue<Motion>(64); protected AndyTerp(boolean loadPrelude, String imageName) throws IOException { super(loadPrelude, imageName); wrapandy = new WrapAndy(); wrapandy.installClasses(this); wrapandy.installMethods(this); } public void clearEventQueue() { eventQueue.clear(); } @Override public String say(String s, Object... objects) { String msg = fmt(s, objects); Log.i("Terse", msg); recordLog(msg); return msg; } @Override public void loadPrelude() throws IOException { new InitialWorldReader("pre0").loadReader(preludeReader()); } @Override public FileInputStream openFileRead(String filename) throws FileNotFoundException { return openFileInput(filename); } @Override public FileOutputStream openFileWrite(String filename) throws FileNotFoundException { return openFileOutput(filename, Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE); } @Override public FileOutputStream openFileAppend(String filename) throws FileNotFoundException { return openFileOutput(filename, Context.MODE_WORLD_READABLE | Context.MODE_WORLD_WRITEABLE | Context.MODE_APPEND); } @Override public File getFilesDir() { return context().getFilesDir(); } @Override public void pushToWeb(String filename, String content) { terp.checkTxtFileNameSyntax(filename); String basename = filename.substring(0, filename.indexOf('.')); DefaultHttpClient client = new DefaultHttpClient(); HttpPost post = new HttpPost(fmt("%s.push.%s", YAK_WEB_PAGE, basename)); try { List<NameValuePair> nameValuePairs = new ArrayList<NameValuePair>(); nameValuePairs.add(new BasicNameValuePair("content", content)); post.setEntity(new UrlEncodedFormEntity(nameValuePairs)); HttpResponse response = client.execute(post); BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); String line = ""; while ((line = rd.readLine()) != null) { line.length(); // Ignore it for now. } } catch (IOException e) { e.printStackTrace(); toss("Error during pushToWeb <%s>: %s", filename, e); } } @Override public String pullFromWeb(String filename) { terp.checkTxtFileNameSyntax(filename); String basename = filename.substring(0, filename.indexOf('.')); DefaultHttpClient client = new DefaultHttpClient(); HttpGet post = new HttpGet(fmt("%s.pull.%s", YAK_WEB_PAGE, basename)); try { HttpResponse response = client.execute(post); BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); StringBuilder sb = new StringBuilder(); while (true) { String line = rd.readLine(); if (line == null) break; sb.append(line); sb.append('\n'); } return sb.toString(); } catch (IOException e) { e.printStackTrace(); toss("Error during pullFromWeb <%s>: %s", filename, e); return null; } } @Override public Vec listOfWebFiles() { DefaultHttpClient client = new DefaultHttpClient(); HttpGet post = new HttpGet(fmt("%s.dir", YAK_WEB_PAGE)); try { HttpResponse response = client.execute(post); BufferedReader rd = new BufferedReader(new InputStreamReader(response.getEntity().getContent())); Vec z = terp.newVec(emptyUrs); while (true) { String line = rd.readLine(); if (line == null) break; if (line.startsWith("<li>")) { line = line.substring(4); if (line.startsWith("<tt>")) { line = line.substring(4); } if (line.endsWith("</tt>")) { line = line.substring(0, line.length() - 5); } String[] words = line.split(" "); if (words.length == 3) { z.vec.add(new Vec(terp, urs(terp.newStr(words[0]), terp.newNum(Integer.parseInt(words[1])), terp.newNum(Integer.parseInt(words[2]))))); } } } return z; } catch (IOException e) { e.printStackTrace(); toss("Error during listOfWebFiles: %s", e); return null; } } private class WorkThread extends Thread { private Runnable innerRunnable; private String desc; private int serial; WorkThread(Runnable runnable, String desc) { this.innerRunnable = runnable; this.desc = desc; this.serial = nextWorkThreadSerial; ++nextWorkThreadSerial; terp.say("WorkThread #%d CTOR<%s>", serial, desc); } @Override public void run() { terp.say("WorkThread #%d RUN<%s>", serial, desc); // Wait for a running WorkThread to remove itself. while (singleWorkThread != null) { // Ask the running WorkThread to die. terp.say("WorkThread #%d TickDown<%s>: %s", serial, desc, AndyTerp.this.tickCounter); AndyTerp.this.tickCounter = 0; try { Thread.sleep(10); } catch (InterruptedException e) { // Don't care if we wake up early. } } singleWorkThread = this; AndyTerp.this.tickCounter = Integer.MAX_VALUE; try { if (innerRunnable != null) innerRunnable.run(); } catch (final Terp.TooManyTicks ex) { // Toast.makeText(getApplicationContext(), // Static.fmt("TOO MANY TICKS: %s", desc), // Toast.LENGTH_SHORT).show(); terp.say("WorkThread #%d CATCH<%s>: Dying quietly on TooManyTicks", serial, desc); } catch (final RuntimeException ex) { runOnUiThread(new Runnable() { @Override public void run() { TextView tv = new TextView(TerseActivity.this); tv.setText(fmt("EXCEPTION IN WorkThread #%d <%s>:\n\n%s", serial, desc, ex)); SetContentViewWithHomeButtonAndScroll(tv); } }); } finally { if (singleWorkThread == this) { singleWorkThread = null; } } terp.say("WorkThread #%d DONE<%s>", serial, desc); } } public void runOnWorkThread(Runnable runnable, String desc) { new WorkThread(runnable, desc).start(); } @Override public boolean deleteFile(String filename) { File dir = getFilesDir(); File f = new File(dir, filename); return f.delete(); // TODO: some regexp. } } public InputStreamReader preludeReader() { InputStream is = getResources().openRawResource(R.raw.prelude); return new InputStreamReader(is); } public void viewPath(String path, String queryStr, Bundle extras, Bundle savedInstanceState) { // Stop any running WorkThread before we continue. viewPath1prepare(path, queryStr); viewPath2parseQuery(queryStr, extras); final LayoutParams widgetParams = new LayoutParams(ViewGroup.LayoutParams.FILL_PARENT, ViewGroup.LayoutParams.FILL_PARENT, 1.0f); TextView splash = new TextView(TerseActivity.this); splash.setText("Launching\n\n" + taPath + "\n\n" + Static.hashMapToMultiLineString(taQuery)); splash.setTextAppearance(this, R.style.teletype); splash.setBackgroundColor(Color.BLACK); splash.setTextColor(Color.DKGRAY); splash.setTextSize(24); Runnable bg = new Runnable() { @Override public void run() { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }; Runnable fg = new Runnable() { @Override public void run() { viewPath9display(taPath, widgetParams); } }; setContentViewThenBgThenFg("viewPath8", splash, bg, fg); } private void viewPath2parseQuery(String queryStr, Bundle extras) { // Url query. taQuery = new HashMap<String, String>(); queryStr = queryStr == null ? "" : queryStr; // Don't be null. String[] parts = queryStr.split("&"); for (String part : parts) { String[] kv = part.split("=", 2); if (kv.length == 2) try { taQuery.put(kv[0], URLDecoder.decode(kv[1], "UTF-8")); } catch (UnsupportedEncodingException e) { // TODO Auto-generated catch block e.printStackTrace(); terp.toss("%s", e); } } if (extras != null) { for (String key : extras.keySet()) { taQuery.put(key, extras.getString(key)); } } } private void viewPath9display(String path, LayoutParams widgetParams) { String explain; if (terp_error != null) { explain = "terp_error = " + terp_error; } else { try { terp.say("Sending to terp: %s", path); final Dict d = terp.handleUrl(path, taQuery); explain = "DEFAULT EXPLANATION:\n\n" + d.toString(); Str TYPE = d.cls.terp.newStr("type"); Str VALUE = d.cls.terp.newStr("value"); Str TITLE = d.cls.terp.newStr("title"); Str type = (Str) d.dict.get(TYPE); Ur value = d.dict.get(VALUE); Ur title = d.dict.get(TITLE); // { // double ticks = Static.floatAt(d, "ticks", -1); // double nanos = Static.floatAt(d, "nanos", -1); // Toast.makeText( // getApplicationContext(), // Static.fmt("%d ticks, %.3f secs", (long) ticks, // (double) nanos / 1e9), Toast.LENGTH_SHORT) // .show(); // } if (type.str.equals("list") && value instanceof Vec) { final ArrayList<Ur> v = ((Vec) value).vec; final ArrayList<String> labels = new ArrayList<String>(); final ArrayList<String> links = new ArrayList<String>(); for (int i = 0; i < v.size(); i++) { Ur item = v.get(i); String label = item instanceof Str ? ((Str) item).str : item.toString(); if (item instanceof Vec && ((Vec) item).vec.size() == 2) { // OLD STYLE label = ((Vec) item).vec.get(0).toString(); Matcher m = LINK_P.matcher(label); if (m.lookingAt()) { label = m.group(2) + " " + m.group(3); } label += " [" + ((Vec) item).vec.get(1).toString().length() + "]"; links.add(null); // Use old style, not links. } else { // NEW STYLE label = item.toString(); if (label.charAt(0) == '/') { String link = Terp.WHITE_PLUS.split(label, 2)[0]; links.add(link); } else { links.add(""); } } labels.add(label); } if (labels.size() != links.size()) terp.toss("lables#%d links#%d", labels.size(), links.size()); ListView listv = new ListView(this); listv.setAdapter(new ArrayAdapter<String>(this, R.layout.list_item, labels)); listv.setLayoutParams(widgetParams); listv.setTextFilterEnabled(true); listv.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { // When clicked, show a toast with the TextView text // Toast.makeText(getApplicationContext(), // ((TextView) view).getText(), // Toast.LENGTH_SHORT).show(); String toast_text = ((TextView) view).getText().toString(); // if (v.get(position) instanceof Vec) { if (links.get(position) == null) { // OLD STYLE Vec pair = (Vec) v.get(position); if (pair.vec.size() == 2) { if (pair.vec.get(0) instanceof Str) { String[] words = ((Str) pair.vec.get(0)).str.split("\\|"); Log.i("TT-WORDS", terp.arrayToString(words)); toast_text += "\n\n" + Static.arrayToString(words); if (words[1].equals("link")) { Uri uri = new Uri.Builder().scheme("terse").path(words[2]).build(); Intent intent = new Intent("android.intent.action.MAIN", uri); intent.setClass(getApplicationContext(), TerseActivity.class); startActivity(intent); } } } } else { // NEW STYLE terp.say("NEW STYLE LIST SELECT #%d link=<%s> label=<%s>", position, links.get(position), labels.get(position)); if (links.get(position).length() > 0) { Uri uri = new Uri.Builder().scheme("terse").path(links.get(position)).build(); Intent intent = new Intent("android.intent.action.MAIN", uri); intent.setClass(getApplicationContext(), TerseActivity.class); startActivity(intent); } } // } // Toast.makeText(getApplicationContext(), // ((TextView) view).getText(), // Toast.LENGTH_SHORT).show(); } }); setContentView(listv); return; } else if (type.str.equals("edit") && value instanceof Str) { final EditText ed = new EditText(this); ed.setText(taSaveMe == null ? value.toString() : taSaveMe); ed.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_VARIATION_LONG_MESSAGE | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); ed.setLayoutParams(widgetParams); // ed.setTextSize(TypedValue.COMPLEX_UNIT_SP, 22); ed.setTextAppearance(this, R.style.teletype); ed.setBackgroundColor(Color.BLACK); ed.setGravity(Gravity.TOP); ed.setScrollBarStyle(View.SCROLLBARS_OUTSIDE_INSET); ed.setVerticalFadingEdgeEnabled(true); ed.setVerticalScrollBarEnabled(true); ed.setOnKeyListener(new OnKeyListener() { public boolean onKey(View v, int keyCode, KeyEvent event) { // If the event is a key-down event on the "enter" // button // if ((event.getAction() == KeyEvent.ACTION_DOWN) // && // (keyCode == KeyEvent.KEYCODE_ENTER)) { // // Perform action on key press // Toast.makeText(TerseActivity.this, ed.getText(), // Toast.LENGTH_SHORT).show(); // return true; // } return false; } }); Button btn = new Button(this); btn.setText("Save"); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { // Perform action on clicks String text = ed.getText().toString(); text = Parser.charSubsts(text); Toast.makeText(TerseActivity.this, text, Toast.LENGTH_SHORT).show(); String action = stringAt(d, "action"); String query = ""; String f1 = stringAt(d, "field1"); String v1 = stringAt(d, "value1"); String f2 = stringAt(d, "field2"); String v2 = stringAt(d, "value2"); f1 = (f1 == null) ? "f1null" : f1; v1 = (v1 == null) ? "v1null" : v1; f2 = (f2 == null) ? "f2null" : f2; v2 = (v2 == null) ? "v2null" : v2; startTerseActivity(action, query, stringAt(d, "field1"), stringAt(d, "value1"), stringAt(d, "field2"), stringAt(d, "value2"), "text", text); } }); LinearLayout linear = new LinearLayout(this); linear.setOrientation(LinearLayout.VERTICAL); linear.addView(btn); linear.addView(ed); setContentView(linear); return; } else if (type.str.equals("draw") && value instanceof Vec) { Vec v = ((Vec) value); DrawView dv = new DrawView(this, v.vec, d); dv.setLayoutParams(widgetParams); setContentView(dv); return; } else if (type.str.equals("live")) { Blk blk = value.mustBlk(); Blk event = Static.urAt(d, "event").asBlk(); TerseSurfView tsv = new TerseSurfView(this, blk, event); setContentView(tsv); return; } else if (type.str.equals("fnord")) { Blk blk = value.mustBlk(); Blk event = Static.urAt(d, "event").asBlk(); FnordView fnord = new FnordView(this, blk, event); setContentView(fnord); return; } else if (type.str.equals("world") && value instanceof Str) { String newWorld = value.toString(); if (Terp.WORLD_P.matcher(newWorld).matches()) { world = newWorld; resetTerp(); explain = Static.fmt("Switching to world <%s>\nUse menu to go Home.", world); Toast.makeText(getApplicationContext(), explain, Toast.LENGTH_LONG).show(); } else { terp.toss("Bad world syntax (must be 3 letters then 0 to 3 digits: <%s>", newWorld); } // Fall thru for explainv.setText(explain). } else if (type.str.equals("text")) { explain = "<<< " + title + " >>>\n\n" + value.toString(); // Fall thru for explainv.setText(explain). } else if (type.str.equals("html")) { final WebView webview = new WebView(this); // webview.loadData(value.toString(), "text/html", null); webview.loadDataWithBaseURL("terse://terse", value.toString(), "text/html", "UTF-8", null); webview.setWebViewClient(new WebViewClient() { @Override public boolean shouldOverrideUrlLoading(WebView view, String url) { // terp.say("WebView UrlLoading: url=%s", url); URI uri = URI.create("" + url); // terp.say("WebView UrlLoading: URI=%s", uri); terp.say("WebView UrlLoading: getPath=%s", uri.getPath()); terp.say("WebView UrlLoading: getQuery=%s", uri.getQuery()); // Toast.makeText(getApplicationContext(), // uri.toASCIIString(), Toast.LENGTH_SHORT) // .show(); // webview.invalidate(); // // TextView quick = new // TextView(TerseActivity.this); // quick.setText(uri.toASCIIString()); // quick.setBackgroundColor(Color.BLACK); // quick.setTextColor(Color.WHITE); // setContentView(quick); startTerseActivity(uri.getPath(), uri.getQuery()); return true; } }); // webview.setWebChromeClient(new WebChromeClient()); webview.getSettings().setBuiltInZoomControls(true); // webview.getSettings().setJavaScriptEnabled(true); webview.getSettings().setDefaultFontSize(18); webview.getSettings().setNeedInitialFocus(true); webview.getSettings().setSupportZoom(true); webview.getSettings().setSaveFormData(true); setContentView(webview); // ScrollView scrollv = new ScrollView(this); // scrollv.addView(webview); // setContentView(scrollv); return; } else { explain = "Unknown page type: " + type.str + " with vaule type: " + value.cls + "\n\n##############\n\n"; explain += value.toString(); // Fall thru for explainv.setText(explain). } } catch (Exception ex) { ex.printStackTrace(); explain = Static.describe(ex); } } TextView explainv = new TextView(this); explainv.setText(explain); explainv.setBackgroundColor(Color.BLACK); explainv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24); explainv.setTextColor(Color.YELLOW); SetContentViewWithHomeButtonAndScroll(explainv); } private void postMortem(Throwable ex) { String explain = Static.describe(ex); TextView explainv = new TextView(this); explainv.setText(explain); explainv.setBackgroundColor(Color.BLACK); explainv.setTextSize(TypedValue.COMPLEX_UNIT_SP, 24); explainv.setTextColor(Color.RED); SetContentViewWithHomeButtonAndScroll(explainv); } private void viewPath1prepare(String path, String queryStr) { try { Thread.sleep(50); terp.runOnWorkThread(null, "null"); Thread.sleep(50); terp.runOnWorkThread(null, "null"); Thread.sleep(50); } catch (InterruptedException e1) { // TODO Auto-generated catch block e1.printStackTrace(); } terp.clearEventQueue(); terp.tickCounter = Integer.MAX_VALUE; // // TRYTHIS if (path.toLowerCase().equals("/reset")) { resetTerp(); path = "/Top"; } this.taPath = path; this.taQueryStr = queryStr; } void SetContentViewWithHomeButtonAndScroll(View v) { Button btn = new Button(this); btn.setText("[HOME]"); // btn.setTextSize(15); // btn.setHeight(25); // btn.setMaxHeight(25); btn.setOnClickListener(new OnClickListener() { public void onClick(View v) { // Perform action on clicks startTerseActivity("/Top", ""); }; }); LinearLayout linear = new LinearLayout(this); linear.setOrientation(LinearLayout.VERTICAL); linear.addView(btn); linear.addView(v); ScrollView scrollv = new ScrollView(this); scrollv.addView(linear); setContentView(scrollv); } @Override public void setContentView(View view) { // view.setOnCreateContextMenuListener(this); super.setContentView(view); } class DrawView extends View { ArrayList<Ur> list; Dict dict; public DrawView(Context context, ArrayList<Ur> list, Dict dict) { super(context); this.list = list; this.dict = dict; } @Override protected void onDraw(Canvas canvas) { Paint green = new Paint(); green.setColor(Color.GREEN); green.setStrokeWidth(2); Paint blue = new Paint(); blue.setColor(Color.BLUE); blue.setStrokeWidth(1); Paint yellow = new Paint(); yellow.setColor(Color.YELLOW); yellow.setStrokeWidth(1); yellow.setTextSize(12); for (int i = 0; i < list.size(); i++) { Ur p = list.get(i); String drawtype = stringAt(p, 0); if (drawtype.equals("line")) { float x1 = floatAt(p, 1); float y1 = floatAt(p, 2); float x2 = floatAt(p, 3); float y2 = floatAt(p, 4); canvas.drawLine(x1, y1, x2, y2, green); } else if (drawtype.equals("rect")) { float x1 = floatAt(p, 1); float y1 = floatAt(p, 2); float x2 = floatAt(p, 3) + x1; float y2 = floatAt(p, 4) + y1; canvas.drawRect(x1, y1, x2, y2, blue); } else if (drawtype.equals("text")) { float x1 = floatAt(p, 1); float y1 = floatAt(p, 2); String text = stringAt(p, 3); canvas.drawText(text, x1, y1, yellow); } } setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); // NOT IN 2.1: int flags = event.getFlags(); float x = event.getX(); float y = event.getY(); terp.say("TOUCH: %s %s %s", action, x, y); if (action == MotionEvent.ACTION_UP) { terp.say("ACTION_UP: %s %s %s", action, x, y); // If they gave us an url, use it. String nextPath = stringAt(dict, "url"); // Otherwise, use the same path. nextPath = nextPath == null ? taPath : nextPath; startTerseActivity(nextPath, taQueryStr, "action", "up", "ex", Float.toString(x), "ey", Float.toString(y)); return true; } return false; } }); setOnClickListener(new OnClickListener() { @Override public void onClick(View v) { terp.say("CLICK"); } }); setOnLongClickListener(new OnLongClickListener() { @Override public boolean onLongClick(View v) { terp.say("LONG CLICK"); taGotLongClick = true; return false; } }); } } public static class Screen extends Obj { AndyTerp aterp; public SurfaceHolder holder; public View view; private Bitmap bm = null; private Canvas canv = null; // =get Screen int width width public int width = 0; // =get Screen int height height public int height = 0; public double firstPost = 0; public double lastPost = 0; public int numPosts = 0; // =cls "draw" Screen Usr public Screen(AndyTerp terp, SurfaceHolder holder, View view, int width, int height) { super(terp.wrapandy.clsScreen); this.aterp = terp; this.holder = holder; this.view = view; this.width = width; this.height = height; } // =meth Screen . newInk: public Ink newInk_(int colorRgbDecimal) { return new Ink(aterp, this, colorRgbDecimal); } public synchronized Canvas canvas() { if (canv == null) { bm = Bitmap.createBitmap(width, height, Bitmap.Config.RGB_565); say("createBitmap w=%d h=%d -> %s", width, height, bm); canv = new Canvas(bm); } return canv; } // =meth Screen "draw" fps public Obj _fps() { if (numPosts > 1) { return aterp.newNum(numPosts / (lastPost - firstPost) * 1000); } else { return aterp.instNil; } } // =meth Screen "draw" post public synchronized void post() { canvas(); // allocate bm & canv, if needed. bm.prepareToDraw(); Canvas locked = holder.lockCanvas(); if (locked != null) { try { locked.drawBitmap(bm, 0, 0, null); if (firstPost == 0) { firstPost = System.currentTimeMillis(); } lastPost = System.currentTimeMillis(); ++numPosts; } finally { holder.unlockCanvasAndPost(locked); view.postInvalidate(); } } else { toss("locked is NULL"); } // Handle Events while (true) { Motion mot = terp.eventQueue.poll(); if (mot == null) break; int action = mot._action(); double x = mot._x(); double y = mot._y(); Vec xy = terp.newVec(urs(terp.newNum(x), terp.newNum(y))); terp.say("MOTION CALLBACK: %s %s --> <%s>", action, xy, mot.block); mot.block.evalWith2Args(terp.newNum(action), xy); } } // =meth Screen "draw" clear: public void clear_(int rgbDecimal) { canvas().drawColor(Ink.colorDecimalToColor(rgbDecimal)); } // =meth ScreenCls "sys" work: // "stop any running WorkThread and start the block arg." public static void work_(Terp terp, Blk block) { final Blk b = block; terp.toss("METHOD work: DISABLED: <%sd>", b); ((AndyTerp) terp).runOnWorkThread(new Runnable() { @Override public void run() { b.evalWithoutArgs(); } }, fmt("evalWithoutArgs[%s]", b)); } } public static class Ink extends Obj { AndyTerp aterp; public Paint paint; // =get Ink . scr scr public Screen scr; // =cls "draw" Ink Usr public Ink(AndyTerp terp, Screen scr, int colorRgbDecimal) { super(terp.wrapandy.clsInk); this.aterp = terp; this.scr = scr; this.paint = new Paint(); this.paint.setColor(colorDecimalToColor(colorRgbDecimal)); } public Canvas canvas() { return scr.canvas(); } // public void setFont(String a) { // Typeface tf = ???; // paint.setTypeface(a); // } // =meth Ink "live" fontsize: public void setFontSize(int a) { paint.setTextSize(a); } // =meth Ink "live" thick: public void setThickness(int pixels) { paint.setStrokeWidth(pixels); } // =meth Ink "live" color: public void setColor(int rgbDecimal) { paint.setColor(colorDecimalToColor(rgbDecimal)); } public static int colorDecimalToColor(int rgbDecimal) { double r = Math.floor(rgbDecimal / 100 % 10 * (255.0 / 9.0)); double g = Math.floor(rgbDecimal / 10 % 10 * (255.0 / 9.0)); double b = Math.floor(rgbDecimal / 1 % 10 * (255.0 / 9.0)); return Color.rgb((int) r, (int) g, (int) b); } // =meth Ink "draw" line:to: public void line_to_(Vec a, Vec b) { canvas().drawLine(floatAt(a, 0), floatAt(a, 1), floatAt(b, 0), floatAt(b, 1), paint); } // =meth Ink "draw" rect:to: public void rect_to_(Vec a, Vec b) { canvas().drawRect(floatAt(a, 0), floatAt(a, 1), floatAt(b, 0), floatAt(b, 1), paint); } // =meth Ink "draw" circ:r: public void circ_r_(Vec a, Num b) { canvas().drawCircle(floatAt(a, 0), floatAt(a, 1), (float) b.num, paint); } // =meth Ink "draw" dot: public void dot_(Vec a) { float[] dots = new float[] { floatAt(a, 0), floatAt(a, 1) }; canvas().drawPoints(dots, paint); } // =meth Ink "draw" dots: public void dots_(Vec a) { final int n = a.vec.size(); final float[] dots = new float[2 * n]; for (int i = 0; i < n; ++i) { dots[2 * i] = floatAt(a.vec.get(i), 0); dots[2 * i + 1] = floatAt(a.vec.get(i), 1); } canvas().drawPoints(dots, paint); } // =meth Ink "draw" text:sw: public void drawText(Str s, Vec b) { canvas().drawText(s.str, floatAt(b, 0), floatAt(b, 1), paint); } } public class FnordView extends GLSurfaceView { // http://developer.android.com/resources/tutorials/opengl/opengl-es10.html public FnordView(Context context, Blk blk, Blk eventBlk) { super(context); // Set the Renderer for drawing on the GLSurfaceView setRenderer(new FnordRenderer()); } public class FnordRenderer implements GLSurfaceView.Renderer { private FloatBuffer triVCB = null; private FloatBuffer axesVCB = null; int width = 0; int height = 0; int frameCount = 0; float angle = 0.0f; FloatBuffer newFloatBuffer(float[] a) { ByteBuffer bb = ByteBuffer.allocateDirect(a.length * 4); bb.order(ByteOrder.nativeOrder()); FloatBuffer zz = bb.asFloatBuffer(); zz.put(a); zz.position(0); return zz; } private void initShapes() { float triangleCoords[] = { // X, Y, Z 0.1f, 0.1f, 0, /**/ 0.3f, 0.3f, 0, 1, 0.1f, 0.9f, 0, /**/ 0.3f, 0.3f, 0, 1, 0.9f, 0.1f, 0, /**/ 0.3f, 0.3f, 0, 1, // X, Y, Z 0, 0.1f, 0.1f, /**/ 0, 0.3f, 0.3f, 1, 0, 0.9f, 0.1f, /**/ 0, 0.3f, 0.3f, 1, 0, 0.1f, 0.9f, /**/ 0, 0.3f, 0.3f, 1, // X, Y, Z 0.1f, 0, 0.1f, /**/ 0.3f, 0, 0.3f, 1, 0.1f, 0, 0.9f, /**/ 0.3f, 0, 0.3f, 1, 0.9f, 0, 0.1f, /**/ 0.3f, 0, 0.3f, 1, };// float refVertexAndColor[] = { 0, 0, 0, /**/ 1, 0, 0, 1, // X axis, red. 1, 0, 0, /**/ 1, 0, 0, 1, // 0, 0, 0, /**/ 0, 1, 0, 1, // Y axis, green. 0, 1, 0, /**/ 0, 1, 0, 1, // 0, 0, 0, /**/ 0, 0, 1, 1, // Z axis, blue. 0, 0, 1, /**/ 0, 0, 1, 1// }; triVCB = newFloatBuffer(triangleCoords); axesVCB = newFloatBuffer(refVertexAndColor); } public void onSurfaceCreated(GL10 gl, EGLConfig config) { try { terp.say("FNORD onSurfaceCreated"); TerseActivity.this.glSurfaceView = FnordView.this; gl.glEnable(GL10.GL_AMBIENT_AND_DIFFUSE); // // Set the background frame color gl.glClearColor(0.4f, 0.2f, 0.4f, 1.0f); // initialize the triangle vertex array initShapes(); // Enable use of vertex arrays gl.glEnableClientState(GL10.GL_VERTEX_ARRAY); } catch (Exception e) { terp.say("FnordRenderer::onSurfaceCreated CAUGHT %s", Static.describe(e)); postMortem(e); } } public void onDrawFrame(GL10 gl) { try { // TODO if (frameCount < 5) { terp.say("FNORD onDrawFrame #" + frameCount); } /////////terp.say("bomb %d", 100 / frameCount); ++frameCount; // Redraw background color gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT); gl.glViewport(0, 0, width, height); gl.glMatrixMode(GL10.GL_PROJECTION); gl.glLoadIdentity(); float h_over_w = height / width; gl.glFrustumf(-1, 1, -h_over_w, h_over_w, -1, 1); gl.glMatrixMode(GL10.GL_MODELVIEW); gl.glLoadIdentity(); gl.glEnable(GL10.GL_DEPTH_TEST); gl.glCullFace(GL10.GL_FRONT_AND_BACK); angle = angle + 0.2f; gl.glRotatef(angle, 1, 0, 0); gl.glRotatef(angle / 3, 0, 1, 0); gl.glRotatef(angle / 10, 0, 0, 1); int stride = 4 /*bytes per float*/ * (3 + 4) /*floats per vertex*/; gl.glEnableClientState(GL10.GL_COLOR_ARRAY); triVCB.position(0); gl.glVertexPointer(3, GL10.GL_FLOAT, stride, triVCB); triVCB.position(3); gl.glColorPointer(4, GL10.GL_FLOAT, stride, triVCB); gl.glDrawArrays(GL10.GL_TRIANGLES, 0, 9); gl.glEnableClientState(GL10.GL_COLOR_ARRAY); axesVCB.position(0); gl.glVertexPointer(3, GL10.GL_FLOAT, stride, axesVCB); axesVCB.position(3); gl.glColorPointer(4, GL10.GL_FLOAT, stride, axesVCB); gl.glDrawArrays(GL10.GL_LINES, 0, 6); } catch (Exception e) { terp.say("FnordRenderer::onDrawFrame CAUGHT %s", Static.describe(e)); postMortem(e); } } public void onSurfaceChanged(GL10 gl, int width, int height) { try { terp.say("FNORD onSurfaceChanged(", width, ",", height, ")"); this.width = width; this.height = height; gl.glViewport(0, 0, width, height); } catch (Exception e) { terp.say("FnordRenderer::onSurfaceChanged CAUGHT %s", Static.describe(e)); postMortem(e); } } } } public class TerseSurfView extends SurfaceView implements Callback { public SurfaceHolder holder0; public Context context; public boolean created; public Blk blk; public Blk eventBlk; public Thread thread; public TerseSurfView(Context context, Blk blk, Blk eventBlk) { super(context); this.context = context; this.created = false; this.blk = blk; this.eventBlk = eventBlk; this.holder0 = this.getHolder(); holder0.addCallback(this); defineOnTouch(); } @Override public void onDraw(Canvas canvas) { super.onDraw(canvas); defineOnTouch(); } private void defineOnTouch() { // terp.say("eventBlk == %s", eventBlk); if (eventBlk != null) { // terp.say("Defining OnTouch ======="); setOnTouchListener(new OnTouchListener() { @Override public boolean onTouch(View v, MotionEvent event) { int action = event.getAction(); if (action == MotionEvent.ACTION_DOWN || action == MotionEvent.ACTION_MOVE) { Motion mot = new Motion(terp, TerseSurfView.this, event, eventBlk); boolean ok = terp.eventQueue.offer(mot); if (!ok) { terp.say("eventQ.offer refused"); } return true; } return false; } }); } } @Override public void surfaceChanged(final SurfaceHolder holder, int arg1, final int width, final int height) { terp.say("surfaceChanged <%d w=%d h=%d>", arg1, width, height); terp.say(".............. <getLeft=%d getTop=%d>", getLeft(), getTop()); terp.runOnWorkThread(new Runnable() { @Override public void run() { Screen scr = new Screen(((AndyTerp) blk.terp()), holder, TerseSurfView.this, width, height); blk.evalWith1Arg(scr); } }, terp.fmt("evalWith1Arg[%s](screen)", blk)); terp.say("started thread <%d w=%d h=%d> holder=%s", arg1, width, height, holder); } @Override public void surfaceCreated(final SurfaceHolder holder) { this.created = true; // this.holder = holder; terp.say("surfaceCreated %s %s", this, holder); } @Override public void surfaceDestroyed(SurfaceHolder holder) { this.created = false; // this.holder = holder; terp.say("surfaceDestroyed %s %s", this, holder); } } void startTerseActivity(String actPath, String actQuery, String... extrasKV) { Uri uri = new Uri.Builder().scheme("terse").path(actPath).encodedQuery(actQuery).build(); Intent intent = new Intent("android.intent.action.MAIN", uri); intent.setClass(getApplicationContext(), TerseActivity.class); for (int i = 0; i < extrasKV.length; i += 2) { intent.putExtra(extrasKV[i], extrasKV[i + 1]); } startActivity(intent); } static String stringAt(Ur p, String i) { return Static.stringAt(p, i); } static String stringAt(Ur p, int i) { return Static.stringAt(p, i); } static float floatAt(Ur p, int i) { return Static.floatAt(p, i); } @Override public boolean onCreateOptionsMenu(Menu menu) { MenuInflater inflater = getMenuInflater(); inflater.inflate(R.menu.main_menu, menu); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { // Handle item selection switch (item.getItemId()) { case R.id.top_menu: startTerseActivity("/", ""); return true; case R.id.help_menu: startTerseActivity("/Help", ""); return true; case R.id.saidWhat_menu: startTerseActivity("/SaidWhat", ""); return true; case R.id.crash_menu: // "UserRequestedCrashError".charAt(666); throw new Error("UserRequestedCrashError"); case R.id.forget_menu: try { FileOutputStream fos = terp.openFileWrite(terp.worldFilename); fos.close(); } catch (FileNotFoundException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } resetTerp(); startTerseActivity("/", ""); return true; default: return super.onOptionsItemSelected(item); } } }