txa-settings-permission 1.0.0

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.
@@ -0,0 +1,744 @@
1
+ package online.txa.settingspermission;
2
+
3
+ /**
4
+ * TXA Settings Permission Plugin
5
+ * Author: TXA (txa@nrotxa.online)
6
+ * GitHub: https://github.com/TXAVLOG/txa-settings-permission
7
+ *
8
+ * Hỗ trợ mở tất cả các màn hình Settings cấp quyền trên Android,
9
+ * tự động tương thích theo Android version.
10
+ */
11
+
12
+ import android.Manifest;
13
+ import android.app.Activity;
14
+ import android.content.Context;
15
+ import android.content.Intent;
16
+ import android.content.pm.PackageManager;
17
+ import android.net.Uri;
18
+ import android.os.Build;
19
+ import android.os.Environment;
20
+ import android.os.PowerManager;
21
+ import android.provider.Settings;
22
+ import android.util.Log;
23
+
24
+ import androidx.core.app.ActivityCompat;
25
+ import androidx.core.content.ContextCompat;
26
+
27
+ import com.getcapacitor.JSArray;
28
+ import com.getcapacitor.JSObject;
29
+ import com.getcapacitor.Plugin;
30
+ import com.getcapacitor.PluginCall;
31
+ import com.getcapacitor.PluginMethod;
32
+ import com.getcapacitor.annotation.CapacitorPlugin;
33
+
34
+ import org.json.JSONArray;
35
+ import org.json.JSONException;
36
+
37
+ import java.util.ArrayList;
38
+ import java.util.List;
39
+
40
+ @CapacitorPlugin(name = "TxaSettingsPermission")
41
+ public class TxaSettingsPermissionPlugin extends Plugin {
42
+
43
+ private static final String TAG = "TxaSettingsPermission";
44
+
45
+ // ─────────────────────────────────────────────────────────────────────────
46
+ // HELPER: build log object đẹp trả về JS
47
+ // ─────────────────────────────────────────────────────────────────────────
48
+
49
+ private JSObject buildLog(String level, String message, String action, boolean success) {
50
+ JSObject log = new JSObject();
51
+ log.put("level", level);
52
+ log.put("message", message);
53
+ log.put("action", action);
54
+ log.put("success", success);
55
+ log.put("androidVersion", Build.VERSION.SDK_INT);
56
+ log.put("androidRelease", Build.VERSION.RELEASE);
57
+ log.put("timestamp", System.currentTimeMillis());
58
+ return log;
59
+ }
60
+
61
+ private void logInfo(String msg) {
62
+ Log.i(TAG, "✅ " + msg);
63
+ }
64
+
65
+ private void logWarn(String msg) {
66
+ Log.w(TAG, "⚠️ " + msg);
67
+ }
68
+
69
+ private void logError(String msg) {
70
+ Log.e(TAG, "❌ " + msg);
71
+ }
72
+
73
+ // ─────────────────────────────────────────────────────────────────────────
74
+ // getDeviceInfo: thông tin thiết bị + Android version
75
+ // ─────────────────────────────────────────────────────────────────────────
76
+
77
+ @PluginMethod
78
+ public void getDeviceInfo(PluginCall call) {
79
+ JSObject result = new JSObject();
80
+ result.put("androidSdk", Build.VERSION.SDK_INT);
81
+ result.put("androidRelease", Build.VERSION.RELEASE);
82
+ result.put("manufacturer", Build.MANUFACTURER);
83
+ result.put("model", Build.MODEL);
84
+ result.put("brand", Build.BRAND);
85
+ result.put("device", Build.DEVICE);
86
+ result.put("packageName", getContext().getPackageName());
87
+
88
+ // Feature flags theo version
89
+ result.put("supportsManageAllFiles", Build.VERSION.SDK_INT >= Build.VERSION_CODES.R);
90
+ result.put("supportsNotificationPolicy", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
91
+ result.put("supportsOverlay", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
92
+ result.put("supportsBatteryOptimization", Build.VERSION.SDK_INT >= Build.VERSION_CODES.M);
93
+ result.put("supportsMediaPermissions", Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU);
94
+ result.put("supportsInstallUnknownApps", Build.VERSION.SDK_INT >= Build.VERSION_CODES.O);
95
+ result.put("supportsUsageStats", Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1);
96
+ result.put("supportsAccessibility", true);
97
+ result.put("supportsDeviceAdmin", true);
98
+
99
+ logInfo("getDeviceInfo → SDK " + Build.VERSION.SDK_INT + " (" + Build.MANUFACTURER + " " + Build.MODEL + ")");
100
+ call.resolve(result);
101
+ }
102
+
103
+ // ─────────────────────────────────────────────────────────────────────────
104
+ // openSettings: mở màn hình Settings tổng quát của app
105
+ // ─────────────────────────────────────────────────────────────────────────
106
+
107
+ @PluginMethod
108
+ public void openSettings(PluginCall call) {
109
+ try {
110
+ Intent intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
111
+ intent.setData(Uri.parse("package:" + getContext().getPackageName()));
112
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
113
+ getActivity().startActivity(intent);
114
+
115
+ logInfo("openSettings → Opened app settings");
116
+ JSObject result = new JSObject();
117
+ result.put("opened", true);
118
+ result.put("screen", "APP_SETTINGS");
119
+ result.put("log", buildLog("info", "Đã mở màn hình cài đặt ứng dụng", "APP_SETTINGS", true));
120
+ call.resolve(result);
121
+ } catch (Exception e) {
122
+ logError("openSettings → " + e.getMessage());
123
+ call.reject("TXA_ERR_OPEN_SETTINGS: Không thể mở Settings → " + e.getMessage(), "OPEN_SETTINGS_FAILED", e);
124
+ }
125
+ }
126
+
127
+ // ─────────────────────────────────────────────────────────────────────────
128
+ // openManageAllFiles: MANAGE_EXTERNAL_STORAGE (Android 11+)
129
+ // ─────────────────────────────────────────────────────────────────────────
130
+
131
+ @PluginMethod
132
+ public void openManageAllFiles(PluginCall call) {
133
+ JSObject result = new JSObject();
134
+
135
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
136
+ if (Environment.isExternalStorageManager()) {
137
+ logInfo("openManageAllFiles → Already granted");
138
+ result.put("granted", true);
139
+ result.put("opened", false);
140
+ result.put("log", buildLog("info", "Đã có quyền MANAGE_EXTERNAL_STORAGE", "MANAGE_ALL_FILES", true));
141
+ call.resolve(result);
142
+ return;
143
+ }
144
+ try {
145
+ Intent intent = new Intent(
146
+ Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION,
147
+ Uri.parse("package:" + getContext().getPackageName())
148
+ );
149
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
150
+ getActivity().startActivity(intent);
151
+ logInfo("openManageAllFiles → Opened per-app screen");
152
+ result.put("granted", false);
153
+ result.put("opened", true);
154
+ result.put("screen", "MANAGE_APP_ALL_FILES_ACCESS");
155
+ result.put("log", buildLog("info", "Đã mở màn hình 'Truy cập tất cả các file'", "MANAGE_APP_ALL_FILES", true));
156
+ call.resolve(result);
157
+ } catch (Exception e) {
158
+ logWarn("openManageAllFiles → Per-app failed, trying global → " + e.getMessage());
159
+ try {
160
+ Intent fallback = new Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION);
161
+ fallback.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
162
+ getActivity().startActivity(fallback);
163
+ result.put("granted", false);
164
+ result.put("opened", true);
165
+ result.put("screen", "MANAGE_ALL_FILES_ACCESS_PERMISSION");
166
+ result.put("log", buildLog("warn", "Fallback: Đã mở màn hình All Files Access chung", "MANAGE_ALL_FILES_FALLBACK", true));
167
+ call.resolve(result);
168
+ } catch (Exception e2) {
169
+ logError("openManageAllFiles → Both failed: " + e2.getMessage());
170
+ call.reject("TXA_ERR_MANAGE_ALL_FILES: Không thể mở màn hình quản lý file → " + e2.getMessage(), "MANAGE_ALL_FILES_FAILED", e2);
171
+ }
172
+ }
173
+ } else {
174
+ logInfo("openManageAllFiles → Android < 11, permission not needed");
175
+ result.put("granted", true);
176
+ result.put("opened", false);
177
+ result.put("log", buildLog("info", "Android " + Build.VERSION.RELEASE + " < 11, không cần quyền MANAGE_EXTERNAL_STORAGE", "MANAGE_ALL_FILES_NOT_NEEDED", true));
178
+ call.resolve(result);
179
+ }
180
+ }
181
+
182
+ // ─────────────────────────────────────────────────────────────────────────
183
+ // checkManageAllFiles
184
+ // ─────────────────────────────────────────────────────────────────────────
185
+
186
+ @PluginMethod
187
+ public void checkManageAllFiles(PluginCall call) {
188
+ JSObject result = new JSObject();
189
+ boolean granted;
190
+
191
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
192
+ granted = Environment.isExternalStorageManager();
193
+ } else {
194
+ granted = true;
195
+ }
196
+
197
+ result.put("granted", granted);
198
+ result.put("androidSdk", Build.VERSION.SDK_INT);
199
+ result.put("log", buildLog(granted ? "info" : "warn",
200
+ granted ? "Đã có quyền MANAGE_EXTERNAL_STORAGE" : "Chưa có quyền MANAGE_EXTERNAL_STORAGE",
201
+ "CHECK_MANAGE_ALL_FILES", granted));
202
+ logInfo("checkManageAllFiles → " + granted);
203
+ call.resolve(result);
204
+ }
205
+
206
+ // ─────────────────────────────────────────────────────────────────────────
207
+ // openOverlaySettings: Hiển thị trên ứng dụng khác (Android 6+)
208
+ // ─────────────────────────────────────────────────────────────────────────
209
+
210
+ @PluginMethod
211
+ public void openOverlaySettings(PluginCall call) {
212
+ JSObject result = new JSObject();
213
+
214
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
215
+ if (Settings.canDrawOverlays(getContext())) {
216
+ logInfo("openOverlaySettings → Already granted");
217
+ result.put("granted", true);
218
+ result.put("opened", false);
219
+ result.put("log", buildLog("info", "Đã có quyền hiển thị trên ứng dụng khác", "OVERLAY", true));
220
+ call.resolve(result);
221
+ return;
222
+ }
223
+ try {
224
+ Intent intent = new Intent(
225
+ Settings.ACTION_MANAGE_OVERLAY_PERMISSION,
226
+ Uri.parse("package:" + getContext().getPackageName())
227
+ );
228
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
229
+ getActivity().startActivity(intent);
230
+ logInfo("openOverlaySettings → Opened");
231
+ result.put("granted", false);
232
+ result.put("opened", true);
233
+ result.put("screen", "MANAGE_OVERLAY_PERMISSION");
234
+ result.put("log", buildLog("info", "Đã mở màn hình 'Hiển thị trên ứng dụng khác'", "OVERLAY", true));
235
+ call.resolve(result);
236
+ } catch (Exception e) {
237
+ logError("openOverlaySettings → " + e.getMessage());
238
+ call.reject("TXA_ERR_OVERLAY: Không thể mở màn hình Overlay → " + e.getMessage(), "OVERLAY_FAILED", e);
239
+ }
240
+ } else {
241
+ logInfo("openOverlaySettings → Android < 6, not needed");
242
+ result.put("granted", true);
243
+ result.put("opened", false);
244
+ result.put("log", buildLog("info", "Android < 6, không cần quyền Overlay", "OVERLAY_NOT_NEEDED", true));
245
+ call.resolve(result);
246
+ }
247
+ }
248
+
249
+ // ─────────────────────────────────────────────────────────────────────────
250
+ // checkOverlay
251
+ // ─────────────────────────────────────────────────────────────────────────
252
+
253
+ @PluginMethod
254
+ public void checkOverlay(PluginCall call) {
255
+ boolean granted = Build.VERSION.SDK_INT < Build.VERSION_CODES.M
256
+ || Settings.canDrawOverlays(getContext());
257
+ JSObject result = new JSObject();
258
+ result.put("granted", granted);
259
+ result.put("log", buildLog(granted ? "info" : "warn",
260
+ granted ? "Đã có quyền Overlay" : "Chưa có quyền Overlay",
261
+ "CHECK_OVERLAY", granted));
262
+ call.resolve(result);
263
+ }
264
+
265
+ // ─────────────────────────────────────────────────────────────────────────
266
+ // openInstallUnknownApps: Cài từ nguồn không rõ (Android 8+)
267
+ // ─────────────────────────────────────────────────────────────────────────
268
+
269
+ @PluginMethod
270
+ public void openInstallUnknownApps(PluginCall call) {
271
+ JSObject result = new JSObject();
272
+
273
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
274
+ if (getContext().getPackageManager().canRequestPackageInstalls()) {
275
+ logInfo("openInstallUnknownApps → Already granted");
276
+ result.put("granted", true);
277
+ result.put("opened", false);
278
+ result.put("log", buildLog("info", "Đã có quyền cài ứng dụng từ nguồn không rõ", "INSTALL_UNKNOWN_APPS", true));
279
+ call.resolve(result);
280
+ return;
281
+ }
282
+ try {
283
+ Intent intent = new Intent(
284
+ Settings.ACTION_MANAGE_UNKNOWN_APP_SOURCES,
285
+ Uri.parse("package:" + getContext().getPackageName())
286
+ );
287
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
288
+ getActivity().startActivity(intent);
289
+ logInfo("openInstallUnknownApps → Opened");
290
+ result.put("granted", false);
291
+ result.put("opened", true);
292
+ result.put("screen", "MANAGE_UNKNOWN_APP_SOURCES");
293
+ result.put("log", buildLog("info", "Đã mở màn hình 'Cài ứng dụng không rõ nguồn'", "INSTALL_UNKNOWN_APPS", true));
294
+ call.resolve(result);
295
+ } catch (Exception e) {
296
+ logError("openInstallUnknownApps → " + e.getMessage());
297
+ call.reject("TXA_ERR_INSTALL_UNKNOWN: Không thể mở màn hình → " + e.getMessage(), "INSTALL_UNKNOWN_FAILED", e);
298
+ }
299
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
300
+ // Android 5-7: chỉ mở settings chung
301
+ try {
302
+ Intent intent = new Intent(Settings.ACTION_SECURITY_SETTINGS);
303
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
304
+ getActivity().startActivity(intent);
305
+ logWarn("openInstallUnknownApps → Android < 8, opened Security Settings");
306
+ result.put("granted", false);
307
+ result.put("opened", true);
308
+ result.put("screen", "SECURITY_SETTINGS");
309
+ result.put("log", buildLog("warn", "Android < 8: Mở Security Settings để bật 'Nguồn không xác định'", "INSTALL_UNKNOWN_LEGACY", true));
310
+ call.resolve(result);
311
+ } catch (Exception e) {
312
+ call.reject("TXA_ERR_SECURITY_SETTINGS: " + e.getMessage(), "SECURITY_SETTINGS_FAILED", e);
313
+ }
314
+ } else {
315
+ result.put("granted", true);
316
+ result.put("log", buildLog("info", "Android quá cũ, mặc định cho phép", "INSTALL_UNKNOWN_OLD", true));
317
+ call.resolve(result);
318
+ }
319
+ }
320
+
321
+ // ─────────────────────────────────────────────────────────────────────────
322
+ // checkInstallUnknownApps
323
+ // ─────────────────────────────────────────────────────────────────────────
324
+
325
+ @PluginMethod
326
+ public void checkInstallUnknownApps(PluginCall call) {
327
+ boolean granted;
328
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
329
+ granted = getContext().getPackageManager().canRequestPackageInstalls();
330
+ } else {
331
+ granted = true;
332
+ }
333
+ JSObject result = new JSObject();
334
+ result.put("granted", granted);
335
+ result.put("log", buildLog(granted ? "info" : "warn",
336
+ granted ? "Đã có quyền cài APK ngoài" : "Chưa có quyền cài APK ngoài",
337
+ "CHECK_INSTALL_UNKNOWN", granted));
338
+ call.resolve(result);
339
+ }
340
+
341
+ // ─────────────────────────────────────────────────────────────────────────
342
+ // openBatteryOptimization: Bỏ qua tối ưu hóa pin (Android 6+)
343
+ // ─────────────────────────────────────────────────────────────────────────
344
+
345
+ @PluginMethod
346
+ public void openBatteryOptimization(PluginCall call) {
347
+ JSObject result = new JSObject();
348
+
349
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
350
+ PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
351
+ if (pm != null && pm.isIgnoringBatteryOptimizations(getContext().getPackageName())) {
352
+ logInfo("openBatteryOptimization → Already ignoring");
353
+ result.put("granted", true);
354
+ result.put("opened", false);
355
+ result.put("log", buildLog("info", "App đã được bỏ qua tối ưu hóa pin", "BATTERY_OPTIMIZATION", true));
356
+ call.resolve(result);
357
+ return;
358
+ }
359
+ try {
360
+ Intent intent = new Intent(
361
+ Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
362
+ Uri.parse("package:" + getContext().getPackageName())
363
+ );
364
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
365
+ getActivity().startActivity(intent);
366
+ logInfo("openBatteryOptimization → Opened");
367
+ result.put("granted", false);
368
+ result.put("opened", true);
369
+ result.put("screen", "REQUEST_IGNORE_BATTERY_OPTIMIZATIONS");
370
+ result.put("log", buildLog("info", "Đã mở màn hình bỏ qua tối ưu hóa pin", "BATTERY_OPTIMIZATION", true));
371
+ call.resolve(result);
372
+ } catch (Exception e) {
373
+ logWarn("openBatteryOptimization → Direct failed, opening battery settings: " + e.getMessage());
374
+ try {
375
+ Intent fallback = new Intent(Settings.ACTION_BATTERY_SAVER_SETTINGS);
376
+ fallback.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
377
+ getActivity().startActivity(fallback);
378
+ result.put("opened", true);
379
+ result.put("screen", "BATTERY_SAVER_SETTINGS");
380
+ result.put("log", buildLog("warn", "Fallback: Đã mở Battery Saver Settings", "BATTERY_OPTIMIZATION_FALLBACK", true));
381
+ call.resolve(result);
382
+ } catch (Exception e2) {
383
+ call.reject("TXA_ERR_BATTERY: Không thể mở màn hình pin → " + e2.getMessage(), "BATTERY_FAILED", e2);
384
+ }
385
+ }
386
+ } else {
387
+ logInfo("openBatteryOptimization → Android < 6, not needed");
388
+ result.put("granted", true);
389
+ result.put("log", buildLog("info", "Android < 6, không cần xử lý Battery Optimization", "BATTERY_NOT_NEEDED", true));
390
+ call.resolve(result);
391
+ }
392
+ }
393
+
394
+ // ─────────────────────────────────────────────────────────────────────────
395
+ // checkBatteryOptimization
396
+ // ─────────────────────────────────────────────────────────────────────────
397
+
398
+ @PluginMethod
399
+ public void checkBatteryOptimization(PluginCall call) {
400
+ boolean ignoring = true;
401
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
402
+ PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
403
+ ignoring = pm != null && pm.isIgnoringBatteryOptimizations(getContext().getPackageName());
404
+ }
405
+ JSObject result = new JSObject();
406
+ result.put("ignoring", ignoring);
407
+ result.put("granted", ignoring);
408
+ result.put("log", buildLog(ignoring ? "info" : "warn",
409
+ ignoring ? "App đang được bỏ qua tối ưu hóa pin" : "App vẫn bị tối ưu hóa pin",
410
+ "CHECK_BATTERY", ignoring));
411
+ call.resolve(result);
412
+ }
413
+
414
+ // ─────────────────────────────────────────────────────────────────────────
415
+ // openNotificationSettings: Cài đặt thông báo
416
+ // ─────────────────────────────────────────────────────────────────────────
417
+
418
+ @PluginMethod
419
+ public void openNotificationSettings(PluginCall call) {
420
+ try {
421
+ Intent intent;
422
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
423
+ intent = new Intent(Settings.ACTION_APP_NOTIFICATION_SETTINGS);
424
+ intent.putExtra(Settings.EXTRA_APP_PACKAGE, getContext().getPackageName());
425
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
426
+ intent = new Intent("android.settings.APP_NOTIFICATION_SETTINGS");
427
+ intent.putExtra("app_package", getContext().getPackageName());
428
+ intent.putExtra("app_uid", getContext().getApplicationInfo().uid);
429
+ } else {
430
+ intent = new Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS);
431
+ intent.setData(Uri.parse("package:" + getContext().getPackageName()));
432
+ }
433
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
434
+ getActivity().startActivity(intent);
435
+ logInfo("openNotificationSettings → Opened (SDK " + Build.VERSION.SDK_INT + ")");
436
+ JSObject result = new JSObject();
437
+ result.put("opened", true);
438
+ result.put("screen", "APP_NOTIFICATION_SETTINGS");
439
+ result.put("log", buildLog("info", "Đã mở màn hình cài đặt thông báo", "NOTIFICATION_SETTINGS", true));
440
+ call.resolve(result);
441
+ } catch (Exception e) {
442
+ logError("openNotificationSettings → " + e.getMessage());
443
+ call.reject("TXA_ERR_NOTIFICATION: Không thể mở cài đặt thông báo → " + e.getMessage(), "NOTIFICATION_FAILED", e);
444
+ }
445
+ }
446
+
447
+ // ─────────────────────────────────────────────────────────────────────────
448
+ // openNotificationPolicyAccess: Chế độ không làm phiền (Android 6+)
449
+ // ─────────────────────────────────────────────────────────────────────────
450
+
451
+ @PluginMethod
452
+ public void openNotificationPolicyAccess(PluginCall call) {
453
+ JSObject result = new JSObject();
454
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
455
+ try {
456
+ Intent intent = new Intent(Settings.ACTION_NOTIFICATION_POLICY_ACCESS_SETTINGS);
457
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
458
+ getActivity().startActivity(intent);
459
+ logInfo("openNotificationPolicyAccess → Opened");
460
+ result.put("opened", true);
461
+ result.put("screen", "NOTIFICATION_POLICY_ACCESS_SETTINGS");
462
+ result.put("log", buildLog("info", "Đã mở màn hình quyền truy cập chính sách thông báo (DND)", "NOTIFICATION_POLICY", true));
463
+ call.resolve(result);
464
+ } catch (Exception e) {
465
+ logError("openNotificationPolicyAccess → " + e.getMessage());
466
+ call.reject("TXA_ERR_NOTIFICATION_POLICY: " + e.getMessage(), "NOTIFICATION_POLICY_FAILED", e);
467
+ }
468
+ } else {
469
+ result.put("granted", true);
470
+ result.put("log", buildLog("info", "Android < 6, không cần quyền Notification Policy", "NOTIFICATION_POLICY_NOT_NEEDED", true));
471
+ call.resolve(result);
472
+ }
473
+ }
474
+
475
+ // ─────────────────────────────────────────────────────────────────────────
476
+ // openAccessibilitySettings: Quyền trợ năng
477
+ // ─────────────────────────────────────────────────────────────────────────
478
+
479
+ @PluginMethod
480
+ public void openAccessibilitySettings(PluginCall call) {
481
+ try {
482
+ Intent intent = new Intent(Settings.ACTION_ACCESSIBILITY_SETTINGS);
483
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
484
+ getActivity().startActivity(intent);
485
+ logInfo("openAccessibilitySettings → Opened");
486
+ JSObject result = new JSObject();
487
+ result.put("opened", true);
488
+ result.put("screen", "ACCESSIBILITY_SETTINGS");
489
+ result.put("log", buildLog("info", "Đã mở màn hình Trợ năng (Accessibility)", "ACCESSIBILITY", true));
490
+ call.resolve(result);
491
+ } catch (Exception e) {
492
+ logError("openAccessibilitySettings → " + e.getMessage());
493
+ call.reject("TXA_ERR_ACCESSIBILITY: " + e.getMessage(), "ACCESSIBILITY_FAILED", e);
494
+ }
495
+ }
496
+
497
+ // ─────────────────────────────────────────────────────────────────────────
498
+ // openUsageStatsSettings: Quyền sử dụng dữ liệu (Android 5.1+)
499
+ // ─────────────────────────────────────────────────────────────────────────
500
+
501
+ @PluginMethod
502
+ public void openUsageStatsSettings(PluginCall call) {
503
+ JSObject result = new JSObject();
504
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
505
+ try {
506
+ Intent intent = new Intent(Settings.ACTION_USAGE_ACCESS_SETTINGS);
507
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
508
+ getActivity().startActivity(intent);
509
+ logInfo("openUsageStatsSettings → Opened");
510
+ result.put("opened", true);
511
+ result.put("screen", "USAGE_ACCESS_SETTINGS");
512
+ result.put("log", buildLog("info", "Đã mở màn hình quyền truy cập dữ liệu sử dụng", "USAGE_STATS", true));
513
+ call.resolve(result);
514
+ } catch (Exception e) {
515
+ logError("openUsageStatsSettings → " + e.getMessage());
516
+ call.reject("TXA_ERR_USAGE_STATS: " + e.getMessage(), "USAGE_STATS_FAILED", e);
517
+ }
518
+ } else {
519
+ result.put("granted", true);
520
+ result.put("log", buildLog("info", "Android < 5.1, không cần quyền Usage Stats", "USAGE_STATS_NOT_NEEDED", true));
521
+ call.resolve(result);
522
+ }
523
+ }
524
+
525
+ // ─────────────────────────────────────────────────────────────────────────
526
+ // openDeviceAdminSettings: Quản trị thiết bị
527
+ // ─────────────────────────────────────────────────────────────────────────
528
+
529
+ @PluginMethod
530
+ public void openDeviceAdminSettings(PluginCall call) {
531
+ try {
532
+ Intent intent = new Intent(Settings.ACTION_DEVICE_INFO_SETTINGS);
533
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
534
+ getActivity().startActivity(intent);
535
+ logInfo("openDeviceAdminSettings → Opened");
536
+ JSObject result = new JSObject();
537
+ result.put("opened", true);
538
+ result.put("screen", "DEVICE_INFO_SETTINGS");
539
+ result.put("log", buildLog("info", "Đã mở màn hình thông tin thiết bị", "DEVICE_ADMIN", true));
540
+ call.resolve(result);
541
+ } catch (Exception e) {
542
+ logError("openDeviceAdminSettings → " + e.getMessage());
543
+ call.reject("TXA_ERR_DEVICE_ADMIN: " + e.getMessage(), "DEVICE_ADMIN_FAILED", e);
544
+ }
545
+ }
546
+
547
+ // ─────────────────────────────────────────────────────────────────────────
548
+ // openWifiSettings / openLocationSettings / openBluetoothSettings
549
+ // ─────────────────────────────────────────────────────────────────────────
550
+
551
+ @PluginMethod
552
+ public void openWifiSettings(PluginCall call) {
553
+ openSimpleSettings(call, Settings.ACTION_WIFI_SETTINGS, "WIFI_SETTINGS", "Đã mở cài đặt WiFi");
554
+ }
555
+
556
+ @PluginMethod
557
+ public void openLocationSettings(PluginCall call) {
558
+ openSimpleSettings(call, Settings.ACTION_LOCATION_SOURCE_SETTINGS, "LOCATION_SETTINGS", "Đã mở cài đặt Vị trí");
559
+ }
560
+
561
+ @PluginMethod
562
+ public void openBluetoothSettings(PluginCall call) {
563
+ openSimpleSettings(call, Settings.ACTION_BLUETOOTH_SETTINGS, "BLUETOOTH_SETTINGS", "Đã mở cài đặt Bluetooth");
564
+ }
565
+
566
+ @PluginMethod
567
+ public void openNfcSettings(PluginCall call) {
568
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
569
+ openSimpleSettings(call, Settings.ACTION_NFC_SETTINGS, "NFC_SETTINGS", "Đã mở cài đặt NFC");
570
+ } else {
571
+ openSimpleSettings(call, Settings.ACTION_WIRELESS_SETTINGS, "WIRELESS_SETTINGS", "Đã mở cài đặt Wireless (NFC fallback)");
572
+ }
573
+ }
574
+
575
+ @PluginMethod
576
+ public void openDateTimeSettings(PluginCall call) {
577
+ openSimpleSettings(call, Settings.ACTION_DATE_SETTINGS, "DATE_SETTINGS", "Đã mở cài đặt Ngày giờ");
578
+ }
579
+
580
+ @PluginMethod
581
+ public void openLanguageSettings(PluginCall call) {
582
+ openSimpleSettings(call, Settings.ACTION_LOCALE_SETTINGS, "LOCALE_SETTINGS", "Đã mở cài đặt Ngôn ngữ");
583
+ }
584
+
585
+ @PluginMethod
586
+ public void openStorageSettings(PluginCall call) {
587
+ openSimpleSettings(call, Settings.ACTION_INTERNAL_STORAGE_SETTINGS, "STORAGE_SETTINGS", "Đã mở cài đặt Bộ nhớ");
588
+ }
589
+
590
+ @PluginMethod
591
+ public void openDisplaySettings(PluginCall call) {
592
+ openSimpleSettings(call, Settings.ACTION_DISPLAY_SETTINGS, "DISPLAY_SETTINGS", "Đã mở cài đặt Màn hình");
593
+ }
594
+
595
+ @PluginMethod
596
+ public void openSoundSettings(PluginCall call) {
597
+ openSimpleSettings(call, Settings.ACTION_SOUND_SETTINGS, "SOUND_SETTINGS", "Đã mở cài đặt Âm thanh");
598
+ }
599
+
600
+ // ─────────────────────────────────────────────────────────────────────────
601
+ // checkRuntimePermission: kiểm tra bất kỳ runtime permission nào
602
+ // ─────────────────────────────────────────────────────────────────────────
603
+
604
+ @PluginMethod
605
+ public void checkRuntimePermission(PluginCall call) {
606
+ String permission = call.getString("permission");
607
+ if (permission == null || permission.isEmpty()) {
608
+ call.reject("TXA_ERR_PARAM: Thiếu tham số 'permission'", "MISSING_PARAM");
609
+ return;
610
+ }
611
+ boolean granted = ContextCompat.checkSelfPermission(getContext(), permission)
612
+ == PackageManager.PERMISSION_GRANTED;
613
+ JSObject result = new JSObject();
614
+ result.put("permission", permission);
615
+ result.put("granted", granted);
616
+ result.put("log", buildLog(granted ? "info" : "warn",
617
+ granted ? "Đã có quyền: " + permission : "Chưa có quyền: " + permission,
618
+ "CHECK_RUNTIME_PERMISSION", granted));
619
+ call.resolve(result);
620
+ }
621
+
622
+ // ─────────────────────────────────────────────────────────────────────────
623
+ // checkMultiplePermissions: check nhiều quyền 1 lúc
624
+ // ─────────────────────────────────────────────────────────────────────────
625
+
626
+ @PluginMethod
627
+ public void checkMultiplePermissions(PluginCall call) {
628
+ JSArray permsArray = call.getArray("permissions");
629
+ if (permsArray == null) {
630
+ call.reject("TXA_ERR_PARAM: Thiếu tham số 'permissions' (array)", "MISSING_PARAM");
631
+ return;
632
+ }
633
+
634
+ JSObject result = new JSObject();
635
+ JSObject permResults = new JSObject();
636
+ boolean allGranted = true;
637
+ List<String> missing = new ArrayList<>();
638
+
639
+ try {
640
+ JSONArray arr = permsArray.toJSONArray();
641
+ if (arr != null) {
642
+ for (int i = 0; i < arr.length(); i++) {
643
+ String perm = arr.getString(i);
644
+ boolean granted = ContextCompat.checkSelfPermission(getContext(), perm)
645
+ == PackageManager.PERMISSION_GRANTED;
646
+ permResults.put(perm, granted);
647
+ if (!granted) {
648
+ allGranted = false;
649
+ missing.add(perm);
650
+ }
651
+ }
652
+ }
653
+ } catch (JSONException e) {
654
+ call.reject("TXA_ERR_PARSE: Lỗi parse permissions array → " + e.getMessage(), "PARSE_ERROR", e);
655
+ return;
656
+ }
657
+
658
+ result.put("results", permResults);
659
+ result.put("allGranted", allGranted);
660
+ try {
661
+ result.put("missing", new JSArray(missing));
662
+ } catch (Exception ignore) {}
663
+ result.put("log", buildLog(allGranted ? "info" : "warn",
664
+ allGranted ? "Tất cả quyền đã được cấp" : "Thiếu " + missing.size() + " quyền: " + missing,
665
+ "CHECK_MULTIPLE_PERMISSIONS", allGranted));
666
+ call.resolve(result);
667
+ }
668
+
669
+ // ─────────────────────────────────────────────────────────────────────────
670
+ // checkAllRequired: kiểm tra toàn bộ các permission cần thiết của app
671
+ // ─────────────────────────────────────────────────────────────────────────
672
+
673
+ @PluginMethod
674
+ public void checkAllRequired(PluginCall call) {
675
+ JSObject result = new JSObject();
676
+ JSObject checks = new JSObject();
677
+
678
+ // MANAGE_EXTERNAL_STORAGE
679
+ boolean manageFiles = Build.VERSION.SDK_INT < Build.VERSION_CODES.R
680
+ || Environment.isExternalStorageManager();
681
+ checks.put("manageAllFiles", manageFiles);
682
+
683
+ // Overlay
684
+ boolean overlay = Build.VERSION.SDK_INT < Build.VERSION_CODES.M
685
+ || Settings.canDrawOverlays(getContext());
686
+ checks.put("overlay", overlay);
687
+
688
+ // Install unknown apps
689
+ boolean installUnknown = Build.VERSION.SDK_INT < Build.VERSION_CODES.O
690
+ || getContext().getPackageManager().canRequestPackageInstalls();
691
+ checks.put("installUnknownApps", installUnknown);
692
+
693
+ // Battery optimization
694
+ boolean batteryIgnoring = true;
695
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
696
+ PowerManager pm = (PowerManager) getContext().getSystemService(Context.POWER_SERVICE);
697
+ batteryIgnoring = pm != null && pm.isIgnoringBatteryOptimizations(getContext().getPackageName());
698
+ }
699
+ checks.put("batteryOptimization", batteryIgnoring);
700
+
701
+ // Runtime permissions thường gặp
702
+ checks.put("camera", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED);
703
+ checks.put("microphone", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED);
704
+ checks.put("location", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.ACCESS_FINE_LOCATION) == PackageManager.PERMISSION_GRANTED);
705
+
706
+ // Media permissions (Android 13+)
707
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
708
+ checks.put("readMediaImages", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_MEDIA_IMAGES) == PackageManager.PERMISSION_GRANTED);
709
+ checks.put("readMediaVideo", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_MEDIA_VIDEO) == PackageManager.PERMISSION_GRANTED);
710
+ checks.put("readMediaAudio", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_MEDIA_AUDIO) == PackageManager.PERMISSION_GRANTED);
711
+ checks.put("postNotifications", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.POST_NOTIFICATIONS) == PackageManager.PERMISSION_GRANTED);
712
+ } else {
713
+ checks.put("readExternalStorage", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.READ_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
714
+ checks.put("writeExternalStorage", ContextCompat.checkSelfPermission(getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED);
715
+ }
716
+
717
+ result.put("checks", checks);
718
+ result.put("androidSdk", Build.VERSION.SDK_INT);
719
+ result.put("androidRelease", Build.VERSION.RELEASE);
720
+ logInfo("checkAllRequired → SDK " + Build.VERSION.SDK_INT + " | checks done");
721
+ call.resolve(result);
722
+ }
723
+
724
+ // ─────────────────────────────────────────────────────────────────────────
725
+ // PRIVATE HELPER
726
+ // ─────────────────────────────────────────────────────────────────────────
727
+
728
+ private void openSimpleSettings(PluginCall call, String action, String screenName, String logMsg) {
729
+ try {
730
+ Intent intent = new Intent(action);
731
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
732
+ getActivity().startActivity(intent);
733
+ logInfo(screenName + " → Opened");
734
+ JSObject result = new JSObject();
735
+ result.put("opened", true);
736
+ result.put("screen", screenName);
737
+ result.put("log", buildLog("info", logMsg, screenName, true));
738
+ call.resolve(result);
739
+ } catch (Exception e) {
740
+ logError(screenName + " → " + e.getMessage());
741
+ call.reject("TXA_ERR_" + screenName + ": Không thể mở → " + e.getMessage(), screenName + "_FAILED", e);
742
+ }
743
+ }
744
+ }