Libraryless. Click here for Pure Java version (5338L/37K/129K).
1 | !7 |
2 | |
3 | set flag NoAWT. set flag Android. set flag AndroidOnly. |
4 | |
5 | import java.util.*; |
6 | import java.util.zip.*; |
7 | import java.util.List; |
8 | import java.util.regex.*; |
9 | import java.util.concurrent.*; |
10 | import java.util.concurrent.atomic.*; |
11 | import java.io.*; |
12 | import java.net.*; |
13 | import java.lang.reflect.*; |
14 | import java.lang.ref.*; |
15 | import java.lang.management.*; |
16 | import java.security.*; |
17 | import java.security.spec.*; |
18 | import java.math.*; |
19 | |
20 | import android.widget.*; |
21 | import android.view.*; |
22 | import android.view.View; |
23 | import android.view.Menu; |
24 | import android.view.MenuItem; |
25 | import android.widget.Button; |
26 | import android.content.Context; |
27 | import android.app.Activity; |
28 | import android.view.KeyEvent; |
29 | import android.view.inputmethod.*; |
30 | import android.content.*; |
31 | import android.text.*; |
32 | |
33 | public class main { |
34 | sS awarenessID = #1004078, catEarID = #1014854; |
35 | |
36 | static volatile Class x30; |
37 | static volatile bool x30loading, x30loadingFailed; |
38 | |
39 | static List<Prog> programsStarted = new ArrayList<Prog>(); |
40 | static File defSnip, defargs; |
41 | static volatile Class visibleProgram; |
42 | sbool dontSwitchToRunningProgram; |
43 | static ReliableSingleThread snippetTitleFinder; |
44 | |
45 | static class Prog { |
46 | String snippetID; |
47 | Thread thread; |
48 | } |
49 | |
50 | static class Lg { |
51 | static int maxBufferLength = 2048; |
52 | |
53 | Activity context; |
54 | ScrollView sv; |
55 | TextView tv; |
56 | EditText inputView; |
57 | LinearLayout ll; |
58 | StringBuffer buf = new StringBuffer(); |
59 | |
60 | Lg(Activity context) { |
61 | this.context = context; |
62 | sv = new ScrollView(context); |
63 | tv = new TextView(context); |
64 | tv.setText(buf.toString()); |
65 | sv.addView(tv); |
66 | sv.setLayoutParams(new LinearLayout.LayoutParams( |
67 | LinearLayout.LayoutParams.MATCH_PARENT, |
68 | LinearLayout.LayoutParams.MATCH_PARENT, |
69 | 1.0f)); |
70 | inputView = new EditText(context); |
71 | inputView.setInputType(InputType.TYPE_CLASS_TEXT); // turn off multiline |
72 | inputView.setOnEditorActionListener(new TextView.OnEditorActionListener() { |
73 | public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { |
74 | if ((event != null && (event.getKeyCode() == KeyEvent.KEYCODE_ENTER)) || (actionId == EditorInfo.IME_ACTION_DONE)) { |
75 | String line = inputView.getText().toString(); |
76 | try { |
77 | pout3.write((line + "\n").getBytes("UTF-8")); |
78 | pout3.flush(); |
79 | } catch (Exception e) {} |
80 | inputView.selectAll(); |
81 | } |
82 | return false; |
83 | } |
84 | }); |
85 | ll = new LinearLayout(context); |
86 | ll.setOrientation(ll.VERTICAL); |
87 | ll.addView(sv); |
88 | ll.addView(inputView); |
89 | } |
90 | |
91 | View getView() { |
92 | return ll; |
93 | } |
94 | |
95 | void shortenBuffer() { |
96 | while (buf.length() > maxBufferLength) { |
97 | String s = buf.toString(); |
98 | int i = s.indexOf('\n'); |
99 | if (i < 0) return; |
100 | buf = new StringBuffer(s.substring(i+1)); |
101 | } |
102 | } |
103 | |
104 | // TODO: coalesce |
105 | void print(final String s) { |
106 | androidUI_noWait(r { |
107 | buf.append(s); |
108 | shortenBuffer(); |
109 | tv.setText(buf.toString()); |
110 | |
111 | sv.post(new Runnable() { |
112 | public void run() { |
113 | // This method works but animates the scrolling |
114 | // which looks weird on first load |
115 | sv.fullScroll(View.FOCUS_DOWN); |
116 | |
117 | // This method works even better because there are no animations. |
118 | //sv.scrollTo(0, sv.getBottom()); |
119 | } |
120 | }); |
121 | }); |
122 | } |
123 | |
124 | void println(String s) { |
125 | print(s + "\n"); |
126 | } |
127 | } |
128 | |
129 | public static String snippetToRun; |
130 | static TextView statusView; |
131 | |
132 | public static View main(final Context _context) throws Exception { |
133 | final Activity context = _context instanceof Activity ? (Activity) _context : null; |
134 | |
135 | visibleProgram = null; |
136 | |
137 | //System.out.println("676 context: " + _context); |
138 | androidContext = _context; |
139 | |
140 | if (context == null) { // I guess this is for boot time execution |
141 | if (snippetToRun != null) { |
142 | new Thread() { |
143 | public void run() { |
144 | String[] a = { snippetToRun }; |
145 | callx30(a); |
146 | } |
147 | }.start(); |
148 | } |
149 | return null; |
150 | } |
151 | |
152 | statusView = aTextView(); |
153 | preloadx30(); |
154 | while (x30 == null && !x30loading && !x30loadingFailed) sleep(10); |
155 | |
156 | int nInjections = l(injections()); |
157 | if (dontSwitchToRunningProgram) |
158 | dontSwitchToRunningProgram = false; |
159 | else if (nInjections > 0) pcall { |
160 | ret getRunningProgramView(nInjections-1); |
161 | } |
162 | |
163 | prepareOptionsMenu(); |
164 | |
165 | defSnip = new File(userHome(), ".javax/defsnip"); |
166 | String id = loadTextFile(defSnip.getPath(), "636"); |
167 | defargs = new File(userHome(), ".javax/defargs"); |
168 | String arg = loadTextFile(defargs.getPath(), ""); |
169 | |
170 | ScrollView sv = new ScrollView(context); |
171 | LinearLayout ll = new LinearLayout(context); |
172 | ll.setOrientation(LinearLayout.VERTICAL); |
173 | |
174 | ll.addView(statusView); |
175 | |
176 | for i to nInjections: { |
177 | final int _i = i; |
178 | ll.addView(aButton("Back to running program " + (i+1), r { showRunningProgram(_i) })); |
179 | } |
180 | for i to nInjections: { |
181 | final int _i = i; |
182 | ll.addView(aButton("Kill running program " + (i+1), r { killRunningProgram(_i) })); |
183 | } |
184 | |
185 | LinearLayout line1 = new LinearLayout(context); |
186 | line1.addView(aTextView("Snippet ID:")); |
187 | |
188 | final EditText tv = new EditText(context); |
189 | // BAD - tv.setInputType(InputType.TYPE_NUMBER_FLAG_DECIMAL); |
190 | tv.setInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_CLASS_NUMBER); |
191 | tv.setText(id); |
192 | line1.addView(tv); |
193 | |
194 | snippetTitleFinder = new ReliableSingleThread(r { |
195 | S id = aGetText(tv); |
196 | if (isSnippetID(id)) |
197 | aSetText(statusView, snippetTitle(id)); |
198 | }); |
199 | |
200 | aOnChange(tv, snippetTitleFinder); |
201 | |
202 | Button btnRun = new Button(context); |
203 | btnRun.setText("Run"); |
204 | line1.addView(btnRun); |
205 | |
206 | ll.addView(line1); |
207 | |
208 | LinearLayout line2 = new LinearLayout(context); |
209 | |
210 | line2.addView(aTextView("Program arguments:")); |
211 | |
212 | final EditText tvArgs = new EditText(context); |
213 | tvArgs.setText(arg); |
214 | line2.addView(tvArgs); |
215 | |
216 | ll.addView(line2); |
217 | |
218 | LinearLayout line3 = new LinearLayout(context); |
219 | |
220 | /*Button btnLeo = new Button(context); |
221 | btnLeo.setText("Sprechen mit Leo"); |
222 | line3.addView(btnLeo);*/ |
223 | |
224 | line3.addView(aButton("Cat Ear", r { |
225 | main.run(context, catEarID, new String[0]); |
226 | })); |
227 | |
228 | line3.addView(aButton("Start Awareness [" + awarenessID + "]", r { |
229 | saveDef(awarenessID, ""); |
230 | main.run(context, awarenessID, new String[0]); |
231 | })); |
232 | |
233 | ll.addView(line3); |
234 | |
235 | btnRun.setOnClickListener(new View.OnClickListener() { |
236 | public void onClick(View v) { |
237 | hideKeyboard(context); |
238 | String snippetID = tv.getText().toString(); |
239 | String text = "Running: " + snippetID; |
240 | Toast.makeText(context, text, 2000).show(); |
241 | |
242 | saveDef(snippetID, tvArgs.getText().toString()); |
243 | |
244 | // TODO: quoted args |
245 | run(context, snippetID, tvArgs.getText().toString().split(" +")); |
246 | } |
247 | }); |
248 | |
249 | // Show initial status for 1 second, then snippet title |
250 | aLater(1000, snippetTitleFinder); |
251 | |
252 | sv.addView(ll); |
253 | return sv; |
254 | } |
255 | |
256 | static void saveDef(String snippetID, String args) { |
257 | try { |
258 | saveTextFile(defSnip.getPath(), snippetID); |
259 | saveTextFile(defargs.getPath(), args); |
260 | } catch (IOException e) { |
261 | } |
262 | } |
263 | |
264 | static Lg lg; |
265 | |
266 | static int systemInPipeSize = 4*1024; // 4 K |
267 | static PipedInputStream pin3; |
268 | static PipedOutputStream pout3; |
269 | |
270 | public static void run(final Activity context, final String mainSnippet, final String[] args) { |
271 | try { |
272 | pin3 = new PipedInputStream(systemInPipeSize); |
273 | pout3 = new PipedOutputStream(pin3); |
274 | } catch (IOException e) {} |
275 | |
276 | lg = new Lg(context); |
277 | |
278 | OutputStream outputStream = new OutputStream() { |
279 | public void write(int b) { |
280 | try { |
281 | lg.print(new String(new byte[] {(byte) b}, "UTF-8")); // This is crap |
282 | } catch (UnsupportedEncodingException e) {} |
283 | } |
284 | |
285 | @Override |
286 | public void write(byte[] b, int off, int len) { |
287 | try { |
288 | lg.print(new String(b, off, len, "UTF-8")); // This is crap |
289 | } catch (UnsupportedEncodingException e) {} |
290 | } |
291 | }; |
292 | |
293 | PrintStream ps = new PrintStream(outputStream, true); |
294 | System.setOut(ps); |
295 | System.setErr(ps); |
296 | System.setIn(pin3); |
297 | |
298 | Prog prog = new Prog(); |
299 | prog.snippetID = mainSnippet; |
300 | |
301 | prog.thread = new Thread() { |
302 | public void run() { |
303 | try { |
304 | String[] a = new String[args.length+1]; |
305 | System.arraycopy(args, 0, a, 1, args.length); |
306 | a[0] = mainSnippet; |
307 | callx30(a); |
308 | } catch (Throwable e) { |
309 | System.out.println("Whoa!"); |
310 | e.printStackTrace(); |
311 | } |
312 | } |
313 | }; |
314 | |
315 | programsStarted.add(prog); |
316 | prog.thread.start(); |
317 | |
318 | context.setContentView(lg.getView()); |
319 | } |
320 | |
321 | static void setContentViewInUIThread(final View view) { |
322 | androidUI(r { |
323 | androidActivity().setContentView(view); |
324 | }); |
325 | } |
326 | |
327 | public static void hideKeyboard(Activity context) { |
328 | View view = context.getCurrentFocus(); |
329 | if (view != null) { |
330 | InputMethodManager inputManager = (InputMethodManager) context.getSystemService(Context.INPUT_METHOD_SERVICE); |
331 | inputManager.hideSoftInputFromWindow(view.getWindowToken(), InputMethodManager.HIDE_NOT_ALWAYS); |
332 | } |
333 | } |
334 | |
335 | static void onActivityResult(int requestCode, int resultCode, Intent data) { |
336 | /* TODO? |
337 | if (x30.mainClass != null) |
338 | x30.call(x30.mainClass, "onActivityResult", requestCode, resultCode, data);*/ |
339 | } |
340 | |
341 | static void onPrepareOptionsPanel(View view, Menu menu) { |
342 | /* TODO |
343 | if (x30.mainClass != null) |
344 | x30.call(x30.mainClass, "onPrepareOptionsPanel", view, menu);*/ |
345 | } |
346 | |
347 | static boolean onOptionsItemSelected(MenuItem item) { |
348 | /* TODO |
349 | if (x30.mainClass != null) |
350 | return (boolean) x30.call(x30.mainClass, "onOptionsItemSelected", item);*/ |
351 | S s = str(item.getTitle()); |
352 | print("Item title: " + quote(s)); |
353 | if (eq(s, "Settings")) |
354 | showStartScreen(); |
355 | else if (eqic(s, "Show Console")) { |
356 | visibleProgram = null; |
357 | androidActivity().setContentView(lg.getView()); |
358 | } |
359 | callOpt(visibleProgram, 'onMenuCommand, s); |
360 | true; |
361 | } |
362 | |
363 | static void callx30(S[] args) { |
364 | while (x30 == null) |
365 | if (x30loadingFailed) fail("x30 load failed"); |
366 | else sleep(10); // guess it's loading |
367 | print("Calling x30 " + join(" ", args)); |
368 | pcall { |
369 | set(x30, "androidContext", androidContext); |
370 | ((ThreadLocal) get(x30, 'onProgramLoad)).set(voidfunc(O paa) { |
371 | visibleProgram = (Class) getOpt(paa, 'mainClass); |
372 | //print("Have visible program: " + visibleProgram); |
373 | }); |
374 | callMain(x30, args); |
375 | L injections = injections(); |
376 | //visibleProgram = (Class) getOpt(last(injections), 'mainClass); // TODO: imprecise if main program tempers with injections |
377 | //print("Have " + l(injections) + " injections, visibleProgram=" + visibleProgram); |
378 | //print("x30 done"); |
379 | } |
380 | } |
381 | |
382 | svoid preloadx30() { thread { getx30(); } } |
383 | |
384 | static void getx30() { |
385 | try { |
386 | S status = ""; |
387 | if (x30 != null) status = "Have x30."; |
388 | else { |
389 | O dataStore = null; |
390 | pcall { |
391 | dataStore = Class.forName("de.tinybrain.javax_allperms.DataStore"); |
392 | } |
393 | if (dataStore != null) { |
394 | x30 = (Class) call(dataStore, "get", "x30"); |
395 | if (x30 != null) status = "Have x30 from data store."; |
396 | } |
397 | if (x30 == null) { |
398 | x30loading = true; |
399 | status("Loading x30"); |
400 | Class x30 = loadx30("#1004182"); |
401 | callOpt(dataStore, "put", "x30", x30); |
402 | setOpt(x30, "bootUpClass", main.class); |
403 | main.x30 = x30; |
404 | status = "x30 loaded."; |
405 | } |
406 | } |
407 | L injections = injections(); |
408 | if (nempty(injections)) |
409 | status += " " + n2(injections, "injection"); |
410 | status(status); |
411 | } catch e { |
412 | printStackTrace(e); |
413 | x30loadingFailed = true; |
414 | } finally { |
415 | x30loading = false; |
416 | } |
417 | } |
418 | |
419 | static L injections() { |
420 | ret (L) callOpt(x30, 'getInjections); |
421 | } |
422 | |
423 | svoid killRunningProgram(int i) { |
424 | try { |
425 | O paa = get(injections(), i); |
426 | if (paa == null) ret; |
427 | call(x30, 'removeInjection, paa); |
428 | showStartScreen(); |
429 | } catch e { |
430 | printStackTrace(e); |
431 | status(str(e)); |
432 | } |
433 | } |
434 | |
435 | svoid showStartScreen() { |
436 | androidUI(r { |
437 | dontSwitchToRunningProgram = true; |
438 | androidActivity().setContentView(main(androidContext())); |
439 | }); |
440 | } |
441 | |
442 | svoid showRunningProgram(int i) { |
443 | View v = getRunningProgramView(i); |
444 | if (v != null) |
445 | setContentViewInUIThread(v); |
446 | } |
447 | |
448 | static View getRunningProgramView(int i) { |
449 | O paa = get(injections(), i); |
450 | if (paa == null) ret null with status("No running program to show"); |
451 | Class mainClass = cast getOpt(paa, 'mainClass); |
452 | View view = cast getOpt(mainClass, 'androidShow_view); |
453 | if (view == null) |
454 | //ret null with status("Program has no view to show"); |
455 | view = lg.getView(); // show log instead |
456 | print("Switched to program " + get(paa, 'progID)); |
457 | visibleProgram = mainClass; |
458 | ViewGroup group = (ViewGroup) view.getParent(); |
459 | if (group != null) |
460 | group.removeView(view); |
461 | ret view; |
462 | } |
463 | |
464 | svoid status(S s) { |
465 | aSetText(statusView, s); |
466 | print(s); |
467 | } |
468 | |
469 | static Class<?> loadx30(String programID) ctex { |
470 | System.out.println("Program ID: " + programID); |
471 | S url = "http://tinybrain.de:8080/dexcompile.php?id=" + parseSnippetID(programID); |
472 | byte[] dexData = loadBinaryPage(url); |
473 | if (!isDex(dexData)) |
474 | throw new RuntimeException("Dex generation error"); |
475 | System.out.println("Dex loaded: " + dexData.length + "b"); |
476 | |
477 | File dexDir = makeAndroidTempDir(); |
478 | File dexFile = new File(dexDir, System.currentTimeMillis() + ".dex"); |
479 | File dexOutputDir = makeAndroidTempDir(); |
480 | |
481 | System.out.println("Saving dex to: " + dexDir.getAbsolutePath()); |
482 | try { |
483 | saveBinaryFile(dexFile.getPath(), dexData); |
484 | } catch (Throwable e) { |
485 | System.out.println("Whoa!"); |
486 | throw new RuntimeException(e); |
487 | } |
488 | |
489 | System.out.println("Getting parent class loader."); |
490 | ClassLoader parentClassLoader = |
491 | main.class.getClassLoader().getParent(); |
492 | |
493 | //System.out.println("Making DexClassLoader."); |
494 | //DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, |
495 | // parentClassLoader); |
496 | Class dcl = Class.forName("dalvik.system.DexClassLoader"); |
497 | Object classLoader = dcl.getConstructors()[0].newInstance(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, |
498 | parentClassLoader); |
499 | |
500 | System.out.println("Loading main class."); |
501 | Class<?> theClass = (Class<?>) call(classLoader, "loadClass", "x30"); |
502 | |
503 | System.out.println("Main class loaded."); |
504 | try { |
505 | set(theClass, "androidContext", androidContext); |
506 | } catch (Throwable e) {} |
507 | |
508 | setVars(theClass, programID); |
509 | hotwire_copyOver(theClass); |
510 | return theClass; |
511 | } |
512 | |
513 | static void setVars(Class<?> theClass, String programID) { |
514 | try { |
515 | set(theClass, "programID", programID); |
516 | } catch (Throwable e) {} |
517 | |
518 | try { |
519 | set(theClass, "__javax", x30); |
520 | } catch (Throwable e) {} |
521 | } |
522 | |
523 | static String getConsoleOutput() { |
524 | ret lg.buf.toString(); |
525 | } |
526 | |
527 | svoid prepareOptionsMenu { |
528 | pcall { |
529 | Menu menu = cast getOpt(androidActivity(), 'optionsMenu); |
530 | if (menu == null) ret; |
531 | if (menu.size() < 2) |
532 | menu.add("Show Console"); |
533 | } |
534 | } |
535 | } |
Began life as a copy of #676
download show line numbers debug dex old transpilations
Travelled to 13 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt
No comments. add comment
Snippet ID: | #1014873 |
Snippet name: | Boot-up code for JavaX/Android (stage 2, JavaX, before logging changes) |
Eternal ID of this version: | #1014873/3 |
Text MD5: | d095c96ea1f225c27fecc6882f154e07 |
Transpilation MD5: | 82a8e459d7faee2ba46c937756307a87 |
Author: | stefan |
Category: | javax |
Type: | JavaX source code (Android) |
Public (visible to everyone): | Yes |
Archived (hidden from active list): | No |
Created/modified: | 2019-05-20 13:59:59 |
Source code size: | 16236 bytes / 535 lines |
Pitched / IR pitched: | No / No |
Views / Downloads: | 377 / 3059 |
Version history: | 2 change(s) |
Referenced in: | [show references] |