diff --git a/app/build.gradle b/app/build.gradle
index 3c3f43a1553b3f33979f663a23c044d5b199ab16..26ba779558d721bef24342454e4e4f8bebaee11f 100755
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -10,8 +10,8 @@ android {
         minSdkVersion 8
         targetSdkVersion 23
 
-        versionCode 108
-        versionName "6.0d"
+        versionCode 111
+        versionName "6.0e"
     }
 
     buildTypes {
@@ -35,12 +35,11 @@ android {
 
     dependencies {
         compile 'com.android.support:appcompat-v7:23.4.0'
-        compile 'com.github.Andre1299:CompareString:1.4.2'
     }
 
     applicationVariants.all { variant ->
         def vn = variant.versionName
-        def x = vn.substring(0,vn.length() - 1)
+        def x = vn.substring(0, vn.length() - 1)
 
         variant.outputs.each { output ->
             output.outputFile = new File(
@@ -48,4 +47,6 @@ android {
                     output.outputFile.name.replace("app-release.apk", "${x}/${variant.applicationId}_${variant.versionName}_${new Date().format("dd-MM_hh.mm.ss")}.apk"))
         }
     }
+}
+dependencies {
 }
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index caefc4f77430f1e641ae43c47a422ba334241191..83368d5ae174b2a12e4baa5bc528766b348531af 100755
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,15 +29,6 @@
     <uses-permission android:name="android.permission.VIBRATE" />
     <uses-permission android:name="android.permission.ACCESS_FINE_LOCATION" />
     <uses-permission android:name="android.permission.ACCESS_COARSE_LOCATION" />
-    <uses-permission android:name="android.permission.READ_PHONE_STATE" />
-
-    <permission android:name="android.permission.MEDIA_CONTENT_CONTROL" />
-    <permission
-        android:name="android.permission.FLASHLIGHT"
-        android:description="@string/help_flash"
-        android:label="@string/help_flash"
-        android:permissionGroup="android.permission-group.HARDWARE_CONTROLS"
-        android:protectionLevel="normal" />
 
     <!-- features -->
     <uses-feature android:name="android.hardware.camera"
diff --git a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
index 83208301da35d7af1b2603e707cfdf970b908b10..816f3c09dcf08274fabd7e631f8af5a251b2959e 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/LauncherActivity.java
@@ -160,11 +160,7 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable {
             TimeManager.create();
         } catch (Exception e) {
             Tuils.log(Tuils.getStackTrace(e));
-
-            try {
-                e.printStackTrace(new PrintStream(new FileOutputStream(new File(Tuils.getFolder(), "crash.txt"), true)));
-            } catch (FileNotFoundException e1) {}
-
+            Tuils.toFile(e);
             return;
         }
 
@@ -213,6 +209,8 @@ public class LauncherActivity extends AppCompatActivity implements Reloadable {
         main = new MainManager(this, in, out, sugg);
         ui = new UIManager(main.getMainPack(), this, mainView, ex, main.getMainPack());
         main.setRedirectionListener(ui.buildRedirectionListener());
+        main.setHintable(ui.getHintable());
+        main.setRooter(ui.getRooter());
 
         in.in(Tuils.EMPTYSTRING);
         ui.focusTerminal();
diff --git a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
index 1a69a4d007e759eb6c486f83bf2d5d919de5efa0..309afe16c4b407c1fc073330adfa680582163ca5 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/MainManager.java
@@ -2,11 +2,14 @@ package ohi.andre.consolelauncher;
 
 import android.content.Context;
 import android.content.Intent;
+import android.os.Environment;
 import android.text.SpannableString;
 import android.text.Spanned;
 import android.text.style.ForegroundColorSpan;
 import android.util.Log;
 
+import java.io.File;
+import java.util.List;
 import java.util.regex.Pattern;
 
 import ohi.andre.consolelauncher.commands.Command;
@@ -18,7 +21,6 @@ import ohi.andre.consolelauncher.commands.specific.RedirectCommand;
 import ohi.andre.consolelauncher.managers.AliasManager;
 import ohi.andre.consolelauncher.managers.AppsManager;
 import ohi.andre.consolelauncher.managers.ContactManager;
-import ohi.andre.consolelauncher.managers.ShellManager;
 import ohi.andre.consolelauncher.managers.TerminalManager;
 import ohi.andre.consolelauncher.managers.XMLPrefsManager;
 import ohi.andre.consolelauncher.managers.music.MusicManager;
@@ -26,11 +28,15 @@ import ohi.andre.consolelauncher.tuils.StoppableThread;
 import ohi.andre.consolelauncher.tuils.TimeManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
 import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
+import ohi.andre.consolelauncher.tuils.interfaces.Hintable;
 import ohi.andre.consolelauncher.tuils.interfaces.Inputable;
 import ohi.andre.consolelauncher.tuils.interfaces.OnRedirectionListener;
 import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
 import ohi.andre.consolelauncher.tuils.interfaces.Redirectator;
+import ohi.andre.consolelauncher.tuils.interfaces.Rooter;
 import ohi.andre.consolelauncher.tuils.interfaces.Suggester;
+import ohi.andre.consolelauncher.tuils.libsuperuser.Shell;
+import ohi.andre.consolelauncher.tuils.libsuperuser.StreamGobbler;
 
 /*Copyright Francesco Andreuzzi
 
@@ -88,7 +94,6 @@ public class MainManager {
             new SystemCommandTrigger()
     };
     private MainPack mainPack;
-    private ShellManager shell;
 
     private Context mContext;
 
@@ -98,6 +103,10 @@ public class MainManager {
     private boolean showAliasValue;
     private boolean showAppHistory;
 
+    public static Shell.Interactive interactive;
+
+    private Hintable hintable;
+
     protected MainManager(LauncherActivity c, Inputable i, Outputable o, Suggester sugg) {
         mContext = c;
 
@@ -127,9 +136,24 @@ public class MainManager {
         AppsManager appsMgr = new AppsManager(c, out, sugg);
         AliasManager aliasManager = new AliasManager();
 
-        shell = new ShellManager(out);
+        interactive = new Shell.Builder()
+                .setOnSTDOUTLineListener(new StreamGobbler.OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        out.onOutput(line);
+                    }
+                })
+                .setOnSTDERRLineListener(new StreamGobbler.OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        out.onOutput(line);
+                    }
+                })
+        .open();
 
-        mainPack = new MainPack(mContext, group, aliasManager, appsMgr, music, cont, c, executer, out, redirectator, shell);
+        interactive.addCommand("cd " + Environment.getExternalStorageDirectory().getAbsolutePath());
+
+        mainPack = new MainPack(mContext, group, aliasManager, appsMgr, music, cont, c, executer, out, redirectator);
     }
 
 //    command manager
@@ -179,12 +203,21 @@ public class MainManager {
 
     public void destroy() {
         mainPack.destroy();
+        interactive.close();
     }
 
     public MainPack getMainPack() {
         return mainPack;
     }
 
+    public void setHintable(Hintable hintable) {
+        this.hintable = hintable;
+    }
+
+    public void setRooter(Rooter rooter) {
+        this.mainPack.rooter = rooter;
+    }
+
     interface CmdTrigger {
         boolean trigger(ExecutePack info, String input) throws Exception;
     }
@@ -205,14 +238,48 @@ public class MainManager {
 
     private class SystemCommandTrigger implements CmdTrigger {
 
+        final int CD_CODE = 10;
+        final int PWD_CODE = 11;
+
+        final Shell.OnCommandResultListener pwdResult = new Shell.OnCommandResultListener() {
+            @Override
+            public void onCommandResult(int commandCode, int exitCode, List<String> output) {
+                if(commandCode == PWD_CODE && output.size() == 1) {
+                    File f = new File(output.get(0));
+                    if(f.exists()) {
+                        mainPack.currentDirectory = f;
+                        if(hintable != null) hintable.updateHint();
+                    }
+                }
+            }
+        };
+
+        final Shell.OnCommandResultListener cdResult = new Shell.OnCommandResultListener() {
+            @Override
+            public void onCommandResult(int commandCode, int exitCode, List<String> output) {
+                if(commandCode == CD_CODE) {
+                    interactive.addCommand("pwd", PWD_CODE, pwdResult);
+                }
+            }
+        };
+
         @Override
         public boolean trigger(final ExecutePack info, final String input) throws Exception {
+
             new Thread() {
                 @Override
                 public void run() {
-                    shell.cmd(input, true);
-                    ((MainPack) info).currentDirectory = shell.currentDir();
-                };
+                    if(input.trim().equalsIgnoreCase("su")) {
+                        if(Shell.SU.available() && mainPack.rooter != null) mainPack.rooter.onRoot();
+                        interactive.addCommand("su");
+
+                    } else if(input.contains("cd ")) {
+                        interactive.addCommand(input, CD_CODE, cdResult);
+                    } else {
+                        interactive.addCommand(input);
+                    }
+
+                }
             }.start();
 
             return true;
diff --git a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
index e231bdda47ae84a9ab7841601dbaa8d46c87ec8d..40f77b61733047be5a27a2a2bfe6f39adbb935f3 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/UIManager.java
@@ -44,7 +44,9 @@ import ohi.andre.consolelauncher.tuils.StoppableThread;
 import ohi.andre.consolelauncher.tuils.TimeManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
 import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
+import ohi.andre.consolelauncher.tuils.interfaces.Hintable;
 import ohi.andre.consolelauncher.tuils.interfaces.OnRedirectionListener;
+import ohi.andre.consolelauncher.tuils.interfaces.Rooter;
 import ohi.andre.consolelauncher.tuils.interfaces.SuggestionViewDecorer;
 import ohi.andre.consolelauncher.tuils.stuff.PolicyReceiver;
 import ohi.andre.consolelauncher.tuils.stuff.TrashInterfaces;
@@ -740,6 +742,19 @@ public class UIManager implements OnTouchListener {
         mTerminalAdapter.scrollToEnd();
     }
 
+    public Hintable getHintable() {
+        return new Hintable() {
+            @Override
+            public void updateHint() {
+                mTerminalAdapter.setDefaultHint();
+            }
+        };
+    }
+
+    public Rooter getRooter() {
+        return mTerminalAdapter.getRooter();
+    }
+
     //	 init detector for double tap
     private void initDetector() {
         det = new GestureDetector(mContext, TrashInterfaces.trashGestureListener);
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java
index 69dfc94edd0539251f0e988deabcff97e212b5a1..1b2f4c177e4b175a7a6ec619914d07b08ee08b4a 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandAbstraction.java
@@ -22,6 +22,7 @@ public interface CommandAbstraction {
     int CONFIG_ENTRY = 23;
     int INT = 24;
     int DEFAULT_APP = 25;
+    int ALL_PACKAGES = 26;
 
     String exec(ExecutePack pack) throws Exception;
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java
index afabfdb364ce5864809c78948c46fbc17bc054b9..d1714868d00af2d0bc5959c349b4c596e72e2049 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/CommandTuils.java
@@ -24,9 +24,6 @@ import ohi.andre.consolelauncher.tuils.Tuils;
 @SuppressLint("DefaultLocale")
 public class CommandTuils {
 
-    private static final int MIN_CONTACT_RATE = 4;
-    private static final int MIN_SONG_RATE = 5;
-
     private static FileManager.SpecificExtensionFileFilter extensionFileFilter = new FileManager.SpecificExtensionFileFilter();
     private static FileManager.SpecificNameFileFilter nameFileFilter = new FileManager.SpecificNameFileFilter();
 
@@ -37,12 +34,6 @@ public class CommandTuils {
     public static Command parse(String input, ExecutePack info, boolean suggestion) throws Exception {
         Command command = new Command();
 
-        boolean pendingSuVerification = false;
-        if (!suggestion && isSuCommand(input)) {
-            input = input.substring(3);
-            pendingSuVerification = true;
-        }
-
         String name = CommandTuils.findName(input);
         if (!Tuils.isAlpha(name))
             return null;
@@ -53,10 +44,6 @@ public class CommandTuils {
         }
         command.cmd = cmd;
 
-        if (pendingSuVerification && info instanceof MainPack) {
-            ((MainPack) info).setSu(Tuils.verifyRoot());
-        }
-
         input = input.substring(name.length());
         input = input.trim();
 
@@ -169,6 +156,8 @@ public class CommandTuils {
             return integer(input);
         } else if(type == CommandAbstraction.DEFAULT_APP) {
             return defaultApp(input, ((MainPack) info).appsManager);
+        } else if(type == CommandAbstraction.ALL_PACKAGES) {
+            return allPackages(input, ((MainPack) info).appsManager);
         }
 
         return null;
@@ -354,6 +343,15 @@ public class CommandTuils {
         return new ArgInfo(info, null, info != null, info != null ? 1 : 0);
     }
 
+    private static ArgInfo allPackages(String input, AppsManager apps) {
+        AppsManager.LaunchInfo info = apps.findLaunchInfoWithLabel(input, AppsManager.SHOWN_APPS);
+        if(info == null) {
+            info = apps.findLaunchInfoWithLabel(input, AppsManager.HIDDEN_APPS);
+        }
+
+        return new ArgInfo(info, null, info != null, info != null ? 1 : 0);
+    }
+
     private static ArgInfo defaultApp(String input, AppsManager apps) {
         AppsManager.LaunchInfo info = apps.findLaunchInfoWithLabel(input, AppsManager.SHOWN_APPS);
         if(info == null) {
@@ -369,14 +367,13 @@ public class CommandTuils {
         if (Tuils.isNumber(input))
             number = input;
         else
-            number = contacts.findNumber(input, MIN_CONTACT_RATE);
+            number = contacts.findNumber(input);
 
         return new ArgInfo(number, null, number != null, 1);
     }
 
     private static ArgInfo song(String input, MusicManager music) {
-        String name = music.getSong(input, MIN_SONG_RATE);
-        return new ArgInfo(name, null, name != null, 1);
+        return new ArgInfo(input, null, true, 1);
     }
 
     private static ArgInfo configEntry(String input) {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java
index f89f0aac194f39c4a5f9bbc4eaf1c2808b0b7c45..d6bfb0701510fcd5276b85b14eb7e95953363cbb 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/MainPack.java
@@ -8,6 +8,7 @@ import android.location.LocationManager;
 import android.net.ConnectivityManager;
 import android.net.wifi.WifiManager;
 import android.os.Build;
+import android.os.Environment;
 
 import java.io.File;
 import java.lang.reflect.Method;
@@ -20,14 +21,13 @@ import ohi.andre.consolelauncher.commands.main.raw.flash;
 import ohi.andre.consolelauncher.managers.AliasManager;
 import ohi.andre.consolelauncher.managers.AppsManager;
 import ohi.andre.consolelauncher.managers.ContactManager;
-import ohi.andre.consolelauncher.managers.ShellManager;
 import ohi.andre.consolelauncher.managers.SkinManager;
-import ohi.andre.consolelauncher.managers.XMLPrefsManager;
 import ohi.andre.consolelauncher.managers.music.MusicManager;
 import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
 import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
 import ohi.andre.consolelauncher.tuils.interfaces.Redirectator;
 import ohi.andre.consolelauncher.tuils.interfaces.Reloadable;
+import ohi.andre.consolelauncher.tuils.interfaces.Rooter;
 
 /**
  * Created by francescoandreuzzi on 24/01/2017.
@@ -40,8 +40,6 @@ public class MainPack extends ExecutePack {
     //	current directory
     public File currentDirectory;
 
-    public ShellManager shellManager;
-
     public SkinManager skinManager;
 
     //	resources references
@@ -55,9 +53,6 @@ public class MainPack extends ExecutePack {
     //	internet
     public WifiManager wifi;
 
-    //	prefs
-    public XMLPrefsManager preferencesManager;
-
     //	3g/data
     public Method setMobileDataEnabledMethod;
     public ConnectivityManager connectivityMgr;
@@ -76,12 +71,12 @@ public class MainPack extends ExecutePack {
     //	reload field
     public Reloadable reloadable;
 
+    public Rooter rooter;
+
     public CommandsPreferences cmdPrefs;
 
     //	execute a command
     public CommandExecuter executer;
-    //	uses su
-    private boolean canUseSu = false;
 
     public LocationManager locationManager;
 
@@ -90,11 +85,10 @@ public class MainPack extends ExecutePack {
     public Redirectator redirectator;
 
     public MainPack(Context context, CommandGroup commandGroup, AliasManager alMgr, AppsManager appmgr, MusicManager p,
-                    ContactManager c, Reloadable r, CommandExecuter executeCommand, Outputable outputable, Redirectator redirectator, ShellManager shellManager) {
+                    ContactManager c, Reloadable r, CommandExecuter executeCommand, Outputable outputable, Redirectator redirectator) {
         super(commandGroup);
 
-        this.shellManager = shellManager;
-        this.currentDirectory = shellManager.currentDir();
+        this.currentDirectory = Environment.getExternalStorageDirectory();
 
         this.outputable = outputable;
 
@@ -119,16 +113,6 @@ public class MainPack extends ExecutePack {
         this.redirectator = redirectator;
     }
 
-    public boolean getSu() {
-        boolean su = canUseSu;
-        canUseSu = false;
-        return su;
-    }
-
-    public void setSu(boolean su) {
-        this.canUseSu = su;
-    }
-
     public void initCamera() {
         try {
             this.camera = Camera.open();
@@ -160,13 +144,5 @@ public class MainPack extends ExecutePack {
     public void destroy() {
         player.destroy(this.context);
         appsManager.onDestroy();
-        shellManager.destroy();
-    }
-
-    @Override
-    public void clear() {
-        super.clear();
-
-        setSu(false);
     }
 }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java
index 7eb3ad7cce7d5c59a4d31507c2e1c1f30fa7404f..27d74d7d30e3ae189d784abe73840c8c5855ef9f 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/apps.java
@@ -132,7 +132,7 @@ public class apps extends ParamCommand {
         frc {
             @Override
             public int[] args() {
-                return new int[] {CommandAbstraction.VISIBLE_PACKAGE};
+                return new int[] {CommandAbstraction.ALL_PACKAGES};
             }
 
             @Override
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/cp.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/cp.java
deleted file mode 100755
index 470df4564af675877df035bbb975de7fa2c641b6..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/cp.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package ohi.andre.consolelauncher.commands.main.raw;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import ohi.andre.consolelauncher.R;
-import ohi.andre.consolelauncher.commands.CommandAbstraction;
-import ohi.andre.consolelauncher.commands.ExecutePack;
-import ohi.andre.consolelauncher.commands.main.MainPack;
-import ohi.andre.consolelauncher.managers.FileManager;
-
-/**
- * Created by andre on 03/12/15.
- */
-public class cp implements CommandAbstraction {
-
-    @Override
-    public String exec(ExecutePack pack) throws Exception {
-        MainPack info = (MainPack) pack;
-
-        ArrayList<File> args = info.get(ArrayList.class, 0);
-
-        File where = args.remove(args.size() - 1);
-        File[] files = new File[args.size()];
-        args.toArray(files);
-
-        int result = FileManager.cp(files, where, info.getSu());
-        switch (result) {
-            case FileManager.ISFILE:
-                return info.res.getString(R.string.output_isfile);
-            case FileManager.IOERROR:
-                return info.res.getString(R.string.output_error);
-            case FileManager.NOT_READABLE:
-                return info.res.getString(R.string.output_noreadable);
-            case FileManager.NOT_WRITEABLE:
-                return info.res.getString(R.string.output_nowriteable);
-        }
-        return null;
-    }
-
-    @Override
-    public int minArgs() {
-        return 2;
-    }
-
-    @Override
-    public int maxArgs() {
-        return CommandAbstraction.UNDEFINIED;
-    }
-
-    @Override
-    public int[] argType() {
-        return new int[]{CommandAbstraction.FILE_LIST};
-    }
-
-    @Override
-    public int priority() {
-        return 4;
-    }
-
-    @Override
-    public int helpRes() {
-        return R.string.help_cp;
-    }
-
-    @Override
-    public String onArgNotFound(ExecutePack pack, int index) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-
-    @Override
-    public String onNotArgEnough(ExecutePack pack, int nArgs) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java
index f8bd9ac29ce139a457bf0d1c316ba9f643c6a31d..81aa1ed805a975849328fcdd441f50571940f6b2 100644
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ctrlc.java
@@ -1,9 +1,14 @@
 package ohi.andre.consolelauncher.commands.main.raw;
 
+import android.os.Environment;
+
+import ohi.andre.consolelauncher.MainManager;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
 import ohi.andre.consolelauncher.commands.main.MainPack;
+import ohi.andre.consolelauncher.tuils.libsuperuser.Shell;
+import ohi.andre.consolelauncher.tuils.libsuperuser.StreamGobbler;
 
 /**
  * Created by francescoandreuzzi on 26/07/2017.
@@ -12,8 +17,37 @@ import ohi.andre.consolelauncher.commands.main.MainPack;
 public class ctrlc implements CommandAbstraction {
 
     @Override
-    public String exec(ExecutePack pack) throws Exception {
-        ((MainPack) pack).shellManager.reset();
+    public String exec(final ExecutePack pack) throws Exception {
+        new Thread() {
+            @Override
+            public void run() {
+                super.run();
+
+                MainManager.interactive.kill();
+                MainManager.interactive.close();
+                MainManager.interactive = null;
+
+                MainManager.interactive = new Shell.Builder()
+                        .setOnSTDOUTLineListener(new StreamGobbler.OnLineListener() {
+                            @Override
+                            public void onLine(String line) {
+                                ((MainPack) pack).outputable.onOutput(line);
+                            }
+                        })
+                        .setOnSTDERRLineListener(new StreamGobbler.OnLineListener() {
+                            @Override
+                            public void onLine(String line) {
+                                ((MainPack) pack).outputable.onOutput(line);
+                            }
+                        })
+                        .open();
+
+                MainManager.interactive.addCommand("cd " + Environment.getExternalStorageDirectory().getAbsolutePath());
+            }
+        }.start();
+
+        ((MainPack) pack).rooter.onStandard();
+
         return null;
     }
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java
index 313f46b97b61bb37f753c5a3d1d9e6261029d62e..7240a73174f29883f792d1fb21c31b99db81db2c 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/data.java
@@ -13,7 +13,6 @@ import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
 import ohi.andre.consolelauncher.commands.main.MainPack;
-import ohi.andre.consolelauncher.tuils.ShellUtils;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class data implements CommandAbstraction {
@@ -27,8 +26,8 @@ public class data implements CommandAbstraction {
             active = toggle(info);
             return info.res.getString(R.string.output_data) + Tuils.SPACE + Boolean.toString(active);
         } else {
-            ShellUtils.CommandResult result = ShellUtils.execCommand("svc data " + (active ? "enable" : "disable"), true, null);
-            return info.res.getString(R.string.output_commandexitvalue) + Tuils.SPACE + result.result;
+//            ShellUtils.CommandResult result = ShellUtils.execCommand("svc data " + (active ? "enable" : "disable"), true, null);
+            return pack.context.getString(R.string.output_nofeature);
         }
     }
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java
index b0d47f0469e35320834e8351e055ad6117a18f76..465f71f540059f7574d88f7411bda4332f0e15a2 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/help.java
@@ -6,7 +6,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
@@ -56,7 +55,7 @@ public class help implements CommandAbstraction {
         Collections.sort(toPrint, new Comparator<String>() {
             @Override
             public int compare(String lhs, String rhs) {
-                return Compare.alphabeticCompare(lhs, rhs);
+                return Tuils.alphabeticCompare(lhs, rhs);
             }
         });
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ls.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ls.java
deleted file mode 100755
index c4995c969810d980972d13b1565fd2d1cd3ea8fd..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/ls.java
+++ /dev/null
@@ -1,85 +0,0 @@
-package ohi.andre.consolelauncher.commands.main.raw;
-
-/*Copyright Francesco Andreuzzi
-
-Licensed under the Apache License, Version 2.0 (the "License");
-you may not use this file except in compliance with the License.
-You may obtain a copy of the License at
-
-    http://www.apache.org/licenses/LICENSE-2.0
-
-Unless required by applicable law or agreed to in writing, software
-distributed under the License is distributed on an "AS IS" BASIS,
-WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-See the License for the specific language governing permissions and
-limitations under the License.*/
-
-import java.io.File;
-import java.util.List;
-
-import ohi.andre.consolelauncher.R;
-import ohi.andre.consolelauncher.commands.CommandAbstraction;
-import ohi.andre.consolelauncher.commands.ExecutePack;
-import ohi.andre.consolelauncher.commands.main.MainPack;
-import ohi.andre.consolelauncher.managers.FileManager;
-import ohi.andre.consolelauncher.tuils.ShellUtils;
-import ohi.andre.consolelauncher.tuils.Tuils;
-
-public class ls implements CommandAbstraction {
-
-    @Override
-    public String exec(ExecutePack pack) throws Exception {
-        MainPack info = (MainPack) pack;
-        File file = info.get(File.class, 0);
-
-        if (info.getSu()) {
-            ShellUtils.CommandResult result = ShellUtils.execCommand("ls " + file.getAbsolutePath(), true, info.currentDirectory.getAbsolutePath());
-            return result.toString();
-        } else {
-            List<File> files = FileManager.lsFile(file, true);
-            return Tuils.filesToPlanString(files, Tuils.NEWLINE);
-        }
-    }
-
-    @Override
-    public int minArgs() {
-        return 1;
-    }
-
-    @Override
-    public int maxArgs() {
-        return 1;
-    }
-
-    @Override
-    public int[] argType() {
-        return new int[]{CommandAbstraction.FILE};
-    }
-
-    @Override
-    public int priority() {
-        return 3;
-    }
-
-    @Override
-    public int helpRes() {
-        return R.string.help_ls;
-    }
-
-    @Override
-    public String onArgNotFound(ExecutePack pack, int index) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-
-    @Override
-    public String onNotArgEnough(ExecutePack pack, int nArgs) {
-        MainPack info = (MainPack) pack;
-        info.set(new File[]{info.currentDirectory});
-        try {
-            return exec(info);
-        } catch (Exception e) {
-            return e.toString();
-        }
-    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/mv.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/mv.java
deleted file mode 100755
index b7c565ba5e74d40650786a04647b4810e053fc0b..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/mv.java
+++ /dev/null
@@ -1,77 +0,0 @@
-package ohi.andre.consolelauncher.commands.main.raw;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import ohi.andre.consolelauncher.R;
-import ohi.andre.consolelauncher.commands.CommandAbstraction;
-import ohi.andre.consolelauncher.commands.ExecutePack;
-import ohi.andre.consolelauncher.commands.main.MainPack;
-import ohi.andre.consolelauncher.managers.FileManager;
-
-/**
- * Created by andre on 03/12/15.
- */
-public class mv implements CommandAbstraction {
-
-    @Override
-    public String exec(ExecutePack pack) throws Exception {
-        MainPack info = (MainPack) pack;
-
-        ArrayList<File> args = info.get(ArrayList.class, 0);
-
-        File where = args.remove(args.size() - 1);
-        File[] files = new File[args.size()];
-        args.toArray(files);
-
-        int result = FileManager.mv(files, where, info.getSu());
-        switch (result) {
-            case FileManager.ISFILE:
-                return info.res.getString(R.string.output_isfile);
-            case FileManager.IOERROR:
-                return info.res.getString(R.string.output_error);
-            case FileManager.NOT_READABLE:
-                return info.res.getString(R.string.output_noreadable);
-            case FileManager.NOT_WRITEABLE:
-                return info.res.getString(R.string.output_nowriteable);
-        }
-        return null;
-    }
-
-    @Override
-    public int minArgs() {
-        return 2;
-    }
-
-    @Override
-    public int maxArgs() {
-        return CommandAbstraction.UNDEFINIED;
-    }
-
-    @Override
-    public int[] argType() {
-        return new int[]{CommandAbstraction.FILE_LIST};
-    }
-
-    @Override
-    public int priority() {
-        return 4;
-    }
-
-    @Override
-    public int helpRes() {
-        return R.string.help_mv;
-    }
-
-    @Override
-    public String onArgNotFound(ExecutePack pack, int index) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-
-    @Override
-    public String onNotArgEnough(ExecutePack pack, int nArgs) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rm.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rm.java
deleted file mode 100755
index 0dd1827343d96bd226406a69316a6bd70a3aa16d..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/rm.java
+++ /dev/null
@@ -1,75 +0,0 @@
-package ohi.andre.consolelauncher.commands.main.raw;
-
-import android.util.Log;
-
-import java.io.File;
-import java.util.ArrayList;
-
-import ohi.andre.consolelauncher.R;
-import ohi.andre.consolelauncher.commands.CommandAbstraction;
-import ohi.andre.consolelauncher.commands.ExecutePack;
-import ohi.andre.consolelauncher.commands.main.MainPack;
-import ohi.andre.consolelauncher.managers.FileManager;
-
-/**
- * Created by andre on 03/12/15.
- */
-public class rm implements CommandAbstraction {
-
-    @Override
-    public String exec(ExecutePack pack) {
-        MainPack info = (MainPack) pack;
-        ArrayList<File> args = info.get(ArrayList.class, 0);
-
-        File[] files = new File[args.size()];
-        args.toArray(files);
-
-        int result = FileManager.rm(files, info.getSu());
-        switch (result) {
-            case FileManager.ISFILE:
-                return info.res.getString(R.string.output_isfile);
-            case FileManager.IOERROR:
-                return info.res.getString(R.string.output_error);
-            case FileManager.NOT_WRITEABLE:
-                return info.res.getString(R.string.output_nowriteable);
-        }
-        return null;
-    }
-
-    @Override
-    public int minArgs() {
-        return 1;
-    }
-
-    @Override
-    public int maxArgs() {
-        return CommandAbstraction.UNDEFINIED;
-    }
-
-    @Override
-    public int[] argType() {
-        return new int[]{CommandAbstraction.FILE_LIST};
-    }
-
-    @Override
-    public int priority() {
-        return 4;
-    }
-
-    @Override
-    public int helpRes() {
-        return R.string.help_rm;
-    }
-
-    @Override
-    public String onArgNotFound(ExecutePack pack, int index) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-
-    @Override
-    public String onNotArgEnough(ExecutePack pack, int nArgs) {
-        MainPack info = (MainPack) pack;
-        return info.res.getString(R.string.output_filenotfound);
-    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java
index 556b86344a9515b66d64b39956a54a30c7dd0dce..3d98020be9c6c0805d25318d71ba542ad2f05111 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/search.java
@@ -9,18 +9,14 @@ import java.io.File;
 import java.util.ArrayList;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
 import ohi.andre.consolelauncher.commands.main.MainPack;
 import ohi.andre.consolelauncher.commands.specific.ParamCommand;
-import ohi.andre.consolelauncher.managers.FileManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
 import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
 
-import static ohi.andre.consolelauncher.managers.FileManager.MIN_FILE_RATE;
-
 public class search extends ParamCommand {
 
     private static final String YOUTUBE_PREFIX = "https://www.youtube.com/results?search_query=";
@@ -177,7 +173,7 @@ public class search extends ParamCommand {
                 super.run();
 
                 String name = Tuils.toPlanString(args);
-                List<String> paths = rightPaths(cd, name, FileManager.USE_SCROLL_COMPARE);
+                List<String> paths = rightPaths(cd, name);
                 if(paths.size() == 0) {
                     outputable.onOutput(res.getString(R.string.output_nothing_found));
                 } else {
@@ -189,31 +185,27 @@ public class search extends ParamCommand {
         return Tuils.EMPTYSTRING;
     }
 
-    private static List<String> rightPaths(File dir, String name, boolean scrollCompare) {
+    private static List<String> rightPaths(File dir, String name) {
         File[] files = dir.listFiles();
         List<String> rightPaths = new ArrayList<>(files.length);
 
         boolean check = false;
         for (File file : files) {
-            if (fileMatch(file, name, scrollCompare)) {
+            if (fileMatch(file, name)) {
                 if (!check)
                     rightPaths.add(dir.getAbsolutePath());
                 check = true;
                 rightPaths.add(Tuils.NEWLINE + Tuils.DOUBLE_SPACE + file.getAbsolutePath());
             }
             if (file.isDirectory())
-                rightPaths.addAll(rightPaths(file, name, scrollCompare));
+                rightPaths.addAll(rightPaths(file, name));
         }
 
         return rightPaths;
     }
 
-    private static boolean fileMatch(File f, String name, boolean scrollCompare) {
-        if (scrollCompare) {
-            return Compare.scrollComparison(f.getName(), name) >= MIN_FILE_RATE;
-        } else {
-            return Compare.linearComparison(f.getName(), name) >= MIN_FILE_RATE;
-        }
+    private static boolean fileMatch(File f, String name) {
+        return f.getName().equalsIgnoreCase(name);
     }
 
     private static String youTube(List<String> args, Context c) {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java
index 250fb4d7f341a214fd609c1d54eb480a3f7913b0..f9ad8ed30264a5a1c9a58e1428f08b5016f6b5ac 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/shellcommands.java
@@ -10,7 +10,6 @@ import java.util.HashSet;
 import java.util.List;
 import java.util.Set;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
@@ -29,7 +28,7 @@ public class shellcommands implements CommandAbstraction {
         Collections.sort(commands, new Comparator<String>() {
             @Override
             public int compare(String lhs, String rhs) {
-                return Compare.alphabeticCompare(lhs, rhs);
+                return Tuils.alphabeticCompare(lhs, rhs);
             }
         });
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tui.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tui.java
index e572ae0bc93afb91433ad2436c7018e5383f048a..26b1f8d4396ca01793e039e901ccfbc01519d506 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tui.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/tui.java
@@ -49,7 +49,7 @@ public class tui extends ParamCommand {
         reset {
             @Override
             public String exec(ExecutePack pack) {
-                FileManager.rm(Tuils.getFolder(), false);
+                FileManager.rm(Tuils.getFolder());
                 return null;
             }
         },
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java
index 881377f4d6b583dcd3dd9ca745c686c0d4a2f6de..949d893d873c561f0a60354de5965806b20df7fe 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/uninstall.java
@@ -8,7 +8,6 @@ import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
 import ohi.andre.consolelauncher.commands.main.MainPack;
 import ohi.andre.consolelauncher.managers.AppsManager;
-import ohi.andre.consolelauncher.tuils.ShellUtils;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class uninstall implements CommandAbstraction {
@@ -18,11 +17,11 @@ public class uninstall implements CommandAbstraction {
         MainPack info = (MainPack) pack;
 
         String packageName = info.get(AppsManager.LaunchInfo.class, 0).componentName.getPackageName();
-        if (info.getSu()) {
-            try {
-                return ShellUtils.execCommand("su pm uninstall " + packageName, true, null).toString();
-            } catch (Exception e) {}
-        }
+//        if (info.getSu()) {
+//            try {
+//                return ShellUtils.execCommand("su pm uninstall " + packageName, true, null).toString();
+//            } catch (Exception e) {}
+//        }
 
         Uri packageURI = Uri.parse("package:" + packageName);
         Intent uninstallIntent = new Intent(Intent.ACTION_DELETE, packageURI);
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java
index fd90d4dfa0fa3ef8ae50cbea0b484dc5ba5e8287..8383a3356d093b7a21ff2c201031b7998fe6f989 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/tuixt/raw/help.java
@@ -6,7 +6,6 @@ import java.util.Collections;
 import java.util.Comparator;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.ExecutePack;
@@ -66,7 +65,7 @@ public class help implements CommandAbstraction {
         Collections.sort(toPrint, new Comparator<String>() {
             @Override
             public int compare(String lhs, String rhs) {
-                return Compare.alphabeticCompare(lhs, rhs);
+                return Tuils.alphabeticCompare(lhs, rhs);
             }
         });
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
index 7a8280bdb7ec79c214ff8476e4b87a2dc0662885..31d9e7671ded0b7044980753c08c243e6006ea34 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/AppsManager.java
@@ -35,7 +35,6 @@ import java.util.regex.Pattern;
 import javax.xml.parsers.DocumentBuilder;
 import javax.xml.parsers.DocumentBuilderFactory;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.R;
 import ohi.andre.consolelauncher.tuils.TimeManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
@@ -52,8 +51,6 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement {
     public static final int SHOWN_APPS = 10;
     public static final int HIDDEN_APPS = 11;
 
-    public static final boolean USE_SCROLL_COMPARE = false;
-
     public static final String PATH = "apps.xml";
     private final String NAME = "APPS";
 
@@ -932,7 +929,7 @@ public class AppsManager implements XMLPrefsManager.XmlPrefsElement {
             Collections.sort(list, new Comparator<String>() {
                 @Override
                 public int compare(String lhs, String rhs) {
-                    return Compare.alphabeticCompare(lhs, rhs);
+                    return Tuils.alphabeticCompare(lhs, rhs);
                 }
             });
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
index 978deb1e361b2d62e6c1d99602925bd156a06dba..10e8aa736aa554369b8e6c0d9d26cd9adb9d53da 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/ContactManager.java
@@ -15,14 +15,11 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.LauncherActivity;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class ContactManager {
 
-    public static final boolean USE_SCROLL_COMPARE = false;
-
     private Context context;
     private List<Contact> contacts;
 
@@ -213,15 +210,12 @@ public class ContactManager {
         return about;
     }
 
-    public String findNumber(String name, int minRate) {
+    public String findNumber(String name) {
         if(contacts == null) refreshContacts(this, context);
 
-        String mostSuitable = Compare.similarString(listNames(), name, minRate, USE_SCROLL_COMPARE);
-        if(mostSuitable == null) return null;
-
         for(int count = 0; count < contacts.size(); count++) {
             Contact c = contacts.get(count);
-            if(c.name.equals(mostSuitable)) {
+            if(c.name.equalsIgnoreCase(name)) {
                 if(c.numbers.size() > 0) return c.numbers.get(0);
             }
         }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
index 8292b0497fa923c29b2287fe7e4fe6eb64f4de51..e81a56e930745513c61bf8b5adc8c98b5bfefcd8 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/FileManager.java
@@ -13,8 +13,7 @@ import java.util.Arrays;
 import java.util.Comparator;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
-import ohi.andre.consolelauncher.tuils.ShellUtils;
+import ohi.andre.consolelauncher.MainManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 public class FileManager {
@@ -62,7 +61,7 @@ public class FileManager {
         }
     }
 
-    public static int mv(File[] files, File where, boolean su) throws IOException {
+    public static int mv(File[] files, File where) throws IOException {
         if (files == null || files.length == 0 || where == null) {
             return FileManager.FILE_NOTFOUND;
         }
@@ -72,43 +71,35 @@ public class FileManager {
         }
 
         for (File f : files) {
-            mv(f, where, su);
+            mv(f, where);
         }
 
         return 0;
     }
 
-    private static int mv(File f, File where, boolean su) throws IOException {
-        ShellUtils.CommandResult result = ShellUtils.execCommand("mv " + Tuils.SPACE +
-                f.getAbsolutePath() + Tuils.SPACE +
-                where.getAbsolutePath(), su, null);
-
-        return result.result;
+    private static int mv(File f, File where) throws IOException {
+        MainManager.interactive.addCommand("mv " + Tuils.SPACE + f.getAbsolutePath() + Tuils.SPACE + where.getAbsolutePath());
+        return 0;
     }
 
-    public static int rm(File[] files, boolean su) {
+    public static int rm(File[] files) {
         if (files == null || files.length == 0) {
             return FileManager.FILE_NOTFOUND;
         }
 
         for (File f : files) {
-            rm(f, su);
+            rm(f);
         }
 
         return 0;
     }
 
-    public static int rm(File f, boolean su) {
-        ShellUtils.CommandResult result = ShellUtils.execCommand("rm " +
-                (f.isDirectory() ? "-r" : Tuils.EMPTYSTRING) +
-                Tuils.SPACE +
-                f.getAbsolutePath(), su, null);
-
-        if(result == null) return IOERROR;
-        return result.result;
+    public static int rm(File f) {
+        MainManager.interactive.addCommand("rm " + (f.isDirectory() ? "-r" : Tuils.EMPTYSTRING) + Tuils.SPACE + f.getAbsolutePath());
+        return 0;
     }
 
-    public static int cp(File[] files, File where, boolean su) throws IOException {
+    public static int cp(File[] files, File where) throws IOException {
         if (files == null || files.length == 0 || where == null) {
             return FileManager.FILE_NOTFOUND;
         }
@@ -118,17 +109,15 @@ public class FileManager {
         }
 
         for (File f : files) {
-            cp(f, where, su);
+            cp(f, where);
         }
 
         return 0;
     }
 
-    private static int cp(File f, File where, boolean su) throws IOException {
-        ShellUtils.CommandResult result = ShellUtils.execCommand("cp " + Tuils.SPACE +
-                f.getAbsolutePath() + Tuils.SPACE +
-                where.getAbsolutePath(), su, null);
-        return result.result;
+    private static int cp(File f, File where) throws IOException {
+        MainManager.interactive.addCommand("cp " + Tuils.SPACE + f.getAbsolutePath() + Tuils.SPACE + where.getAbsolutePath());
+        return 0;
     }
 
     public static List<File> lsFile(File f, boolean showHidden) {
@@ -156,7 +145,7 @@ public class FileManager {
                 if (rhs.isDirectory() && !lhs.isDirectory())
                     return 1;
 
-                return Compare.alphabeticCompare(lhs.getName(), rhs.getName());
+                return Tuils.alphabeticCompare(lhs.getName(), rhs.getName());
             }
         });
 
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/ShellManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/ShellManager.java
deleted file mode 100644
index 2636bc14ed0ee8894212d5c4511e7ca5dfe16281..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/ShellManager.java
+++ /dev/null
@@ -1,259 +0,0 @@
-package ohi.andre.consolelauncher.managers;
-
-import android.os.Environment;
-
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.OutputStream;
-
-import ohi.andre.consolelauncher.tuils.StoppableThread;
-import ohi.andre.consolelauncher.tuils.Tuils;
-import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
-
-/**
- * Created by francescoandreuzzi on 25/07/2017.
- */
-
-public class ShellManager {
-
-    Process p;
-    Thread t;
-
-    File file;
-
-    InputStream output;
-    InputStream error;
-    OutputStream cmdStream;
-
-    Outputable outputable;
-
-    public ShellManager(Outputable outputable) {
-        this.outputable = outputable;
-
-        init(Environment.getExternalStorageDirectory());
-    }
-
-    private void init(File f) {
-        try {
-            p = Runtime.getRuntime().exec("/system/bin/sh");
-        } catch (IOException e) {
-            p = null;
-            return;
-        }
-
-        this.file = f;
-
-        output = p.getInputStream();
-        error = p.getErrorStream();
-        cmdStream = p.getOutputStream();
-
-        new Thread() {
-            @Override
-            public void run() {
-                super.run();
-
-                cd(file);
-            }
-        }.start();
-    }
-
-    public String cmd(final String cmd, final boolean write) {
-
-        if(t != null && t.isAlive()) {
-            try {
-                synchronized (t) {
-                    t.wait();
-                }
-            } catch (InterruptedException e) {}
-        }
-
-        try {
-            cmdStream.write((cmd + Tuils.NEWLINE).getBytes());
-            cmdStream.write(("echo EOF" + Tuils.NEWLINE).getBytes());
-        } catch (IOException e) {
-            return null;
-        }
-
-        final String[] outputMsg = {Tuils.EMPTYSTRING};
-
-        t = new StoppableThread() {
-            String line = Tuils.EMPTYSTRING;
-            String errorMsg = Tuils.EMPTYSTRING;
-
-            @Override
-            public void run() {
-                super.run();
-
-                do {
-                    if(Thread.currentThread().isInterrupted()) return;
-
-                    int n = 0;
-                    try {
-                        n = output.read();
-                    } catch (IOException e) {}
-
-                    if(n == -1) continue;
-
-                    char c = (char) n;
-                    if(c == '\n') {
-                        if(line.equals("EOF")) break;
-                        else {
-                            if(write) outputable.onOutput(line);
-                            outputMsg[0] = outputMsg[0] + Tuils.EMPTYSTRING + line;
-                            line = Tuils.EMPTYSTRING;
-                        }
-                    } else {
-                        line = line + c;
-                    }
-                } while (true);
-
-                if(write) {
-                    try {
-                        int available = error.available();
-                        for (int i = 0; i < available; i++) errorMsg = errorMsg + (char) error.read();
-                    } catch (IOException e) {}
-
-                    if(Thread.currentThread().isInterrupted()) return;
-
-                    outputable.onOutput(errorMsg);
-                }
-
-
-                if(cmd.startsWith("cd ")) {
-                    line = Tuils.EMPTYSTRING;
-
-//                    update the current dir
-                    try {
-                        cmdStream.write(("pwd" + Tuils.NEWLINE).getBytes());
-                    } catch (IOException e) {}
-
-                    do {
-                        int n = 0;
-                        try {
-                            n = output.read();
-                        } catch (IOException e) {}
-
-                        if(n == -1) continue;
-                        char c = (char) n;
-
-                        if(c == '\n') {
-                            file = new File(line);
-                            break;
-                        } else {
-                            line = line + c;
-                        }
-                    } while (true);
-                }
-
-                synchronized (outputMsg) {
-                    outputMsg.notify();
-                }
-
-                synchronized (this) {
-                    this.notify();
-                }
-            }
-        };
-
-        t.start();
-
-        try {
-            synchronized (outputMsg) {
-                outputMsg.wait();
-            }
-        } catch (InterruptedException e) {}
-
-        return outputMsg[0];
-    }
-
-    public void cd(File file) {
-        cmd("cd" + Tuils.SPACE + file.getAbsolutePath(), false);
-    }
-
-    public File currentDir() {
-        return file == null ? Environment.getExternalStorageDirectory() : file;
-    }
-
-    public void reset() {
-
-        if(this.p != null) {
-            p.destroy();
-            p = null;
-
-            try {
-                output.close();
-                error.close();
-                cmdStream.close();
-            } catch (IOException e) {}
-
-            output = null;
-            error = null;
-            cmdStream = null;
-        }
-
-        if(this.t != null) {
-            t.interrupt();
-            t = null;
-        }
-
-        init(this.file);
-    }
-
-    public void destroy() {
-        if(t != null) t.interrupt();
-        if(p != null) {
-            p.destroy();
-
-            try {
-                output.close();
-                error.close();
-                cmdStream.close();
-            } catch (IOException e) {}
-        }
-    }
-
-//    public boolean sendSigint() {
-//
-//        if(t != null) {
-//            t.interrupt();
-//
-//            int pid = getPid();
-//            if(pid != 0) {
-//
-//                try {
-//                    p.destroy();
-//                    Runtime.getRuntime().exec("kill -SIGINT" + Tuils.SPACE + pid);
-//                } catch (IOException e) {}
-//            }
-//
-//            try {
-//                int av = output.available();
-//                for(int i = 0; i < av; i++) {
-//                    output.read();
-//                }
-//            } catch (IOException e) {}
-//
-//            try {
-//                int av = error.available();
-//                for(int i = 0; i < av; i++) {
-//                    error.read();
-//                }
-//            } catch (IOException e) {}
-//
-//            return true;
-//        }
-//        return false;
-//    }
-//
-//    public int getPid() {
-//        String s = p.toString();
-//        String sPid = s.replaceAll("Process", Tuils.EMPTYSTRING).replace("[", Tuils.EMPTYSTRING).replace("]", Tuils.EMPTYSTRING).replaceAll("pid=", Tuils.EMPTYSTRING);
-//
-//        try {
-//            return Integer.parseInt(sPid);
-//        } catch (Exception e) {
-//            return 0;
-//        }
-//    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
index 80bf0b843dd4ad02dcbdfb361cfe6a39c683f254..457685b3595d78f1c2564cd03971c32d4f855cd6 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/TerminalMAnager.java
@@ -1,5 +1,6 @@
 package ohi.andre.consolelauncher.managers;
 
+import android.app.Activity;
 import android.content.Context;
 import android.graphics.Typeface;
 import android.os.IBinder;
@@ -27,6 +28,7 @@ import ohi.andre.consolelauncher.commands.main.MainPack;
 import ohi.andre.consolelauncher.commands.main.raw.clear;
 import ohi.andre.consolelauncher.tuils.TimeManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
+import ohi.andre.consolelauncher.tuils.interfaces.Rooter;
 
 /*Copyright Francesco Andreuzzi
 
@@ -61,6 +63,9 @@ public class TerminalManager {
     private TextView mTerminalView;
     private EditText mInputView;
 
+    private TextView mPrefix;
+    private boolean suMode;
+
     private List<String> cmdList = new ArrayList<>(CMD_LIST_SIZE);
     private int howBack = -1;
 
@@ -98,11 +103,15 @@ public class TerminalManager {
     private String inputFormat;
     private String outputFormat;
 
+    private Context mContext;
+
     public TerminalManager(final TextView terminalView, EditText inputView, TextView prefixView, ImageButton submitView, final ImageButton backView, ImageButton nextView, ImageButton deleteView,
                            ImageButton pasteView, SkinManager skinManager, final Context context, MainPack mainPack) {
         if (terminalView == null || inputView == null || prefixView == null || skinManager == null)
             throw new UnsupportedOperationException();
 
+        this.mContext = context;
+
         final Typeface lucidaConsole = Typeface.createFromAsset(context.getAssets(), "lucida_console.ttf");
 
         this.mSkinManager = skinManager;
@@ -122,6 +131,7 @@ public class TerminalManager {
         prefixView.setTextColor(this.mSkinManager.inputColor);
         prefixView.setTextSize(this.mSkinManager.getTextSize());
         prefixView.setText(prefix.endsWith(Tuils.SPACE) ? prefix : prefix + Tuils.SPACE);
+        this.mPrefix = prefixView;
 
         if (submitView != null) {
             submitView.setColorFilter(mSkinManager.enter_color);
@@ -369,7 +379,7 @@ public class TerminalManager {
             case CATEGORY_INPUT:
                 t = t.toString().trim();
 
-                boolean su = t.toString().startsWith("su ");
+                boolean su = t.toString().startsWith("su ") || suMode;
 
                 SpannableString si = new SpannableString(inputFormat);
                 si.setSpan(new ForegroundColorSpan(mSkinManager.inputColor), 0, inputFormat.length(), Spanned.SPAN_EXCLUSIVE_EXCLUSIVE);
@@ -474,6 +484,32 @@ public class TerminalManager {
         clearCmdsCount = 0;
     }
 
+    public Rooter getRooter() {
+        return new Rooter() {
+            @Override
+            public void onRoot() {
+                ((Activity) mContext).runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        suMode = true;
+                        mPrefix.setText(suPrefix.endsWith(Tuils.SPACE) ? suPrefix : suPrefix + Tuils.SPACE);
+                    }
+                });
+            }
+
+            @Override
+            public void onStandard() {
+                ((Activity) mContext).runOnUiThread(new Runnable() {
+                    @Override
+                    public void run() {
+                        suMode = false;
+                        mPrefix.setText(prefix.endsWith(Tuils.SPACE) ? prefix : prefix + Tuils.SPACE);
+                    }
+                });
+            }
+        };
+    }
+
     public static class Messager {
 
         int n;
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/XMLPrefsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/XMLPrefsManager.java
index ce03da4b451f9cfcf0902908977650ec3e41110f..4e4a19e0ba6434b4687559c6effe6b564ff60479 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/XMLPrefsManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/XMLPrefsManager.java
@@ -433,6 +433,18 @@ public class XMLPrefsManager {
             public String defaultValue() {
                 return "#03A9F4";
             }
+        },
+        suggest_alias_default {
+            @Override
+            public String defaultValue() {
+                return "true";
+            }
+        },
+        click_to_launch {
+            @Override
+            public String defaultValue() {
+                return "true";
+            }
         };
 
         @Override
@@ -524,12 +536,6 @@ public class XMLPrefsManager {
                 return "true";
             }
         },
-        suggest_alias_default {
-            @Override
-            public String defaultValue() {
-                return "true";
-            }
-        },
         clear_after_cmds {
             @Override
             public String defaultValue() {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager.java
index 316e0a8f678c85f7e2a906c449d33ebb29e7e94f..1e9a4ac7667f215d949365d9d9b23244d51819c8 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager.java
@@ -12,7 +12,6 @@ import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.managers.XMLPrefsManager;
 import ohi.andre.consolelauncher.tuils.Tuils;
 import ohi.andre.consolelauncher.tuils.broadcast.HeadsetBroadcast;
@@ -22,8 +21,6 @@ public class MusicManager implements OnCompletionListener {
 
     public static final String[] MUSIC_EXTENSIONS = {".mp3", ".wav", ".ogg", ".flac"};
 
-    public static final boolean USE_SCROLL_COMPARE = true;
-
     private List<File> files;
     private MediaPlayer mp;
 
@@ -78,9 +75,9 @@ public class MusicManager implements OnCompletionListener {
     }
 
     //	return a song by incomplete name
-    public String getSong(String s, int minRate) {
-        return Compare.similarString(getNames(), s, minRate, USE_SCROLL_COMPARE);
-    }
+//    public String getSong(String s) {
+//        return s;
+//    }
 
     //	return the path by complete name
     public String getPath(String name) {
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java b/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java
index 9b740f438a9b0baae14fa495260f89b2f96315e2..7cb42704d0713cb24a0b9f63e7b1ab3be3800803 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/suggestions/SuggestionsManager.java
@@ -6,7 +6,6 @@ import java.util.Arrays;
 import java.util.Collections;
 import java.util.List;
 
-import ohi.andre.comparestring.Compare;
 import ohi.andre.consolelauncher.commands.Command;
 import ohi.andre.consolelauncher.commands.CommandAbstraction;
 import ohi.andre.consolelauncher.commands.CommandTuils;
@@ -18,9 +17,10 @@ import ohi.andre.consolelauncher.managers.AliasManager;
 import ohi.andre.consolelauncher.managers.AppsManager;
 import ohi.andre.consolelauncher.managers.ContactManager;
 import ohi.andre.consolelauncher.managers.FileManager;
-import ohi.andre.consolelauncher.managers.music.MusicManager;
 import ohi.andre.consolelauncher.managers.XMLPrefsManager;
 import ohi.andre.consolelauncher.managers.notifications.NotificationManager;
+import ohi.andre.consolelauncher.tuils.Compare;
+import ohi.andre.consolelauncher.tuils.SimpleMutableEntry;
 import ohi.andre.consolelauncher.tuils.Tuils;
 
 import static ohi.andre.consolelauncher.commands.CommandTuils.xmlPrefsEntrys;
@@ -33,25 +33,26 @@ public class SuggestionsManager {
 
     private final int MIN_COMMAND_PRIORITY = 5;
 
-    private int min_command_rate = 4;
-    private int min_apps_rate = 4;
-    private int min_contacts_rate = 4;
-    private int min_file_rate = 2;
-    private int min_songs_rate = 4;
+//    private int min_command_rate = 4;
+//    private int min_apps_rate = 2;
+//    private int min_contacts_rate = 2;
+//    private int min_file_rate = 2;
+//    private int min_songs_rate = 2;
 
 //    use to place something at the top
     private final int MAX_RATE = 100;
     private final int NO_RATE = -1;
 
-    private final int FIRST_INTERVAL = 7;
+    private final int FIRST_INTERVAL = 6;
 
-    private boolean showAliasDefault, showAliasWasSet = false;
+    private boolean showAliasDefault, set = false, clickToLaunch;
 
     public Suggestion[] getSuggestions(MainPack info, String before, String lastWord) {
 
-        if(!showAliasWasSet) {
-            showAliasDefault = XMLPrefsManager.get(boolean.class, XMLPrefsManager.Behavior.suggest_alias_default);
-            showAliasWasSet = true;
+        if(!set) {
+            showAliasDefault = XMLPrefsManager.get(boolean.class, XMLPrefsManager.Suggestions.suggest_alias_default);
+            set = true;
+            clickToLaunch = XMLPrefsManager.get(boolean.class, XMLPrefsManager.Suggestions.click_to_launch);
         }
 
         List<Suggestion> suggestionList = new ArrayList<>();
@@ -62,6 +63,7 @@ public class SuggestionsManager {
 //        lastword = 0
         if (lastWord.length() == 0) {
 //            lastword = 0 && before = 0
+
             if (before.length() == 0) {
                 String[] apps = info.appsManager.getSuggestedApps();
                 if (apps != null) {
@@ -72,7 +74,7 @@ public class SuggestionsManager {
 
                         float shift = count + 1;
                         float rate = 1f / shift;
-                        suggestionList.add(new Suggestion(before, apps[count], true, (int) Math.ceil(rate), Suggestion.TYPE_APP));
+                        suggestionList.add(new Suggestion(before, apps[count], clickToLaunch, (int) Math.ceil(rate), Suggestion.TYPE_APP));
                     }
                 }
 
@@ -80,6 +82,7 @@ public class SuggestionsManager {
 
                 return suggestionList.toArray(new Suggestion[suggestionList.size()]);
             }
+
 //            lastword == 0 && before > 0
             else {
 //                check if this is a command
@@ -111,15 +114,31 @@ public class SuggestionsManager {
                     else suggestArgs(info, cmd.nextArg(), suggestionList, before);
 
                 } else {
-//                    >>word
-//                    not a command
-//                    ==> app
-                    suggestApp(info, suggestionList, before, Tuils.EMPTYSTRING);
+
+                    String[] split = before.replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE);
+                    boolean isShellCmd = false;
+                    for(String s : split) {
+                        if(needsFileSuggestion(s)) {
+                            isShellCmd = true;
+                            break;
+                        }
+                    }
+
+                    if(isShellCmd) {
+                        suggestFile(info, suggestionList, Tuils.EMPTYSTRING, before);
+                    } else {
+//                        ==> app
+                        suggestApp(info, suggestionList, before, Tuils.EMPTYSTRING);
+                    }
+
                 }
             }
         }
+
+
 //        lastWord > 0
         else {
+
             if (before.length() > 0) {
 //                lastword > 0 && before > 0
                 Command cmd = null;
@@ -142,10 +161,23 @@ public class SuggestionsManager {
                         suggestParams(info, suggestionList, (ParamCommand) cmd.cmd, before, lastWord);
                     } else suggestArgs(info, cmd.nextArg(), suggestionList, lastWord, before);
                 } else {
-//                    not a command
-//                    ==> app
-                    suggestApp(info, suggestionList, before + lastWord, Tuils.EMPTYSTRING);
+
+                    String[] split = before.replaceAll("['\"]", Tuils.EMPTYSTRING).split(Tuils.SPACE);
+                    boolean isShellCmd = false;
+                    for(String s : split) {
+                        if(needsFileSuggestion(s)) {
+                            isShellCmd = true;
+                            break;
+                        }
+                    }
+
+                    if(isShellCmd) {
+                        suggestFile(info, suggestionList, lastWord, before);
+                    } else {
+                        suggestApp(info, suggestionList, before + lastWord, Tuils.EMPTYSTRING);
+                    }
                 }
+
             } else {
 //                lastword > 0 && before = 0
                 suggestCommand(info, suggestionList, lastWord, before);
@@ -159,6 +191,10 @@ public class SuggestionsManager {
         return suggestionList.toArray(array);
     }
 
+    private boolean needsFileSuggestion(String cmd) {
+        return cmd.equalsIgnoreCase("ls") || cmd.equalsIgnoreCase("cd") || cmd.equalsIgnoreCase("mv") || cmd.equalsIgnoreCase("cp") || cmd.equalsIgnoreCase("rm");
+    }
+
     private void suggestPermanentSuggestions(List<Suggestion> suggestions, PermanentSuggestionCommand cmd) {
         for(String s : cmd.permanentSuggestions()) {
             Suggestion sugg = new Suggestion(null, s, false, NO_RATE, Suggestion.TYPE_PERMANENT);
@@ -167,8 +203,8 @@ public class SuggestionsManager {
     }
 
     private void suggestAlias(AliasManager aliasManager, List<Suggestion> suggestions, String lastWord) {
-        if(lastWord.length() == 0) for(String s : aliasManager.getAliases()) suggestions.add(new Suggestion(Tuils.EMPTYSTRING, s, true, NO_RATE, Suggestion.TYPE_ALIAS));
-        else for(String s : aliasManager.getAliases()) if(s.startsWith(lastWord)) suggestions.add(new Suggestion(Tuils.EMPTYSTRING, s, true, NO_RATE, Suggestion.TYPE_ALIAS));
+        if(lastWord.length() == 0) for(String s : aliasManager.getAliases()) suggestions.add(new Suggestion(Tuils.EMPTYSTRING, s, clickToLaunch, NO_RATE, Suggestion.TYPE_ALIAS));
+        else for(String s : aliasManager.getAliases()) if(s.startsWith(lastWord)) suggestions.add(new Suggestion(Tuils.EMPTYSTRING, s, clickToLaunch, NO_RATE, Suggestion.TYPE_ALIAS));
     }
 
     private void suggestParams(MainPack pack, List<Suggestion> suggestions, ParamCommand cmd, String before, String lastWord) {
@@ -182,7 +218,7 @@ public class SuggestionsManager {
                 Param p = cmd.getParam(pack, s).getValue();
                 if(p == null) continue;
 
-                suggestions.add(new Suggestion(before, s, p.args().length == 0, NO_RATE, 0));
+                suggestions.add(new Suggestion(before, s, p.args().length == 0 && clickToLaunch, NO_RATE, 0));
             }
         }
         else {
@@ -191,7 +227,7 @@ public class SuggestionsManager {
                 if(p == null) continue;
 
                 if (s.startsWith(lastWord) || s.replace("-", Tuils.EMPTYSTRING).startsWith(lastWord)) {
-                    suggestions.add(new Suggestion(before, s, p.args().length == 0, NO_RATE, 0));
+                    suggestions.add(new Suggestion(before, s, p.args().length == 0 && clickToLaunch, NO_RATE, 0));
                 }
             }
         }
@@ -232,6 +268,10 @@ public class SuggestionsManager {
                 break;
             case CommandAbstraction.DEFAULT_APP:
                 suggestDefaultApp(info, suggestions, prev, before);
+                break;
+            case CommandAbstraction.ALL_PACKAGES:
+                suggestAllPackages(info, suggestions, prev, before);
+                break;
         }
     }
 
@@ -240,8 +280,8 @@ public class SuggestionsManager {
     }
 
     private void suggestBoolean(List<Suggestion> suggestions, String before) {
-        suggestions.add(new Suggestion(before, "true", true, NO_RATE, Suggestion.TYPE_BOOLEAN));
-        suggestions.add(new Suggestion(before, "false", true, NO_RATE, Suggestion.TYPE_BOOLEAN));
+        suggestions.add(new Suggestion(before, "true", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN));
+        suggestions.add(new Suggestion(before, "false", clickToLaunch, NO_RATE, Suggestion.TYPE_BOOLEAN));
     }
 
     private void suggestFile(MainPack info, List<Suggestion> suggestions, String prev, String before) {
@@ -290,12 +330,9 @@ public class SuggestionsManager {
         if(files == null) {
             return;
         }
-        Arrays.sort(files);
-        List<Compare.CompareInfo> infos = Compare.compareInfo(files, prev, min_file_rate,
-                FileManager.USE_SCROLL_COMPARE);
 
-        for(Compare.CompareInfo i : infos) {
-            suggestions.add(new Suggestion(before, i.s, false, i.rate, Suggestion.TYPE_FILE));
+        for(SimpleMutableEntry<String, Integer> s : Compare.matchesWithRate(files, prev, false)) {
+            suggestions.add(new Suggestion(before, s.getKey(), false, s.getValue(), Suggestion.TYPE_FILE));
         }
     }
 
@@ -322,20 +359,22 @@ public class SuggestionsManager {
                 suggestions.add(new Suggestion(before, contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact));
         }
 
-        else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
-
-            for (ContactManager.Contact contact : info.contacts.getContacts())
-                if(contact.name.toLowerCase().trim().startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact));
-                }
-        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//
+//            for (ContactManager.Contact contact : info.contacts.getContacts())
+//                if(contact.name.toLowerCase().trim().startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact));
+//                }
+//        }
 
         else {
             for(ContactManager.Contact contact : info.contacts.getContacts()) {
-                int rate = ContactManager.USE_SCROLL_COMPARE ? Compare.scrollComparison(contact.name, prev) : Compare.linearComparison(contact.name, prev);
-                if(rate >= min_contacts_rate) {
-                    suggestions.add(new Suggestion(before, contact.name, true, NO_RATE, Suggestion.TYPE_CONTACT, contact));
+                if(Thread.currentThread().isInterrupted()) return;
+
+                int rate = Compare.matches(contact.name, prev, true);
+                if(rate != -1) {
+                    suggestions.add(new Suggestion(before, contact.name, true, rate, Suggestion.TYPE_CONTACT, contact));
                 }
             }
         }
@@ -344,20 +383,21 @@ public class SuggestionsManager {
     private void suggestSong(MainPack info, List<Suggestion> suggestions, String prev, String before) {
         if (prev == null || prev.length() == 0) {
             for (String s : info.player.getNames())
-                suggestions.add(new Suggestion(before, s, true, NO_RATE, Suggestion.TYPE_SONG));
-        } else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
-            List<String> names = info.player.getNames();
-            for (String n : names) {
-                if(n.toLowerCase().trim().startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_SONG));
-                }
-            }
-        } else {
-            List<Compare.CompareInfo> infos = Compare.compareInfo(info.player.getNames(), prev, min_songs_rate,
-                    MusicManager.USE_SCROLL_COMPARE);
-            for(Compare.CompareInfo i : infos) {
-                suggestions.add(new Suggestion(before, i.s, true, i.rate, Suggestion.TYPE_SONG));
+                suggestions.add(new Suggestion(before, s, clickToLaunch, NO_RATE, Suggestion.TYPE_SONG));
+        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//            List<String> names = info.player.getNames();
+//            for (String n : names) {
+//                if(n.toLowerCase().trim().startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_SONG));
+//                }
+//            }
+//        }
+        else {
+            List<SimpleMutableEntry<String, Integer>> infos = Compare.matchesWithRate(info.player.getNames(), prev, true);
+            for(SimpleMutableEntry<String, Integer> i : infos) {
+                suggestions.add(new Suggestion(before, i.getKey(), clickToLaunch, i.getValue(), Suggestion.TYPE_SONG));
             }
         }
     }
@@ -372,22 +412,15 @@ public class SuggestionsManager {
             prev = prev.toLowerCase().trim();
             String[] cmds = info.commandGroup.getCommandNames();
             for (String s : cmds) {
+                if(Thread.currentThread().isInterrupted()) return;
+
                 if(s.startsWith(prev)) {
                     CommandAbstraction cmd = info.commandGroup.getCommandByName(s);
                     int[] args = cmd.argType();
                     boolean exec = args == null || args.length == 0;
-                    suggestions.add(new Suggestion(before, s, exec, MAX_RATE, Suggestion.TYPE_COMMAND));
+                    suggestions.add(new Suggestion(before, s, exec && clickToLaunch, MAX_RATE, Suggestion.TYPE_COMMAND));
                 }
             }
-            return;
-        }
-
-        List<Compare.CompareInfo> infos = Compare.compareInfo(info.commandGroup.getCommandNames(), prev, min_command_rate, false);
-        for(Compare.CompareInfo i : infos) {
-            CommandAbstraction cmd = info.commandGroup.getCommandByName(i.s);
-            int[] args = cmd.argType();
-            boolean exec = args == null || args.length == 0;
-            suggestions.add(new Suggestion(before, i.s, exec, i.rate, Suggestion.TYPE_COMMAND));
         }
     }
 
@@ -399,11 +432,13 @@ public class SuggestionsManager {
 
     private void suggestCommand(MainPack info, List<Suggestion> suggestions, String before) {
         for (String s : info.commandGroup.getCommandNames()) {
+            if(Thread.currentThread().isInterrupted()) return;
+
             CommandAbstraction cmd = info.commandGroup.getCommandByName(s);
             if (cmd != null && cmd.priority() >= MIN_COMMAND_PRIORITY) {
                 int[] args = cmd.argType();
                 boolean exec = args == null || args.length == 0;
-                suggestions.add(new Suggestion(before, s, exec, cmd.priority(), Suggestion.TYPE_COMMAND));
+                suggestions.add(new Suggestion(before, s, exec && clickToLaunch, cmd.priority(), Suggestion.TYPE_COMMAND));
             }
         }
     }
@@ -412,20 +447,21 @@ public class SuggestionsManager {
         List<String> names = info.appsManager.getAppLabels();
         if (prev == null || prev.length() == 0) {
             for (String s : names) {
-                suggestions.add(new Suggestion(before, s, true, NO_RATE, Suggestion.TYPE_APP));
+                suggestions.add(new Suggestion(before, s, clickToLaunch, NO_RATE, Suggestion.TYPE_APP));
             }
-        } else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
-            for (String n : names) {
-                if(n.toLowerCase().trim().startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
-                }
-            }
-        } else {
-            List<Compare.CompareInfo> infos = Compare.compareInfo(names, prev, min_apps_rate,
-                    AppsManager.USE_SCROLL_COMPARE);
-            for(Compare.CompareInfo i : infos) {
-                suggestions.add(new Suggestion(before, i.s, true, i.rate, Suggestion.TYPE_APP));
+        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//            for (String n : names) {
+//                if(n.toLowerCase().trim().startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
+//                }
+//            }
+//        }
+        else {
+            List<SimpleMutableEntry<String, Integer>> infos = Compare.matchesWithRate(names, prev, true);
+            for(SimpleMutableEntry<String, Integer> i : infos) {
+                suggestions.add(new Suggestion(before, i.getKey(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP));
             }
         }
     }
@@ -434,20 +470,37 @@ public class SuggestionsManager {
         List<String> names = info.appsManager.getHiddenAppsLabels();
         if (prev == null || prev.length() == 0) {
             for (String s : names) {
-                suggestions.add(new Suggestion(before, s, true, NO_RATE, Suggestion.TYPE_APP));
+                suggestions.add(new Suggestion(before, s, clickToLaunch, NO_RATE, Suggestion.TYPE_APP));
             }
-        } else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
-            for (String n : names) {
-                if(n.toLowerCase().trim().startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
-                }
+        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//            for (String n : names) {
+//                if(n.toLowerCase().trim().startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
+//                }
+//            }
+//        }
+        else {
+            List<SimpleMutableEntry<String, Integer>> infos = Compare.matchesWithRate(names, prev, true);
+            for(SimpleMutableEntry<String, Integer> i : infos) {
+                suggestions.add(new Suggestion(before, i.getKey(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP));
+            }
+        }
+    }
+
+    private void suggestAllPackages(MainPack info, List<Suggestion> suggestions, String prev, String before) {
+        List<String> apps = info.appsManager.getHiddenAppsLabels();
+        apps.addAll(info.appsManager.getAppLabels());
+
+        if (prev == null || prev.length() == 0) {
+            for (String s : apps) {
+                suggestions.add(new Suggestion(before, s, clickToLaunch, NO_RATE, Suggestion.TYPE_APP));
             }
         } else {
-            List<Compare.CompareInfo> infos = Compare.compareInfo(names, prev, min_apps_rate,
-                    AppsManager.USE_SCROLL_COMPARE);
-            for(Compare.CompareInfo i : infos) {
-                suggestions.add(new Suggestion(before, i.s, true, i.rate, Suggestion.TYPE_APP));
+            List<SimpleMutableEntry<String, Integer>> infos = Compare.matchesWithRate(apps, prev, true);
+            for(SimpleMutableEntry<String, Integer> i : infos) {
+                suggestions.add(new Suggestion(before, i.getKey(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP));
             }
         }
     }
@@ -459,20 +512,21 @@ public class SuggestionsManager {
         List<String> names = info.appsManager.getAppLabels();
         if (prev == null || prev.length() == 0) {
             for (String s : names) {
-                suggestions.add(new Suggestion(before, s, true, NO_RATE, Suggestion.TYPE_APP));
+                suggestions.add(new Suggestion(before, s, clickToLaunch, NO_RATE, Suggestion.TYPE_APP));
             }
-        } else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
-            for (String n : names) {
-                if(n.toLowerCase().trim().startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
-                }
-            }
-        } else {
-            List<Compare.CompareInfo> infos = Compare.compareInfo(names, prev, min_apps_rate,
-                    AppsManager.USE_SCROLL_COMPARE);
-            for(Compare.CompareInfo i : infos) {
-                suggestions.add(new Suggestion(before, i.s, true, i.rate, Suggestion.TYPE_APP));
+        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//            for (String n : names) {
+//                if(n.toLowerCase().trim().startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, n, true, MAX_RATE, Suggestion.TYPE_APP));
+//                }
+//            }
+//        }
+        else {
+            List<SimpleMutableEntry<String, Integer>> infos = Compare.matchesWithRate(names, prev, true);
+            for(SimpleMutableEntry<String, Integer> i : infos) {
+                suggestions.add(new Suggestion(before, i.getKey(), clickToLaunch, i.getValue(), Suggestion.TYPE_APP));
             }
         }
     }
@@ -480,12 +534,11 @@ public class SuggestionsManager {
     private void suggestConfigEntry(List<Suggestion> suggestions, String prev, String before) {
         if(xmlPrefsEntrys == null) {
             xmlPrefsEntrys = new ArrayList<>();
-            for(XMLPrefsManager.XMLPrefsRoot element : XMLPrefsManager.XMLPrefsRoot.values()) {
-                for(XMLPrefsManager.XMLPrefsSave save : element.copy)
-                    xmlPrefsEntrys.add(save);
-            }
-            for(XMLPrefsManager.XMLPrefsSave save : AppsManager.Options.values()) xmlPrefsEntrys.add(save);
-            for(XMLPrefsManager.XMLPrefsSave save : NotificationManager.Options.values()) xmlPrefsEntrys.add(save);
+
+            for(XMLPrefsManager.XMLPrefsRoot element : XMLPrefsManager.XMLPrefsRoot.values()) xmlPrefsEntrys.addAll(element.copy);
+
+            Collections.addAll(xmlPrefsEntrys, AppsManager.Options.values());
+            Collections.addAll(xmlPrefsEntrys, NotificationManager.Options.values());
         }
 
         if(prev == null || prev.length() == 0) {
@@ -493,12 +546,24 @@ public class SuggestionsManager {
                 Suggestion sg = new Suggestion(before, s.label(), false, NO_RATE, Suggestion.TYPE_COMMAND);
                 suggestions.add(sg);
             }
-        } else if(prev.length() <= FIRST_INTERVAL) {
-            prev = prev.trim().toLowerCase();
+        }
+//        else if(prev.length() <= FIRST_INTERVAL) {
+//            prev = prev.trim().toLowerCase();
+//            for (XMLPrefsManager.XMLPrefsSave s : xmlPrefsEntrys) {
+//                String label = s.label();
+//                if(label.startsWith(prev)) {
+//                    suggestions.add(new Suggestion(before, label, false, MAX_RATE, Suggestion.TYPE_COMMAND));
+//                }
+//            }
+//        }
+        else {
             for (XMLPrefsManager.XMLPrefsSave s : xmlPrefsEntrys) {
+                if(Thread.currentThread().isInterrupted()) return;
+
                 String label = s.label();
-                if(label.startsWith(prev)) {
-                    suggestions.add(new Suggestion(before, label, false, MAX_RATE, Suggestion.TYPE_COMMAND));
+                int rate = Compare.matches(label, prev, true);
+                if(rate != -1) {
+                    suggestions.add(new Suggestion(before, label, false, rate, Suggestion.TYPE_COMMAND));
                 }
             }
         }
@@ -521,6 +586,8 @@ public class SuggestionsManager {
         } else if(prev.length() <= FIRST_INTERVAL) {
             prev = prev.trim().toLowerCase();
             for (String s : xmlPrefsFiles) {
+                if(Thread.currentThread().isInterrupted()) return;
+
                 if(s.startsWith(prev)) {
                     suggestions.add(new Suggestion(before, s, false, MAX_RATE, Suggestion.TYPE_FILE));
                 }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/Compare.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/Compare.java
index 74e34dcc972ef05e3646ef5aa285c31792eb9931..6f6849d63943527b953ef349e7c7fc6a1a1d7cd8 100644
--- a/app/src/main/java/ohi/andre/consolelauncher/tuils/Compare.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/Compare.java
@@ -25,9 +25,9 @@ public class Compare {
         return s;
     }
 
-    public static boolean matches(String compared, String comparator, boolean allowSkip, int minRate) {
-        compared = removeAccents(compared);
-        comparator = removeAccents(comparator);
+    public static int matches(String compared, String comparator, boolean allowSkip) {
+        compared = removeAccents(compared).toLowerCase().trim();
+        comparator = removeAccents(comparator).toLowerCase().trim();
 
         List<String> s = new ArrayList<>();
         if(allowSkip) {
@@ -38,30 +38,60 @@ public class Compare {
         }
         s.add(compared);
 
+        float maxRate = -1;
         for(String st : s) {
-            int rate = 0;
+            float rate = 0;
             for(int i = 0; i < st.length() && i < comparator.length(); i++) {
                 char c1 = st.charAt(i);
                 char c2 = comparator.charAt(i);
 
-                if(c1 == c2) rate++;
+                if(c1 == c2) {
+                    rate += (double) (st.length() - i) / (double) st.length();
+                }
             }
 
-            if(rate >= minRate) return true;
+            if(rate >= (double) comparator.length() / 2d) maxRate = Math.max(maxRate, rate);
         }
 
-        return false;
+        return Math.round(maxRate);
     }
 
-    public static List<String> matches(List<String> compared, String comparator, boolean allowSkip, int minRate) {
-        List<String> ms = new ArrayList<>();
+    public static List<String> matches(List<String> compared, String comparator, boolean allowSkip) {
+        List<SimpleMutableEntry<String, Integer>> ms = matchesWithRate(compared, comparator, allowSkip);
+
+        List<String> result = new ArrayList<>(ms.size());
+        for(SimpleMutableEntry<String, Integer> e : ms) {
+            result.add(e.getKey());
+        }
+
+        return result;
+    }
+
+    public static List<String> matches(String[] compared, String comparator, boolean allowSkip) {
+        return matches(Arrays.asList(compared), comparator, allowSkip);
+    }
+
+    public static List<SimpleMutableEntry<String, Integer>> matchesWithRate(List<String> compared, String comparator, boolean allowSkip) {
+        List<SimpleMutableEntry<String, Integer>> ms = new ArrayList<>();
 
         for(String s : compared) {
-            if(matches(s, comparator, allowSkip, minRate)) {
-                ms.add(s);
-            }
+            if(Thread.currentThread().isInterrupted()) return ms;
+
+            int rate = matches(s, comparator, allowSkip);
+            if(rate != -1) ms.add(new SimpleMutableEntry<>(s, rate));
         }
 
+//        Collections.sort(ms, new Comparator<SimpleMutableEntry<String, Integer>>() {
+//            @Override
+//            public int compare(SimpleMutableEntry<String, Integer> o1, SimpleMutableEntry<String, Integer> o2) {
+//                return o1.getValue() - o2.getValue();
+//            }
+//        });
+
         return ms;
     }
+
+    public static List<SimpleMutableEntry<String, Integer>> matchesWithRate(String[] compared, String comparator, boolean allowSkip) {
+        return matchesWithRate(Arrays.asList(compared), comparator, allowSkip);
+    }
 }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/ShellUtils.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/ShellUtils.java
deleted file mode 100755
index 89b9b7fce882a4c28b3fe14255f5b3a7f6c8cc8b..0000000000000000000000000000000000000000
--- a/app/src/main/java/ohi/andre/consolelauncher/tuils/ShellUtils.java
+++ /dev/null
@@ -1,150 +0,0 @@
-package ohi.andre.consolelauncher.tuils;
-
-import android.util.Log;
-
-import java.io.BufferedReader;
-import java.io.File;
-import java.io.IOException;
-import java.io.InputStreamReader;
-import java.io.InterruptedIOException;
-
-import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
-
-/**
- * Created by francescoandreuzzi on 24/04/2017.
- */
-
-public class ShellUtils {
-
-    public static class CommandResult {
-        public int result;
-        public String msg;
-
-        public CommandResult(int exit, String msg) {
-            this.result = exit;
-            this.msg = msg;
-        }
-
-        @Override
-        public String toString() {
-            return msg;
-        }
-    }
-
-    public static CommandResult execCommand(String cmd , boolean root, String path) {
-        return execCommand(new String[] {cmd}, root, path, null);
-    }
-
-    public static CommandResult execCommand(String[] cmd , boolean root, String path) {
-        return execCommand(cmd, root, path, null);
-    }
-
-    public static CommandResult execCommand(String cmd , boolean root, String path, Outputable outputable) {
-        return execCommand(new String[] {cmd}, root, path, outputable);
-    }
-
-//    custom dir doesnt work, and also multiple commands
-    public static CommandResult execCommand(String[] cmds , boolean root, String path, final Outputable outputable) {
-//        try {
-//            Process process = Runtime.getRuntime().exec(cmds[0]);
-//            process.waitFor();
-//
-//            BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
-//
-//            StringBuilder log = new StringBuilder();
-//            String line;
-//            while ((line = bufferedReader.readLine()) != null) {
-//                Log.e("andre", "uh");
-//                log.append(line + "\n");
-//            }
-//            process.destroy();
-//
-//            return new CommandResult(0, log.toString());
-//        } catch (Exception e) {
-//            return new CommandResult(0, e.toString());
-//        }
-
-        if(cmds.length > 1 || cmds.length == 0) return null;
-
-        int result = -1;
-
-        BufferedReader errorResult = null;
-        StringBuilder errorMsg = null;
-        final StringBuilder output = new StringBuilder();
-
-        try {
-
-            final Process process = Runtime.getRuntime().exec((root ? "su -c " : "") + cmds[0], null, path != null ? new File(path) : null);
-            final Thread externalThread = Thread.currentThread();
-
-            Thread readerThread = new StoppableThread() {
-
-                @Override
-                public void run() {
-                    super.run();
-
-                    if (Thread.interrupted() || externalThread.isInterrupted()) return;
-
-                    BufferedReader successResult = new BufferedReader(new InputStreamReader(process.getInputStream()));
-                    String s;
-                    try {
-                        while ((s = successResult.readLine()) != null) {
-                            if (Thread.currentThread().isInterrupted() || externalThread.isInterrupted())
-                                return;
-
-                            if (outputable != null) outputable.onOutput(Tuils.NEWLINE + s);
-                            else {
-                                output.append(Tuils.NEWLINE);
-                                output.append(s);
-                            }
-                        }
-
-                        sleep(25);
-                    } catch (StackOverflowError | Exception e) {
-                        if(outputable != null && ! (e instanceof InterruptedException)) outputable.onOutput(e.toString());
-
-                        try {
-                            successResult.close();
-                        } catch (IOException e1) {}
-
-                        return;
-                    }
-                }
-            };
-            readerThread.start();
-
-            result = process.waitFor();
-            readerThread.interrupt();
-
-            if(output.length() == 0) {
-                errorMsg = new StringBuilder();
-
-                errorResult = new BufferedReader(new InputStreamReader(process.getErrorStream()));
-
-                String s;
-                while ((s = errorResult.readLine()) != null) {
-                    if(errorMsg.length() > 0) errorMsg.append(Tuils.NEWLINE);
-                    errorMsg.append(s);
-                }
-            }
-
-            process.destroy();
-        }
-        catch (Exception e) {}
-        finally {
-            try {
-                if (errorResult != null) {
-                    errorResult.close();
-                }
-            } catch (IOException e) {}
-        }
-
-        if(output.length() > 0) {
-            return new CommandResult(result, output.toString());
-        }
-        else if(errorMsg != null) {
-            return new CommandResult(result, errorMsg.toString());
-        }
-        return null;
-    }
-}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
index 892ab32df68235fd2101b15855500e8ee6d5f8d5..a5ca000a3d2aed0fa05fe42cd1098507ecd6a107 100755
--- a/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/Tuils.java
@@ -30,13 +30,13 @@ import android.view.ViewGroup;
 import android.webkit.MimeTypeMap;
 
 import java.io.BufferedReader;
-import java.io.DataOutputStream;
 import java.io.File;
 import java.io.FileInputStream;
 import java.io.FileNotFoundException;
 import java.io.FileOutputStream;
 import java.io.IOException;
 import java.io.InputStreamReader;
+import java.io.PrintStream;
 import java.io.PrintWriter;
 import java.io.StringWriter;
 import java.math.BigDecimal;
@@ -49,9 +49,9 @@ import java.util.regex.Pattern;
 
 import dalvik.system.DexFile;
 import ohi.andre.consolelauncher.BuildConfig;
-import ohi.andre.consolelauncher.managers.music.MusicManager;
 import ohi.andre.consolelauncher.managers.SkinManager;
 import ohi.andre.consolelauncher.managers.XMLPrefsManager;
+import ohi.andre.consolelauncher.managers.music.MusicManager;
 import ohi.andre.consolelauncher.tuils.stuff.FakeLauncherActivity;
 
 public class Tuils {
@@ -387,27 +387,6 @@ public class Tuils {
         return -1;
     }
 
-    public static boolean verifyRoot() {
-        Process p;
-        try {
-            p = Runtime.getRuntime().exec("su");
-
-            DataOutputStream os = new DataOutputStream(p.getOutputStream());
-            os.writeBytes("echo \"root?\" >/system/sd/temporary.txt\n");
-
-            os.writeBytes("exit\n");
-            os.flush();
-            try {
-                p.waitFor();
-                return p.exitValue() != 255;
-            } catch (InterruptedException e) {
-                return false;
-            }
-        } catch (IOException e) {
-            return false;
-        }
-    }
-
     public static void insertHeaders(List<String> s, boolean newLine) {
         char current = 0;
         for (int count = 0; count < s.size(); count++) {
@@ -466,6 +445,12 @@ public class Tuils {
         }
     }
 
+    public static void toFile(Throwable e) {
+        try {
+            e.printStackTrace(new PrintStream(new FileOutputStream(new File(Tuils.getFolder(), "crash.txt"), true)));
+        } catch (FileNotFoundException e1) {}
+    }
+
     static FileOutputStream logStream = null;
     public static void openLogStream(String name) {
         closeStream();
@@ -756,4 +741,31 @@ public class Tuils {
         folder = tuiFolder;
         return folder;
     }
+
+    public static int alphabeticCompare(String s1, String s2) {
+        String cmd1 = removeSpaces(s1);
+        cmd1 = cmd1.toLowerCase();
+        String cmd2 = removeSpaces(s2);
+        cmd2 = cmd2.toLowerCase();
+
+        for (int count = 0; count < cmd1.length() && count < cmd2.length(); count++) {
+            if (cmd1.charAt(count) < cmd2.charAt(count)) {
+                return -1;
+            } else if (cmd1.charAt(count) > cmd2.charAt(count)) {
+                return 1;
+            }
+        }
+
+        if (cmd1.length() > cmd2.length()) {
+            return 1;
+        } else if (cmd1.length() < cmd2.length()) {
+            return -1;
+        }
+        return 0;
+    }
+
+    private static final String SPACE_REGEXP = "\\s";
+    public static String removeSpaces(String string) {
+        return string.replaceAll(SPACE_REGEXP, EMPTYSTRING);
+    }
 }
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Hintable.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Hintable.java
new file mode 100644
index 0000000000000000000000000000000000000000..0db778566bfbb6c4f8048383741b64db36503de8
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Hintable.java
@@ -0,0 +1,9 @@
+package ohi.andre.consolelauncher.tuils.interfaces;
+
+/**
+ * Created by francescoandreuzzi on 31/07/2017.
+ */
+
+public interface Hintable {
+    void updateHint();
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Rooter.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Rooter.java
new file mode 100644
index 0000000000000000000000000000000000000000..a104c5f64f5525a6e59d9fa269509f3b356f1fbf
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/interfaces/Rooter.java
@@ -0,0 +1,10 @@
+package ohi.andre.consolelauncher.tuils.interfaces;
+
+/**
+ * Created by francescoandreuzzi on 31/07/2017.
+ */
+
+public interface Rooter {
+    void onRoot();
+    void onStandard();
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/Shell.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/Shell.java
new file mode 100755
index 0000000000000000000000000000000000000000..929a589c4b85614cb960006f61b2b4a8bea512a6
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/Shell.java
@@ -0,0 +1,1747 @@
+/*
+ * Copyright (C) 2012-2015 Jorrit "Chainfire" Jongma
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ohi.andre.consolelauncher.tuils.libsuperuser;
+
+import android.os.Handler;
+import android.os.Looper;
+
+import java.io.DataOutputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Locale;
+import java.util.Map;
+import java.util.UUID;
+import java.util.concurrent.ScheduledThreadPoolExecutor;
+import java.util.concurrent.TimeUnit;
+
+import ohi.andre.consolelauncher.tuils.Tuils;
+
+import static ohi.andre.consolelauncher.tuils.libsuperuser.StreamGobbler.*;
+
+/**
+ * Class providing functionality to execute commands in a (root) shell
+ */
+public class Shell {
+    /**
+     * <p>
+     * Runs commands using the supplied shell, and returns the output, or null
+     * in case of errors.
+     * </p>
+     * <p>
+     * This method is deprecated and only provided for backwards compatibility.
+     * Use {@link #run(String, String[], String[], boolean)} instead, and see
+     * that same method for usage notes.
+     * </p>
+     *
+     * @param shell      The shell to use for executing the commands
+     * @param commands   The commands to execute
+     * @param wantSTDERR Return STDERR in the output ?
+     * @return Output of the commands, or null in case of an error
+     */
+    @Deprecated
+    public static List<String> run(String shell, String[] commands, boolean wantSTDERR) {
+        return run(shell, commands, null, wantSTDERR);
+    }
+
+    /**
+     * <p>
+     * Runs commands using the supplied shell, and returns the output, or null
+     * in case of errors.
+     * </p>
+     * <p>
+     * Note that due to compatibility with older Android versions, wantSTDERR is
+     * not implemented using redirectErrorStream, but rather appended to the
+     * output. STDOUT and STDERR are thus not guaranteed to be in the correct
+     * order in the output.
+     * </p>
+     * <p>
+     * Note as well that this code will intentionally crash when run in debug
+     * mode from the main thread of the application. You should always execute
+     * shell commands from a background thread.
+     * </p>
+     * <p>
+     * When in debug mode, the code will also excessively log the commands
+     * passed to and the output returned from the shell.
+     * </p>
+     * <p>
+     * Though this function uses background threads to gobble STDOUT and STDERR
+     * so a deadlock does not occur if the shell produces massive output, the
+     * output is still stored in a List&lt;String&gt;, and as such doing
+     * something like <em>'ls -lR /'</em> will probably have you run out of
+     * memory.
+     * </p>
+     *
+     * @param shell       The shell to use for executing the commands
+     * @param commands    The commands to execute
+     * @param environment List of all environment variables (in 'key=value'
+     *                    format) or null for defaults
+     * @param wantSTDERR  Return STDERR in the output ?
+     * @return Output of the commands, or null in case of an error
+     */
+    public static List<String> run(String shell, String[] commands, String[] environment,
+                                   boolean wantSTDERR) {
+        String shellUpper = shell.toUpperCase(Locale.ENGLISH);
+
+        List<String> res = Collections.synchronizedList(new ArrayList<String>());
+
+        try {
+            // Combine passed environment with system environment
+            if (environment != null) {
+                Map<String, String> newEnvironment = new HashMap<String, String>();
+                newEnvironment.putAll(System.getenv());
+                int split;
+                for (String entry : environment) {
+                    if ((split = entry.indexOf("=")) >= 0) {
+                        newEnvironment.put(entry.substring(0, split), entry.substring(split + 1));
+                    }
+                }
+                int i = 0;
+                environment = new String[newEnvironment.size()];
+                for (Map.Entry<String, String> entry : newEnvironment.entrySet()) {
+                    environment[i] = entry.getKey() + "=" + entry.getValue();
+                    i++;
+                }
+            }
+
+            // setup our process, retrieve STDIN stream, and STDOUT/STDERR
+            // gobblers
+            Process process = Runtime.getRuntime().exec(shell, environment);
+            DataOutputStream STDIN = new DataOutputStream(process.getOutputStream());
+            StreamGobbler STDOUT = new StreamGobbler(shellUpper + "-", process.getInputStream(),
+                    res);
+            StreamGobbler STDERR = new StreamGobbler(shellUpper + "*", process.getErrorStream(),
+                    wantSTDERR ? res : null);
+
+            // start gobbling and write our commands to the shell
+            STDOUT.start();
+            STDERR.start();
+            try {
+                for (String write : commands) {
+                    STDIN.write((write + "\n").getBytes("UTF-8"));
+                    STDIN.flush();
+                }
+                STDIN.write("exit\n".getBytes("UTF-8"));
+                STDIN.flush();
+            } catch (IOException e) {
+                if (e.getMessage().contains("EPIPE") || e.getMessage().contains("Stream closed")) {
+                    // Method most horrid to catch broken pipe, in which case we
+                    // do nothing. The command is not a shell, the shell closed
+                    // STDIN, the script already contained the exit command, etc.
+                    // these cases we want the output instead of returning null.
+                } else {
+                    // other issues we don't know how to handle, leads to
+                    // returning null
+                    throw e;
+                }
+            }
+
+            // wait for our process to finish, while we gobble away in the
+            // background
+            process.waitFor();
+
+            // make sure our threads are done gobbling, our streams are closed,
+            // and the process is destroyed - while the latter two shouldn't be
+            // needed in theory, and may even produce warnings, in "normal" Java
+            // they are required for guaranteed cleanup of resources, so lets be
+            // safe and do this on Android as well
+            try {
+                STDIN.close();
+            } catch (IOException e) {
+                // might be closed already
+            }
+            STDOUT.join();
+            STDERR.join();
+            process.destroy();
+
+            // in case of su, 255 usually indicates access denied
+            if (SU.isSU(shell) && (process.exitValue() == 255)) {
+                res = null;
+            }
+        } catch (IOException e) {
+            // shell probably not found
+            Tuils.log(e);
+            res = null;
+        } catch (InterruptedException e) {
+            // this should really be re-thrown
+            Tuils.log(e);
+            res = null;
+        }
+
+        return res;
+    }
+
+    protected static String[] availableTestCommands = new String[]{
+            "echo -BOC-",
+            "id"
+    };
+
+    /**
+     * See if the shell is alive, and if so, check the UID
+     *
+     * @param ret          Standard output from running availableTestCommands
+     * @param checkForRoot true if we are expecting this shell to be running as
+     *                     root
+     * @return true on success, false on error
+     */
+    protected static boolean parseAvailableResult(List<String> ret, boolean checkForRoot) {
+        if (ret == null)
+            return false;
+
+        // this is only one of many ways this can be done
+        boolean echo_seen = false;
+
+        for (String line : ret) {
+            if (line.contains("uid=")) {
+                // id command is working, let's see if we are actually root
+                return !checkForRoot || line.contains("uid=0");
+            } else if (line.contains("-BOC-")) {
+                // if we end up here, at least the su command starts some kind
+                // of shell, let's hope it has root privileges - no way to know without
+                // additional native binaries
+                echo_seen = true;
+            }
+        }
+
+        return echo_seen;
+    }
+
+    /**
+     * This class provides utility functions to easily execute commands using SH
+     */
+    public static class SH {
+        /**
+         * Runs command and return output
+         *
+         * @param command The command to run
+         * @return Output of the command, or null in case of an error
+         */
+        public static List<String> run(String command) {
+            return Shell.run("sh", new String[]{
+                    command
+            }, null, false);
+        }
+
+        /**
+         * Runs commands and return output
+         *
+         * @param commands The commands to run
+         * @return Output of the commands, or null in case of an error
+         */
+        public static List<String> run(List<String> commands) {
+            return Shell.run("sh", commands.toArray(new String[commands.size()]), null, false);
+        }
+
+        /**
+         * Runs commands and return output
+         *
+         * @param commands The commands to run
+         * @return Output of the commands, or null in case of an error
+         */
+        public static List<String> run(String[] commands) {
+            return Shell.run("sh", commands, null, false);
+        }
+    }
+
+    /**
+     * This class provides utility functions to easily execute commands using SU
+     * (root shell), as well as detecting whether or not root is available, and
+     * if so which version.
+     */
+    public static class SU {
+        private static Boolean isSELinuxEnforcing = null;
+        private static String[] suVersion = new String[]{
+                null, null
+        };
+
+        /**
+         * Runs command as root (if available) and return output
+         *
+         * @param command The command to run
+         * @return Output of the command, or null if root isn't available or in
+         * case of an error
+         */
+        public static List<String> run(String command) {
+            return Shell.run("su", new String[]{
+                    command
+            }, null, false);
+        }
+
+        /**
+         * Runs commands as root (if available) and return output
+         *
+         * @param commands The commands to run
+         * @return Output of the commands, or null if root isn't available or in
+         * case of an error
+         */
+        public static List<String> run(List<String> commands) {
+            return Shell.run("su", commands.toArray(new String[commands.size()]), null, false);
+        }
+
+        /**
+         * Runs commands as root (if available) and return output
+         *
+         * @param commands The commands to run
+         * @return Output of the commands, or null if root isn't available or in
+         * case of an error
+         */
+        public static List<String> run(String[] commands) {
+            return Shell.run("su", commands, null, false);
+        }
+
+        /**
+         * Detects whether or not superuser access is available, by checking the
+         * output of the "id" command if available, checking if a shell runs at
+         * all otherwise
+         *
+         * @return True if superuser access available
+         */
+        public static boolean available() {
+            // this is only one of many ways this can be done
+
+            List<String> ret = run(Shell.availableTestCommands);
+            return Shell.parseAvailableResult(ret, true);
+        }
+
+        /**
+         * <p>
+         * Detects the version of the su binary installed (if any), if supported
+         * by the binary. Most binaries support two different version numbers,
+         * the public version that is displayed to users, and an internal
+         * version number that is used for version number comparisons. Returns
+         * null if su not available or retrieving the version isn't supported.
+         * </p>
+         * <p>
+         * Note that su binary version and GUI (APK) version can be completely
+         * different.
+         * </p>
+         * <p>
+         * This function caches its result to improve performance on multiple
+         * calls
+         * </p>
+         *
+         * @param internal Request human-readable version or application
+         *                 internal version
+         * @return String containing the su version or null
+         */
+        public static synchronized String version(boolean internal) {
+            int idx = internal ? 0 : 1;
+            if (suVersion[idx] == null) {
+                String version = null;
+
+                List<String> ret = Shell.run(
+                        internal ? "su -V" : "su -v",
+                        new String[] { "exit" },
+                        null,
+                        false
+                );
+
+                if (ret != null) {
+                    for (String line : ret) {
+                        if (!internal) {
+                            if (!line.trim().equals("")) {
+                                version = line;
+                                break;
+                            }
+                        } else {
+                            try {
+                                if (Integer.parseInt(line) > 0) {
+                                    version = line;
+                                    break;
+                                }
+                            } catch (NumberFormatException e) {
+                                // should be parsable, try next line otherwise
+                            }
+                        }
+                    }
+                }
+
+                suVersion[idx] = version;
+            }
+            return suVersion[idx];
+        }
+
+        /**
+         * Attempts to deduce if the shell command refers to a su shell
+         *
+         * @param shell Shell command to run
+         * @return Shell command appears to be su
+         */
+        public static boolean isSU(String shell) {
+            // Strip parameters
+            int pos = shell.indexOf(' ');
+            if (pos >= 0) {
+                shell = shell.substring(0, pos);
+            }
+
+            // Strip path
+            pos = shell.lastIndexOf('/');
+            if (pos >= 0) {
+                shell = shell.substring(pos + 1);
+            }
+
+            return shell.equals("su");
+        }
+
+        /**
+         * Constructs a shell command to start a su shell using the supplied uid
+         * and SELinux context. This is can be an expensive operation, consider
+         * caching the result.
+         *
+         * @param uid     Uid to use (0 == root)
+         * @param context (SELinux) context name to use or null
+         * @return Shell command
+         */
+        public static String shell(int uid, String context) {
+            // su[ --context <context>][ <uid>]
+            String shell = "su";
+
+            if ((context != null) && isSELinuxEnforcing()) {
+                String display = version(false);
+                String internal = version(true);
+
+                // We only know the format for SuperSU v1.90+ right now
+                //TODO add detection for other su's that support this
+                if ((display != null) &&
+                        (internal != null) &&
+                        (display.endsWith("SUPERSU")) &&
+                        (Integer.valueOf(internal) >= 190)) {
+                    shell = String.format(Locale.ENGLISH, "%s --context %s", shell, context);
+                }
+            }
+
+            // Most su binaries support the "su <uid>" format, but in case
+            // they don't, lets skip it for the default 0 (root) case
+            if (uid > 0) {
+                shell = String.format(Locale.ENGLISH, "%s %d", shell, uid);
+            }
+
+            return shell;
+        }
+
+        /**
+         * Constructs a shell command to start a su shell connected to mount
+         * master daemon, to perform public mounts on Android 4.3+ (or 4.2+ in
+         * SELinux enforcing mode)
+         *
+         * @return Shell command
+         */
+        public static String shellMountMaster() {
+            if (android.os.Build.VERSION.SDK_INT >= 17) {
+                return "su --mount-master";
+            }
+            return "su";
+        }
+
+        /**
+         * Detect if SELinux is set to enforcing, caches result
+         *
+         * @return true if SELinux set to enforcing, or false in the case of
+         * permissive or not present
+         */
+        public static synchronized boolean isSELinuxEnforcing() {
+            if (isSELinuxEnforcing == null) {
+                Boolean enforcing = null;
+
+                // First known firmware with SELinux built-in was a 4.2 (17)
+                // leak
+                if (android.os.Build.VERSION.SDK_INT >= 17) {
+                    // Detect enforcing through sysfs, not always present
+                    File f = new File("/sys/fs/selinux/enforce");
+                    if (f.exists()) {
+                        try {
+                            InputStream is = new FileInputStream("/sys/fs/selinux/enforce");
+                            try {
+                                enforcing = (is.read() == '1');
+                            } finally {
+                                is.close();
+                            }
+                        } catch (Exception e) {
+                            // we might not be allowed to read, thanks SELinux
+                        }
+                    }
+
+                    // 4.4+ has a new API to detect SELinux mode, so use it
+                    // SELinux is typically in enforced mode, but emulators may have SELinux disabled
+                    if (enforcing == null) {
+                        try {
+                            Class seLinux = Class.forName("android.os.SELinux");
+                            Method isSELinuxEnforced = seLinux.getMethod("isSELinuxEnforced");
+                            enforcing = (Boolean) isSELinuxEnforced.invoke(seLinux.newInstance());
+                        } catch (Exception e) {
+                            // 4.4+ release builds are enforcing by default, take the gamble
+                            enforcing = (android.os.Build.VERSION.SDK_INT >= 19);
+                        }
+                    }
+                }
+
+                if (enforcing == null) {
+                    enforcing = false;
+                }
+
+                isSELinuxEnforcing = enforcing;
+            }
+            return isSELinuxEnforcing;
+        }
+
+        /**
+         * <p>
+         * Clears results cached by isSELinuxEnforcing() and version(boolean
+         * internal) calls.
+         * </p>
+         * <p>
+         * Most apps should never need to call this, as neither enforcing status
+         * nor su version is likely to change on a running device - though it is
+         * not impossible.
+         * </p>
+         */
+        public static synchronized void clearCachedResults() {
+            isSELinuxEnforcing = null;
+            suVersion[0] = null;
+            suVersion[1] = null;
+        }
+    }
+
+    private interface OnResult {
+        // for any onCommandResult callback
+        int WATCHDOG_EXIT = -1;
+        int SHELL_DIED = -2;
+
+        // for Interactive.open() callbacks only
+        int SHELL_EXEC_FAILED = -3;
+        int SHELL_WRONG_UID = -4;
+        int SHELL_RUNNING = 0;
+    }
+
+    /**
+     * Command result callback, notifies the recipient of the completion of a
+     * command block, including the (last) exit code, and the full output
+     */
+    public interface OnCommandResultListener extends OnResult {
+        /**
+         * <p>
+         * Command result callback
+         * </p>
+         * <p>
+         * Depending on how and on which thread the shell was created, this
+         * callback may be executed on one of the gobbler threads. In that case,
+         * it is important the callback returns as quickly as possible, as
+         * delays in this callback may pause the native process or even result
+         * in a deadlock
+         * </p>
+         * <p>
+         * See {@link Interactive} for threading details
+         * </p>
+         *
+         * @param commandCode Value previously supplied to addCommand
+         * @param exitCode    Exit code of the last command in the block
+         * @param output      All output generated by the command block
+         */
+        void onCommandResult(int commandCode, int exitCode, List<String> output);
+    }
+
+    /**
+     * Command per line callback for parsing the output line by line without
+     * buffering It also notifies the recipient of the completion of a command
+     * block, including the (last) exit code.
+     */
+    public interface OnCommandLineListener extends OnResult, OnLineListener {
+        /**
+         * <p>
+         * Command result callback
+         * </p>
+         * <p>
+         * Depending on how and on which thread the shell was created, this
+         * callback may be executed on one of the gobbler threads. In that case,
+         * it is important the callback returns as quickly as possible, as
+         * delays in this callback may pause the native process or even result
+         * in a deadlock
+         * </p>
+         * <p>
+         * See {@link Interactive} for threading details
+         * </p>
+         *
+         * @param commandCode Value previously supplied to addCommand
+         * @param exitCode    Exit code of the last command in the block
+         */
+        void onCommandResult(int commandCode, int exitCode);
+    }
+
+    /**
+     * Internal class to store command block properties
+     */
+    private static class Command {
+        private static int commandCounter = 0;
+
+        private final String[] commands;
+        private final int code;
+        private final OnCommandResultListener onCommandResultListener;
+        private final OnCommandLineListener onCommandLineListener;
+        private final String marker;
+
+        public Command(String[] commands, int code,
+                       OnCommandResultListener onCommandResultListener,
+                       OnCommandLineListener onCommandLineListener) {
+            this.commands = commands;
+            this.code = code;
+            this.onCommandResultListener = onCommandResultListener;
+            this.onCommandLineListener = onCommandLineListener;
+            this.marker = UUID.randomUUID().toString() + String.format("-%08x", ++commandCounter);
+        }
+    }
+
+    /**
+     * Builder class for {@link Interactive}
+     */
+    public static class Builder {
+        private Handler handler = null;
+        private boolean autoHandler = true;
+        private String shell = "sh";
+        private boolean wantSTDERR = false;
+        private List<Command> commands = new LinkedList<Command>();
+        private Map<String, String> environment = new HashMap<String, String>();
+        private OnLineListener onSTDOUTLineListener = null;
+        private OnLineListener onSTDERRLineListener = null;
+        private int watchdogTimeout = 0;
+
+        /**
+         * <p>
+         * Set a custom handler that will be used to post all callbacks to
+         * </p>
+         * <p>
+         * See {@link Interactive} for further details on threading and
+         * handlers
+         * </p>
+         *
+         * @param handler Handler to use
+         * @return This Builder object for method chaining
+         */
+        public Builder setHandler(Handler handler) {
+            this.handler = handler;
+            return this;
+        }
+
+        /**
+         * <p>
+         * Automatically create a handler if possible ? Default to true
+         * </p>
+         * <p>
+         * See {@link Interactive} for further details on threading and
+         * handlers
+         * </p>
+         *
+         * @param autoHandler Auto-create handler ?
+         * @return This Builder object for method chaining
+         */
+        public Builder setAutoHandler(boolean autoHandler) {
+            this.autoHandler = autoHandler;
+            return this;
+        }
+
+        /**
+         * Set shell binary to use. Usually "sh" or "su", do not use a full path
+         * unless you have a good reason to
+         *
+         * @param shell Shell to use
+         * @return This Builder object for method chaining
+         */
+        public Builder setShell(String shell) {
+            this.shell = shell;
+            return this;
+        }
+
+        /**
+         * Convenience function to set "sh" as used shell
+         *
+         * @return This Builder object for method chaining
+         */
+        public Builder useSH() {
+            return setShell("sh");
+        }
+
+        /**
+         * Convenience function to set "su" as used shell
+         *
+         * @return This Builder object for method chaining
+         */
+        public Builder useSU() {
+            return setShell("su");
+        }
+
+        /**
+         * Set if error output should be appended to command block result output
+         *
+         * @param wantSTDERR Want error output ?
+         * @return This Builder object for method chaining
+         */
+        public Builder setWantSTDERR(boolean wantSTDERR) {
+            this.wantSTDERR = wantSTDERR;
+            return this;
+        }
+
+        /**
+         * Add or update an environment variable
+         *
+         * @param key   Key of the environment variable
+         * @param value Value of the environment variable
+         * @return This Builder object for method chaining
+         */
+        public Builder addEnvironment(String key, String value) {
+            environment.put(key, value);
+            return this;
+        }
+
+        /**
+         * Add or update environment variables
+         *
+         * @param addEnvironment Map of environment variables
+         * @return This Builder object for method chaining
+         */
+        public Builder addEnvironment(Map<String, String> addEnvironment) {
+            environment.putAll(addEnvironment);
+            return this;
+        }
+
+        /**
+         * Add a command to execute
+         *
+         * @param command Command to execute
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(String command) {
+            return addCommand(command, 0, null);
+        }
+
+        /**
+         * <p>
+         * Add a command to execute, with a callback to be called on completion
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param command                 Command to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(String command, int code,
+                                  OnCommandResultListener onCommandResultListener) {
+            return addCommand(new String[]{
+                    command
+            }, code, onCommandResultListener);
+        }
+
+        /**
+         * Add commands to execute
+         *
+         * @param commands Commands to execute
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(List<String> commands) {
+            return addCommand(commands, 0, null);
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback to be called on completion
+         * (of all commands)
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands                Commands to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         *                                (of all commands)
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(List<String> commands, int code,
+                                  OnCommandResultListener onCommandResultListener) {
+            return addCommand(commands.toArray(new String[commands.size()]), code,
+                    onCommandResultListener);
+        }
+
+        /**
+         * Add commands to execute
+         *
+         * @param commands Commands to execute
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(String[] commands) {
+            return addCommand(commands, 0, null);
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback to be called on completion
+         * (of all commands)
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands                Commands to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         *                                (of all commands)
+         * @return This Builder object for method chaining
+         */
+        public Builder addCommand(String[] commands, int code,
+                                  OnCommandResultListener onCommandResultListener) {
+            this.commands.add(new Command(commands, code, onCommandResultListener, null));
+            return this;
+        }
+
+        /**
+         * <p>
+         * Set a callback called for every line output to STDOUT by the shell
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param onLineListener Callback to be called for each line
+         * @return This Builder object for method chaining
+         */
+        public Builder setOnSTDOUTLineListener(OnLineListener onLineListener) {
+            this.onSTDOUTLineListener = onLineListener;
+            return this;
+        }
+
+        /**
+         * <p>
+         * Set a callback called for every line output to STDERR by the shell
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param onLineListener Callback to be called for each line
+         * @return This Builder object for method chaining
+         */
+        public Builder setOnSTDERRLineListener(OnLineListener onLineListener) {
+            this.onSTDERRLineListener = onLineListener;
+            return this;
+        }
+
+        /**
+         * <p>
+         * Enable command timeout callback
+         * </p>
+         * <p>
+         * This will invoke the onCommandResult() callback with exitCode
+         * WATCHDOG_EXIT if a command takes longer than watchdogTimeout seconds
+         * to complete.
+         * </p>
+         * <p>
+         * If a watchdog timeout occurs, it generally means that the Interactive
+         * session is out of sync with the shell process. The caller should
+         * close the current session and open a new one.
+         * </p>
+         *
+         * @param watchdogTimeout Timeout, in seconds; 0 to disable
+         * @return This Builder object for method chaining
+         */
+        public Builder setWatchdogTimeout(int watchdogTimeout) {
+            this.watchdogTimeout = watchdogTimeout;
+            return this;
+        }
+
+        /**
+         * Construct a {@link Interactive} instance, and start the shell
+         *
+         * @return Interactive shell
+         */
+        public Interactive open() {
+            return new Interactive(this, null);
+        }
+
+        /**
+         * Construct a {@link Interactive} instance, try to start the
+         * shell, and call onCommandResultListener to report success or failure
+         *
+         * @param onCommandResultListener Callback to return shell open status
+         * @return Interactive shell
+         */
+        public Interactive open(OnCommandResultListener onCommandResultListener) {
+            return new Interactive(this, onCommandResultListener);
+        }
+    }
+
+    /**
+     * <p>
+     * An interactive shell - initially created with {@link Builder} -
+     * that executes blocks of commands you supply in the background, optionally
+     * calling callbacks as each block completes.
+     * </p>
+     * <p>
+     * STDERR output can be supplied as well, but due to compatibility with
+     * older Android versions, wantSTDERR is not implemented using
+     * redirectErrorStream, but rather appended to the output. STDOUT and STDERR
+     * are thus not guaranteed to be in the correct order in the output.
+     * </p>
+     * <p>
+     * Note as well that the close() and waitForIdle() methods will
+     * intentionally crash when run in debug mode from the main thread of the
+     * application. Any blocking call should be run from a background thread.
+     * </p>
+     * <p>
+     * When in debug mode, the code will also excessively log the commands
+     * passed to and the output returned from the shell.
+     * </p>
+     * <p>
+     * Though this function uses background threads to gobble STDOUT and STDERR
+     * so a deadlock does not occur if the shell produces massive output, the
+     * output is still stored in a List&lt;String&gt;, and as such doing
+     * something like <em>'ls -lR /'</em> will probably have you run out of
+     * memory when using a {@link OnCommandResultListener}. A work-around
+     * is to not supply this callback, but using (only)
+     * {@link Builder#setOnSTDOUTLineListener(OnLineListener)}. This way,
+     * an internal buffer will not be created and wasting your memory.
+     * </p>
+     * <h3>Callbacks, threads and handlers</h3>
+     * <p>
+     * On which thread the callbacks execute is dependent on your
+     * initialization. You can supply a custom Handler using
+     * {@link Builder#setHandler(Handler)} if needed. If you do not supply
+     * a custom Handler - unless you set
+     * {@link Builder#setAutoHandler(boolean)} to false - a Handler will
+     * be auto-created if the thread used for instantiation of the object has a
+     * Looper.
+     * </p>
+     * <p>
+     * If no Handler was supplied and it was also not auto-created, all
+     * callbacks will be called from either the STDOUT or STDERR gobbler
+     * threads. These are important threads that should be blocked as little as
+     * possible, as blocking them may in rare cases pause the native process or
+     * even create a deadlock.
+     * </p>
+     * <p>
+     * The main thread must certainly have a Looper, thus if you call
+     * {@link Builder#open()} from the main thread, a handler will (by
+     * default) be auto-created, and all the callbacks will be called on the
+     * main thread. While this is often convenient and easy to code with, you
+     * should be aware that if your callbacks are 'expensive' to execute, this
+     * may negatively impact UI performance.
+     * </p>
+     * <p>
+     * Background threads usually do <em>not</em> have a Looper, so calling
+     * {@link Builder#open()} from such a background thread will (by
+     * default) result in all the callbacks being executed in one of the gobbler
+     * threads. You will have to make sure the code you execute in these
+     * callbacks is thread-safe.
+     * </p>
+     */
+    public static class Interactive {
+        private final Handler handler;
+        private final boolean autoHandler;
+        private final String shell;
+        private final boolean wantSTDERR;
+        private final List<Command> commands;
+        private final Map<String, String> environment;
+        private final OnLineListener onSTDOUTLineListener;
+        private final OnLineListener onSTDERRLineListener;
+        private int watchdogTimeout;
+
+        private Process process = null;
+        private DataOutputStream STDIN = null;
+        private StreamGobbler STDOUT = null;
+        private StreamGobbler STDERR = null;
+        private ScheduledThreadPoolExecutor watchdog = null;
+
+        private volatile boolean running = false;
+        private volatile boolean idle = true; // read/write only synchronized
+        private volatile boolean closed = true;
+        private volatile int callbacks = 0;
+        private volatile int watchdogCount;
+
+        private final Object idleSync = new Object();
+        private final Object callbackSync = new Object();
+
+        private volatile int lastExitCode = 0;
+        private volatile String lastMarkerSTDOUT = null;
+        private volatile String lastMarkerSTDERR = null;
+        private volatile Command command = null;
+        private volatile List<String> buffer = null;
+
+        /**
+         * The only way to create an instance: Shell.Builder::open()
+         *
+         * @param builder Builder class to take values from
+         */
+        private Interactive(final Builder builder,
+                            final OnCommandResultListener onCommandResultListener) {
+            autoHandler = builder.autoHandler;
+            shell = builder.shell;
+            wantSTDERR = builder.wantSTDERR;
+            commands = builder.commands;
+            environment = builder.environment;
+            onSTDOUTLineListener = builder.onSTDOUTLineListener;
+            onSTDERRLineListener = builder.onSTDERRLineListener;
+            watchdogTimeout = builder.watchdogTimeout;
+
+            // If a looper is available, we offload the callbacks from the
+            // gobbling threads
+            // to whichever thread created us. Would normally do this in open(),
+            // but then we could not declare handler as final
+            if ((Looper.myLooper() != null) && (builder.handler == null) && autoHandler) {
+                handler = new Handler();
+            } else {
+                handler = builder.handler;
+            }
+
+            if (onCommandResultListener != null) {
+                // Allow up to 60 seconds for SuperSU/Superuser dialog, then enable
+                // the user-specified timeout for all subsequent operations
+                watchdogTimeout = 60;
+                commands.add(0, new Command(Shell.availableTestCommands, 0, new OnCommandResultListener() {
+                    public void onCommandResult(int commandCode, int exitCode, List<String> output) {
+                        if ((exitCode == OnCommandResultListener.SHELL_RUNNING) &&
+                                !Shell.parseAvailableResult(output, SU.isSU(shell))) {
+                            // shell is up, but it's brain-damaged
+                            exitCode = OnCommandResultListener.SHELL_WRONG_UID;
+                        }
+                        watchdogTimeout = builder.watchdogTimeout;
+                        onCommandResultListener.onCommandResult(0, exitCode, output);
+                    }
+                }, null));
+            }
+
+            if (!open() && (onCommandResultListener != null)) {
+                onCommandResultListener.onCommandResult(0,
+                        OnCommandResultListener.SHELL_EXEC_FAILED, null);
+            }
+        }
+
+        @Override
+        protected void finalize() throws Throwable {
+            super.finalize();
+        }
+
+        /**
+         * Add a command to execute
+         *
+         * @param command Command to execute
+         */
+        public void addCommand(String command) {
+            addCommand(command, 0, (OnCommandResultListener) null);
+        }
+
+        /**
+         * <p>
+         * Add a command to execute, with a callback to be called on completion
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param command                 Command to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         */
+        public void addCommand(String command, int code,
+                               OnCommandResultListener onCommandResultListener) {
+            addCommand(new String[]{
+                    command
+            }, code, onCommandResultListener);
+        }
+
+        /**
+         * <p>
+         * Add a command to execute, with a callback. This callback gobbles the
+         * output line by line without buffering it and also returns the result
+         * code on completion.
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param command               Command to execute
+         * @param code                  User-defined value passed back to the callback
+         * @param onCommandLineListener Callback
+         */
+        public void addCommand(String command, int code, OnCommandLineListener onCommandLineListener) {
+            addCommand(new String[]{
+                    command
+            }, code, onCommandLineListener);
+        }
+
+        /**
+         * Add commands to execute
+         *
+         * @param commands Commands to execute
+         */
+        public void addCommand(List<String> commands) {
+            addCommand(commands, 0, (OnCommandResultListener) null);
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback to be called on completion
+         * (of all commands)
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands                Commands to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         *                                (of all commands)
+         */
+        public void addCommand(List<String> commands, int code,
+                               OnCommandResultListener onCommandResultListener) {
+            addCommand(commands.toArray(new String[commands.size()]), code, onCommandResultListener);
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback. This callback gobbles the
+         * output line by line without buffering it and also returns the result
+         * code on completion.
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands              Commands to execute
+         * @param code                  User-defined value passed back to the callback
+         * @param onCommandLineListener Callback
+         */
+        public void addCommand(List<String> commands, int code,
+                               OnCommandLineListener onCommandLineListener) {
+            addCommand(commands.toArray(new String[commands.size()]), code, onCommandLineListener);
+        }
+
+        /**
+         * Add commands to execute
+         *
+         * @param commands Commands to execute
+         */
+        public void addCommand(String[] commands) {
+            addCommand(commands, 0, (OnCommandResultListener) null);
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback to be called on completion
+         * (of all commands)
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands                Commands to execute
+         * @param code                    User-defined value passed back to the callback
+         * @param onCommandResultListener Callback to be called on completion
+         *                                (of all commands)
+         */
+        public synchronized void addCommand(String[] commands, int code,
+                                            OnCommandResultListener onCommandResultListener) {
+            this.commands.add(new Command(commands, code, onCommandResultListener, null));
+            runNextCommand();
+        }
+
+        /**
+         * <p>
+         * Add commands to execute, with a callback. This callback gobbles the
+         * output line by line without buffering it and also returns the result
+         * code on completion.
+         * </p>
+         * <p>
+         * The thread on which the callback executes is dependent on various
+         * factors, see {@link Interactive} for further details
+         * </p>
+         *
+         * @param commands              Commands to execute
+         * @param code                  User-defined value passed back to the callback
+         * @param onCommandLineListener Callback
+         */
+        public synchronized void addCommand(String[] commands, int code,
+                                            OnCommandLineListener onCommandLineListener) {
+            this.commands.add(new Command(commands, code, null, onCommandLineListener));
+            runNextCommand();
+        }
+
+        /**
+         * Run the next command if any and if ready, signals idle state if no
+         * commands left
+         */
+        private void runNextCommand() {
+            runNextCommand(true);
+        }
+
+        /**
+         * Called from a ScheduledThreadPoolExecutor timer thread every second
+         * when there is an outstanding command
+         */
+        private synchronized void handleWatchdog() {
+            final int exitCode;
+
+            if (watchdog == null)
+                return;
+            if (watchdogTimeout == 0)
+                return;
+
+            if (!isRunning()) {
+                exitCode = OnCommandResultListener.SHELL_DIED;
+            } else if (watchdogCount++ < watchdogTimeout) {
+                return;
+            } else {
+                exitCode = OnCommandResultListener.WATCHDOG_EXIT;
+            }
+
+            postCallback(command, exitCode, buffer);
+
+            // prevent multiple callbacks for the same command
+            command = null;
+            buffer = null;
+            idle = true;
+
+            watchdog.shutdown();
+            watchdog = null;
+            kill();
+        }
+
+        /**
+         * Start the periodic timer when a command is submitted
+         */
+        private void startWatchdog() {
+            if (watchdogTimeout == 0) {
+                return;
+            }
+            watchdogCount = 0;
+            watchdog = new ScheduledThreadPoolExecutor(1);
+            watchdog.scheduleAtFixedRate(new Runnable() {
+                @Override
+                public void run() {
+                    handleWatchdog();
+                }
+            }, 1, 1, TimeUnit.SECONDS);
+        }
+
+        /**
+         * Disable the watchdog timer upon command completion
+         */
+        private void stopWatchdog() {
+            if (watchdog != null) {
+                watchdog.shutdownNow();
+                watchdog = null;
+            }
+        }
+
+        /**
+         * Run the next command if any and if ready
+         *
+         * @param notifyIdle signals idle state if no commands left ?
+         */
+        private void runNextCommand(boolean notifyIdle) {
+            // must always be called from a synchronized method
+
+            boolean running = isRunning();
+            if (!running)
+                idle = true;
+
+            if (running && idle && (commands.size() > 0)) {
+                Command command = commands.get(0);
+                commands.remove(0);
+
+                buffer = null;
+                lastExitCode = 0;
+                lastMarkerSTDOUT = null;
+                lastMarkerSTDERR = null;
+
+                if (command.commands.length > 0) {
+                    try {
+                        if (command.onCommandResultListener != null) {
+                            // no reason to store the output if we don't have an
+                            // OnCommandResultListener
+                            // user should catch the output with an
+                            // OnLineListener in this case
+                            buffer = Collections.synchronizedList(new ArrayList<String>());
+                        }
+
+                        idle = false;
+                        this.command = command;
+                        startWatchdog();
+                        for (String write : command.commands) {
+                            STDIN.write((write + "\n").getBytes("UTF-8"));
+                        }
+                        STDIN.write(("echo " + command.marker + " $?\n").getBytes("UTF-8"));
+                        STDIN.write(("echo " + command.marker + " >&2\n").getBytes("UTF-8"));
+                        STDIN.flush();
+                    } catch (IOException e) {
+                        // STDIN might have closed
+                    }
+                } else {
+                    runNextCommand(false);
+                }
+            } else if (!running) {
+                // our shell died for unknown reasons - abort all submissions
+                while (commands.size() > 0) {
+                    postCallback(commands.remove(0), OnCommandResultListener.SHELL_DIED, null);
+                }
+            }
+
+            if (idle && notifyIdle) {
+                synchronized (idleSync) {
+                    idleSync.notifyAll();
+                }
+            }
+        }
+
+        /**
+         * Processes a STDOUT/STDERR line containing an end/exitCode marker
+         */
+        private synchronized void processMarker() {
+            if (command.marker.equals(lastMarkerSTDOUT)
+                    && (command.marker.equals(lastMarkerSTDERR))) {
+                postCallback(command, lastExitCode, buffer);
+                stopWatchdog();
+                command = null;
+                buffer = null;
+                idle = true;
+                runNextCommand();
+            }
+        }
+
+        /**
+         * Process a normal STDOUT/STDERR line
+         *
+         * @param line     Line to process
+         * @param listener Callback to call or null
+         */
+        private synchronized void processLine(String line, OnLineListener listener) {
+            if (listener != null) {
+                if (handler != null) {
+                    final String fLine = line;
+                    final OnLineListener fListener = listener;
+
+                    startCallback();
+                    handler.post(new Runnable() {
+                        @Override
+                        public void run() {
+                            try {
+                                fListener.onLine(fLine);
+                            } finally {
+                                endCallback();
+                            }
+                        }
+                    });
+                } else {
+                    listener.onLine(line);
+                }
+            }
+        }
+
+        /**
+         * Add line to internal buffer
+         *
+         * @param line Line to add
+         */
+        private synchronized void addBuffer(String line) {
+            if (buffer != null) {
+                buffer.add(line);
+            }
+        }
+
+        /**
+         * Increase callback counter
+         */
+        private void startCallback() {
+            synchronized (callbackSync) {
+                callbacks++;
+            }
+        }
+
+        /**
+         * Schedule a callback to run on the appropriate thread
+         */
+        private void postCallback(final Command fCommand, final int fExitCode,
+                                  final List<String> fOutput) {
+            if (fCommand.onCommandResultListener == null && fCommand.onCommandLineListener == null) {
+                return;
+            }
+            if (handler == null) {
+                if (fCommand.onCommandResultListener != null)
+                    fCommand.onCommandResultListener.onCommandResult(fCommand.code, fExitCode,
+                            fOutput);
+                if (fCommand.onCommandLineListener != null)
+                    fCommand.onCommandLineListener.onCommandResult(fCommand.code, fExitCode);
+                return;
+            }
+            startCallback();
+            handler.post(new Runnable() {
+                @Override
+                public void run() {
+                    try {
+                        if (fCommand.onCommandResultListener != null)
+                            fCommand.onCommandResultListener.onCommandResult(fCommand.code,
+                                    fExitCode, fOutput);
+                        if (fCommand.onCommandLineListener != null)
+                            fCommand.onCommandLineListener
+                                    .onCommandResult(fCommand.code, fExitCode);
+                    } finally {
+                        endCallback();
+                    }
+                }
+            });
+        }
+
+        /**
+         * Decrease callback counter, signals callback complete state when
+         * dropped to 0
+         */
+        private void endCallback() {
+            synchronized (callbackSync) {
+                callbacks--;
+                if (callbacks == 0) {
+                    callbackSync.notifyAll();
+                }
+            }
+        }
+
+        /**
+         * Internal call that launches the shell, starts gobbling, and starts
+         * executing commands. See {@link Interactive}
+         *
+         * @return Opened successfully ?
+         */
+        private synchronized boolean open() {
+            try {
+                // setup our process, retrieve STDIN stream, and STDOUT/STDERR
+                // gobblers
+                if (environment.size() == 0) {
+                    process = Runtime.getRuntime().exec(shell);
+                } else {
+                    Map<String, String> newEnvironment = new HashMap<String, String>();
+                    newEnvironment.putAll(System.getenv());
+                    newEnvironment.putAll(environment);
+                    int i = 0;
+                    String[] env = new String[newEnvironment.size()];
+                    for (Map.Entry<String, String> entry : newEnvironment.entrySet()) {
+                        env[i] = entry.getKey() + "=" + entry.getValue();
+                        i++;
+                    }
+                    process = Runtime.getRuntime().exec(shell, env);
+                }
+
+                STDIN = new DataOutputStream(process.getOutputStream());
+                STDOUT = new StreamGobbler(shell.toUpperCase(Locale.ENGLISH) + "-",
+                        process.getInputStream(), new OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        synchronized (Interactive.this) {
+                            if (command == null) {
+                                return;
+                            }
+
+                            String contentPart = line;
+                            String markerPart = null;
+
+                            int markerIndex = line.indexOf(command.marker);
+                            if (markerIndex == 0) {
+                                contentPart = null;
+                                markerPart = line;
+                            } else if (markerIndex > 0) {
+                                contentPart = line.substring(0, markerIndex);
+                                markerPart = line.substring(markerIndex);
+                            }
+
+                            if (contentPart != null) {
+                                addBuffer(contentPart);
+                                processLine(contentPart, onSTDOUTLineListener);
+                                processLine(contentPart, command.onCommandLineListener);
+                            }
+
+                            if (markerPart != null) {
+                                try {
+                                    lastExitCode = Integer.valueOf(
+                                            markerPart.substring(command.marker.length() + 1), 10);
+                                } catch (Exception e) {
+                                    // this really shouldn't happen
+                                    e.printStackTrace();
+                                }
+                                lastMarkerSTDOUT = command.marker;
+                                processMarker();
+                            }
+                        }
+                    }
+                });
+                STDERR = new StreamGobbler(shell.toUpperCase(Locale.ENGLISH) + "*",
+                        process.getErrorStream(), new OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        synchronized (Interactive.this) {
+                            if (command == null) {
+                                return;
+                            }
+
+                            String contentPart = line;
+
+                            int markerIndex = line.indexOf(command.marker);
+                            if (markerIndex == 0) {
+                                contentPart = null;
+                            } else if (markerIndex > 0) {
+                                contentPart = line.substring(0, markerIndex);
+                            }
+
+                            if (contentPart != null) {
+                                if (wantSTDERR)
+                                    addBuffer(contentPart);
+                                processLine(contentPart, onSTDERRLineListener);
+                            }
+
+                            if (markerIndex >= 0) {
+                                lastMarkerSTDERR = command.marker;
+                                processMarker();
+                            }
+                        }
+                    }
+                });
+
+                // start gobbling and write our commands to the shell
+                STDOUT.start();
+                STDERR.start();
+
+                running = true;
+                closed = false;
+
+                runNextCommand();
+
+                return true;
+            } catch (IOException e) {
+                // shell probably not found
+                return false;
+            }
+        }
+
+        /**
+         * Close shell and clean up all resources. Call this when you are done
+         * with the shell. If the shell is not idle (all commands completed) you
+         * should not call this method from the main UI thread because it may
+         * block for a long time. This method will intentionally crash your app
+         * (if in debug mode) if you try to do this anyway.
+         */
+        public void close() {
+            boolean _idle = isIdle(); // idle must be checked synchronized
+
+            synchronized (this) {
+                if (!running)
+                    return;
+                running = false;
+                closed = true;
+            }
+
+            if (!_idle)
+                waitForIdle();
+
+            try {
+                try {
+                    STDIN.write(("exit\n").getBytes("UTF-8"));
+                    STDIN.flush();
+                } catch (IOException e) {
+                    if (e.getMessage().contains("EPIPE") || e.getMessage().contains("Stream closed")) {
+                        // we're not running a shell, the shell closed STDIN,
+                        // the script already contained the exit command, etc.                        
+                    } else {
+                        throw e;
+                    }
+                }
+
+                // wait for our process to finish, while we gobble away in the
+                // background
+                process.waitFor();
+
+                // make sure our threads are done gobbling, our streams are
+                // closed, and the process is destroyed - while the latter two
+                // shouldn't be needed in theory, and may even produce warnings,
+                // in "normal" Java they are required for guaranteed cleanup of
+                // resources, so lets be safe and do this on Android as well
+                try {
+                    STDIN.close();
+                } catch (IOException e) {
+                    // STDIN going missing is no reason to abort 
+                }
+                STDOUT.join();
+                STDERR.join();
+                stopWatchdog();
+                process.destroy();
+            } catch (IOException e) {
+                // various unforseen IO errors may still occur
+            } catch (InterruptedException e) {
+                // this should really be re-thrown
+            }
+        }
+
+        /**
+         * Try to clean up as much as possible from a shell that's gotten itself
+         * wedged. Hopefully the StreamGobblers will croak on their own when the
+         * other side of the pipe is closed.
+         */
+        public synchronized void kill() {
+            running = false;
+            closed = true;
+
+            try {
+                STDIN.close();
+            } catch (IOException e) {
+                // in case it was closed
+            }
+            try {
+                process.destroy();
+            } catch (Exception e) {
+                // in case it was already destroyed or can't be
+            }
+
+            idle = true;
+            synchronized (idleSync) {
+                idleSync.notifyAll();
+            }
+        }
+
+        /**
+         * Is our shell still running ?
+         *
+         * @return Shell running ?
+         */
+        public boolean isRunning() {
+            if (process == null) {
+                return false;
+            }
+            try {
+                process.exitValue();
+                return false;
+            } catch (IllegalThreadStateException e) {
+                // if this is thrown, we're still running
+            }
+            return true;
+        }
+
+        /**
+         * Have all commands completed executing ?
+         *
+         * @return Shell idle ?
+         */
+        public synchronized boolean isIdle() {
+            if (!isRunning()) {
+                idle = true;
+                synchronized (idleSync) {
+                    idleSync.notifyAll();
+                }
+            }
+            return idle;
+        }
+
+        /**
+         * <p>
+         * Wait for idle state. As this is a blocking call, you should not call
+         * it from the main UI thread. If you do so and debug mode is enabled,
+         * this method will intentionally crash your app.
+         * </p>
+         * <p>
+         * If not interrupted, this method will not return until all commands
+         * have finished executing. Note that this does not necessarily mean
+         * that all the callbacks have fired yet.
+         * </p>
+         * <p>
+         * If no Handler is used, all callbacks will have been executed when
+         * this method returns. If a Handler is used, and this method is called
+         * from a different thread than associated with the Handler's Looper,
+         * all callbacks will have been executed when this method returns as
+         * well. If however a Handler is used but this method is called from the
+         * same thread as associated with the Handler's Looper, there is no way
+         * to know.
+         * </p>
+         * <p>
+         * In practice this means that in most simple cases all callbacks will
+         * have completed when this method returns, but if you actually depend
+         * on this behavior, you should make certain this is indeed the case.
+         * </p>
+         * <p>
+         * See {@link Interactive} for further details on threading and
+         * handlers
+         * </p>
+         *
+         * @return True if wait complete, false if wait interrupted
+         */
+        public boolean waitForIdle() {
+            if (isRunning()) {
+                synchronized (idleSync) {
+                    while (!idle) {
+                        try {
+                            idleSync.wait();
+                        } catch (InterruptedException e) {
+                            return false;
+                        }
+                    }
+                }
+
+                if ((handler != null) &&
+                        (handler.getLooper() != null) &&
+                        (handler.getLooper() != Looper.myLooper())) {
+                    // If the callbacks are posted to a different thread than
+                    // this one, we can wait until all callbacks have called
+                    // before returning. If we don't use a Handler at all, the
+                    // callbacks are already called before we get here. If we do
+                    // use a Handler but we use the same Looper, waiting here
+                    // would actually block the callbacks from being called
+
+                    synchronized (callbackSync) {
+                        while (callbacks > 0) {
+                            try {
+                                callbackSync.wait();
+                            } catch (InterruptedException e) {
+                                return false;
+                            }
+                        }
+                    }
+                }
+            }
+
+            return true;
+        }
+
+        /**
+         * Are we using a Handler to post callbacks ?
+         *
+         * @return Handler used ?
+         */
+        public boolean hasHandler() {
+            return (handler != null);
+        }
+    }
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/StreamGobbler.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/StreamGobbler.java
new file mode 100755
index 0000000000000000000000000000000000000000..1d1e462e1deec8d1ef5afb6ee7e4c43664dd466f
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/StreamGobbler.java
@@ -0,0 +1,104 @@
+/*
+ * Copyright (C) 2012-2015 Jorrit "Chainfire" Jongma
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package ohi.andre.consolelauncher.tuils.libsuperuser;
+
+import java.io.BufferedReader;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.util.List;
+
+/**
+ * Thread utility class continuously reading from an InputStream
+ */
+public class StreamGobbler extends Thread {
+    /**
+     * Line callback interface
+     */
+    public interface OnLineListener {
+        /**
+         * <p>Line callback</p>
+         *
+         * <p>This callback should process the line as quickly as possible.
+         * Delays in this callback may pause the native process or even
+         * result in a deadlock</p>
+         *
+         * @param line String that was gobbled
+         */
+        void onLine(String line);
+    }
+
+    private String shell = null;
+    private BufferedReader reader = null;
+    private List<String> writer = null;
+    private OnLineListener listener = null;
+
+    /**
+     * <p>StreamGobbler constructor</p>
+     *
+     * <p>We use this class because shell STDOUT and STDERR should be read as quickly as
+     * possible to prevent a deadlock from occurring, or Process.waitFor() never
+     * returning (as the buffer is full, pausing the native process)</p>
+     *
+     * @param shell       Name of the shell
+     * @param inputStream InputStream to read from
+     * @param outputList  {@literal List<String>} to write to, or null
+     */
+    public StreamGobbler(String shell, InputStream inputStream, List<String> outputList) {
+        this.shell = shell;
+        reader = new BufferedReader(new InputStreamReader(inputStream));
+        writer = outputList;
+    }
+
+    /**
+     * <p>StreamGobbler constructor</p>
+     *
+     * <p>We use this class because shell STDOUT and STDERR should be read as quickly as
+     * possible to prevent a deadlock from occurring, or Process.waitFor() never
+     * returning (as the buffer is full, pausing the native process)</p>
+     *
+     * @param shell          Name of the shell
+     * @param inputStream    InputStream to read from
+     * @param onLineListener OnLineListener callback
+     */
+    public StreamGobbler(String shell, InputStream inputStream, OnLineListener onLineListener) {
+        this.shell = shell;
+        reader = new BufferedReader(new InputStreamReader(inputStream));
+        listener = onLineListener;
+    }
+
+    @Override
+    public void run() {
+        // keep reading the InputStream until it ends (or an error occurs)
+        try {
+            String line;
+            while ((line = reader.readLine()) != null) {
+                if (writer != null) writer.add(line);
+                if (listener != null) listener.onLine(line);
+            }
+        } catch (IOException e) {
+            // reader probably closed, expected exit condition
+        }
+
+        // make sure our stream is closed and resources will be freed
+        try {
+            reader.close();
+        } catch (IOException e) {
+            // read already closed
+        }
+    }
+}
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 4b15629d8ad084d7d476c0c1a4ef959b23a81a18..420929d5a719cd77c1bc4844cf8103b298b3166f 100755
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -13,6 +13,7 @@
     <string name="location_off">Turn on GPS or network location</string>
     <string name="invalid_integer">Invalid integer</string>
     <string name="activity_not_found">Ativity not found</string>
+    <string name="use_suc">su: use "su -c" instead</string>
 
     <!-- busy -->
     <string name="busy">T-UI is busy, wait until the execution finishes or use the command \"ctrlc\"</string>
@@ -20,7 +21,7 @@
 
     <string name="rate_donate_text">\nDo you like my work? Rate on Play Store (command: >>rate) or offer me a coffee (command: >>donate). Thank you for using T-UI.
         \n\nYou can disable this message with the command\nconfig -set donation_message false</string>
-    <string name="firsthelp_text">First time with t-ui? Use the command "tutorial" or "help"</string>
+    <string name="firsthelp_text">First time with t-ui? Use the command \"tutorial\" or \"help\"</string>
 
     <!-- app mgr -->
     <string name="output_appnotfound">Application not found</string>
@@ -73,7 +74,7 @@
     <string name="output_data">Mobile Data active:</string>
     <string name="output_bluetooth">Bluetooth active:</string>
     <string name="output_airplane">Airplane Mode active:</string>
-    <string name="output_nofeature">Can\'t use this feature on your Android version</string>
+    <string name="output_nofeature">This feature isn\'t available on your Android version</string>
 
     <!-- files -->
     <string name="output_isfile">Cant\'t write here. It\'s a file</string>
@@ -300,7 +301,7 @@
     <string name="help_exit">Close T-UI and reset launcher preferences</string>
 
     <!-- linux -->
-    <string name="help_ctrlc">Send SIGINT to the current shell process</string>
+    <string name="help_ctrlc">Interrupt the current shell process and create a new one</string>
     <string name="help_open">Open a file with the default application
         \nUsage:
         \n>>open [pathToFile]
diff --git a/settings.gradle b/settings.gradle
index e7b4def49cb53d9aa04228dd3edb14c9e635e003..92098412bad323815a6aa08244e63f4816e6e993 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -1 +1 @@
-include ':app'
+include ':app', ':emulatorview'