diff --git a/app/libs/anrwatchdog-1.3.0.jar b/app/libs/anrwatchdog-1.3.0.jar
new file mode 100644
index 0000000000000000000000000000000000000000..b8ff41bc9b51d130ca4e0f0220f7c6c0864fee1f
Binary files /dev/null and b/app/libs/anrwatchdog-1.3.0.jar differ
diff --git a/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/music.java b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/music.java
new file mode 100755
index 0000000000000000000000000000000000000000..36ea9e2981df5566be1442d3af398f295a77af44
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/commands/main/raw/music.java
@@ -0,0 +1,208 @@
+package ohi.andre.consolelauncher.commands.main.raw;
+
+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.music.MusicManager2;
+import ohi.andre.consolelauncher.managers.music.Song;
+import ohi.andre.consolelauncher.tuils.Tuils;
+
+public class music extends ParamCommand {
+
+    private enum Param implements ohi.andre.consolelauncher.commands.main.Param {
+        next {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                String title = ((MainPack) pack).player.playNext();
+                if(title != null) return pack.context.getString(R.string.output_playing) + Tuils.SPACE + title;
+                return null;
+            }
+        },
+        previous {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                String title = ((MainPack) pack).player.playPrev();
+                if(title != null) return pack.context.getString(R.string.output_playing) + Tuils.SPACE + title;
+                return null;
+            }
+        },
+        ls {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                return ((MainPack) pack).player.lsSongs();
+            }
+        },
+        play {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                String title = ((MainPack) pack).player.play();
+                if(title == null) return null;
+                return pack.context.getString(R.string.output_playing) + Tuils.SPACE + title;
+            }
+        },
+        stop {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                ((MainPack) pack).player.stop();
+                return null;
+            }
+        },
+        select {
+            @Override
+            public int[] args() {
+                return new int[] {CommandAbstraction.SONG};
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                String s = pack.get(String.class, 1);
+                ((MainPack) pack).player.select(s);
+                return null;
+            }
+        },
+        info {
+            @Override
+            public int[] args() {
+                return new int[0];
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                StringBuilder builder = new StringBuilder();
+
+                MusicManager2 m = ((MainPack) pack).player;
+                Song song = m.get(m.getSongIndex());
+
+                builder.append("Name: " + song.getTitle()).append(Tuils.NEWLINE);
+                if(song.getID() == -1) builder.append("Path: " + song.getPath()).append(Tuils.NEWLINE);
+                builder.append(Tuils.NEWLINE);
+
+                int curS = m.getCurrentPosition() / 1000;
+                int curMin = 0;
+                if(curS >= 60) {
+                    curMin = curS / 60;
+                    curS = curS % 60;
+                }
+
+                int s = m.getDuration() / 1000;
+                int min = 0;
+                if(s >= 60) {
+                    min = s / 60;
+                    s = s % 60;
+                }
+
+                builder.append((curMin > 0 ? curMin + "." + curS : curS + "s") + " of " + (min > 0 ? min + "." + s : s + "s") + " (" + (Tuils.percentage(m.getCurrentPosition(), m.getDuration())) + "%)");
+                return builder.toString();
+            }
+        },
+        seekto {
+            @Override
+            public int[] args() {
+                return new int[] {CommandAbstraction.INT};
+            }
+
+            @Override
+            public String exec(ExecutePack pack) {
+                ((MainPack) pack).player.seekTo(pack.get(int.class, 1) * 1000);
+                return null;
+            }
+        };
+
+        static Param get(String p) {
+            p = p.toLowerCase();
+            Param[] ps = values();
+            for (Param p1 : ps)
+                if (p.endsWith(p1.label()))
+                    return p1;
+            return null;
+        }
+
+        static String[] labels() {
+            Param[] ps = values();
+            String[] ss = new String[ps.length];
+
+            for (int count = 0; count < ps.length; count++) {
+                ss[count] = ps[count].label();
+            }
+
+            return ss;
+        }
+
+        @Override
+        public String label() {
+            return Tuils.MINUS + name();
+        }
+    }
+
+    @Override
+    protected ohi.andre.consolelauncher.commands.main.Param paramForString(MainPack pack, String param) {
+        return Param.get(param);
+    }
+
+    @Override
+    public int minArgs() {
+        return 0;
+    }
+
+    @Override
+    public int maxArgs() {
+        return 2;
+    }
+
+    @Override
+    public int priority() {
+        return 4;
+    }
+
+    @Override
+    public int helpRes() {
+        return R.string.help_music;
+    }
+
+    @Override
+    public String onArgNotFound(ExecutePack pack, int indexNotFound) {
+        return pack.context.getString(R.string.output_songnotfound);
+    }
+
+    @Override
+    public String onNotArgEnough(ExecutePack pack, int nArgs) {
+        return pack.context.getString(helpRes());
+    }
+
+    @Override
+    public String[] params() {
+        return Param.labels();
+    }
+
+    @Override
+    protected String doThings(ExecutePack pack) {
+        return null;
+    }
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicController.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicController.java
new file mode 100644
index 0000000000000000000000000000000000000000..fd7858d4df0480d2d4fc381af1a4e4466f7b26f9
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicController.java
@@ -0,0 +1,14 @@
+package ohi.andre.consolelauncher.managers.music;
+
+import android.content.Context;
+import android.widget.MediaController;
+
+public class MusicController extends MediaController {
+
+    public MusicController(Context c){
+        super(c);
+    }
+
+    public void hide(){}
+}
+
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java
new file mode 100644
index 0000000000000000000000000000000000000000..0a73ed81477792948bdc644c83f28dc8a77a79ca
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicManager2.java
@@ -0,0 +1,335 @@
+package ohi.andre.consolelauncher.managers.music;
+
+import android.content.ComponentName;
+import android.content.ContentResolver;
+import android.content.Context;
+import android.content.Intent;
+import android.content.ServiceConnection;
+import android.database.Cursor;
+import android.net.Uri;
+import android.os.IBinder;
+import android.provider.MediaStore;
+import android.widget.MediaController;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.List;
+
+import ohi.andre.consolelauncher.managers.XMLPrefsManager;
+import ohi.andre.consolelauncher.tuils.Tuils;
+
+/**
+ * Created by francescoandreuzzi on 17/08/2017.
+ */
+
+public class MusicManager2 implements MediaController.MediaPlayerControl {
+
+    public static final String[] MUSIC_EXTENSIONS = {".mp3", ".wav", ".ogg", ".flac"};
+
+    final int WAITING_NEXT = 10, WAITING_PREVIOUS = 11, WAITING_PLAY = 12, WAITING_LISTEN = 13;
+
+    Context mContext;
+
+    List<Song> songs;
+    List<String> titles;
+
+    MusicService musicSrv;
+    boolean musicBound=false;
+    Intent playIntent;
+
+    boolean playbackPaused=true, stopped = true;
+
+    Thread loader;
+
+    int waitingMethod = 0;
+    String savedParam;
+
+    public MusicManager2(Context c) {
+        mContext = c;
+        updateSongs();
+
+        init();
+    }
+
+    public void init() {
+        playIntent = new Intent(mContext, MusicService.class);
+        mContext.bindService(playIntent, musicConnection, Context.BIND_AUTO_CREATE);
+        mContext.startService(playIntent);
+    }
+
+    public void refresh() {
+        destroy();
+        updateSongs();
+    }
+
+    public void destroy() {
+        if(musicSrv != null && musicBound) {
+            musicSrv.stop();
+            mContext.unbindService(musicConnection);
+            mContext.stopService(playIntent);
+            musicSrv = null;
+        }
+
+        musicBound = false;
+        playbackPaused = true;
+        stopped = true;
+    }
+
+    public String playNext() {
+        if(!musicBound) {
+            init();
+            waitingMethod = WAITING_NEXT;
+
+            return null;
+        }
+
+        playbackPaused=false;
+        stopped = false;
+
+        return musicSrv.playNext();
+    }
+
+    public String playPrev() {
+        if(!musicBound) {
+            init();
+            waitingMethod = WAITING_PREVIOUS;
+
+            return null;
+        }
+
+        playbackPaused = false;
+        stopped = false;
+
+        return musicSrv.playPrev();
+    }
+
+    @Override
+    public void pause() {
+        if(musicSrv == null) return;
+
+        playbackPaused=true;
+        musicSrv.pausePlayer();
+    }
+
+    public String play() {
+        if(!musicBound) {
+            init();
+            waitingMethod = WAITING_PLAY;
+
+            return null;
+        }
+
+        if(stopped) {
+            musicSrv.playSong();
+            playbackPaused = false;
+            stopped = false;
+        } else if(playbackPaused) {
+            playbackPaused = false;
+            musicSrv.playPlayer();
+        } else pause();
+
+        return null;
+    }
+
+    public String lsSongs() {
+        List<String> ss = new ArrayList<>();
+        for(Song s : songs) {
+            ss.add(s.getTitle());
+        }
+
+        Collections.sort(ss);
+        Tuils.addPrefix(ss, Tuils.DOUBLE_SPACE);
+        Tuils.insertHeaders(ss, false);
+
+        return Tuils.toPlanString(ss, Tuils.NEWLINE);
+    }
+
+    public void updateSongs() {
+        loader = new Thread() {
+            @Override
+            public void run() {
+                try {
+                    if(songs == null) songs = new ArrayList<>();
+                    else songs.clear();
+
+                    if(titles == null) titles = new ArrayList<>();
+                    else titles.clear();
+
+                    if(XMLPrefsManager.get(boolean.class, XMLPrefsManager.Behavior.songs_from_mediastore)) {
+                        ContentResolver musicResolver = mContext.getContentResolver();
+                        Uri musicUri = MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
+                        Cursor musicCursor = musicResolver.query(musicUri, null, null, null, null);
+                        if(musicCursor!=null && musicCursor.moveToFirst()){
+                            int titleColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE);
+                            int idColumn = musicCursor.getColumnIndex(MediaStore.Audio.Media._ID);
+                            do {
+                                long thisId = musicCursor.getLong(idColumn);
+                                String thisTitle = musicCursor.getString(titleColumn);
+                                songs.add(new Song(thisId, thisTitle));
+                                titles.add(thisTitle);
+                            }
+                            while (musicCursor.moveToNext());
+                        }
+                        musicCursor.close();
+                    } else {
+                        String path = XMLPrefsManager.get(String.class, XMLPrefsManager.Behavior.songs_folder);
+                        if(path.length() > 0) {
+                            File dir = new File(path);
+                            if(dir.isDirectory()) songs.addAll(Tuils.getSongsInFolder(dir));
+
+                            for(Song s : songs) {
+                                titles.add(s.getTitle());
+                            }
+                        }
+                    }
+                } catch (Exception e) {
+                    Tuils.toFile(e);
+                }
+
+                synchronized (songs) {
+                    songs.notify();
+                }
+            }
+        };
+        loader.start();
+    }
+
+    private ServiceConnection musicConnection = new ServiceConnection(){
+
+        @Override
+        public void onServiceConnected(ComponentName name, IBinder service) {
+            MusicService.MusicBinder binder = (MusicService.MusicBinder)service;
+            musicSrv = binder.getService();
+            musicSrv.setShuffle(XMLPrefsManager.get(boolean.class, XMLPrefsManager.Behavior.random_play));
+
+            if(songs == null || loader.isAlive()) {
+                synchronized (songs) {
+                    try {
+                        songs.wait();
+                    } catch (InterruptedException e) {}
+                }
+            }
+            musicSrv.setList(songs);
+            musicBound = true;
+
+            switch (waitingMethod) {
+                case WAITING_NEXT:
+                    playNext();
+                    break;
+                case WAITING_PREVIOUS:
+                    playPrev();
+                    break;
+                case WAITING_PLAY:
+                    play();
+                    break;
+                case WAITING_LISTEN:
+                    select(savedParam);
+                    break;
+            }
+
+            waitingMethod = 0;
+            savedParam = null;
+        }
+
+        @Override
+        public void onServiceDisconnected(ComponentName name) {
+            musicBound = false;
+        }
+    };
+
+    @Override
+    public boolean canPause() {
+        return true;
+    }
+
+    @Override
+    public boolean canSeekBackward() {
+        return true;
+    }
+
+    @Override
+    public boolean canSeekForward() {
+        return true;
+    }
+
+    @Override
+    public int getAudioSessionId() {
+        return 0;
+    }
+
+    @Override
+    public int getBufferPercentage() {
+        return 0;
+    }
+
+    @Override
+    public int getCurrentPosition() {
+        if(musicSrv != null && musicBound && musicSrv.isPng())
+            return musicSrv.getPosn();
+        else return -1;
+    }
+
+    @Override
+    public int getDuration() {
+        if(musicSrv != null && musicBound && musicSrv.isPng())
+            return musicSrv.getDur();
+        else return -1;
+    }
+
+    public int getSongIndex() {
+        if(musicSrv != null) return musicSrv.getSongIndex();
+        return -1;
+    }
+
+    @Override
+    public boolean isPlaying() {
+        if(musicSrv != null && musicBound)
+            return musicSrv.isPng();
+        return false;
+    }
+
+    public void stop() {
+        destroy();
+    }
+
+    public Song get(int index) {
+        return songs.get(index);
+    }
+
+    @Override
+    public void seekTo(int pos) {
+        musicSrv.seek(pos);
+    }
+
+    public void select(String song) {
+        if(!musicBound) {
+            init();
+            waitingMethod = WAITING_LISTEN;
+            savedParam = song;
+
+            return;
+        }
+
+        int i = -1;
+        for(int index = 0; index < songs.size(); index++) {
+            if(songs.get(index).getTitle().equals(song)) i = index;
+        }
+
+        if(i == -1) {
+            return;
+        }
+
+        musicSrv.setSong(i);
+        musicSrv.playSong();
+    }
+
+    public List<String> getTitles() {
+        return titles;
+    }
+
+    @Override
+    public void start() {
+        musicSrv.go();
+    }
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicService.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicService.java
new file mode 100644
index 0000000000000000000000000000000000000000..f01971b776cb193ca8b7e87400424ff1b2ee23e6
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/MusicService.java
@@ -0,0 +1,245 @@
+package ohi.andre.consolelauncher.managers.music;
+
+import android.app.Notification;
+import android.app.PendingIntent;
+import android.app.Service;
+import android.content.ContentUris;
+import android.content.Context;
+import android.content.Intent;
+import android.media.AudioManager;
+import android.media.MediaPlayer;
+import android.net.Uri;
+import android.os.Binder;
+import android.os.Build;
+import android.os.IBinder;
+import android.os.PowerManager;
+import android.support.v4.app.NotificationCompat;
+import android.support.v4.app.RemoteInput;
+
+import java.io.IOException;
+import java.util.List;
+import java.util.Random;
+
+import ohi.andre.consolelauncher.LauncherActivity;
+import ohi.andre.consolelauncher.R;
+import ohi.andre.consolelauncher.tuils.InputOutputReceiver;
+import ohi.andre.consolelauncher.tuils.Tuils;
+
+public class MusicService extends Service implements
+        MediaPlayer.OnPreparedListener, MediaPlayer.OnErrorListener,
+        MediaPlayer.OnCompletionListener {
+
+    public static final int NOTIFY_ID=100001;
+
+    public static final String SONGTITLE_KEY = "songTitle";
+
+    private MediaPlayer player;
+    private List<Song> songs;
+    private int songPosn;
+    private final IBinder musicBind = new MusicBinder();
+    private String songTitle="";
+    private boolean shuffle=false;
+    private Random rand;
+
+    public void onCreate(){
+        super.onCreate();
+        songPosn=0;
+        rand=new Random();
+        player = new MediaPlayer();
+        initMusicPlayer();
+    }
+
+    public void initMusicPlayer(){
+        player.setWakeMode(getApplicationContext(), PowerManager.PARTIAL_WAKE_LOCK);
+        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
+        player.setOnPreparedListener(this);
+        player.setOnCompletionListener(this);
+        player.setOnErrorListener(this);
+    }
+
+    public void setList(List<Song> theSongs){
+        songs=theSongs;
+    }
+
+    public class MusicBinder extends Binder {
+        MusicService getService() {
+            return MusicService.this;
+        }
+    }
+
+    @Override
+    public IBinder onBind(Intent intent) {
+        return musicBind;
+    }
+
+    @Override
+    public boolean onUnbind(Intent intent){
+        return super.onUnbind(intent);
+    }
+
+    public String playSong(){
+        try {
+            player.reset();
+        } catch (Exception e) {}
+
+        Song playSong = songs.get(songPosn);
+
+        long id = playSong.getID();
+        if(id == -1) {
+            String path = playSong.getPath();
+            try {
+                player.setDataSource(path);
+            } catch (IOException e) {
+                Tuils.log(e);
+                Tuils.toFile(e);
+                return null;
+            }
+        } else {
+            songTitle=playSong.getTitle();
+            long currSong = playSong.getID();
+            Uri trackUri = ContentUris.withAppendedId(android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, currSong);
+            try {
+                player.setDataSource(getApplicationContext(), trackUri);
+            }
+            catch(Exception e) {
+                Tuils.log(e);
+                Tuils.toFile(e);
+                return null;
+            }
+        }
+        player.prepareAsync();
+
+        return playSong.getTitle();
+    }
+
+    public void setSong(int songIndex){
+        songPosn=songIndex;
+    }
+
+    @Override
+    public void onCompletion(MediaPlayer mp) {
+        if(player.getCurrentPosition()>0){
+            mp.reset();
+            playNext();
+        }
+    }
+
+    @Override
+    public boolean onError(MediaPlayer mp, int what, int extra) {
+        mp.reset();
+        return false;
+    }
+
+    @Override
+    public void onPrepared(MediaPlayer mp) {
+        mp.start();
+        startForeground(NOTIFY_ID, buildNotification(this.getApplicationContext(), songTitle));
+    }
+
+    public static Notification buildNotification(Context context, String songTitle) {
+        Intent notIntent = new Intent(context, LauncherActivity.class);
+        PendingIntent pendInt = PendingIntent.getActivity(context, 0, notIntent, PendingIntent.FLAG_UPDATE_CURRENT);
+
+        Notification not;
+        NotificationCompat.Builder builder = new NotificationCompat.Builder(context);
+        builder.setContentIntent(pendInt)
+                .setSmallIcon(R.mipmap.ic_launcher)
+                .setTicker(songTitle)
+                .setOngoing(true)
+                .setContentTitle("Playing")
+                .setContentText(songTitle);
+
+        if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
+            String label = "cmd";
+            RemoteInput remoteInput = new RemoteInput.Builder(InputOutputReceiver.TEXT)
+                    .setLabel(label)
+                    .build();
+
+            Intent i = new Intent(InputOutputReceiver.ACTION_CMD);
+            i.putExtra(InputOutputReceiver.WAS_KEY, InputOutputReceiver.WAS_MUSIC_SERVICE);
+
+            NotificationCompat.Action action = new NotificationCompat.Action.Builder(R.mipmap.ic_launcher, label,
+                    PendingIntent.getBroadcast(context.getApplicationContext(), 10, i, PendingIntent.FLAG_UPDATE_CURRENT))
+                    .addRemoteInput(remoteInput)
+                    .build();
+
+            builder.addAction(action);
+        }
+
+        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) not = builder.build();
+        else not = builder.getNotification();
+
+        return not;
+    }
+
+    public int getPosn(){
+        return player.getCurrentPosition();
+    }
+
+    public int getDur(){
+        return player.getDuration();
+    }
+
+    public boolean isPng(){
+        return player.isPlaying();
+    }
+
+    public void pausePlayer(){
+        player.pause();
+    }
+
+    public void stop() {
+        player.stop();
+        player.release();
+        setSong(0);
+    }
+
+    public void playPlayer() {
+        player.start();
+    }
+
+    public void seek(int posn){
+        player.seekTo(posn);
+    }
+
+    public void go(){
+        player.start();
+    }
+
+    public String playPrev(){
+        songPosn--;
+        if(songPosn<0) songPosn=songs.size()-1;
+        return playSong();
+    }
+
+    public String playNext(){
+        if(shuffle){
+            int newSong = songPosn;
+            while(newSong==songPosn){
+                newSong=rand.nextInt(songs.size());
+            }
+            songPosn=newSong;
+        }
+        else{
+            songPosn++;
+            if(songPosn>=songs.size()) songPosn=0;
+        }
+
+        return playSong();
+    }
+
+    public int getSongIndex() {
+        return songPosn;
+    }
+
+    @Override
+    public void onDestroy() {
+        super.onDestroy();
+        stopForeground(true);
+    }
+
+    public void setShuffle(boolean shuffle){
+        this.shuffle = shuffle;
+    }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/ohi/andre/consolelauncher/managers/music/Song.java b/app/src/main/java/ohi/andre/consolelauncher/managers/music/Song.java
new file mode 100644
index 0000000000000000000000000000000000000000..27f64cdfe57d504640e7366847bf7335fe281ce1
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/managers/music/Song.java
@@ -0,0 +1,41 @@
+package ohi.andre.consolelauncher.managers.music;
+
+import java.io.File;
+
+/**
+ * Created by francescoandreuzzi on 17/08/2017.
+ */
+
+public class Song {
+
+    private long id;
+    private String title, path;
+
+    public Song(long songID, String songTitle) {
+        id = songID;
+        title = songTitle;
+    }
+
+    public Song(File file) {
+        String name = file.getName();
+        int dot = name.lastIndexOf(".");
+        name = name.substring(0,dot);
+
+        this.title = name;
+        this.path = file.getAbsolutePath();
+        this.id = -1;
+    }
+
+    public long getID() {
+        return id;
+    }
+
+    public String getTitle() {
+        return title;
+    }
+
+    public String getPath() {
+        return path;
+    }
+}
+
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java
new file mode 100644
index 0000000000000000000000000000000000000000..122374dfba52341e423fbda1073f53997d2570e6
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/InputOutputReceiver.java
@@ -0,0 +1,64 @@
+package ohi.andre.consolelauncher.tuils;
+
+import android.content.BroadcastReceiver;
+import android.content.Context;
+import android.content.Intent;
+import android.os.Bundle;
+import android.support.v4.app.NotificationManagerCompat;
+import android.support.v4.app.RemoteInput;
+
+import ohi.andre.consolelauncher.managers.SkinManager;
+import ohi.andre.consolelauncher.tuils.interfaces.CommandExecuter;
+import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
+
+/**
+ * Created by francescoandreuzzi on 18/08/2017.
+ */
+
+public class InputOutputReceiver extends BroadcastReceiver {
+
+    public static final int WAS_MUSIC_SERVICE = 10;
+    public static final int WAS_KEEPER_SERVICE = 11;
+
+    public static final String WAS_KEY = "was";
+
+    public static final String ACTION_CMD = "ohi.andre.consolelauncher.action_cmd";
+    public static final String ACTION_OUTPUT = "ohi.andre.consolelauncher.action_output";
+    public static final String TEXT = "ohi.andre.consolelauncher.text";
+    public static final String COLOR = "ohi.andre.consolelauncher.color";
+
+    CommandExecuter executer;
+    Outputable outputable;
+
+    public InputOutputReceiver(CommandExecuter executer, Outputable outputable) {
+        this.executer = executer;
+        this.outputable = outputable;
+    }
+
+    @Override
+    public void onReceive(Context context, Intent intent) {
+        Bundle remoteInput = RemoteInput.getResultsFromIntent(intent);
+        if(remoteInput == null || remoteInput.size() == 0) {
+            CharSequence text = intent.getCharSequenceExtra(TEXT);
+            if(text == null) text = intent.getStringExtra(TEXT);
+            if(text == null) return;
+
+            if(intent.getAction().equals(ACTION_CMD)) {
+                executer.exec(text.toString());
+            } else {
+                int color = intent.getIntExtra(COLOR, SkinManager.COLOR_NOT_SET);
+                outputable.onOutput(color, text);
+            }
+        } else {
+            String cmd = remoteInput.getString(TEXT);
+            executer.exec(cmd, true);
+
+            int was = intent.getIntExtra(WAS_KEY, 0);
+            if(was == WAS_KEEPER_SERVICE) {
+                NotificationManagerCompat.from(context).notify(KeeperService.ONGOING_NOTIFICATION_ID, KeeperService.buildNotification(context));
+            } else if(was == WAS_MUSIC_SERVICE) {
+//                do nothing
+            }
+        }
+    }
+}
diff --git a/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/ShellHolder.java b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/ShellHolder.java
new file mode 100644
index 0000000000000000000000000000000000000000..73cbbe94fc89627549fde7eb69b48f38282ba4f8
--- /dev/null
+++ b/app/src/main/java/ohi/andre/consolelauncher/tuils/libsuperuser/ShellHolder.java
@@ -0,0 +1,37 @@
+package ohi.andre.consolelauncher.tuils.libsuperuser;
+
+import android.os.Environment;
+
+import ohi.andre.consolelauncher.tuils.interfaces.Outputable;
+
+/**
+ * Created by francescoandreuzzi on 18/08/2017.
+ */
+
+public class ShellHolder {
+
+    private Outputable outputable;
+
+    public ShellHolder(Outputable outputable) {
+        this.outputable = outputable;
+    }
+
+    public Shell.Interactive build() {
+        Shell.Interactive interactive = new Shell.Builder()
+                .setOnSTDOUTLineListener(new StreamGobbler.OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        outputable.onOutput(line);
+                    }
+                })
+                .setOnSTDERRLineListener(new StreamGobbler.OnLineListener() {
+                    @Override
+                    public void onLine(String line) {
+                        outputable.onOutput(line);
+                    }
+                })
+                .open();
+        interactive.addCommand("cd " + Environment.getExternalStorageDirectory().getAbsolutePath());
+        return interactive;
+    }
+}