@@ -38,6 +38,7 @@ public abstract class ShellHandlerBase extends Handler {
3838 private static final Pattern AM_PATTERN = Pattern .compile ("am:\\ [(.*?)\\ ]" );
3939 private static final Pattern PROGRESS_PATTERN = Pattern .compile ("progress:\\ [(.*?)\\ ]" );
4040 private static final Pattern INPUT_PATTERN = Pattern .compile ("input:\\ [(.*?)\\ ]" );
41+ private static final Pattern EXIT_PATTERN = Pattern .compile ("exit:\\ [(.*?)\\ ]" );
4142
4243 protected abstract void onProgress (int current , int total );
4344 protected abstract void onStart (Object msg );
@@ -102,6 +103,13 @@ public boolean writeInput(String text) {
102103 protected void onInputRequest (String prompt ) {
103104 }
104105
106+ /**
107+ * Được gọi ngay trước khi tiến trình app bị kill (do "exit:[kill]" hoặc "exit:[restart]"),
108+ * để lớp con có cơ hội dọn dẹp UI (đóng dialog, finish activity...). Mặc định không làm gì.
109+ */
110+ protected void onKillRequest () {
111+ }
112+
105113 @ Override
106114 public void handleMessage (Message msg ) {
107115 super .handleMessage (msg );
@@ -129,7 +137,19 @@ protected void onReaderMsg(Object msg) {
129137
130138 String log = msg .toString ();
131139 String cleanLog = ANSI_ESCAPE_PATTERN .matcher (log ).replaceAll ("" ).trim ();
132-
140+
141+ // === XỬ LÝ LỆNH THOÁT APP: exit:[kill] / exit:[restart] ===
142+ Matcher exitMatcher = EXIT_PATTERN .matcher (cleanLog );
143+ if (exitMatcher .find ()) {
144+ String args = exitMatcher .group (1 ).trim ().toLowerCase (Locale .US );
145+ if (args .equals ("kill" )) {
146+ killApp (false );
147+ } else if (args .equals ("restart" )) {
148+ killApp (true );
149+ }
150+ return ;
151+ }
152+
133153 // === TỰ ĐỘNG PHÁT HIỆN PROMPT CẦN INPUT ===
134154 if (shouldShowInputPrompt (cleanLog )) {
135155 onInputRequest (cleanLog );
@@ -202,6 +222,37 @@ protected void onError(Object msg) {
202222 updateLog (msg , "#ff0000" );
203223 }
204224
225+ /**
226+ * Kill toàn bộ tiến trình app hiện tại (bao gồm mọi thread/service nền đang chạy trong
227+ * cùng process), phục vụ cú pháp "exit:[kill]" (restart=false) và "exit:[restart]"
228+ * (restart=true, sẽ khởi động lại app ngay trước khi kill process cũ).
229+ *
230+ * Lưu ý: killProcess() chỉ dừng process hiện tại. Nếu app có khai báo service ở process
231+ * riêng (android:process=":other" trong Manifest), process đó KHÔNG bị ảnh hưởng bởi lệnh
232+ * này — cần killBackgroundProcesses() riêng nếu muốn dọn luôn.
233+ */
234+ private void killApp (boolean restart ) {
235+ try {
236+ onKillRequest ();
237+ } catch (Exception ignored ) {
238+ // Không để lỗi dọn dẹp UI cản trở việc kill
239+ } finally {
240+ if (restart ) {
241+ try {
242+ Intent launch = context .getPackageManager ().getLaunchIntentForPackage (context .getPackageName ());
243+ if (launch != null ) {
244+ launch .addFlags (Intent .FLAG_ACTIVITY_NEW_TASK | Intent .FLAG_ACTIVITY_CLEAR_TASK );
245+ context .startActivity (launch );
246+ }
247+ } catch (Exception ignored ) {
248+ // Không để lỗi khởi động lại cản trở việc kill process cũ
249+ }
250+ }
251+ android .os .Process .killProcess (android .os .Process .myPid ());
252+ System .exit (10 );
253+ }
254+ }
255+
205256 private String getAmHelp () {
206257 return "am:[command] syntax:\n \n " +
207258 "am:[start -a ACTION -d URI -n PACKAGE/CLASS]\n " +
@@ -492,4 +543,4 @@ protected void updateLog(final Object msg, final int color) {
492543 updateLog (spannableString );
493544 }
494545 }
495- }
546+ }
0 commit comments