package net.zhuoweizhang.mcpelauncher.ui; import static net.zhuoweizhang.mcpelauncher.ScriptManager.SCRIPTS_DIR; import java.io.ByteArrayOutputStream; import java.io.File; import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStream; import java.io.PrintWriter; import java.io.StringWriter; import java.net.HttpURLConnection; import java.net.MalformedURLException; import java.net.URL; import java.util.ArrayList; import java.util.List; import net.zhuoweizhang.mcpelauncher.CoffeeScriptCompiler; import net.zhuoweizhang.mcpelauncher.MinecraftConstants; import net.zhuoweizhang.mcpelauncher.R; import net.zhuoweizhang.mcpelauncher.ScriptManager; import net.zhuoweizhang.mcpelauncher.Utils; import net.zhuoweizhang.mcpelauncher.patch.PatchUtils; import net.zhuoweizhang.mcpelauncher.ui.RefreshContentListThread.OnRefreshContentList; import org.mozilla.javascript.RhinoException; import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.PackageManager; import android.content.res.Resources; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.preference.PreferenceManager; import android.text.ClipboardManager; import android.util.Base64; import android.view.Menu; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.ArrayAdapter; import android.widget.Button; import android.widget.CompoundButton; import android.widget.EditText; import android.widget.ListView; import android.widget.Toast; import com.ipaulpro.afilechooser.FileChooserActivity; import com.ipaulpro.afilechooser.utils.FileUtils; public class ManageScriptsActivity extends ListActivity implements View.OnClickListener, OnRefreshContentList { private static final int DIALOG_MANAGE_PATCH = 1; private static final int DIALOG_MANAGE_PATCH_CURRENTLY_DISABLED = 2; private static final int DIALOG_MANAGE_PATCH_CURRENTLY_ENABLED = 3; private static final int DIALOG_PATCH_INFO = 4; private static final int DIALOG_IMPORT_SOURCES = 5; private static final int DIALOG_IMPORT_FROM_CFGY = 6; private static final int DIALOG_IMPORT_FROM_URL = 7; private static final int DIALOG_VERSION_INCOMPATIBLE = 8; private static final int DIALOG_IMPORT_FROM_CLIPBOARD = 9; private static final int DIALOG_IMPORT_FROM_CLIPBOARD_CODE = 10; private static final int DIALOG_IMPORT_FROM_INTENT = 11; private static final int REQUEST_IMPORT_PATCH = 212; private static final String[] ALL_SCRIPT_MIMETYPES = {"application/javascript", "text/coffeescript", "text/literate-coffeescript"}; private Thread refreshThread; private List<ContentListItem> patches; private ContentListItem selectedPatchItem; private Button importButton; private String importClipboardName = ""; protected CompoundButton master; /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { Utils.setLanguageOverride(); super.onCreate(savedInstanceState); setResult(RESULT_CANCELED); setContentView(R.layout.manage_patches); ListView lv = getListView(); lv.setTextFilterEnabled(true); lv.setOnItemClickListener(new OnItemClickListener() { public void onItemClick(AdapterView<?> parent, View view, int position, long id) { openManagePatchWindow(patches.get(position)); } }); importButton = (Button) findViewById(R.id.manage_patches_import_button); importButton.setOnClickListener(this); ScriptManager.androidContext = this.getApplicationContext(); //if (!versionIsSupported()) { // showDialog(DIALOG_VERSION_INCOMPATIBLE); //} } @TargetApi(Build.VERSION_CODES.ICE_CREAM_SANDWICH) @Override public boolean onCreateOptionsMenu(Menu menu) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) { getMenuInflater().inflate(R.menu.ab_master, menu); master = (CompoundButton) menu.findItem(R.id.ab_switch_container) .getActionView().findViewById(R.id.ab_switch); if (master != null) { master.setOnCheckedChangeListener(new android.widget.CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (isChecked) { findScripts(); } else { ((ArrayAdapter<?>) getListAdapter()).clear(); } SharedPreferences.Editor sh = PreferenceManager .getDefaultSharedPreferences(ManageScriptsActivity.this).edit(); sh.putBoolean("zz_script_enable", isChecked); sh.apply(); refreshABToggle(); } }); refreshABToggle(); } else { System.err.println("WTF?"); } return true; } else { return false; } } @Override protected void onStart() { super.onStart(); findScripts(); } @Override protected void onPause() { super.onPause(); refreshABToggle(); } @Override protected void onResume() { super.onResume(); refreshABToggle(); } protected void refreshABToggle() { if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) && (master != null)) { SharedPreferences sh = PreferenceManager .getDefaultSharedPreferences(ManageScriptsActivity.this); master.setChecked(sh.getBoolean("zz_script_enable", false)); } } public void onClick(View v) { if (v == importButton) { importPatch(); } } public void importPatch() { showDialog(DIALOG_IMPORT_SOURCES); } public void importPatchFromFile() { Intent target = FileUtils.createGetContentIntent(); target.setType("application/javascript"); target.setClass(this, FileChooserActivity.class); target.putExtra(FileUtils.EXTRA_MIME_TYPES, ALL_SCRIPT_MIMETYPES); target.putExtra(FileUtils.EXTRA_SORT_METHOD, FileUtils.SORT_LAST_MODIFIED); startActivityForResult(target, REQUEST_IMPORT_PATCH); } /** gets the maximum number of patches this application can patch. A negative value indicates unlimited amount. */ protected int getMaxPatchCount() { return this.getResources().getInteger(R.integer.max_num_scripts); } protected void setPatchListModified() { setResult(RESULT_OK); } @Override protected void onActivityResult(int requestCode, int resultCode, Intent data) { switch (requestCode) { case REQUEST_IMPORT_PATCH: if (resultCode == RESULT_OK) { final Uri uri = data.getData(); File file = FileUtils.getFile(uri); try { File to; if (CoffeeScriptCompiler.isCoffeeScript(file)) { to = new File(getDir(SCRIPTS_DIR, 0), CoffeeScriptCompiler.outputName(file.getName())); CoffeeScriptCompiler.compileFile(file, to); } else { to = new File(getDir(SCRIPTS_DIR, 0), file.getName()); PatchUtils.copy(file, to); } ScriptManager.setEnabled(to, false); int maxPatchCount = getMaxPatchCount(); if (Utils.hasTooManyScripts()) { Toast.makeText(this, R.string.script_import_too_many, Toast.LENGTH_SHORT).show(); } else { ScriptManager.setEnabled(to, true); afterPatchToggle(new ContentListItem(to, true)); } setPatchListModified(); findScripts(); } catch (Exception e) { e.printStackTrace(); reportError(e); Toast.makeText(this, R.string.manage_patches_import_error, Toast.LENGTH_LONG).show(); } } break; } } private void findScripts() { refreshThread = new Thread(new RefreshContentListThread(this, this)); refreshThread.start(); } private void openManagePatchWindow(ContentListItem item) { this.selectedPatchItem = item; showDialog(item.enabled? DIALOG_MANAGE_PATCH_CURRENTLY_ENABLED : DIALOG_MANAGE_PATCH_CURRENTLY_DISABLED); } public Dialog onCreateDialog(int dialogId) { switch (dialogId) { case DIALOG_MANAGE_PATCH: return createManagePatchDialog(-1); case DIALOG_MANAGE_PATCH_CURRENTLY_ENABLED: return createManagePatchDialog(1); case DIALOG_MANAGE_PATCH_CURRENTLY_DISABLED: return createManagePatchDialog(0); case DIALOG_PATCH_INFO: return createPatchInfoDialog(); case DIALOG_IMPORT_SOURCES: return createImportSourcesDialog(); case DIALOG_IMPORT_FROM_CFGY: return createImportFromCfgyDialog(); case DIALOG_IMPORT_FROM_URL: return createImportFromUrlDialog(); case DIALOG_VERSION_INCOMPATIBLE: return createVersionIncompatibleDialog(); case DIALOG_IMPORT_FROM_CLIPBOARD: return createImportFromClipboardDialog(); case DIALOG_IMPORT_FROM_CLIPBOARD_CODE: return createImportFromClipboardCodeDialog(); case DIALOG_IMPORT_FROM_INTENT: return createImportFromIntentDialog(); default: return super.onCreateDialog(dialogId); } } public void onPrepareDialog(int dialogId, Dialog dialog) { switch (dialogId) { case DIALOG_MANAGE_PATCH: case DIALOG_MANAGE_PATCH_CURRENTLY_ENABLED: case DIALOG_MANAGE_PATCH_CURRENTLY_DISABLED: AlertDialog aDialog = (AlertDialog) dialog; aDialog.setTitle(selectedPatchItem.toString(getResources())); break; case DIALOG_PATCH_INFO: preparePatchInfo((AlertDialog) dialog, selectedPatchItem); break; case DIALOG_IMPORT_FROM_CLIPBOARD_CODE: AlertDialog bDialog = (AlertDialog) dialog; android.text.ClipboardManager cmgr = (android.text.ClipboardManager) this.getSystemService(Context.CLIPBOARD_SERVICE); CharSequence cSequence = cmgr.getText(); ((EditText) bDialog.findViewById(20130805)).setText(cSequence); break; case DIALOG_IMPORT_FROM_INTENT: AlertDialog cDialog = (AlertDialog) dialog; cDialog.setTitle(getIntent().getData().toString()); break; default: super.onPrepareDialog(dialogId, dialog); } } public void togglePatch(ContentListItem patch) { int maxPatchCount = getMaxPatchCount(); if (!patch.enabled && Utils.hasTooManyScripts()) { Toast.makeText(this, R.string.script_import_too_many, Toast.LENGTH_SHORT).show(); return; } try { ScriptManager.setEnabled(patch.file, !patch.enabled); } catch (Exception e) { e.printStackTrace(); reportScriptLoadError(e); } patch.enabled = !patch.enabled; afterPatchToggle(patch); } private void reportScriptLoadError(Exception e) { reportError(e); } private void afterPatchToggle(ContentListItem patch) { } public void deletePatch(ContentListItem patch) throws Exception { patch.enabled = false; try { ScriptManager.setEnabled(patch.file, false); } catch (Exception e) {e.printStackTrace();} setPatchListModified(); patch.file.delete(); } public void preparePatchInfo(AlertDialog dialog, ContentListItem patch) { dialog.setTitle(patch.toString(getResources())); String patchInfo; try { patchInfo = getPatchInfo(patch); } catch (Exception e) { patchInfo = "Cannot show info: " + e.getStackTrace(); } dialog.setMessage(patchInfo); } private String getPatchInfo(ContentListItem patchItem) throws IOException { StringBuilder builder = new StringBuilder(); builder.append(this.getResources().getString(R.string.manage_patches_path)); builder.append(": "); builder.append(patchItem.file.getAbsolutePath()); builder.append('\n'); /*com.joshuahuelsman.patchtool.PTPatch patch = new com.joshuahuelsman.patchtool.PTPatch(); patch.loadPatch(patchItem.file); String desc = patch.getDescription(); if (desc.length() > 0) { builder.append(this.getResources().getString(R.string.manage_patches_metadata)); builder.append(": "); builder.append(desc); } else { builder.append(this.getResources().getString(R.string.manage_patches_no_metadata)); }*/ return builder.toString(); } /** * @param enableStatus -1 = can't disable, 0 = currently disabled, 1 = currently enabled */ protected AlertDialog createManagePatchDialog(int enableStatus) { CharSequence patchInfoStr = this.getResources().getText(R.string.manage_patches_info); CharSequence viewSourceStr = this.getResources().getText(R.string.script_view_source); CharSequence[] options = null; if (enableStatus == -1) { options = new CharSequence[] {this.getResources().getText(R.string.manage_patches_delete), patchInfoStr, viewSourceStr}; } else { options = new CharSequence[] {this.getResources().getText(R.string.manage_patches_delete), patchInfoStr, viewSourceStr, (enableStatus == 0? this.getResources().getText(R.string.manage_patches_enable): this.getResources().getText(R.string.manage_patches_disable))}; } return new AlertDialog.Builder(this).setTitle("Patch name goes here"). setItems(options, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { if (button == 0) { //selectedPatchItem.file.delete(); try { deletePatch(selectedPatchItem); findScripts(); } catch (Exception e) { e.printStackTrace(); } } else if (button == 1) { showDialog(DIALOG_PATCH_INFO); } else if (button == 2) { viewSource(selectedPatchItem); } else if (button == 3) { togglePatch(selectedPatchItem); findScripts(); } } }).create(); } private AlertDialog createPatchInfoDialog() { return new AlertDialog.Builder(this) .setTitle("Whoops! info fail") //will get filled in by prepare .setMessage("Whoops - try again, this is a tiny fail") .setPositiveButton(android.R.string.ok, null) .create(); } private AlertDialog createImportSourcesDialog() { Resources res = this.getResources(); CharSequence[] options = {res.getString(R.string.script_import_from_local), res.getString(R.string.script_import_from_cfgy), res.getString(R.string.script_import_from_url), res.getString(R.string.script_import_from_clipboard)}; return new AlertDialog.Builder(this).setTitle(R.string.script_import_from). setItems(options, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { if (button == 0) { //selectedPatchItem.file.delete(); importPatchFromFile(); } else if (button == 1) { showDialog(DIALOG_IMPORT_FROM_CFGY); } else if (button == 2) { showDialog(DIALOG_IMPORT_FROM_URL); } else if (button == 3) { showDialog(DIALOG_IMPORT_FROM_CLIPBOARD); } } }).create(); } private AlertDialog createImportFromCfgyDialog() { final EditText view = new EditText(this); return new AlertDialog.Builder(this).setTitle(R.string.script_import_from_cfgy_id). setView(view). setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { importFromCfgy(view.getText().toString()); } }). setNegativeButton(android.R.string.cancel, null). create(); } private AlertDialog createImportFromUrlDialog() { final EditText view = new EditText(this); return new AlertDialog.Builder(this).setTitle(R.string.script_import_from_url_enter). setView(view). setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { importFromUrl(view.getText().toString()); } }). setNegativeButton(android.R.string.cancel, null). create(); } private AlertDialog createVersionIncompatibleDialog() { return new AlertDialog.Builder(this).setMessage(R.string.script_minecraft_version_incompatible). setPositiveButton(android.R.string.ok, null). create(); } private AlertDialog createImportFromClipboardDialog() { final EditText view = new EditText(this); return new AlertDialog.Builder(this).setTitle(R.string.script_import_from_clipboard_name). setView(view). setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { importClipboardName = view.getText().toString(); showDialog(DIALOG_IMPORT_FROM_CLIPBOARD_CODE); } }). setNegativeButton(android.R.string.cancel, null). create(); } private AlertDialog createImportFromClipboardCodeDialog() { final EditText view = new EditText(this); view.setId(20130805); return new AlertDialog.Builder(this).setTitle(R.string.script_import_from_clipboard_code). setView(view). setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { importFromClipboard(view.getText().toString()); } }). setNegativeButton(android.R.string.cancel, null). create(); } private AlertDialog createImportFromIntentDialog() { return new AlertDialog.Builder(this).setTitle("Unable to get script location").setMessage(R.string.script_import_from_intent_warning). setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() { public void onClick(DialogInterface dialogI, int button) { importFromIntent(); } }). setNegativeButton(android.R.string.cancel, null). create(); } private boolean isValidPatch(ContentListItem patch) { if (patch.file.length() < 1) { return false; } return true; } private void importFromCfgy(String id) { ImportScriptFromCfgyTask task = new ImportScriptFromCfgyTask(); task.execute(id); } //the following method and array is adapted from http://modpe.cf.gy/modEditor.html /** * Method to convert a Cfgy ID to a filename for downloading scripts off Treebl's server. * Each cfgy ID is a base 36 number. * Each cfgy number is the base10 representation of the ID with replaced digits. You know, because logic. */ private static String cfgyIdToFilename(String str) { int cfgyID = Integer.parseInt(str, 36); char[] cfgyFilename = Integer.toString(cfgyID).toCharArray(); for (int i = 0; i < cfgyFilename.length; i++) { cfgyFilename[i] = cfgyMappings[(char) ((int)cfgyFilename[i] - 48)]; } String retval = new String(cfgyFilename); System.out.println(retval); return retval; } /** The mapping table used to map between normal digits and Treebl digits. */ private static char[] cfgyMappings = new char[] {'f', 't', 'a', 'm', 'b', 'q', 'g', 'r', 'z', 'o'}; //end adapted private void importFromUrl(String url) { ImportScriptFromUrlTask task = new ImportScriptFromUrlTask(); task.execute(url); } private void reportError(final Throwable t) { this.runOnUiThread(new Runnable() { public void run() { final StringWriter strWriter = new StringWriter(); PrintWriter pWriter = new PrintWriter(strWriter); if (t instanceof RhinoException) { String lineSource = ((RhinoException) t).lineSource(); if (lineSource != null) pWriter.println(lineSource); } t.printStackTrace(pWriter); new AlertDialog.Builder(ManageScriptsActivity.this).setTitle(R.string.manage_patches_import_error).setMessage(strWriter.toString()). setPositiveButton(android.R.string.ok, null). setNeutralButton(android.R.string.copy, new DialogInterface.OnClickListener() { public void onClick(DialogInterface aDialog, int button) { ClipboardManager mgr = (ClipboardManager) getSystemService(CLIPBOARD_SERVICE); mgr.setText(strWriter.toString()); } }). show(); } }); } private boolean versionIsSupported() { try { return getPackageManager().getPackageInfo("com.mojang.minecraftpe", 0).versionCode == MinecraftConstants.MINECRAFT_VERSION_CODE; } catch (PackageManager.NameNotFoundException ex) { return false; //Not possible } } private static URL getCfgyUrl(String id) throws MalformedURLException { id = id.trim(); boolean legacyScript = id.length() >= 6 || id.matches("[a-zA-Z]"); //more than 6 characters or contains non-numeric characters if (legacyScript) { return new URL("http://modpe.cf.gy/mods/" + cfgyIdToFilename(id) + ".js"); } else { return new URL("http://betamodpe2.cf.gy/user/getScr.php?scrid=" + id); } } private void importFromClipboard(String code) { try { File patchesFolder = getDir(SCRIPTS_DIR, 0); File scriptFile = new File(patchesFolder, importClipboardName + ".js"); PrintWriter printWriter = new PrintWriter(scriptFile); printWriter.write(code); printWriter.flush(); printWriter.close(); ScriptManager.setEnabled(scriptFile, false); int maxPatchCount = getMaxPatchCount(); if (Utils.hasTooManyScripts()) { Toast.makeText(ManageScriptsActivity.this, R.string.script_import_too_many, Toast.LENGTH_SHORT).show(); } else { ScriptManager.setEnabled(scriptFile, true); } } catch (Exception e) { e.printStackTrace(); reportError(e); } findScripts(); } private void importFromIntent() { Uri uri = getIntent().getData(); ImportScriptFromIntentTask task = new ImportScriptFromIntentTask(); task.execute(uri); } private void viewSource(ContentListItem item) { try { //copy the script to the sdcard, then send an intent to view it File outDir = new File(this.getExternalFilesDir(null), "scripts"); outDir.mkdirs(); File outFile = new File(outDir, item.file.getName()); PatchUtils.copy(item.file, outFile); Intent intent = new Intent(Intent.ACTION_VIEW); Uri derp = Uri.fromFile(outFile); intent.setDataAndType(derp, "text/plain"); startActivity(intent); } catch (Exception e) { e.printStackTrace(); //on an Android device there is always an activity to handle plain text (HTMLViewer in stock Android) //so this shouldn't be hit } } private abstract class ImportScriptTask<I> extends AsyncTask<I, Void, File> { protected void onPostExecute(File file) { if (file == null) { Toast.makeText(ManageScriptsActivity.this, R.string.manage_patches_import_error, Toast.LENGTH_SHORT).show(); return; } try { ScriptManager.setEnabled(file, false); int maxPatchCount = getMaxPatchCount(); if (Utils.hasTooManyScripts()) { Toast.makeText(ManageScriptsActivity.this, R.string.script_import_too_many, Toast.LENGTH_SHORT).show(); } else { ScriptManager.setEnabled(file, true); } } catch (Exception e) { e.printStackTrace(); reportError(e); Toast.makeText(ManageScriptsActivity.this, R.string.manage_patches_import_error, Toast.LENGTH_SHORT).show(); } findScripts(); } } /** Import a script from Treebl's repo. */ private class ImportScriptFromCfgyTask extends ImportScriptTask<String> { @SuppressWarnings("resource") protected File doInBackground(String... ids) { String id = ids[0]; InputStream is = null; byte[] content = null; int response = 0; FileOutputStream fos = null; File file = new File(getDir(SCRIPTS_DIR, 0), id + ".js"); HttpURLConnection conn; URL url; try { //Hurl the file to a byte array url = getCfgyUrl(id); conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("User-Agent", "BlockLauncher"); conn.setDoInput(true); conn.connect(); try { response = conn.getResponseCode(); is = conn.getInputStream(); } catch (Exception e) { is = conn.getErrorStream(); } if (response >= 400) return null; if (is != null) { content = bytesFromInputStream(is, conn.getContentLength() > 0 ? conn.getContentLength() : 1024); } String contentStr = new String(content).replaceAll(" ", "+"); //WTF, Treebl. //now we have the bytes, let's base64-decode it (Why, treebl?) then write it to a file byte[] decoded = Base64.decode(contentStr, Base64.DEFAULT); fos = new FileOutputStream(file); fos.write(decoded); fos.flush(); return file; } catch (Exception e) { e.printStackTrace(); reportError(e); return null; } finally { if (is != null) { try { is.close(); } catch (Exception e) { } } if (fos != null) { try { fos.close(); } catch (Exception e) { } } } } } /** Import a script from a url. */ private class ImportScriptFromUrlTask extends ImportScriptTask<String> { protected File doInBackground(String... ids) { InputStream is = null; byte[] content = null; int response = 0; FileOutputStream fos = null; File file = null; HttpURLConnection conn; URL url; try { //Hurl the file to a byte array url = new URL(ids[0]); System.out.println(url); String urlPath = url.getPath(); String fileName = urlPath.substring(urlPath.lastIndexOf("/") + 1); if (fileName.length() < 1) fileName = "nameless_script.js"; file = new File(getDir(SCRIPTS_DIR, 0), fileName); conn = (HttpURLConnection) url.openConnection(); conn.setRequestProperty("User-Agent", "BlockLauncher"); conn.setDoInput(true); conn.connect(); try { response = conn.getResponseCode(); is = conn.getInputStream(); } catch (Exception e) { is = conn.getErrorStream(); } if (response >= 400) return null; if (is == null) return null; content = bytesFromInputStream(is, conn.getContentLength() > 0 ? conn.getContentLength() : 1024); //no processing done on URL scripts fos = new FileOutputStream(file); fos.write(content); fos.flush(); return file; } catch (Exception e) { reportError(e); e.printStackTrace(); return null; } finally { if (is != null) { try { is.close(); } catch (Exception e) { } } if (fos != null) { try { fos.close(); } catch (Exception e) { } } } } } private class ImportScriptFromIntentTask extends ImportScriptTask<Uri> { protected File doInBackground(Uri... ids) { Uri data = ids[0]; InputStream is = null; FileOutputStream fos = null; byte[] content; String fileName = data.getLastPathSegment(); File file = new File(getDir(SCRIPTS_DIR, 0), fileName); try { is = getContentResolver().openInputStream(data); content = bytesFromInputStream(is, 1024); //no processing done on URI scripts either fos = new FileOutputStream(file); fos.write(content); fos.flush(); return file; } catch (Exception e) { e.printStackTrace(); return null; } finally { if (is != null) { try { is.close(); } catch (Exception e) {} } if (fos != null) { try { fos.close(); } catch (Exception e) { } } } } } private static byte[] bytesFromInputStream(InputStream in, int startingLength) throws IOException { ByteArrayOutputStream bytes = new ByteArrayOutputStream(startingLength); try { byte[] buffer = new byte[1024]; int count; while ((count = in.read(buffer)) != -1) { bytes.write(buffer, 0, count); } return bytes.toByteArray(); } finally { bytes.close(); } } @Override public void onRefreshComplete(final List<ContentListItem> items) { runOnUiThread(new Runnable() { @Override public void run() { ContentListItem.sort(items); ManageScriptsActivity.this.patches = items; ArrayAdapter<ContentListItem> adapter = new ContentListAdapter(ManageScriptsActivity.this, R.layout.patch_list_item, patches); setListAdapter(adapter); List<String> allPaths = new ArrayList<String>(patches.size()); for (ContentListItem i: patches) { String name = i.file.getName(); allPaths.add(name); } ScriptManager.removeDeadEntries(allPaths); } }); } @Override public List<File> getFolders() { List<File> folders = new ArrayList<File>(); folders.add(getDir(SCRIPTS_DIR, 0)); return folders; } @Override public boolean isEnabled(File f) { return ScriptManager.isEnabled(f); } }