capacitor-dex-editor 0.0.49 → 0.0.51

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -6,11 +6,17 @@ import android.graphics.Color;
6
6
  import android.graphics.Typeface;
7
7
  import android.os.Build;
8
8
  import android.os.Bundle;
9
+ import android.os.Handler;
10
+ import android.os.Looper;
11
+ import android.text.Editable;
9
12
  import android.text.InputType;
13
+ import android.text.TextWatcher;
14
+ import android.view.Gravity;
10
15
  import android.view.View;
11
16
  import android.view.ViewGroup;
12
17
  import android.view.Window;
13
18
  import android.view.WindowManager;
19
+ import android.view.inputmethod.InputMethodManager;
14
20
  import android.widget.CheckBox;
15
21
  import android.widget.EditText;
16
22
  import android.widget.FrameLayout;
@@ -43,6 +49,20 @@ public class SmaliEditorActivity extends AppCompatActivity {
43
49
  private LinearLayout root;
44
50
  private View topSpacer;
45
51
  private View bottomSpacer;
52
+
53
+ // 搜索面板相关
54
+ private LinearLayout searchPanel;
55
+ private EditText searchInput;
56
+ private EditText replaceInput;
57
+ private TextView searchResultText;
58
+ private TextView prevBtn, nextBtn, replaceBtn, replaceAllBtn;
59
+ private int searchMatchCount = 0;
60
+ private int currentMatchIndex = 0;
61
+ private boolean isSearchPanelVisible = false;
62
+
63
+ // 搜索防抖
64
+ private Handler searchHandler = new Handler(Looper.getMainLooper());
65
+ private Runnable searchRunnable;
46
66
 
47
67
  @Override
48
68
  protected void onCreate(Bundle savedInstanceState) {
@@ -91,9 +111,14 @@ public class SmaliEditorActivity extends AppCompatActivity {
91
111
  editView.setSyntaxLanguageFileName(syntaxFile);
92
112
  editView.setEditedMode(!readOnly);
93
113
  editView.setTypeface(Typeface.MONOSPACE);
94
- editView.setTextSize(14);
114
+ editView.setTextSize(16);
95
115
  root.addView(editView);
96
116
 
117
+ // 搜索面板(初始隐藏)
118
+ searchPanel = createSearchPanel();
119
+ searchPanel.setVisibility(View.GONE);
120
+ root.addView(searchPanel);
121
+
97
122
  // 底部安全区域占位
98
123
  bottomSpacer = new View(this);
99
124
  bottomSpacer.setBackgroundColor(Color.parseColor("#1E1E1E"));
@@ -168,7 +193,7 @@ public class SmaliEditorActivity extends AppCompatActivity {
168
193
 
169
194
  // 查找按钮
170
195
  ImageButton searchBtn = createIconButton("🔍");
171
- searchBtn.setOnClickListener(v -> showSearchDialog());
196
+ searchBtn.setOnClickListener(v -> toggleSearchPanel());
172
197
  toolbar.addView(searchBtn);
173
198
 
174
199
  // 跳转行号按钮
@@ -217,89 +242,243 @@ public class SmaliEditorActivity extends AppCompatActivity {
217
242
  }
218
243
 
219
244
  private String lastSearchText = "";
220
- private String lastReplaceText = "";
221
-
222
- private void showSearchDialog() {
223
- LinearLayout layout = new LinearLayout(this);
224
- layout.setOrientation(LinearLayout.VERTICAL);
225
- layout.setPadding(48, 32, 48, 16);
226
245
 
227
- // 查找输入框
228
- EditText searchInput = new EditText(this);
229
- searchInput.setHint("查找内容");
230
- searchInput.setText(lastSearchText);
246
+ // 创建 MT 风格的底部搜索面板
247
+ private LinearLayout createSearchPanel() {
248
+ LinearLayout panel = new LinearLayout(this);
249
+ panel.setOrientation(LinearLayout.VERTICAL);
250
+ panel.setBackgroundColor(Color.parseColor("#2D2D2D"));
251
+ panel.setPadding(16, 8, 16, 8);
252
+
253
+ // 第一行:搜索输入框 + 结果计数
254
+ LinearLayout row1 = new LinearLayout(this);
255
+ row1.setOrientation(LinearLayout.HORIZONTAL);
256
+ row1.setGravity(Gravity.CENTER_VERTICAL);
257
+
258
+ // 搜索输入框
259
+ searchInput = new EditText(this);
260
+ searchInput.setHint("查找");
231
261
  searchInput.setTextColor(Color.WHITE);
232
262
  searchInput.setHintTextColor(Color.GRAY);
233
- layout.addView(searchInput);
263
+ searchInput.setBackgroundResource(R.drawable.round_edittext_bg);
264
+ searchInput.setPadding(24, 16, 24, 16);
265
+ searchInput.setSingleLine(true);
266
+ LinearLayout.LayoutParams searchParams = new LinearLayout.LayoutParams(
267
+ 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
268
+ searchParams.setMargins(0, 0, 8, 0);
269
+ searchInput.setLayoutParams(searchParams);
270
+
271
+ // 实时搜索(带防抖)
272
+ searchInput.addTextChangedListener(new TextWatcher() {
273
+ @Override
274
+ public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
275
+ @Override
276
+ public void onTextChanged(CharSequence s, int start, int before, int count) {}
277
+ @Override
278
+ public void afterTextChanged(Editable s) {
279
+ // 移除之前的延迟任务,避免重复触发
280
+ if (searchRunnable != null) {
281
+ searchHandler.removeCallbacks(searchRunnable);
282
+ }
283
+ // 延迟300ms再搜索(用户停止输入后再执行)
284
+ searchRunnable = () -> performSearch();
285
+ searchHandler.postDelayed(searchRunnable, 300);
286
+ }
287
+ });
288
+ row1.addView(searchInput);
289
+
290
+ // 搜索结果计数
291
+ searchResultText = new TextView(this);
292
+ searchResultText.setText("0");
293
+ searchResultText.setTextColor(Color.parseColor("#888888"));
294
+ searchResultText.setTextSize(14);
295
+ searchResultText.setPadding(16, 0, 16, 0);
296
+ row1.addView(searchResultText);
297
+
298
+ // 关闭按钮
299
+ TextView closeBtn = createTextButton("×");
300
+ closeBtn.setTextSize(24);
301
+ closeBtn.setOnClickListener(v -> hideSearchPanel());
302
+ row1.addView(closeBtn);
303
+
304
+ panel.addView(row1);
305
+
306
+ // 第二行:上一个、下一个、替换、全部
307
+ LinearLayout row2 = new LinearLayout(this);
308
+ row2.setOrientation(LinearLayout.HORIZONTAL);
309
+ row2.setGravity(Gravity.CENTER_VERTICAL);
310
+ row2.setPadding(0, 8, 0, 0);
311
+
312
+ prevBtn = createTextButton("上个");
313
+ prevBtn.setOnClickListener(v -> findPrevious());
314
+ prevBtn.setEnabled(false);
315
+ row2.addView(prevBtn);
316
+
317
+ nextBtn = createTextButton("下个");
318
+ nextBtn.setOnClickListener(v -> findNext());
319
+ nextBtn.setEnabled(false);
320
+ row2.addView(nextBtn);
321
+
322
+ replaceBtn = createTextButton("替换");
323
+ replaceBtn.setOnClickListener(v -> replaceCurrent());
324
+ replaceBtn.setEnabled(false);
325
+ row2.addView(replaceBtn);
326
+
327
+ replaceAllBtn = createTextButton("全部");
328
+ replaceAllBtn.setOnClickListener(v -> replaceAll());
329
+ replaceAllBtn.setEnabled(false);
330
+ row2.addView(replaceAllBtn);
234
331
 
235
332
  // 替换输入框
236
- EditText replaceInput = new EditText(this);
237
- replaceInput.setHint("替换为 (可选)");
238
- replaceInput.setText(lastReplaceText);
333
+ replaceInput = new EditText(this);
334
+ replaceInput.setHint("替换为");
239
335
  replaceInput.setTextColor(Color.WHITE);
240
336
  replaceInput.setHintTextColor(Color.GRAY);
241
- layout.addView(replaceInput);
242
-
243
- // 正则表达式选项
244
- CheckBox regexCheckbox = new CheckBox(this);
245
- regexCheckbox.setText("使用正则表达式");
246
- regexCheckbox.setTextColor(Color.WHITE);
247
- layout.addView(regexCheckbox);
248
-
249
- AlertDialog dialog = new AlertDialog.Builder(this)
250
- .setTitle("查找与替换")
251
- .setView(layout)
252
- .setPositiveButton("查找下一个", null)
253
- .setNegativeButton("替换", null)
254
- .setNeutralButton("全部替换", null)
255
- .create();
256
-
257
- dialog.setOnShowListener(d -> {
258
- dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener(v -> {
259
- String text = searchInput.getText().toString();
260
- if (!text.isEmpty()) {
261
- lastSearchText = text;
262
- if (regexCheckbox.isChecked()) {
263
- editView.find(text);
264
- } else {
265
- editView.find(java.util.regex.Pattern.quote(text));
266
- }
267
- }
268
- });
337
+ replaceInput.setBackgroundResource(R.drawable.round_edittext_bg);
338
+ replaceInput.setPadding(24, 16, 24, 16);
339
+ replaceInput.setSingleLine(true);
340
+ LinearLayout.LayoutParams replaceParams = new LinearLayout.LayoutParams(
341
+ 0, ViewGroup.LayoutParams.WRAP_CONTENT, 1);
342
+ replaceParams.setMargins(8, 0, 0, 0);
343
+ replaceInput.setLayoutParams(replaceParams);
344
+ row2.addView(replaceInput);
345
+
346
+ panel.addView(row2);
347
+
348
+ return panel;
349
+ }
269
350
 
270
- dialog.getButton(AlertDialog.BUTTON_NEGATIVE).setOnClickListener(v -> {
271
- String text = searchInput.getText().toString();
272
- String replace = replaceInput.getText().toString();
273
- if (!text.isEmpty()) {
274
- lastSearchText = text;
275
- lastReplaceText = replace;
276
- if (regexCheckbox.isChecked()) {
277
- editView.find(text);
278
- } else {
279
- editView.find(java.util.regex.Pattern.quote(text));
280
- }
281
- editView.replaceFirst(replace);
282
- }
283
- });
351
+ private TextView createTextButton(String text) {
352
+ TextView btn = new TextView(this);
353
+ btn.setText(text);
354
+ btn.setTextColor(Color.parseColor("#63B5F7"));
355
+ btn.setTextSize(14);
356
+ btn.setPadding(24, 12, 24, 12);
357
+ btn.setClickable(true);
358
+ btn.setFocusable(true);
359
+ return btn;
360
+ }
284
361
 
285
- dialog.getButton(AlertDialog.BUTTON_NEUTRAL).setOnClickListener(v -> {
286
- String text = searchInput.getText().toString();
287
- String replace = replaceInput.getText().toString();
288
- if (!text.isEmpty()) {
289
- lastSearchText = text;
290
- lastReplaceText = replace;
291
- if (regexCheckbox.isChecked()) {
292
- editView.find(text);
293
- } else {
294
- editView.find(java.util.regex.Pattern.quote(text));
295
- }
296
- editView.replaceAll(replace);
297
- Toast.makeText(this, "已替换所有匹配项", Toast.LENGTH_SHORT).show();
298
- }
299
- });
300
- });
362
+ private void toggleSearchPanel() {
363
+ if (isSearchPanelVisible) {
364
+ hideSearchPanel();
365
+ } else {
366
+ showSearchPanel();
367
+ }
368
+ }
369
+
370
+ private void showSearchPanel() {
371
+ searchPanel.setVisibility(View.VISIBLE);
372
+ isSearchPanelVisible = true;
373
+ searchInput.requestFocus();
374
+ // 显示键盘
375
+ InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
376
+ imm.showSoftInput(searchInput, InputMethodManager.SHOW_IMPLICIT);
377
+
378
+ // 如果有之前的搜索内容,重新搜索
379
+ if (!lastSearchText.isEmpty()) {
380
+ searchInput.setText(lastSearchText);
381
+ searchInput.setSelection(lastSearchText.length());
382
+ }
383
+ }
384
+
385
+ private void hideSearchPanel() {
386
+ searchPanel.setVisibility(View.GONE);
387
+ isSearchPanelVisible = false;
388
+ // 隐藏键盘
389
+ InputMethodManager imm = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);
390
+ imm.hideSoftInputFromWindow(searchInput.getWindowToken(), 0);
391
+ // 清除高亮
392
+ editView.find("");
393
+ editView.postInvalidate();
394
+ }
395
+
396
+ private void performSearch() {
397
+ String text = searchInput.getText().toString();
398
+ lastSearchText = text;
399
+
400
+ if (text.isEmpty()) {
401
+ searchMatchCount = 0;
402
+ currentMatchIndex = 0;
403
+ searchResultText.setText("0/0");
404
+ editView.find("");
405
+ updateSearchButtonsState();
406
+ return;
407
+ }
408
+
409
+ // 执行搜索(使用普通文本,非正则)
410
+ editView.find(java.util.regex.Pattern.quote(text));
411
+
412
+ // 获取匹配数量
413
+ searchMatchCount = editView.getMatchCount();
414
+ currentMatchIndex = 0;
415
+
416
+ if (searchMatchCount > 0) {
417
+ // 跳转到第一个匹配
418
+ editView.next();
419
+ currentMatchIndex = 1;
420
+ searchResultText.setText(currentMatchIndex + "/" + searchMatchCount);
421
+ } else {
422
+ searchResultText.setText("0/0");
423
+ }
424
+ updateSearchButtonsState();
425
+ }
301
426
 
302
- dialog.show();
427
+ private void findNext() {
428
+ if (searchMatchCount > 0) {
429
+ editView.next();
430
+ currentMatchIndex++;
431
+ if (currentMatchIndex > searchMatchCount) {
432
+ currentMatchIndex = 1;
433
+ }
434
+ searchResultText.setText(currentMatchIndex + "/" + searchMatchCount);
435
+ }
436
+ }
437
+
438
+ private void findPrevious() {
439
+ if (searchMatchCount > 0) {
440
+ editView.previous();
441
+ currentMatchIndex--;
442
+ if (currentMatchIndex < 1) {
443
+ currentMatchIndex = searchMatchCount;
444
+ }
445
+ searchResultText.setText(currentMatchIndex + "/" + searchMatchCount);
446
+ }
447
+ }
448
+
449
+ private void replaceCurrent() {
450
+ if (searchMatchCount > 0 && !readOnly) {
451
+ String replacement = replaceInput.getText().toString();
452
+ editView.replaceFirst(replacement);
453
+ Toast.makeText(this, "已替换当前匹配项", Toast.LENGTH_SHORT).show();
454
+ performSearch(); // 重新搜索更新计数
455
+ }
456
+ }
457
+
458
+ private void replaceAll() {
459
+ if (searchMatchCount > 0 && !readOnly) {
460
+ String replacement = replaceInput.getText().toString();
461
+ int count = searchMatchCount;
462
+ editView.replaceAll(replacement);
463
+ Toast.makeText(this, "已替换 " + count + " 处", Toast.LENGTH_SHORT).show();
464
+ performSearch(); // 重新搜索更新计数
465
+ }
466
+ }
467
+
468
+ private void updateSearchButtonsState() {
469
+ boolean isEnabled = !searchInput.getText().toString().isEmpty() && searchMatchCount > 0;
470
+ prevBtn.setEnabled(isEnabled);
471
+ nextBtn.setEnabled(isEnabled);
472
+ replaceBtn.setEnabled(isEnabled && !readOnly);
473
+ replaceAllBtn.setEnabled(isEnabled && !readOnly);
474
+
475
+ // 更新按钮颜色
476
+ int enabledColor = Color.parseColor("#63B5F7");
477
+ int disabledColor = Color.parseColor("#555555");
478
+ prevBtn.setTextColor(isEnabled ? enabledColor : disabledColor);
479
+ nextBtn.setTextColor(isEnabled ? enabledColor : disabledColor);
480
+ replaceBtn.setTextColor((isEnabled && !readOnly) ? enabledColor : disabledColor);
481
+ replaceAllBtn.setTextColor((isEnabled && !readOnly) ? enabledColor : disabledColor);
303
482
  }
304
483
 
305
484
  private void showGotoLineDialog() {
@@ -286,10 +286,10 @@ public class EditView extends View {
286
286
  // Initialize magnifier
287
287
  mMagnifier = new Magnifier(this);
288
288
 
289
- // Reduce cursor width and make it responsive
289
+ // Cursor width responsive to screen density
290
290
  int density = (int) getResources().getDisplayMetrics().density;
291
- mCursorWidth = Math.max(2, density); // Minimum 2px, scales with density
292
- if (mCursorWidth > 5) mCursorWidth = 5; // Max 4px
291
+ mCursorWidth = Math.max(4, density * 2); // Minimum 4px, scales with density
292
+ if (mCursorWidth > 8) mCursorWidth = 8; // Max 8px for visibility
293
293
 
294
294
  // handle left - scale down selection handles
295
295
  mTextSelectHandleLeftRes = context.getDrawable(R.drawable.abc_text_select_handle_left_mtrl);
@@ -1753,11 +1753,22 @@ public class EditView extends View {
1753
1753
  if (!mReplaceList.isEmpty())
1754
1754
  mReplaceList.clear();
1755
1755
 
1756
+ if (regex == null || regex.isEmpty()) {
1757
+ postInvalidate();
1758
+ return;
1759
+ }
1760
+
1756
1761
  Matcher matcher = Pattern.compile(regex).matcher(mGapBuffer.toString());
1757
1762
 
1758
1763
  while (matcher.find()) {
1759
1764
  mReplaceList.add(new Pair<Integer, Integer>(matcher.start(), matcher.end()));
1760
1765
  }
1766
+ postInvalidate();
1767
+ }
1768
+
1769
+ // Get number of matches
1770
+ public int getMatchCount() {
1771
+ return mReplaceList.size();
1761
1772
  }
1762
1773
 
1763
1774
  // Replace first match
@@ -0,0 +1,6 @@
1
+ <?xml version="1.0" encoding="utf-8"?>
2
+ <shape xmlns:android="http://schemas.android.com/apk/res/android"
3
+ android:shape="rectangle">
4
+ <solid android:color="#3D3D3D"/>
5
+ <corners android:radius="8dp"/>
6
+ </shape>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "capacitor-dex-editor",
3
- "version": "0.0.49",
3
+ "version": "0.0.51",
4
4
  "description": "Capacitor-plugin-for-editing-DEX-files-in-APK",
5
5
  "main": "dist/plugin.cjs.js",
6
6
  "module": "dist/esm/index.js",