3939import io .github .muntashirakon .AppManager .BaseActivity ;
4040import io .github .muntashirakon .AppManager .R ;
4141import io .github .muntashirakon .AppManager .compat .ProcessCompat ;
42+ import io .github .muntashirakon .AppManager .logs .Log ;
4243import io .github .muntashirakon .AppManager .utils .CpuUtils ;
4344
4445// TODO: 11/9/23 Replace it with an actual terminal
4546public class TermActivity extends BaseActivity {
47+ public static final String TAG = TermActivity .class .getSimpleName ();
4648 private final Object mLock = new Object ();
4749 private AppCompatEditText mCommandInput ;
4850 private AppCompatTextView mCommandOutput ;
@@ -52,6 +54,7 @@ public class TermActivity extends BaseActivity {
5254 private final ExecutorService mExecutor = Executors .newFixedThreadPool (3 );
5355 private int mDefaultForegroundColor ;
5456 private int mDefaultBackgroundColor ;
57+ private final AnsiState mAnsiState = new AnsiState ();
5558
5659 @ Override
5760 protected void onAuthenticated (@ Nullable Bundle savedInstanceState ) {
@@ -67,6 +70,7 @@ protected void onAuthenticated(@Nullable Bundle savedInstanceState) {
6770 String command = Objects .requireNonNull (mCommandInput .getText ()).toString ();
6871 appendBoldOutput (command );
6972 appendOutput ("\n " );
73+ mAnsiState .homePosition = mCommandOutput .getEditableText ().length ();
7074 if (mProcessOutputStream != null ) {
7175 if (!ProcessCompat .isAlive (mProc )) {
7276 // Process is dead
@@ -112,13 +116,12 @@ private void initShell() {
112116 mProcessOutputStream = new BufferedOutputStream (mProc .getOutputStream ());
113117 mExecutor .submit (() -> {
114118 try (InputStream in = mProc .getInputStream ()) {
115- AnsiState state = new AnsiState ();
116119 byte [] buffer = new byte [1024 ];
117120 int len ;
118121 while ((len = in .read (buffer )) != -1 ) {
119122 synchronized (mLock ) {
120123 String chunk = new String (buffer , 0 , len , StandardCharsets .UTF_8 );
121- runOnUiThread (() -> processChunk (chunk , state ));
124+ runOnUiThread (() -> processChunk (chunk , mAnsiState ));
122125 }
123126 }
124127 } catch (Throwable e ) {
@@ -132,7 +135,7 @@ private void initShell() {
132135 while ((len = in .read (buffer )) != -1 ) {
133136 synchronized (mLock ) {
134137 String chunk = new String (buffer , 0 , len , StandardCharsets .UTF_8 );
135- runOnUiThread (() -> appendOutput (chunk ));
138+ runOnUiThread (() -> processChunk (chunk , mAnsiState ));
136139 }
137140 }
138141 } catch (Throwable e ) {
@@ -159,6 +162,9 @@ static class AnsiState {
159162 boolean strike = false ;
160163 boolean reverse = false ;
161164 boolean hide = false ;
165+ int savedPosition = -1 ;
166+ boolean navigateToHome = false ;
167+ int homePosition = 0 ;
162168
163169 void reset () {
164170 foreground = -1 ;
@@ -169,6 +175,8 @@ void reset() {
169175 strike = false ;
170176 reverse = false ;
171177 hide = false ;
178+ savedPosition = -1 ;
179+ navigateToHome = false ;
172180 }
173181 }
174182
@@ -199,7 +207,7 @@ private void processChunk(@NonNull String chunk, @NonNull AnsiState state) {
199207 if (c >= '0' && c <= '9' ) {
200208 // A number
201209 numberBuilder .append (c );
202- } else if (c == ';' ) {
210+ } else if (c == ';' || c == '?' ) {
203211 // Separator
204212 numberBuilder .append (c );
205213 } else {
@@ -225,9 +233,35 @@ private void processChunk(@NonNull String chunk, @NonNull AnsiState state) {
225233
226234 @ MainThread
227235 void processAnsiSeq (@ NonNull String seq , @ NonNull AnsiState state ) {
228- if (seq .equals ("[2J" )) {
229- resetOutput ();
230- state .reset ();
236+ if (seq .matches ("\\ [[0-2]?J" )) {
237+ String code = seq .substring (1 , seq .length () - 1 );
238+ switch (code ) {
239+ case "2" : // Clear entire screen
240+ resetOutput (state );
241+ state .reset ();
242+ break ;
243+ case "1" : // Clear screen from cursor up
244+ Log .i (TAG , "Unhandled escape sequence: " + seq );
245+ break ;
246+ case "0" : // Clear screen from cursor down
247+ default :
248+ if (state .navigateToHome ) {
249+ resetOutputToPosition (state .homePosition );
250+ state .reset ();
251+ } // else Not handled
252+ }
253+ } else if (seq .equals ("[H" ) || seq .equals ("[;H" ) || seq .equals ("[f" )) {
254+ state .navigateToHome = true ;
255+ } else if (seq .equals ("[2K" )) {
256+ clearLastLine ();
257+ } else if (seq .equals ("[s" )) {
258+ Editable editable = mCommandOutput .getEditableText ();
259+ state .savedPosition = editable .length ();
260+ } else if (seq .equals ("[u" )) {
261+ Editable editable = mCommandOutput .getEditableText ();
262+ if (state .savedPosition >= 0 && state .savedPosition <= editable .length ()) {
263+ editable .delete (state .savedPosition , editable .length ());
264+ }
231265 } else if (seq .matches ("\\ [[0-9;]*m" )) {
232266 // Parse the numbers
233267 String params = seq .substring (1 , seq .length () - 1 );
@@ -335,14 +369,43 @@ void processAnsiSeq(@NonNull String seq, @NonNull AnsiState state) {
335369 case 47 :
336370 state .background = Color .WHITE ;
337371 break ;
372+ default :
373+ Log .i (TAG , "Unhandled escape sequence: " + seq );
338374 }
339375 }
376+ } else {
377+ Log .i (TAG , "Unhandled escape sequence: " + seq );
340378 }
341379 }
342380
343381 @ MainThread
344- private void resetOutput () {
345- mCommandOutput .setText ("" , TextView .BufferType .EDITABLE );
382+ private void resetOutput (@ NonNull AnsiState state ) {
383+ mCommandOutput .getEditableText ().clear ();
384+ state .homePosition = 0 ;
385+ }
386+
387+ @ MainThread
388+ private void resetOutputToPosition (int position ) {
389+ Editable editable = mCommandOutput .getEditableText ();
390+ if (position <= 0 ) {
391+ editable .clear ();
392+ } else {
393+ int length = editable .length ();
394+ if (position < length ) {
395+ editable .delete (position , length );
396+ }
397+ }
398+ }
399+
400+ private void clearLastLine () {
401+ Editable editable = mCommandOutput .getEditableText ();
402+ int length = editable .length ();
403+ if (length > 0 ) {
404+ // Find the index of the last newline character
405+ int lastNewline = editable .toString ().lastIndexOf ('\n' , length - 2 );
406+ // If no newline found, delete everything
407+ resetOutputToPosition (lastNewline + 1 );
408+ }
346409 }
347410
348411 @ MainThread
0 commit comments