import android.view.*; import android.widget.*; import android.content.Context; import android.os.AsyncTask; import android.support.v4.app.Fragment; import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentManager; import java.io.*; import java.lang.reflect.Method; import java.net.*; import dalvik.system.DexClassLoader; public class main { public static View main(final FragmentActivity context) { ScrollView sv = new ScrollView(context); LinearLayout ll = new LinearLayout(context); ll.setOrientation(LinearLayout.VERTICAL); LinearLayout line1 = new LinearLayout(context); TextView label = new TextView(context); label.setText("Run which snippet ID?"); line1.addView(label); final EditText tv = new EditText(context); tv.setText("675"); line1.addView(tv); Button btnGo = new Button(context); btnGo.setText("Go"); line1.addView(btnGo); ll.addView(line1); btnGo.setOnClickListener(new View.OnClickListener() { public void onClick(View v) { String snippetID = tv.getText().toString(); String text = "Running: " + snippetID; Toast.makeText(context, text, 2000).show(); new DynamicRunner(context).run(snippetID); } }); sv.addView(ll); return sv; } } class DynamicRunner { private static final String mainClassName = "main" /*"test"*/; private static final String mainMethodName = "main" /*"run"*/; private final FragmentActivity activity; StringBuffer info = new StringBuffer(); public DynamicRunner(FragmentActivity activity) { this.activity = activity; } public void run(String snippetID) { try { URL url = new URL( /*"http://tinybrain.de/downloads/test.dex"*/ "http://tinybrain.de:8080/dexcompile.php?id=" + snippetID); new DownloadFilesTask().execute(url); } catch (Throwable e) { showError(e); } } static byte[] DEX_FILE_MAGIC = { 0x64, 0x65, 0x78, 0x0a, 0x30, 0x33, 0x35, 0x00 }; private Class runDynamic(URL url) throws IOException, ClassNotFoundException { info.append("Loading dex: " + url.toString() + "\n"); byte[] dexData = loadBinaryPage(url.openConnection()); if (!isDex(dexData)) throw new RuntimeException("Dex generation error: " + dexData.length + " bytes - " + new String(dexData, "UTF-8")); info.append("Dex data length: " + dexData.length + "\n"); File dexDir = activity.getDir("dex", Context.MODE_PRIVATE); File dexFile; do { dexFile = new File(dexDir, System.currentTimeMillis() + ".dex"); } while (dexFile.exists()); File dexOutputDir; do { dexOutputDir = activity.getDir("dex-optimized-" + System.currentTimeMillis(), Context.MODE_PRIVATE); } while (dexOutputDir.list().length != 0); FileOutputStream fileOutputStream = new FileOutputStream(dexFile); fileOutputStream.write(dexData); fileOutputStream.close(); ClassLoader parentClassLoader = //ClassLoader.getSystemClassLoader(); // does not find support jar //getClass().getClassLoader(); // Let's try this... getClass().getClassLoader().getParent(); // XXX ! DexClassLoader classLoader = new DexClassLoader(dexFile.getAbsolutePath(), dexOutputDir.getAbsolutePath(), null, parentClassLoader); Class theClass = classLoader.loadClass(mainClassName); return theClass; } private boolean isDex(byte[] dexData) { if (dexData.length < DEX_FILE_MAGIC.length) return false; for (int i = 0; i < DEX_FILE_MAGIC.length; i++) if (dexData[i] != DEX_FILE_MAGIC[i]) return false; return true; } private void showError(Throwable e) { ScrollView scrollView = new ScrollView(activity); TextView errorView = new TextView(activity); errorView.setText(info + "\n" + getStackTrace(e)); scrollView.addView(errorView); activity.setContentView(scrollView); } public static String getStackTrace(Throwable throwable) { StringWriter writer = new StringWriter(); throwable.printStackTrace(new PrintWriter(writer)); return writer.toString(); } private class DownloadFilesTask extends AsyncTask> { volatile Throwable error; URL url; protected Class doInBackground(URL... urls) { try { url = urls[0]; return runDynamic(url); } catch (Throwable e) { error = e; return null; } } protected void onProgressUpdate(Integer... progress) { //setProgressPercent(progress[0]); } protected void onPostExecute(Class theClass) { try { if (error != null) showError(error); else if (theClass != null) { Method run = findMainMethod(theClass); Object result = run.invoke(null, activity); if (result instanceof Fragment) { FragmentManager fm = activity.getSupportFragmentManager(); android.support.v4.app.Fragment fragment = fm.findFragmentByTag("mainFragment"); if (fragment == null) { android.support.v4.app.FragmentTransaction ft = fm.beginTransaction(); fragment = (Fragment) result; ft.add(android.R.id.content, fragment, "mainFragment"); ft.commit(); } } else if (result instanceof View) { activity.setContentView((View) result); } else showError(new Throwable("Bad return value from " + url + ", expected View or Fragment: " + result)); } else showError(new Throwable("What?")); } catch (Throwable e) { showError(e); } } } static Method findMainMethod(Class theClass) { for (Method method : theClass.getMethods()) if (method.getName().equals(mainMethodName) && method.getParameterTypes().length == 1) return method; throw new RuntimeException("Method " + mainMethodName + " with 1 paramter not found in " + theClass.getName()); } public static byte[] loadBinaryPage(URLConnection con) throws IOException { //setHeaders(con); ByteArrayOutputStream buf = new ByteArrayOutputStream(); InputStream inputStream = con.getInputStream(); while (true) { int ch = inputStream.read(); if (ch < 0) break; buf.write(ch); } inputStream.close(); return buf.toByteArray(); } }