package net.zhuoweizhang.mcpelauncher.ui; import java.io.*; import java.util.List; import java.util.ArrayList; import android.annotation.TargetApi; import android.app.AlertDialog; import android.app.Dialog; import android.app.ListActivity; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.content.pm.*; import android.net.Uri; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.view.Menu; import android.view.View; import static android.widget.AdapterView.OnItemClickListener; import android.widget.*; import com.ipaulpro.afilechooser.FileChooserActivity; import com.ipaulpro.afilechooser.utils.FileUtils; import static net.zhuoweizhang.mcpelauncher.ui.LauncherActivity.PT_PATCHES_DIR; import net.zhuoweizhang.mcpelauncher.MinecraftVersion; import net.zhuoweizhang.mcpelauncher.PatchManager; import net.zhuoweizhang.mcpelauncher.R; import net.zhuoweizhang.mcpelauncher.Utils; import net.zhuoweizhang.mcpelauncher.patch.*; import net.zhuoweizhang.mcpelauncher.ui.RefreshContentListThread.OnRefreshContentList; import com.mojang.minecraftpe.*; @SuppressWarnings("deprecation") public class ManagePatchesActivity 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 REQUEST_IMPORT_PATCH = 212; private Thread refreshThread; private List<ContentListItem> patches; private ContentListItem selectedPatchItem; private Button importButton; private byte[] libBytes = null; private boolean prePatchConfigure = true; protected CompoundButton master = null; /** 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); prePatchConfigure = getIntent().getBooleanExtra("prePatchConfigure", true); PatchUtils.minecraftVersion = MinecraftVersion.get(this); } @Override protected void onStart() { super.onStart(); findPatches(); } @Override protected void onPause() { super.onPause(); libBytes = null; } public void onClick(View v) { if (v.equals(importButton)) { importPatch(); } } public void importPatch() { Intent target = FileUtils.createGetContentIntent(); target.setType("application/x-ptpatch"); target.setClass(this, FileChooserActivity.class); 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 void setPatchListModified() { setResult(RESULT_OK); Utils.getPrefs(1).edit().putBoolean("force_prepatch", true).apply(); } @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 = new File(getDir(PT_PATCHES_DIR, 0), file.getName()); PatchUtils.copy(file, to); PatchManager.getPatchManager(this).setEnabled(to, false); if (Utils.hasTooManyPatches()) { Toast.makeText(this, R.string.manage_patches_too_many, Toast.LENGTH_SHORT) .show(); } else { PatchManager.getPatchManager(this).setEnabled(to, true); afterPatchToggle(new ContentListItem(to, true)); } setPatchListModified(); findPatches(); } catch (Exception e) { e.printStackTrace(); Toast.makeText(this, R.string.manage_patches_import_error, Toast.LENGTH_LONG) .show(); } } break; } } private void findPatches() { refreshThread = new Thread(new RefreshContentListThread(this, this)); refreshThread.start(); } private void openManagePatchWindow(ContentListItem item) { this.selectedPatchItem = item; if (prePatchConfigure || canLivePatch(item)) { showDialog(item.enabled ? DIALOG_MANAGE_PATCH_CURRENTLY_ENABLED : DIALOG_MANAGE_PATCH_CURRENTLY_DISABLED); } else { Toast.makeText(this, "This patch cannot be disabled in game.", Toast.LENGTH_SHORT) .show(); } } 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(); 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; default: super.onPrepareDialog(dialogId, dialog); } } public void togglePatch(ContentListItem patch) { if (!patch.enabled && Utils.hasTooManyPatches()) { Toast.makeText(this, R.string.manage_patches_too_many, Toast.LENGTH_SHORT).show(); return; } PatchManager.getPatchManager(this).setEnabled(patch.file, !patch.enabled); patch.enabled = !patch.enabled; afterPatchToggle(patch); } private void afterPatchToggle(ContentListItem patch) { if (!isValidPatch(patch)) { PatchManager.getPatchManager(this).setEnabled(patch.file, false); new AlertDialog.Builder(this) .setMessage( getResources().getString(R.string.manage_patches_invalid_patches) + " " + patch.displayName) .setPositiveButton(android.R.string.ok, null).show(); return; } if (prePatchConfigure) { setPatchListModified(); // should really be called requestPrePatch } else if (canLivePatch(patch)) { try { livePatch(patch); Utils.getPrefs(1).edit().putBoolean("force_prepatch", true).apply(); /* defer the prepatch until it is needed next time */ } catch (Exception e) { e.printStackTrace(); } } else { setPatchListModified(); // should really be called requestPrePatch } } public void livePatch(ContentListItem patchItem) throws Exception { ApplicationInfo mcAppInfo = getPackageManager().getApplicationInfo( "com.mojang.minecraftpe", 0); File patched = getDir("patched", 0); File originalLibminecraft = new File(mcAppInfo.nativeLibraryDir + "/libminecraftpe.so"); new File(patched, "libminecraftpe.so"); com.joshuahuelsman.patchtool.PTPatch patch = new com.joshuahuelsman.patchtool.PTPatch(); patch.loadPatch(patchItem.file); boolean enabled = patchItem.enabled; if (enabled) { // patch.applyPatch(MainActivity.minecraftLibBuffer); PatchUtils.patch(MainActivity.minecraftLibBuffer, patch); } else { if (libBytes == null) { libBytes = new byte[(int) originalLibminecraft.length()]; InputStream is = new FileInputStream(originalLibminecraft); is.read(libBytes); is.close(); } PatchUtils.unpatch(MainActivity.minecraftLibBuffer, libBytes, patch); } } public boolean canLivePatch(ContentListItem patch) { try { return PatchUtils.canLivePatch(patch.file); } catch (Exception e) { return false; } } public void deletePatch(ContentListItem patch) throws Exception { patch.enabled = false; if (!prePatchConfigure) { livePatch(patch); Utils.getPrefs(1).edit().putBoolean("force_prepatch", true).apply(); } 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[] options = null; if (enableStatus == -1) { options = new CharSequence[] { this.getResources().getText(R.string.manage_patches_delete), patchInfoStr }; } else { options = new CharSequence[] { this.getResources().getText(R.string.manage_patches_delete), patchInfoStr, (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); findPatches(); } catch (Exception e) { e.printStackTrace(); } } else if (button == 1) { showDialog(DIALOG_PATCH_INFO); } else if (button == 2) { togglePatch(selectedPatchItem); findPatches(); } } }).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 boolean isValidPatch(ContentListItem patch) { if (patch.file.length() < 6) { return false; } return true; } @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) { findPatches(); } else { ((ArrayAdapter<?>) getListAdapter()).clear(); } SharedPreferences.Editor sh = Utils.getPrefs(0).edit(); sh.putBoolean("zz_manage_patches", isChecked); sh.apply(); refreshABToggle(); } }); refreshABToggle(); } else { System.err.println("WTF?"); } return true; } else { return false; } } protected void refreshABToggle() { if ((Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB) && (master != null)) { master.setChecked(Utils.getPrefs(0).getBoolean("zz_manage_patches", true)); } } @Override public void onRefreshComplete(final List<ContentListItem> items) { runOnUiThread(new Runnable() { @Override public void run() { ContentListItem.sort(items); ManagePatchesActivity.this.patches = items; ArrayAdapter<ContentListItem> adapter = new ContentListAdapter( ManagePatchesActivity.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.getAbsolutePath(); allPaths.add(name); } PatchManager.getPatchManager(ManagePatchesActivity.this) .removeDeadEntries(allPaths); } }); } @Override public List<File> getFolders() { List<File> folders = new ArrayList<File>(); folders.add(getDir(PT_PATCHES_DIR, 0)); folders.add(new File(Environment.getExternalStorageDirectory(), "Android/data/com.snowbound.pockettool.free/Patches")); folders.add(new File(Environment.getExternalStorageDirectory(), "Android/data/com.joshuahuelsman.pockettool/Patches")); return folders; } @Override public boolean isEnabled(File f) { PatchManager patchMgr = PatchManager.getPatchManager(ManagePatchesActivity.this); return patchMgr.isEnabled(f); } }