public interface Core extends Serializable {

    public static final String PLUGIN_UI = "UIPlugin";
    public static final String PLUGIN_SUPERUSER = "SuperUserPlugin";
    public static final String PLUGIN_RECOVERY = "RecoveryPlugin";
    public static final String PLUGIN_STORAGE = "StoragePlugin";
    public static final String PLUGIN_REBOOT = "RebootPlugin";
    public static final String PLUGIN_LICENSE = "LicensePlugin";
    public static final String PLUGIN_UPDATE = "UpdatePlugin";

    public interface CoreListener {

        public void pluginStarted(String name);

        public void pluginStopped(String name);

        public void coreMessage(int resId);

        public void coreStarted();

        public void coreStopped();
    }

    public void destroy();

    public boolean isStarted();

    public Context getContext();

    public void setContext(Context context);

    public Plugin getPlugin(String name);

    public Preferences getPreferences();

    public CloudStorage getCloudStorage();

    public void setCloudStorage(int cloudStorage);

    public ONandroid getONandroid();

    public Version getVersion();

    public void moveToInstall();
} public abstract class Plugin {

    private Core mCore;
    private String mName;

    protected Plugin(Core core, String name) {
        mCore = core;
        mName = name;
    }

    public Core getCore() {
        return mCore;
    }

    public String getName() {
        return mName;
    }

    public abstract void start();

    public abstract void stop();

    protected void started() {
        ((CoreImpl) mCore).nextPlugin(true);
    }

    protected void stopped() {
        ((CoreImpl) mCore).nextPlugin(false);
    }
} public abstract class RecoveryInfo {

    public static final int RECOVERY_CWM = 0;
    public static final int RECOVERY_CWM_BASED = 1;
    public static final int RECOVERY_TWRP = 2;
    public static final int RECOVERY_4EXT = 3;

    private Core mCore;
    private boolean mOldBackup = false;
    private int id;
    private String name = null;
    private String internalSdcard = null;
    private String externalSdcard = null;

    public RecoveryInfo(Core core) {

        mCore = core;

        StoragePlugin rPlugin = (StoragePlugin) core.getPlugin(Core.PLUGIN_STORAGE);
        setExternalSdcard(rPlugin.getExternalStoragePath());
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getInternalSdcard() {
        return internalSdcard;
    }

    public void setInternalSdcard(String sdcard) {
        this.internalSdcard = sdcard;
    }

    public String getExternalSdcard() {
        return externalSdcard;
    }

    public void setExternalSdcard(String sdcard) {
        this.externalSdcard = sdcard;
    }

    public abstract String getFullName(Context context);

    public abstract String getFolderPath();

    public abstract String getCommandsFile();

    public abstract String getBackupFolder(String sdcard, boolean force, boolean external);

    public abstract List<String> getCommands(String storage, boolean external, boolean wipeSystem,
            boolean wipeData, boolean wipeCaches, boolean fixPermissions, String backupFolder,
            String backupOptions, String restore) throws Exception;

    protected Core getCore() {
        return mCore;
    }

    public boolean isOldBackup() {
        checkForOldBackup();
        return mOldBackup;
    }

    protected void checkForOldBackup() {
        Preferences prefs = getCore().getPreferences();
        File file = new File("/data/media/clockworkmod/");
        if (prefs.isForceDataMedia()) {
            mOldBackup = true;
        } else {
            mOldBackup = VERSION.SDK_INT >= 16
                    || (file.exists() && file.isDirectory());
        }
    }

} public class RecoveryPlugin extends Plugin {

    private SparseArray<RecoveryInfo> mRecoveries = new SparseArray<RecoveryInfo>();
    private int mInstalledRecovery = -1;
    private String mBootBlock;
    private String mRecoveryBlock;

    public RecoveryPlugin(Core core) {
        super (core, Core.PLUGIN_RECOVERY);
    }

    @Override
    public void start() {
        ((CoreImpl) getCore()).setMessage(R.string.reading_recovery_info);

        (new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {

                Core core = getCore();

                mRecoveries.put(RecoveryInfo.RECOVERY_CWM_BASED, new CwmBasedRecovery(core));
                mRecoveries.put(RecoveryInfo.RECOVERY_CWM, new CwmRecovery(core));
                mRecoveries.put(RecoveryInfo.RECOVERY_TWRP, new TwrpRecovery(core));
                mRecoveries.put(RecoveryInfo.RECOVERY_4EXT, new FourExtRecovery(core));

                SuperUserPlugin suPlugin = (SuperUserPlugin) core.getPlugin(Core.PLUGIN_SUPERUSER);
                suPlugin.run("chmod 777 /cache/recovery/");
                suPlugin.run("chmod 777 /cache/recovery/last_log");

                Preferences prefs = getCore().getPreferences();
                mBootBlock = prefs.getBootBlock();
                mRecoveryBlock = prefs.getRecoveryBlock();

                mInstalledRecovery = testLastLog(new File("/cache/recovery/last_log"), suPlugin, true);

                suPlugin.run("chmod -R 777 /data/media/clockworkmod/");
                suPlugin.run("chmod -R 777 /data/media/clockworkmod/backup/");
                String path = mRecoveries.get(RecoveryInfo.RECOVERY_TWRP).getBackupFolder("sdcard", true,
                        false);
                File file = new File(path);
                suPlugin.run("chmod -R 777 " + file.getAbsolutePath());
                if (file.getParentFile() != null) {
                    suPlugin.run("chmod -R 777 " + file.getParentFile().getAbsolutePath());
                }

                if (mRecoveryBlock != null) {
                    prefs.setRecoveryBlock(mRecoveryBlock);
                }
                if (mBootBlock != null) {
                    prefs.setBootBlock(mBootBlock);
                }

                return (Void) null;
            }

            @Override
            protected void onPostExecute(Void result) {

                Core core = getCore();
                final Preferences prefs = core.getPreferences();
                final Context context = core.getContext();

                if (!prefs.existsRecovery()) {
                    if (mInstalledRecovery != -1) {
                        setRecovery(mInstalledRecovery);
                        started();
                    } else {
                        test(mRecoveries.get(RecoveryInfo.RECOVERY_4EXT), true);
                    }
                } else {
                    if (prefs.isAlertOnChangeRecovery()) {
                        if (mInstalledRecovery != -1 && mInstalledRecovery != getRecovery().getId()) {
                            AlertDialog.Builder alert = new AlertDialog.Builder(context);
                            alert.setTitle(R.string.alert_changed_recovery_title);
                            View view = LayoutInflater.from(context).inflate(R.layout.dialog_changed_recovery,
                                    (ViewGroup) ((Activity) context).findViewById(R.id.changed_recovery_dialog_layout));
                            alert.setView(view);

                            TextView text = (TextView) view.findViewById(R.id.text);
                            text.setText(context.getResources().getString(
                                    R.string.alert_changed_recovery_message,
                                    getRecovery().getFullName(context),
                                    mRecoveries.get(mInstalledRecovery).getFullName(context)));

                            final CheckBox cbDontShow = (CheckBox) view.findViewById(R.id.dontshow);
                            cbDontShow.setChecked(!prefs.isAlertOnChangeRecovery());

                            alert.setPositiveButton(android.R.string.yes, new DialogInterface.OnClickListener() {

                                public void onClick(DialogInterface dialog, int whichButton) {
                                    dialog.dismiss();

                                    setRecovery(mInstalledRecovery);
                                    started();

                                    prefs.setAlertOnChangeRecovery(!cbDontShow.isChecked());
                                }
                            });

                            alert.setNegativeButton(android.R.string.no, new DialogInterface.OnClickListener() {

                                public void onClick(DialogInterface dialog, int whichButton) {
                                    dialog.dismiss();

                                    started();

                                    prefs.setAlertOnChangeRecovery(cbDontShow.isChecked());
                                }
                            });

                            alert.show();
                        } else {
                            started();
                        }
                    } else {
                        started();
                    }
                }

            }
        }).execute((Void) null);

    }

    @Override
    public void stop() {
        stopped();
    }

    public boolean hasBootBlock() {
        return mBootBlock != null && !"".equals(mBootBlock);
    }

    public boolean hasRecoveryBlock() {
        return mRecoveryBlock != null && !"".equals(mRecoveryBlock);
    }

    public RecoveryInfo getRecovery() {
        String recovery = getCore().getPreferences().getRecovery();
        for (int i = 0; i < mRecoveries.size(); i++) {
            int key = mRecoveries.keyAt(i);
            RecoveryInfo info = mRecoveries.get(key);
            if (info.getName().equals(recovery)) {
                return info;
            }
        }
        return null;
    }

    public void setRecovery(int id) {
        RecoveryInfo info = mRecoveries.get(id);
        Preferences prefs = getCore().getPreferences();
        prefs.setRecovery(info.getName());
        prefs.setInternalStorage(info.getInternalSdcard());
        prefs.setExternalStorage(info.getExternalSdcard());
    }

    public String getBackupDir(boolean force) {

        RecoveryInfo info = getRecovery();

        Preferences prefs = getCore().getPreferences();
        StoragePlugin sPlugin = (StoragePlugin) getCore().getPlugin(Core.PLUGIN_STORAGE);

        boolean external = sPlugin.hasExternalStorage()
                && prefs.isBackupExternalStorage();
        String sdcard = external ? sPlugin.getExternalStoragePath() : "sdcard";

        return info.getBackupFolder(sdcard, force, external);
    }

    public String[] getBackupList() {

        RecoveryInfo info = getRecovery();

        Preferences prefs = getCore().getPreferences();
        StoragePlugin sPlugin = (StoragePlugin) getCore().getPlugin(Core.PLUGIN_STORAGE);

        boolean external = sPlugin.hasExternalStorage()
                && prefs.isBackupExternalStorage();
        String sdcard = external ? sPlugin.getExternalStoragePath() : "sdcard";

        String folder = info.getBackupFolder(sdcard, true, external);

        List<String> list = new ArrayList<String>();

        if (folder != null && !"".equals(folder)) {
            File f = new File(folder);
            if (f.exists()) {
                File[] fs = f.listFiles();
                for (int i = 0; i < fs.length; i++) {
                    if (fs[i].isDirectory()) {
                        list.add(fs[i].getName());
                    }
                }
            }
        }

        Collections.sort(list, new Comparator<String>() {

            @Override
            public int compare(String s1, String s2) {
                int value = s1.compareTo(s2);
                return -value;
            }
        });

        return list.toArray(new String[list.size()]);
    }

    public String getCommandsFile() {

        RecoveryInfo info = getRecovery();

        return info.getCommandsFile();
    }

    public String[] getCommands(boolean wipeSystem, boolean wipeData, boolean wipeCaches,
            boolean fixPermissions, String backupFolder, String backupOptions, String restore)
            throws Exception {
        List<String> commands = new ArrayList<String>();

        RecoveryInfo info = getRecovery();

        Preferences prefs = getCore().getPreferences();
        String internalStorage = prefs.getInternalStorage();
        while (internalStorage.startsWith("/")) {
            internalStorage = internalStorage.substring(1);
        }

        String externalStorage = prefs.getExternalStorage();
        while (externalStorage.startsWith("/")) {
            externalStorage = externalStorage.substring(1);
        }

        StoragePlugin sPlugin = (StoragePlugin) getCore().getPlugin(Core.PLUGIN_STORAGE);
        boolean external = sPlugin.hasExternalStorage()
                && prefs.isBackupExternalStorage();
        String storage = external ? externalStorage : internalStorage;

        commands.addAll(info.getCommands(storage, external, wipeSystem, wipeData, wipeCaches,
                fixPermissions, backupFolder, backupOptions, restore));

        return commands.toArray(new String[commands.size()]);
    }

    private int testLastLog(File file, SuperUserPlugin suPlugin, boolean retry) {
        int retValue = -1;
        try {
            Scanner scanner = null;
            try {
                scanner = new Scanner(file);
                int lineNumber = 0;
                while ((retValue == -1 || mBootBlock == null || mRecoveryBlock == null) && scanner.hasNext() && lineNumber < 1000) {
                    String line = scanner.nextLine();
                    if (line != null) {
                        if (retValue == -1) {
                            if (line.indexOf("CWM-based Recovery") >= 0) {
                                retValue = RecoveryInfo.RECOVERY_CWM_BASED;
                            } else if (line.indexOf("ClockworkMod Recovery") >= 0) {
                                retValue = RecoveryInfo.RECOVERY_CWM;
                            } else if (line.indexOf("TWRP") >= 0) {
                                retValue = RecoveryInfo.RECOVERY_TWRP;
                            } else if (line.indexOf("4EXT") >= 0) {
                                retValue = RecoveryInfo.RECOVERY_4EXT;
                            }
                        }
                        if (mBootBlock == null) {
                            if (line.indexOf("/boot") >= 0 && line.indexOf("/dev/block/") >= 0) {
                                mBootBlock = line.substring(line.indexOf("/dev/block"));
                                int index = -1;
                                if ((index = mBootBlock.indexOf(" ")) > 0) {
                                    mBootBlock = mBootBlock.substring(0, index);
                                }
                            }
                        }
                        if (mRecoveryBlock == null) {
                            if (line.indexOf("/recovery") >= 0 && line.indexOf("/dev/block/") >= 0) {
                                mRecoveryBlock = line.substring(line.indexOf("/dev/block"));
                                int index = -1;
                                if ((index = mRecoveryBlock.indexOf(" ")) > 0) {
                                    mRecoveryBlock = mRecoveryBlock.substring(0, index);
                                }
                            }
                        }
                    }
                    lineNumber++;
                }
                if (mRecoveryBlock == null) {
                    mRecoveryBlock = "";
                }
                if (mBootBlock == null) {
                    mBootBlock = "";
                }
            } finally {
                if (scanner != null) {
                    scanner.close();
                }
            }
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            if (retry) {
                suPlugin.run("cp " + file.getAbsolutePath() + " /sdcard/last_log");
                return testLastLog(new File("/sdcard/last_log"), suPlugin, false);
            }
        }
        return retValue;
    }

    private void test(final RecoveryInfo info, boolean first) {

        final Context context = getCore().getContext();
        final String recoveryName = info.getFullName(context);

        if (first) {
            int recNumber = 0;
            File folder = new File(mRecoveries.get(RecoveryInfo.RECOVERY_CWM_BASED).getFolderPath());
            if (folder.exists()) {
                recNumber++;
            }
            folder = new File(mRecoveries.get(RecoveryInfo.RECOVERY_TWRP).getFolderPath());
            if (folder.exists()) {
                recNumber++;
            }
            folder = new File(mRecoveries.get(RecoveryInfo.RECOVERY_4EXT).getFolderPath());
            if (folder.exists()) {
                recNumber++;
            }
            if (recNumber != 1) {
                if (mInstalledRecovery == -1) {
                    selectRecovery(context);
                    return;
                } else {
                    setRecovery(mInstalledRecovery);
                    Toast.makeText(context,
                            context.getString(R.string.recovery_changed, recoveryName),
                            Toast.LENGTH_LONG).show();
                    started();
                    return;
                }
            }
        }

        if (info.getId() == RecoveryInfo.RECOVERY_CWM_BASED) {
            if (mInstalledRecovery == -1) {
                selectRecovery(context);
            } else {
                setRecovery(mInstalledRecovery);
                Toast.makeText(context,
                        context.getString(R.string.recovery_changed, recoveryName),
                        Toast.LENGTH_LONG).show();
                started();
            }
            return;
        }

        File folder = new File(info.getFolderPath());
        if (folder.exists()) {
            AlertDialog.Builder alert = new AlertDialog.Builder(context);
            alert.setCancelable(false);
            alert.setTitle(R.string.recovery_change_alert_title);
            alert.setMessage(context.getString(R.string.recovery_change_alert_message,
                    recoveryName));
            alert.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int whichButton) {
                    dialog.dismiss();
                    setRecovery(info.getId());
                    Toast.makeText(context,
                            context.getString(R.string.recovery_changed, recoveryName),
                            Toast.LENGTH_LONG).show();
                    started();
                }
            });
            alert.setNegativeButton(android.R.string.cancel, new DialogInterface.OnClickListener() {

                public void onClick(DialogInterface dialog, int which) {
                    dialog.dismiss();

                    selectRecovery(context);
                }
            });
            alert.show();
        } else {
            switch (info.getId()) {
                case RecoveryInfo.RECOVERY_4EXT:
                    test(mRecoveries.get(RecoveryInfo.RECOVERY_TWRP), false);
                    break;
                case RecoveryInfo.RECOVERY_TWRP:
                    test(mRecoveries.get(RecoveryInfo.RECOVERY_CWM_BASED), false);
                    break;
            }
        }
    }

    private void selectRecovery(Context context) {
        View view = LayoutInflater.from(context).inflate(R.layout.dialog_recovery, null);

        final RadioGroup mGroup = (RadioGroup) view.findViewById(R.id.recovery_radio_group);

        new AlertDialog.Builder(context).setCancelable(false).setTitle(R.string.recovery_alert_title)
                .setMessage(R.string.recovery_alert_summary).setView(view)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {

                    public void onClick(DialogInterface dialog, int whichButton) {

                        int id = mGroup.getCheckedRadioButtonId();
                        int recovery = -1;
                        switch (id) {
                            case R.id.cwm :
                                recovery = RecoveryInfo.RECOVERY_CWM;
                                break;
                            case R.id.cwmbased :
                                recovery = RecoveryInfo.RECOVERY_CWM_BASED;
                                break;
                            case R.id.twrp :
                                recovery = RecoveryInfo.RECOVERY_TWRP;
                                break;
                            case R.id.fourext :
                                recovery = RecoveryInfo.RECOVERY_4EXT;
                                break;
                        }

                        setRecovery(recovery);
                        started();

                        dialog.dismiss();
                    }
                }).show();
    }

    public void installBoot(String path) {
        if (mBootBlock == null) {
            return;
        }
        String command = "dd if=" + path + " of=" + mBootBlock;
        SuperUserPlugin suPlugin = (SuperUserPlugin) getCore().getPlugin(Core.PLUGIN_SUPERUSER);
        suPlugin.run(command);
    }

    public void installRecovery(String path) {
        if (mRecoveryBlock == null) {
            return;
        }
        String command = "dd if=" + path + " of=" + mRecoveryBlock;
        SuperUserPlugin suPlugin = (SuperUserPlugin) getCore().getPlugin(Core.PLUGIN_SUPERUSER);
        suPlugin.run(command);
    }

} public class StoragePlugin extends Plugin {

    public static final String ROOT_ID_PRIMARY_EMULATED = "primary";

    private static boolean sScanned = false;

    private String mInternalStoragePath;
    private String mExternalStoragePath;

    public StoragePlugin(Core core) {
        super(core, Core.PLUGIN_STORAGE);
    }

    @Override
    public void start() {
        ((CoreImpl) getCore()).setMessage(R.string.reading_storage_paths);

        (new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {

                if (!sScanned) {
                    sScanned = true;
                    readMounts();
                    mExternalStoragePath = externalStorage();
                }

                return (Void) null;
            }

            @Override
            protected void onPostExecute(Void result) {
                started();
            }
        }).execute((Void) null);
    }

    @Override
    public void stop() {
        stopped();
    }

    public boolean hasExternalStorage() {
        return mExternalStoragePath != null;
//        return Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState());
    }

    public boolean isExternalStorage(String path) {
        if (mInternalStoragePath == null) {
            mInternalStoragePath = "/sdcard";
        }
        return !path.startsWith(mInternalStoragePath) && !path.startsWith("/sdcard")
                && !path.startsWith("/mnt/sdcard");
    }

    public String getExternalStoragePath() {
        return mExternalStoragePath;
    }

    public String getInternalStoragePath() {
        return mInternalStoragePath;
    }

    public String getPath(String path) {
        String filePath = new String(path);

        Preferences prefs = getCore().getPreferences();
        String internalStorage = prefs.getInternalStorage();
        String externalStorage = prefs.getExternalStorage();

        String[] internalNames = new String[] {
                mInternalStoragePath,
                "/mnt/sdcard",
                "/storage/sdcard/",
                "/sdcard",
                "/storage/sdcard0",
                "/storage/emulated/0" };
        String[] externalNames = new String[] {
                mExternalStoragePath == null ? " " : mExternalStoragePath,
                "/mnt/extSdCard",
                "/storage/extSdCard/",
                "/extSdCard",
                "/storage/sdcard1",
                "/storage/emulated/1" };
        for (int i = 0; i < internalNames.length; i++) {
            String internalName = internalNames[i];
            String externalName = externalNames[i];
            boolean external = isExternalStorage(filePath);
            if (filePath.endsWith(".sh")) {
                if (!external) {
                    if (filePath.startsWith(internalName)) {
                        if (internalName.endsWith("/")) {
                            filePath = filePath.replace(internalName, "/" + "sdcard" + "/");
                        } else {
                            filePath = filePath.replace(internalName, "/" + "sdcard");
                        }
                        break;
                    }
                }
            } else {
                if (filePath.startsWith(externalName)) {
                    if (externalName.endsWith("/")) {
                        filePath = filePath.replace(externalName, "/" + externalStorage + "/");
                    } else {
                        filePath = filePath.replace(externalName, "/" + externalStorage);
                    }
                    break;
                } else if (filePath.startsWith(internalName)) {
                    if (internalName.endsWith("/")) {
                        filePath = filePath.replace(internalName, "/" + internalStorage + "/");
                    } else {
                        filePath = filePath.replace(internalName, "/" + internalStorage);
                    }
                    break;
                }
            }
        }
        if (filePath.startsWith("//")) {
            filePath = filePath.substring(1);
        }
        return filePath;
    }

    @SuppressWarnings("deprecation")
    public double getSpaceLeft() {
        StatFs stat = new StatFs(Environment.getExternalStorageDirectory().getPath());
        double sdAvailSize = 0;
        if (Build.VERSION.SDK_INT > 17) {
            sdAvailSize = stat.getAvailableBlocksLong() * stat.getBlockSizeLong();
        } else {
            sdAvailSize = stat.getAvailableBlocks() * stat.getBlockSize();
        }
        // One binary gigabyte equals 1,073,741,824 bytes.
        return sdAvailSize / 1073741824;
    }

    public void addFileItemToStore(String filePath) {

        Context context = getCore().getContext();

        if (filePath == null || (!filePath.endsWith(".zip") && !filePath.endsWith(".sh"))) {
            Toast.makeText(context, R.string.error_file_manager_invalid_zip, Toast.LENGTH_SHORT)
                    .show();
            return;
        }

        if (!filePath.endsWith(".zip") && !filePath.endsWith(".sh")) {
            Toast.makeText(context, R.string.error_file_manager_zip, Toast.LENGTH_SHORT).show();
            return;
        }

        String sdcardPath = new String(filePath);

        filePath = getPath(filePath);

        File file = new File(sdcardPath);
        if (!file.exists()) {
            Toast.makeText(context, R.string.error_file_manager_not_found_zip, Toast.LENGTH_LONG)
                    .show();
        } else {

            FileItemStore.addItem(filePath, sdcardPath, false, getCore().getPreferences()
                    .getZipPosition());
        }
    }

    private void readMounts() {

        Core core = getCore();

        ArrayList<String> mounts = new ArrayList<String>();
        ArrayList<String> vold = new ArrayList<String>();

        Scanner scanner = null;
        try {
            scanner = new Scanner(new File("/proc/mounts"));
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                if (line.startsWith("/dev/block/vold/")) {
                    String[] lineElements = line.split(" ");
                    String element = lineElements[1];

                    mounts.add(element);
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }
        boolean addExternal = mounts.size() == 1 && hasExternalStorage();
        if (mounts.size() == 0 || addExternal) {
            mounts.add("/mnt/sdcard");
        }

        File fstab = findFstab(core);
        if (fstab != null) {
            try {
                String path = core.getContext().getExternalCacheDir() + File.separator
                        + fstab.getName();
                SuperUserPlugin suPlugin = (SuperUserPlugin) core.getPlugin(Core.PLUGIN_SUPERUSER);
                suPlugin.run("cp " + fstab.getAbsolutePath() + " " + path);
                suPlugin.run("chmod -R 777 " + path);

                scanner = new Scanner(new File(path));
                while (scanner.hasNext()) {
                    String line = scanner.nextLine();
                    if (line.startsWith("dev_mount")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[2];

                        if (element.contains(":")) {
                            element = element.substring(0, element.indexOf(":"));
                        }

                        if (element.toLowerCase().indexOf("usb") < 0) {
                            vold.add(element);
                        }
                    } else if (line.startsWith("/devices/platform")) {
                        String[] lineElements = line.split(" ");
                        String element = lineElements[1];

                        if (element.contains(":")) {
                            element = element.substring(0, element.indexOf(":"));
                        }

                        if (element.toLowerCase().indexOf("usb") < 0) {
                            vold.add(element);
                        }
                    }
                }
            } catch (Exception e) {
                e.printStackTrace();
            } finally {
                if (scanner != null) {
                    scanner.close();
                }
            }
        }
        if (addExternal && (vold.size() == 1 && hasExternalStorage())) {
            mounts.add(vold.get(0));
        }
        if (vold.size() == 0 || (vold.size() == 1 && hasExternalStorage())) {
            vold.add("/mnt/sdcard");
        }

        for (int i = 0; i < mounts.size(); i++) {
            String mount = mounts.get(i);
            File root = new File(mount);
            if (!vold.contains(mount)
                    || (!root.exists() || !root.isDirectory() || !root.canWrite())) {
                mounts.remove(i--);
            }
        }

        for (int i = 0; i < mounts.size(); i++) {
            String mount = mounts.get(i);
            if (mount.indexOf("sdcard0") >= 0 || mount.equalsIgnoreCase("/mnt/sdcard")
                    || mount.equalsIgnoreCase("/sdcard")) {
                mInternalStoragePath = mount;
//            } else {
//                mExternalStoragePath = mount;
            }
        }

        if (mInternalStoragePath == null) {
            mInternalStoragePath = "/sdcard";
        }
    }

    private String externalStorage() {
        Context context = getCore().getContext();
        String dirPath = null;
        try {
            String[] volumePaths = null;
            ArrayList<String> volumePathsList = null;
            String path = null;
            if (Build.VERSION.SDK_INT >= 14) {
                volumePaths = volumePaths(context);
                if (volumePaths != null) {
                    volumePathsList = new ArrayList<String>();
                    path = Environment.getExternalStorageDirectory().getAbsolutePath();
                }
            }
            try {
                String primaryVolumePath = primaryVolumePath(context);
                int i = volumePaths.length;
                for (int j = 0;; j++)
                    if (j < i) {
                        String volumePath = volumePaths[j];
                        try {
                            if ((volumePath.equals(System.getenv("EMULATED_STORAGE_SOURCE")))
                                    || (volumePath.equals(System.getenv("EXTERNAL_STORAGE")))
                                    || (volumePath.equals(path))
                                    || (volumePath.equals(primaryVolumePath))
                                    || (volumePath.toLowerCase().contains("usb")))
                                continue;
                            volumePathsList.add(volumePath);
                        } catch (Exception ex) {
                            ex.printStackTrace();
                        }
                    } else {
                        if (volumePathsList.size() == 1) {
                            dirPath = (String) volumePathsList.get(0);
                        }
                        return dirPath;
                    }
            } catch (Exception ex) {
                ex.printStackTrace();

            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return dirPath;
    }

    private String[] volumePaths(Context context) {
        try {
            StorageManager localStorageManager = (StorageManager) context
                    .getSystemService("storage");
            return (String[]) (String[]) localStorageManager.getClass()
                    .getMethod("getVolumePaths", new Class[0])
                    .invoke(localStorageManager, new Object[0]);
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private String primaryVolumePath(Context context) {
        try {
            StorageManager localStorageManager = (StorageManager) context
                    .getSystemService("storage");
            Object localObject = localStorageManager.getClass()
                    .getMethod("getPrimaryVolume", new Class[0])
                    .invoke(localStorageManager, new Object[0]);
            return (String) localObject.getClass().getMethod("getPath", new Class[0])
                    .invoke(localObject, new Object[0]);
        } catch (Exception ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private File findFstab(Core core) {

        File file = null;

        file = new File("/system/etc/vold.fstab");
        if (file.exists()) {
            return file;
        }

        SuperUserPlugin suPlugin = (SuperUserPlugin) core.getPlugin(Core.PLUGIN_SUPERUSER);
        CommandResult cm = suPlugin
                .run("grep -ls \"/dev/block/\" * --include=fstab.* --exclude=fstab.goldfish");
        if (cm.getOutString() != null) {
            String[] files = cm.getOutString().split("\n");
            for (int i = 0; i < files.length; i++) {
                file = new File(files[i]);
                if (file.exists()) {
                    return file;
                }
            }
        }

        return null;
    }

} public class SuperUserPlugin extends Plugin {

    private static Shell sShell;
    private static boolean sAccess = false;

    public SuperUserPlugin(Core core) {
        super(core, Core.PLUGIN_SUPERUSER);
    }

    @Override
    public void start() {
        ((CoreImpl) getCore()).setMessage(R.string.requesting_su);
        if (sShell == null) {
            sShell = new Shell();
        }
        (new AsyncTask<Void, Void, Void>() {

            @Override
            protected Void doInBackground(Void... params) {
                sAccess = test();
                return (Void) null;
            }

            @Override
            protected void onPostExecute(Void result) {
                if (sAccess) {
                    started();
                } else {
                    Dialog.alert(getCore().getContext(), R.string.alert_no_superuser, new OnDialogClosedListener() {

                        @Override
                        public void dialogOk() {
                            started();
                        }

                        @Override
                        public void dialogCancel() {
                        }
                    });
                }
            }
        }).execute((Void) null);
    }

    @Override
    public void stop() {
        stopped();
    }

    public CommandResult run(String s) {
        return sShell.runWaitFor(s, null);
    }

    public void run(String s, ShellCallback callback) {
        sShell.runWaitFor(s, callback);
    }

    public boolean test() {
        return (sAccess = sShell.test());
    }

    public boolean hasAccess() {
        return sAccess;
    }
} public class Files {

    public static boolean hasAndroidSecure() {
        String sdcard = "sdcard";
        return folderExists("/" + sdcard + "/.android-secure");
    }

    public static boolean hasSdExt() {
        return folderExists("/sd-ext");
    }

    public static String getSBINFolder() {
        if (folderExists("/sbin")) {
            return "/sbin/";
        } else if (folderExists("/system/sbin")) {
            return "/system/sbin/";
        }
        return null;
    }

    private static boolean folderExists(String path) {
        File f = new File(path);
        return f.exists() && f.isDirectory();
    }

    public static String[] readMd5File(File file) {
        try {
            StringBuffer fileData = new StringBuffer(1000);
            BufferedReader reader;

            reader = new BufferedReader(new FileReader(file));
            char[] buf = new char[1024];
            int numRead = 0;
            while ((numRead = reader.read(buf)) != -1) {
                String readData = String.valueOf(buf, 0, numRead);
                fileData.append(readData);
                buf = new char[1024];
            }
            reader.close();
            String content = fileData.toString();
            StringTokenizer st = new StringTokenizer(content, " ");
            return new String[] { st.nextToken(), st.nextToken() };
        } catch (Exception e) {
        }
        return null;
    }

    public static String md5(File file) {
        InputStream is = null;
        try {
            is = new FileInputStream(file);
            MessageDigest digest = MessageDigest.getInstance("MD5");
            byte[] buffer = new byte[8192];
            int read = 0;
            while ((read = is.read(buffer)) > 0) {
                digest.update(buffer, 0, read);
            }
            byte[] md5sum = digest.digest();
            BigInteger bigInt = new BigInteger(1, md5sum);
            String md5 = bigInt.toString(16);
            while (md5.length() < 32) {
                md5 = "0" + md5;
            }
            return md5;
        } catch (Exception e) {
            throw new RuntimeException(e);
        } finally {
            try {
                is.close();
            } catch (Exception e) {
            }
        }
    }

    public static boolean writeToFile(String data, String path, String fileName) {

        File folder = new File(path);
        File file = new File(folder, fileName);

        folder.mkdirs();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream(file);
            fos.write(data.getBytes());
            return true;
        } catch (IOException ex) {
            ex.printStackTrace();
            return false;
        } finally {
            if (fos != null) {
                try {
                    fos.close();
                } catch (IOException ex) {
                }
            }
        }
    }

    public static String readAssets(Context contex, String fileName) {
        BufferedReader in = null;
        StringBuilder data = null;
        try {
            data = new StringBuilder(2048);
            char[] buf = new char[2048];
            int nRead = -1;
            in = new BufferedReader(new InputStreamReader(contex.getAssets().open(fileName)));
            while ((nRead = in.read(buf)) != -1) {
                data.append(buf, 0, nRead);
            }
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        } finally {
            if (in != null) {
                try {
                    in.close();
                } catch (IOException e) {
                }
            }
        }

        if (TextUtils.isEmpty(data)) {
            return null;
        }
        return data.toString();
    }

    public static String[] readAssetsSplit(Context context, String fileName) {
        try {
            return readFileSplit(context.getAssets().open(fileName));
        } catch (IOException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    public static String[] readFileSplit(String fileName) {
        try {
            return readFileSplit(new FileInputStream(fileName));
        } catch (FileNotFoundException ex) {
            ex.printStackTrace();
            return null;
        }
    }

    private static String[] readFileSplit(InputStream is) {
        List<String> data = new ArrayList<String>();
        Scanner scanner = null;
        try {
            scanner = new Scanner(new InputStreamReader(is));
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                data.add(line);
            }
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

        return data.toArray(new String[data.size()]);
    }

    public static String findLineInFile(Context contex, File file, String regExp) {
        Scanner scanner = null;
        try {
            scanner = new Scanner(new InputStreamReader(new FileInputStream(file)));
            while (scanner.hasNext()) {
                String line = scanner.nextLine();
                if (line.matches(regExp)) {
                    return line;
                }
            }
        } catch (IOException ex) {
            ex.printStackTrace();
        } finally {
            if (scanner != null) {
                scanner.close();
            }
        }

        return null;
    }

    public static boolean recursiveDelete(File f) {
        try {
            if (f.isDirectory()) {
                File[] files = f.listFiles();
                for (int i = 0; i < files.length; i++) {
                    if (!recursiveDelete(files[i])) {
                        return false;
                    }
                }
                if (!f.delete()) {
                    return false;
                }
            } else {
                if (!f.delete()) {
                    return false;
                }
            }
        } catch (Exception ignore) {
        }
        return true;
    }

    public static String getPathFromUri(Core core, Uri uri) {
        StoragePlugin sPlugin = (StoragePlugin) core.getPlugin(Core.PLUGIN_STORAGE);
        String filePath = uri.getPath();
        if (!(new File(filePath)).exists()) {
            ContentResolver cr = core.getContext().getContentResolver();
            Cursor cursor = cr.query(uri, null, null, null, null);
            try {
                if (cursor.moveToNext()) {
                    int index = cursor.getColumnIndex(MediaStore.MediaColumns.DATA);
                    if (index >= 0) {
                        filePath = cursor.getString(index);
                    } else if (Build.VERSION.SDK_INT >= 19
                            && uri.toString().startsWith(ContentResolver.SCHEME_CONTENT)) {
                        String newUri = new Uri.Builder()
                                .scheme(ContentResolver.SCHEME_CONTENT)
                                .authority(uri.getAuthority()).appendPath("document")
                                .build().toString();
                        String path = uri.toString();
                        if (path.startsWith(newUri)) {
                            String firstPath = filePath.substring(0, filePath.indexOf(":"));
                            filePath = filePath.substring(filePath.indexOf(":") + 1);
                            String storage = sPlugin.getInternalStoragePath();
                            if (firstPath.indexOf(StoragePlugin.ROOT_ID_PRIMARY_EMULATED) < 0) {
                                storage = sPlugin.getExternalStoragePath();
                            }
                            filePath = storage + "/" + filePath;
                        }

                    }
                }
            } finally {
                cursor.close();
            }
        }
        return filePath;
    }
} public class InstallOptionsCursor extends AbstractCursor {

    private static final String[] COLUMN_NAMES = { "_id", "TEXT", "CHECKED" };

    private String[] mOption;
    private String[] mText;
    private int[] mChecked;
    private int mCount = 0;

    public InstallOptionsCursor(Core core) {
        Preferences prefs = core.getPreferences();

        for (int i = 0; i < Preferences.INSTALL_OPTIONS.length; i++) {
            if (prefs.isShowOption(Preferences.INSTALL_OPTIONS[i])) {
                mCount++;
            }
        }
        mOption = new String[mCount];
        mText = new String[mCount];
        mChecked = new int[mCount];
        int count = 0;
        for (int i = 0; i < Preferences.INSTALL_OPTIONS.length; i++) {
            if (prefs.isShowOption(Preferences.INSTALL_OPTIONS[i])) {
                mOption[count] = Preferences.INSTALL_OPTIONS[i];
                mText[count] = core.getContext().getResources().getString(
                        getText(Preferences.INSTALL_OPTIONS[i]));
                mChecked[count] = 0;
                count++;
            }
        }
    }

    @Override
    public int getCount() {
        return mCount;
    }

    @Override
    public String[] getColumnNames() {
        return COLUMN_NAMES;
    }

    @Override
    public String getString(int column) {
        switch (column) {
            case 0:
                return mOption[getPosition()];
            case 1:
                return mText[getPosition()];
        }
        return null;
    }

    @Override
    public short getShort(int column) {
        return 0;
    }

    @Override
    public int getInt(int column) {
        if (column == 2) {
            return mChecked[getPosition()];
        }
        return 0;
    }

    @Override
    public long getLong(int column) {
        return 0;
    }

    @Override
    public float getFloat(int column) {
        return 0;
    }

    @Override
    public double getDouble(int column) {
        return 0;
    }

    @Override
    public boolean isNull(int column) {
        return false;
    }

    public void setOption(int which, boolean isChecked) {
        mChecked[which] = isChecked ? 1 : 0;
    }

    public boolean isBackup() {
        for (int i = 0; i < mCount; i++) {
            if (Preferences.INSTALL_BACKUP.equals(mOption[i])) {
                return mChecked[i] == 1;
            }
        }
        return false;
    }

    public boolean isWipeSystem() {
        for (int i = 0; i < mCount; i++) {
            if (Preferences.INSTALL_WIPESYSTEM.equals(mOption[i])) {
                return mChecked[i] == 1;
            }
        }
        return false;
    }

    public boolean isWipeData() {
        for (int i = 0; i < mCount; i++) {
            if (Preferences.INSTALL_WIPEDATA.equals(mOption[i])) {
                return mChecked[i] == 1;
            }
        }
        return false;
    }

    public boolean isWipeCaches() {
        for (int i = 0; i < mCount; i++) {
            if (Preferences.INSTALL_WIPECACHES.equals(mOption[i])) {
                return mChecked[i] == 1;
            }
        }
        return false;
    }

    public boolean isFixPermissions() {
        for (int i = 0; i < mCount; i++) {
            if (Preferences.INSTALL_FIXPERM.equals(mOption[i])) {
                return mChecked[i] == 1;
            }
        }
        return false;
    }

    public String getIsCheckedColumn() {
        return "CHECKED";
    }

    public String getLabelColumn() {
        return "TEXT";
    }

    private int getText(String option) {
        if (Preferences.INSTALL_BACKUP.equals(option)) {
            return R.string.backup;
        } else if (Preferences.INSTALL_WIPESYSTEM.equals(option)) {
            return R.string.wipe_system;
        } else if (Preferences.INSTALL_WIPEDATA.equals(option)) {
            return R.string.wipe_data;
        } else if (Preferences.INSTALL_WIPECACHES.equals(option)) {
            return R.string.wipe_caches;
        } else if (Preferences.INSTALL_FIXPERM.equals(option)) {
            return R.string.fix_permissions;
        }
        return -1;
    }
} public class Strings {

    private static final SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd.HH.mm.ss");

    private static final long K = 1024;
    private static final long M = K * K;
    private static final long G = M * K;
    private static final long T = G * K;

    public static String formatSize(final long value) {
        final long[] dividers = new long[] { T, G, M, K, 1 };
        final String[] units = new String[] { "TB", "GB", "MB", "KB", "B" };
        String result = null;
        for (int i = 0; i < dividers.length; i++) {
            final long divider = dividers[i];
            if (value >= divider) {
                result = format(value, divider, units[i]);
                break;
            }
        }
        return result;
    }

    private static String format(final long value, final long divider, final String unit) {
        final double result = divider > 1 ? (double) value / (double) divider : (double) value;
        return String.format("%.1f %s", Double.valueOf(result), unit);
    }

    public static String formatDate(final long value) {
        return SDF.format(value);
    }

    public static String getDateAndTime() {
        return SDF.format(new Date(System.currentTimeMillis()));
    }

    public static String replace(String original, String starts, String replace) {
        return !original.startsWith(starts) ? original : replace
                + original.substring(starts.length());
    }
} public class Preferences {

    public static final String INSTALL_BACKUP = "BACKUP";
    public static final String INSTALL_WIPESYSTEM = "WIPESYSTEM";
    public static final String INSTALL_WIPEDATA = "WIPEDATA";
    public static final String INSTALL_WIPECACHES = "WIPECACHES";
    public static final String INSTALL_FIXPERM = "FIXPERM";
    public static final String[] INSTALL_OPTIONS = {
            INSTALL_BACKUP,
            INSTALL_WIPESYSTEM,
            INSTALL_WIPEDATA,
            INSTALL_WIPECACHES,
            INSTALL_FIXPERM };

    private static final String PROPERTY_FIRST_RUN = "first-run";
    private static final String PROPERTY_INTERNAL_STORAGE = "internal-storage";
    private static final String PROPERTY_EXTERNAL_STORAGE = "external-storage";
    private static final String PROPERTY_FORCE_EXTERNAL_STORAGE = "mountextsdcard";
    private static final String PROPERTY_FORCE_DATA_MEDIA = "forcedatamedia";
    private static final String PROPERTY_BACKUP_EXTERNAL_STORAGE = "backupextsdcard";
    private static final String PROPERTY_RECOVERY = "recovery";
    private static final String PROPERTY_CHANGED_RECOVERY = "changedrecovery";
    private static final String PROPERTY_LIST = "list";
    private static final String PROPERTY_DRAG_AND_DROP = "drag-and-drop";
    private static final String PROPERTY_SHOW_OPTIONS = "show-options";
    private static final String PROPERTY_DARK_THEME = "dark-theme";
    private static final String PROPERTY_CHECK_EXISTS = "check_exists";
    private static final String PROPERTY_CHECK_UPDATES_STARTUP = "check_updates_startup";
    private static final String PROPERTY_DOWNLOAD_PATH = "download_path";
    private static final String PROPERTY_CHECK_MD5 = "check_md5";
    private static final String PROPERTY_OVERRIDE_LIST = "override_list";
    private static final String PROPERTY_AUTOLOAD_LIST = "autoload_list";
    private static final String PROPERTY_ZIP_POSITION = "zip_position";
    private static final String PROPERTY_SPACE_LEFT = "space_left";
    private static final String PROPERTY_SHOW_WIPESYSTEM_ALERT = "wipesystem_alert";
    private static final String PROPERTY_USE_FOLDER = "usefolder";
    private static final String PROPERTY_FOLDER = "folder";
    private static final String PROPERTY_TO_DELETE = "to_delete";
    private static final String PROPERTY_RULES = "rules";
    private static final String PROPERTY_LOGIN = "goologin";
    private static final String PROPERTY_LOGIN_USERNAME = "goologinusername";
    private static final String PROPERTY_CLOUD_STORAGE = "cloudstorage";
    private static final String PROPERTY_DROPBOX_KEY = "dropboxkey";
    private static final String PROPERTY_DROPBOX_SECRET = "dropboxsecret";
    private static final String PROPERTY_LICENSE = "license";
    private static final String PROPERTY_RECOVERY_BLOCK = "recoveryblock";
    private static final String PROPERTY_BOOT_BLOCK = "bootblock";
    private static final String PROPERTY_ENABLE_NOTIFICATIONS = "enable_notifications";
    private static final String PROPERTY_TIME_NOTIFICATIONS = "time_notifications";
    private static final String PROPERTY_USE_ONANDROID = "use_onandroid";

    private static final String DEFAULT_RECOVERY = "cwmbased";
    private static final String DEFAULT_INTERNAL_STORAGE = "emmc";
    private static final String DEFAULT_EXTERNAL_STORAGE = "sdcard";
    private static final String DEFAULT_DOWNLOAD_PATH = "/sdcard/zipinstaller/";
    private static final String DEFAULT_ZIP_POSITION = "last";
    private static final String DEFAULT_TIME_NOTIFICATIONS = "3600000";
    private static final Set<String> DEFAULT_SHOW_OPTIONS = new HashSet<String>();
    private static final String DEFAULT_SPACE_LEFT = "-1";
    private static final int DEFAULT_CLOUD_STORAGE = CloudStorage.STORAGE_NONE;
    private static final boolean DEFAULT_FIRST_RUN = true;
    private static final boolean DEFAULT_CHANGED_RECOVERY = true;
    private static final boolean DEFAULT_FORCE_EXTERNAL_STORAGE = true;
    private static final boolean DEFAULT_FORCE_DATA_MEDIA = VERSION.SDK_INT >= 16;
    private static final boolean DEFAULT_BACKUP_EXTERNAL_STORAGE = false;
    private static final boolean DEFAULT_DRAG_AND_DROP = true;
    private static final boolean DEFAULT_DARK_THEME = false;
    private static final boolean DEFAULT_CHECK_EXISTS = true;
    private static final boolean DEFAULT_CHECK_UPDATES_STARTUP = true;
    private static final boolean DEFAULT_CHECK_MD5 = true;
    private static final boolean DEFAULT_OVERRIDE_LIST = true;
    private static final boolean DEFAULT_AUTOLOAD_LIST = false;
    private static final boolean DEFAULT_ENABLE_NOTIFICATIONS = true;
    private static final boolean DEFAULT_SHOW_WIPESYSTEM_ALERT = true;
    private static final boolean DEFAULT_USE_ONANDROID = false;
    private static final boolean DEFAULT_USE_FOLDER = false;
    private static final String DEFAULT_FOLDER = "/sdcard/download/";

    private Core mCore;
    private Context mContext;
    private SharedPreferences mSettings;

    public Preferences(Core core) {
        this(core.getContext());
        mCore = core;
        mContext = core.getContext();
    }

    public Preferences(Context context) {

        mSettings = PreferenceManager.getDefaultSharedPreferences(context);
        mContext = context;

        DEFAULT_SHOW_OPTIONS.add(INSTALL_BACKUP);
        DEFAULT_SHOW_OPTIONS.add(INSTALL_WIPESYSTEM);
        DEFAULT_SHOW_OPTIONS.add(INSTALL_WIPEDATA);
        DEFAULT_SHOW_OPTIONS.add(INSTALL_WIPECACHES);
        DEFAULT_SHOW_OPTIONS.add(INSTALL_FIXPERM);

        File file = new File(DEFAULT_DOWNLOAD_PATH);
        if (!file.exists()) {
            file.mkdirs();
        }
    }

    public int getCloudStorage() {
        return mSettings.getInt(PROPERTY_CLOUD_STORAGE, DEFAULT_CLOUD_STORAGE);
    }

    public void setCloudStorage(int value) {
        savePreference(PROPERTY_CLOUD_STORAGE, value);
        mCore.setCloudStorage(value);
    }

    public boolean isFirstRun() {
        return mSettings.getBoolean(PROPERTY_FIRST_RUN, DEFAULT_FIRST_RUN);
    }

    public void setFirstRun(boolean value) {
        savePreference(PROPERTY_FIRST_RUN, value);
    }

    public String getInternalStorage() {
        return mSettings.getString(PROPERTY_INTERNAL_STORAGE, DEFAULT_INTERNAL_STORAGE);
    }

    public void setInternalStorage(String value) {
        savePreference(PROPERTY_INTERNAL_STORAGE, value);
    }

    public String getExternalStorage() {
        return mSettings.getString(PROPERTY_EXTERNAL_STORAGE, DEFAULT_EXTERNAL_STORAGE);
    }

    public void setExternalStorage(String value) {
        savePreference(PROPERTY_EXTERNAL_STORAGE, value);
    }

    public boolean isForceExternalStorage() {
        return mSettings.getBoolean(PROPERTY_FORCE_EXTERNAL_STORAGE, DEFAULT_FORCE_EXTERNAL_STORAGE);
    }

    public void setForceExternalStorage(boolean value) {
        savePreference(PROPERTY_FORCE_EXTERNAL_STORAGE, value);
    }

    public boolean isForceDataMedia() {
        return mSettings.getBoolean(PROPERTY_FORCE_DATA_MEDIA, DEFAULT_FORCE_DATA_MEDIA);
    }

    public void setForceDataMedia(boolean value) {
        savePreference(PROPERTY_FORCE_DATA_MEDIA, value);
    }

    public boolean isBackupExternalStorage() {
        return mSettings.getBoolean(PROPERTY_BACKUP_EXTERNAL_STORAGE, DEFAULT_BACKUP_EXTERNAL_STORAGE);
    }

    public void setBackupExternalStorage(boolean value) {
        savePreference(PROPERTY_BACKUP_EXTERNAL_STORAGE, value);
    }

    public boolean existsRecovery() {
        return mSettings.contains(PROPERTY_RECOVERY);
    }

    public String getRecovery() {
        return mSettings.getString(PROPERTY_RECOVERY, DEFAULT_RECOVERY);
    }

    public void setRecovery(String value) {
        savePreference(PROPERTY_RECOVERY, value);
    }

    public String getList() {
        return mSettings.getString(PROPERTY_LIST, "");
    }

    public void setList(String value) {
        savePreference(PROPERTY_LIST, value);
    }

    public boolean isShowOption(String option) {
        Set<String> options = getShowOptions();
        return options.contains(option);
    }

    public Set<String> getShowOptions() {
        return mSettings.getStringSet(PROPERTY_SHOW_OPTIONS, DEFAULT_SHOW_OPTIONS);
    }

    public void setShowOptions(String options) {
        savePreference(PROPERTY_SHOW_OPTIONS, options);
    }

    public boolean isUseDragAndDrop() {
        return mSettings.getBoolean(PROPERTY_DRAG_AND_DROP, DEFAULT_DRAG_AND_DROP);
    }

    public void setUseDragAndDrop(boolean value) {
        savePreference(PROPERTY_DRAG_AND_DROP, value);
    }

    public boolean isDarkTheme() {
        return mSettings.getBoolean(PROPERTY_DARK_THEME, DEFAULT_DARK_THEME);
    }

    public void setDarkTheme(boolean value) {
        savePreference(PROPERTY_DARK_THEME, value);
    }

    public boolean isAlertOnChangeRecovery() {
        return mSettings.getBoolean(PROPERTY_CHANGED_RECOVERY, DEFAULT_CHANGED_RECOVERY);
    }

    public void setAlertOnChangeRecovery(boolean value) {
        savePreference(PROPERTY_CHANGED_RECOVERY, value);
    }

    public boolean isCheckExists() {
        return mSettings.getBoolean(PROPERTY_CHECK_EXISTS, DEFAULT_CHECK_EXISTS);
    }

    public void setCheckExists(boolean value) {
        savePreference(PROPERTY_CHECK_EXISTS, value);
    }

    public boolean isCheckUpdatesStartup() {
        return mSettings.getBoolean(PROPERTY_CHECK_UPDATES_STARTUP, DEFAULT_CHECK_UPDATES_STARTUP);
    }

    public void setCheckUpdatesStartup(boolean value) {
        savePreference(PROPERTY_CHECK_UPDATES_STARTUP, value);
    }

    public boolean isCheckMD5() {
        return mSettings.getBoolean(PROPERTY_CHECK_MD5, DEFAULT_CHECK_MD5);
    }

    public void setCheckMD5(boolean value) {
        savePreference(PROPERTY_CHECK_MD5, value);
    }

    public boolean isOverrideList() {
        return mSettings.getBoolean(PROPERTY_OVERRIDE_LIST, DEFAULT_OVERRIDE_LIST);
    }

    public void setOverrideList(boolean value) {
        savePreference(PROPERTY_OVERRIDE_LIST, value);
    }

    public boolean isAutoloadList() {
        return mSettings.getBoolean(PROPERTY_AUTOLOAD_LIST, DEFAULT_AUTOLOAD_LIST);
    }

    public void setAutoloadList(boolean value) {
        savePreference(PROPERTY_AUTOLOAD_LIST, value);
    }

    public String getDownloadPath() {
        return mSettings.getString(PROPERTY_DOWNLOAD_PATH, DEFAULT_DOWNLOAD_PATH);
    }

    public void setDownloadPath(String value) {
        if (!value.endsWith("/"))
            value = value + "/";
        savePreference(PROPERTY_DOWNLOAD_PATH, value);
    }

    public boolean isAcceptNotifications() {
        return mSettings.getBoolean(PROPERTY_ENABLE_NOTIFICATIONS, DEFAULT_ENABLE_NOTIFICATIONS);
    }

    public void setAcceptNotifications(boolean value) {
        savePreference(PROPERTY_ENABLE_NOTIFICATIONS, value);
    }

    public long getTimeNotifications() {
        return Long.parseLong(mSettings.getString(PROPERTY_TIME_NOTIFICATIONS,
                DEFAULT_TIME_NOTIFICATIONS));
    }

    public void setTimeNotifications(long value) {
        savePreference(PROPERTY_TIME_NOTIFICATIONS, String.valueOf(value));
    }

    public boolean isUseONandroid() {
        return mSettings.getBoolean(PROPERTY_USE_ONANDROID, DEFAULT_USE_ONANDROID);
    }

    public void setUseONandroid(boolean value) {
        savePreference(PROPERTY_USE_ONANDROID, value);
    }

    public String getZipPosition() {
        return mSettings.getString(PROPERTY_ZIP_POSITION, DEFAULT_ZIP_POSITION);
    }

    public void setZipPosition(String value) {
        savePreference(PROPERTY_ZIP_POSITION, value);
    }

    public double getSpaceLeft() {
        return Double.parseDouble(mSettings.getString(PROPERTY_SPACE_LEFT, DEFAULT_SPACE_LEFT));
    }

    public void setSpaceLeft(double value) {
        savePreference(PROPERTY_SPACE_LEFT, String.valueOf(value));
    }

    public boolean isShowSystemWipeAlert() {
        return mSettings.getBoolean(PROPERTY_SHOW_WIPESYSTEM_ALERT, DEFAULT_SHOW_WIPESYSTEM_ALERT);
    }

    public void setShowSystemWipeAlert(boolean value) {
        savePreference(PROPERTY_SHOW_WIPESYSTEM_ALERT, value);
    }

    public boolean isUseFolder() {
        return mSettings.getBoolean(PROPERTY_USE_FOLDER, DEFAULT_USE_FOLDER);
    }

    public void setUseFolder(boolean value) {
        savePreference(PROPERTY_USE_FOLDER, value);
    }

    public String getFolder() {
        return mSettings.getString(PROPERTY_FOLDER, DEFAULT_FOLDER);
    }

    public void setFolder(String value) {
        savePreference(PROPERTY_FOLDER, value);
    }

    public String[] getToDelete() {
        return getArrayProperty(PROPERTY_TO_DELETE);
    }

    public void setToDelete(String[] value) {
        setArrayProperty(PROPERTY_TO_DELETE, value);
    }

    public List<Rule> getRules() {
        return Rule.createRulesAsList(mContext, mSettings.getString(PROPERTY_RULES, ""));
    }

    public void setRules(List<Rule> rules) {
        savePreference(PROPERTY_RULES, Rule.storeRules(rules));
    }

    public void addRule(String name, int type) {
        List<Rule> rules = getRules();
        List<Rule> newRules = new ArrayList<Rule>();
        for (Rule rule : rules) {
            newRules.add(rule);
        }
        newRules.add(new Rule(mContext, name, type));
        savePreference(PROPERTY_RULES, Rule.storeRules(newRules));
    }

    public void removeRule(int index) {
        List<Rule> rules = getRules();
        rules.remove(index);
        savePreference(PROPERTY_RULES, Rule.storeRules(rules));
    }

    public String getRecoveryBlock() {
        return mSettings.getString(PROPERTY_RECOVERY_BLOCK, null);
    }

    public void setRecoveryBlock(String value) {
        savePreference(PROPERTY_RECOVERY_BLOCK, value);
    }

    public String getBootBlock() {
        return mSettings.getString(PROPERTY_BOOT_BLOCK, null);
    }

    public void setBootBlock(String value) {
        savePreference(PROPERTY_BOOT_BLOCK, value);
    }

    public void logout() {
        savePreference(PROPERTY_LOGIN, "");
    }

    public void login(String value) {
        savePreference(PROPERTY_LOGIN, value);
    }

    public void setLoginUserName(String value) {
        savePreference(PROPERTY_LOGIN_USERNAME, value);
    }

    public boolean isLogged() {
        return !"".equals(mSettings.getString(PROPERTY_LOGIN, ""));
    }

    public String getLogin() {
        return mSettings.getString(PROPERTY_LOGIN, "");
    }

    public String getLoginUserName() {
        return mSettings.getString(PROPERTY_LOGIN_USERNAME, null);
    }

    public void setDropboxKey(String value) {
        savePreference(PROPERTY_DROPBOX_KEY, value);
    }

    public String getDropboxKey() {
        return mSettings.getString(PROPERTY_DROPBOX_KEY, null);
    }

    public void setDropboxSecret(String value) {
        savePreference(PROPERTY_DROPBOX_SECRET, value);
    }

    public String getDropboxSecret() {
        return mSettings.getString(PROPERTY_DROPBOX_SECRET, null);
    }

    public void setLicense(boolean value) {
        savePreference(PROPERTY_LICENSE, value);
    }

    public boolean getLicense() {
        return mSettings.getBoolean(PROPERTY_LICENSE, false);
    }

    private String[] getArrayProperty(String property) {
        String value = mSettings.getString(property, "");
        String[] array = value.split("\n");
        if (array.length == 1 && "".equals(array[0])) {
            return new String[0];
        }
        return array;
    }

    public void setArrayProperty(String property, String[] value) {
        String array = "";
        for (int i = 0; i < value.length; i++) {
            array += value[i] + "\n";
        }
        savePreference(property, array);
    }

    private void savePreference(String preference, String value) {
        SharedPreferences.Editor editor = mSettings.edit();
        editor.putString(preference, value);
        editor.commit();
    }

    private void savePreference(String preference, boolean value) {
        SharedPreferences.Editor editor = mSettings.edit();
        editor.putBoolean(preference, value);
        editor.commit();
    }

    private void savePreference(String preference, int value) {
        SharedPreferences.Editor editor = mSettings.edit();
        editor.putInt(preference, value);
        editor.commit();
    }
} public class FileItem implements Serializable {

    private String mKey;
    private String mName;
    private String mPath;
    private String mMd5;
    private boolean mDelete;
    private long mSize = -1;
    private int mImageAttr = 0;
    private int mImage = 0;

    public FileItem(String key, String name, String path, boolean delete) {
        mKey = key;
        mName = name;
        mPath = path;
        mDelete = delete;
    }

    public String getKey() {
        return mKey;
    }

    public String getName() {
        return mName;
    }

    public String getPath() {
        return mPath;
    }

    protected void setPath(String path) {
        mPath = path;
    }

    protected void setName(String name) {
        mName = name;
    }

    public String getShortPath() {
        return mPath.substring(0, mPath.lastIndexOf("/") + 1);
    }

    public void setDelete(boolean delete) {
        mDelete = delete;
    }

    public boolean isDelete() {
        return mDelete;
    }

    public void setMd5(String md5) {
        mMd5 = md5;
    }

    public String getMd5() {
        return mMd5;
    }

    public void setSize(long size) {
        mSize = size;
    }

    public long getSize() {
        return mSize;
    }

    public void setImage(int image) {
        mImage = image;
    }

    public int getImage() {
        return mImage;
    }

    public void setImageAttr(int imageAttr) {
        mImageAttr = imageAttr;
    }

    public int getImageAttr() {
        return mImageAttr;
    }

    public boolean isZip() {
        return mKey.endsWith(".zip") || mPath.endsWith(".zip");
    }

    public boolean isScript() {
        return mKey.endsWith(".sh") || mPath.endsWith(".sh");
    }
} public class FileItemStore {

    public interface FileItemStoreListener {

        public void storeChanged();
    }

    private static FileItemStoreListener sListener;
    private static List<FileItem> sList = new ArrayList<FileItem>();

    public static void setFileItemStoreListener(FileItemStoreListener listener) {
        sListener = listener;
    }

    public static int size() {
        return sList.size();
    }

    public static List<FileItem> getItems() {
        return sList;
    }

    public static String[] getPaths() {
        String[] paths = new String[sList.size()];
        int i = 0;
        for (FileItem item : sList) {
            paths[i] = item.getPath();
            i++;
        }
        return paths;
    }

    public static FileItem getItem(int i) {
        return sList.get(i);
    }

    public static void addItem(String realPath, String sdcardPath, boolean delete, String zipPosition) {

        sList.remove(realPath);

        FileItem item = new FileItem(realPath, sdcardPath.substring(sdcardPath.lastIndexOf("/") + 1),
                sdcardPath, delete);

        if ("first".equals(zipPosition)) {
            sList.add(0, item);
        } else {
            sList.add(item);
        }

        if (sListener != null) {
            sListener.storeChanged();
        }
    }

    public static void removeItems() {
        sList.clear();
        if (sListener != null) {
            sListener.storeChanged();
        }
    }

    public static void removeItem(String key) {
        int size = size(), i = 0;
        for (; i < size; i++) {
            if (key.equals(sList.get(i).getKey())) {
                sList.remove(i);
                return;
            }
        }
        if (sListener != null) {
            sListener.storeChanged();
        }
    }

    public static void move(int from, int to) {
        if (from == to)
            return;
        FileItem toMove = sList.get(from);
        while (sList.indexOf(toMove) != to) {
            int i = sList.indexOf(toMove);
            Collections.swap(sList, i, to < from ? i - 1 : i + 1);
        }
        if (sListener != null) {
            sListener.storeChanged();
        }
    }

    private FileItemStore() {
    }
}
import java.io.DataOutputStream;
import java.io.File;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.PowerManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.CheckBox;
import android.widget.EditText;
import android.widget.TextView;
import com.beerbong.zipinst.R;
import com.beerbong.zipinst.core.Core;
import com.beerbong.zipinst.core.Plugin;
import com.beerbong.zipinst.core.plugins.recovery.RecoveryInfo;
import com.beerbong.zipinst.core.plugins.recovery.RecoveryPlugin;
import com.beerbong.zipinst.core.plugins.storage.StoragePlugin;
import com.beerbong.zipinst.core.plugins.superuser.SuperUserPlugin;
import com.beerbong.zipinst.io.Files;
import com.beerbong.zipinst.io.InstallOptionsCursor;
import com.beerbong.zipinst.io.Strings;
import com.beerbong.zipinst.preferences.Preferences;
import com.beerbong.zipinst.store.FileItem;
import com.beerbong.zipinst.store.FileItemStore;
/*
 * Copyright 2014 ZipInstaller Project
 *
 * This file is part of ZipInstaller.
 *
 * ZipInstaller is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * ZipInstaller is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with ZipInstaller.  If not, see <http://www.gnu.org/licenses/>.
 */

package com.beerbong.zipinst.core.plugins.reboot;




public class RebootPlugin extends Plugin {

    private int mSelectedBackup;
    private int mIsSystemApp = -1;

    public RebootPlugin(Core core) {