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; + } +}