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