!7 set flag NoAWT. set flag Android. set flag AndroidOnly. import java.util.*; import java.util.zip.*; import java.util.List; import java.util.regex.*; import java.util.concurrent.*; import java.util.concurrent.atomic.*; import java.io.*; import java.net.*; import java.lang.reflect.*; import java.lang.ref.*; import java.lang.management.*; import java.security.*; import java.security.spec.*; import java.math.*; import android.widget.*; import android.view.*; import android.view.View; import android.view.Menu; import android.view.MenuItem; import android.widget.Button; import android.content.Context; import android.app.Activity; import android.view.KeyEvent; import android.view.inputmethod.*; import android.content.*; import android.text.*; public class main { sS awarenessID = #1004078, catEarID = #1014854; static volatile Class x30; static volatile bool x30loading, x30loadingFailed; static List programsStarted = new ArrayList(); static File defSnip, defargs; static volatile Class visibleProgram; sbool dontSwitchToRunningProgram; static ReliableSingleThread snippetTitleFinder; static class Prog { String snippetID; Thread thread; } static class Lg { static int maxBufferLength = 2048; Activity context; ScrollView sv; TextView tv; EditText inputView; LinearLayout ll; StringBuffer buf = new StringBuffer(); Lg(Activity context) { this.context = context; sv = new ScrollView(context); tv = new TextView(context); tv.setText(buf.toString()); sv.addView(tv); sv.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT, 1.0f)); inputView = new EditText(context); inputView.setInputType(InputType.TYPE_CLASS_TEXT); // turn off multiline inputView.setOnEditorActionListener(new TextView.OnEditorActionListener() { public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { String line = inputView.getText().toString(); try { pout3.write((line + "\n").getBytes("UTF-8")); pout3.flush(); } catch (Exception e) {} inputView.selectAll(); } return false; } }); ll = new LinearLayout(context); ll.setOrientation(ll.VERTICAL); ll.addView(sv); ll.addView(inputView); } View getView() { return ll; } void shortenBuffer() { while (buf.length() > maxBufferLength) { String s = buf.toString(); int i = s.indexOf('\n'); if (i < 0) return; buf = new StringBuffer(s.substring(i+1)); } } // TODO: coalesce void print(final String s) { androidUI_noWait(r { buf.append(s); shortenBuffer(); tv.setText(buf.toString()); sv.post(new Runnable() { public void run() { // This method works but animates the scrolling // which looks weird on first load sv.fullScroll(View.FOCUS_DOWN); // This method works even better because there are no animations. //sv.scrollTo(0, sv.getBottom()); } }); }); } void println(String s) { print(s + "\n"); } } public static String snippetToRun; static TextView statusView; public static View main(final Context _context) throws Exception { final Activity context = _context instanceof Activity ? (Activity) _context : null; visibleProgram = null; //System.out.println("676 context: " + _context); androidContext = _context; if (context == null) { // I guess this is for boot time execution if (snippetToRun != null) { new Thread() { public void run() { String[] a = { snippetToRun }; callx30(a); } }.start(); } return null; } statusView = aTextView(); preloadx30(); while (x30 == null && !x30loading && !x30loadingFailed) sleep(10); int nInjections = l(injections()); if (dontSwitchToRunningProgram) dontSwitchToRunningProgram = false; else if (nInjections > 0) pcall { ret getRunningProgramView(nInjections-1); } prepareOptionsMenu(); defSnip = new File(userHome(), ".javax/defsnip"); String id = loadTextFile(defSnip.getPath(), "636"); defargs = new File(userHome(), ".javax/defargs"); String arg = loadTextFile(defargs.getPath(), ""); ScrollView sv = new ScrollView(context); LinearLayout ll = new LinearLayout(context); ll.setOrientation(LinearLayout.VERTICAL); ll.addView(statusView); for i to nInjections: { final int _i = i; ll.addView(aButton("Back to running program " + (i+1), r { showRunningProgram(_i) })); } for i to nInjections: { final int _i = i; ll.addView(aButton("Kill running program " + (i+1), r { killRunningProgram(_i) })); } LinearLayout line1 = new LinearLayout(context); line1.addView(aTextView("Snippet ID:")); final EditText tv = new EditText(context); // BAD - tv.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL); tv.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_CLASS_NUMBER); tv.setText(id); line1.addView(tv); snippetTitleFinder = new ReliableSingleThread(r { S id = aGetText(tv); if (isSnippetID(id)) aSetText(statusView, snippetTitle(id)); }); aOnChange(tv, snippetTitleFinder); Button btnRun = new Button(context); btnRun.setText("Run"); line1.addView(btnRun); ll.addView(line1); LinearLayout line2 = new LinearLayout(context); line2.addView(aTextView("Program arguments:")); final EditText tvArgs = new EditText(context); tvArgs.setText(arg); line2.addView(tvArgs); ll.addView(line2); LinearLayout line3 = new LinearLayout(context); /*Button btnLeo = new Button(context); btnLeo.setText("Sprechen mit Leo"); line3.addView(btnLeo);*/ line3.addView(aButton("Cat Ear", r { main.run(context, catEarID, new String[0]); })); line3.addView(aButton("Start Awareness [" + awarenessID + "]", r { saveDef(awarenessID, ""); main.run(context, awarenessID, new String[0]); })); ll.addView(line3); btnRun.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { hideKeyboard(context); String snippetID = tv.getText().toString(); String text = "Running: " + snippetID; Toast.makeText(context, text, 2000).show(); saveDef(snippetID, tvArgs.getText().toString()); // TODO: quoted args run(context, snippetID, tvArgs.getText().toString().split(" +")); } }); // Show initial status for 1 second, then snippet title aLater(1000, snippetTitleFinder); sv.addView(ll); return sv; } static void saveDef(String snippetID, String args) { try { saveTextFile(defSnip.getPath(), snippetID); saveTextFile(defargs.getPath(), args); } catch (IOException e) { } } static Lg lg; static int systemInPipeSize = 4*1024; // 4 K static PipedInputStream pin3; static PipedOutputStream pout3; public static void run(final Activity context, final String mainSnippet, final String[] args) { try { pin3 = new PipedInputStream(systemInPipeSize); pout3 = new PipedOutputStream(pin3); } catch (IOException e) {} lg = new Lg(context); OutputStream outputStream = new OutputStream() { public void write(int b) { try { lg.print(new String(new byte[] {(byte) b}, "UTF-8")); // This is crap } catch (UnsupportedEncodingException e) {} } @Override public void write(byte[] b, int off, int len) { try { lg.print(new String(b, off, len, "UTF-8")); // This is crap } catch (UnsupportedEncodingException e) {} } }; PrintStream ps = new PrintStream(outputStream, true); System.setOut(ps); System.setErr(ps); System.setIn(pin3); Prog prog = new Prog(); prog.snippetID = mainSnippet; prog.thread = new Thread() { public void run() { try { String[] a = new String[args.length+1]; System.arraycopy(args, 0, a, 1, args.length); a[0] = mainSnippet; callx30(a); } catch (Throwable e) { System.out.println("Whoa!"); e.printStackTrace(); } } }; programsStarted.add(prog); prog.thread.start(); context.setContentView(lg.getView()); } static void setContentViewInUIThread(final View view) { androidUI(r { androidActivity().setContentView(view); }); } public static void hideKeyboard(Activity context) { View view = context.getCurrentFocus(); if (view != null) { InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); } } static void onActivityResult(int requestCode, int resultCode, Intent data) { /* TODO? if (x30.mainClass != null) x30.call(x30.mainClass, "onActivityResult", requestCode, resultCode, data);*/ } static void onPrepareOptionsPanel(View view, Menu menu) { /* TODO if (x30.mainClass != null) x30.call(x30.mainClass, "onPrepareOptionsPanel", view, menu);*/ } static boolean onOptionsItemSelected(MenuItem item) { /* TODO if (x30.mainClass != null) return (boolean) x30.call(x30.mainClass, "onOptionsItemSelected", item);*/ S s = str(item.getTitle()); print("Item title: " + quote(s)); if (eq(s, "Settings")) showStartScreen(); else if (eqic(s, "Show Console")) { visibleProgram = null; androidActivity().setContentView(lg.getView()); } callOpt(visibleProgram, 'onMenuCommand, s); true; } static void callx30(S[] args) { while (x30 == null) if (x30loadingFailed) fail("x30 load failed"); else sleep(10); // guess it's loading print("Calling x30 " + join(" ", args)); pcall { set(x30, "androidContext", androidContext); ((ThreadLocal) get(x30, 'onProgramLoad)).set(voidfunc(O paa) { visibleProgram = (Class) getOpt(paa, 'mainClass); //print("Have visible program: " + visibleProgram); }); callMain(x30, args); L injections = injections(); //visibleProgram = (Class) getOpt(last(injections), 'mainClass); // TODO: imprecise if main program tempers with injections //print("Have " + l(injections) + " injections, visibleProgram=" + visibleProgram); //print("x30 done"); } } svoid preloadx30() { thread { getx30(); } } static void getx30() { try { S status = ""; if (x30 != null) status = "Have x30."; else { O dataStore = null; pcall { dataStore = Class.forName("de.tinybrain.javax_allperms.DataStore"); } if (dataStore != null) { x30 = (Class) call(dataStore, "get", "x30"); if (x30 != null) status = "Have x30 from data store."; } if (x30 == null) { x30loading = true; status("Loading x30"); Class x30 = loadx30("#1004182"); callOpt(dataStore, "put", "x30", x30); setOpt(x30, "bootUpClass", main.class); main.x30 = x30; status = "x30 loaded."; } } L injections = injections(); if (nempty(injections)) status += " " + n2(injections, "injection"); status(status); } catch e { printStackTrace(e); x30loadingFailed = true; } finally { x30loading = false; } } static L injections() { ret (L) callOpt(x30, 'getInjections); } svoid killRunningProgram(int i) { try { O paa = get(injections(), i); if (paa == null) ret; call(x30, 'removeInjection, paa); showStartScreen(); } catch e { printStackTrace(e); status(str(e)); } } svoid showStartScreen() { androidUI(r { dontSwitchToRunningProgram = true; androidActivity().setContentView(main(androidContext())); }); } svoid showRunningProgram(int i) { View v = getRunningProgramView(i); if (v != null) setContentViewInUIThread(v); } static View getRunningProgramView(int i) { O paa = get(injections(), i); if (paa == null) ret null with status("No running program to show"); Class mainClass = cast getOpt(paa, 'mainClass); View view = cast getOpt(mainClass, 'androidShow_view); if (view == null) //ret null with status("Program has no view to show"); view = lg.getView(); // show log instead print("Switched to program " + get(paa, 'progID)); visibleProgram = mainClass; ViewGroup group = (ViewGroup) view.getParent(); if (group != null) group.removeView(view); ret view; } svoid status(S s) { aSetText(statusView, s); print(s); } static Class loadx30(String programID) ctex { System.out.println("Program ID: " + programID); S url = "http://tinybrain.de:8080/dexcompile.php?id=" + parseSnippetID(programID); byte[] dexData = loadBinaryPage(url); if (!isDex(dexData)) throw new RuntimeException("Dex generation error"); System.out.println("Dex loaded: " + dexData.length + "b"); File dexDir = makeAndroidTempDir(); File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex"); File dexOutputDir = makeAndroidTempDir(); System.out.println("Saving dex to: " + dexDir.getAbsolutePath()); try { saveBinaryFile(dexFile.getPath(), dexData); } catch (Throwable e) { System.out.println("Whoa!"); throw new RuntimeException(e); } System.out.println("Getting parent class loader."); ClassLoader parentClassLoader = main.class.getClassLoader().getParent(); //System.out.println("Making DexClassLoader."); //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, // parentClassLoader); Class dcl = Class.forName("dalvik.system.DexClassLoader"); Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, parentClassLoader); System.out.println("Loading main class."); Class theClass = (Class) call(classLoader, "loadClass", "x30"); System.out.println("Main class loaded."); try { set(theClass, "androidContext", androidContext); } catch (Throwable e) {} setVars(theClass, programID); hotwire_copyOver(theClass); return theClass; } static void setVars(Class theClass, String programID) { try { set(theClass, "programID", programID); } catch (Throwable e) {} try { set(theClass, "__javax", x30); } catch (Throwable e) {} } static String getConsoleOutput() { ret lg.buf.toString(); } svoid prepareOptionsMenu { pcall { Menu menu = cast getOpt(androidActivity(), 'optionsMenu); if (menu == null) ret; if (menu.size() < 2) menu.add("Show Console"); } } }