|
| 1 | +# Hướng dẫn sử dụng thuộc tính `depend-*` trong KR Script |
| 2 | + |
| 3 | +Tài liệu này giải thích chi tiết cách dùng từng thuộc tính `depend-*` để điều khiển ẩn/hiện param theo giá trị của (các) param khác, dựa theo logic hiện có trong `ActionParamsLayoutRender.kt` + `PageConfigReader.kt`. |
| 4 | + |
| 5 | +## Cơ chế chung |
| 6 | + |
| 7 | +Mỗi `<param>` có thể khai báo phụ thuộc vào 1 hoặc nhiều `<param>` khác (gọi là **param cha**). Khi giá trị param cha thay đổi, hàm `evaluateDependencies()` chạy lại, tính `shouldShow` cho từng param (có tính cả cascade — xem mục 10) rồi set `View.VISIBLE` / `View.GONE`. |
| 8 | + |
| 9 | +--- |
| 10 | + |
| 11 | +## 1. `depend-on` (hoặc `depend`) |
| 12 | + |
| 13 | +**Mục đích:** khai báo (các) param cha mà param này phụ thuộc vào. Nhiều param cha nối bằng `|`. |
| 14 | + |
| 15 | +```xml |
| 16 | +<param name="mode" type="select" title="Chế độ" option-sh="echo -e 'basic\nadvanced'" /> |
| 17 | + |
| 18 | +<param name="advanced_opt" type="input" title="Tùy chọn nâng cao" |
| 19 | + depend-on="mode" depend-value="advanced" /> |
| 20 | +``` |
| 21 | +→ `advanced_opt` chỉ hiện khi `mode = advanced`. |
| 22 | + |
| 23 | +Nhiều cha: |
| 24 | +```xml |
| 25 | +depend-on="mode|target_fs" |
| 26 | +``` |
| 27 | +→ phụ thuộc đồng thời `mode` và `target_fs` (cách kết hợp do `depend-logic` quyết định). |
| 28 | + |
| 29 | +--- |
| 30 | + |
| 31 | +## 2. `depend-value` |
| 32 | + |
| 33 | +**Mục đích:** giá trị cần khớp ở param cha tương ứng. Vị trí trong `depend-value` khớp theo đúng thứ tự khai báo ở `depend-on` (cách nhau bởi `|`). Trong mỗi vị trí, nhiều giá trị chấp nhận (OR) cách nhau bởi dấu phẩy. |
| 34 | + |
| 35 | +```xml |
| 36 | +depend-on="mode|target_fs" |
| 37 | +depend-value="advanced|EXT4,F2FS" |
| 38 | +``` |
| 39 | +→ điều kiện 1: `mode` khớp khi = `advanced`. |
| 40 | +→ điều kiện 2: `target_fs` khớp khi = `EXT4` **hoặc** `F2FS`. |
| 41 | + |
| 42 | +Giá trị so khớp không chỉ khớp `value` thực tế mà còn khớp `title` hiển thị, hoặc phần trong ngoặc `()` của title (ví dụ option có title `"A (so)"` thì `depend-value="A"` hoặc `depend-value="so"` hoặc `depend-value="(so)"` đều khớp được). |
| 43 | + |
| 44 | +--- |
| 45 | + |
| 46 | +## 3. `depend-mode` |
| 47 | + |
| 48 | +**Mục đích:** quy định khi khớp `depend-value` thì **hiện** hay **ẩn**. Mặc định `"show"`. Có thể khai riêng cho từng param cha, nối bằng `|`. |
| 49 | + |
| 50 | +```xml |
| 51 | +depend-on="mode" |
| 52 | +depend-value="basic" |
| 53 | +depend-mode="hide" |
| 54 | +``` |
| 55 | +→ Khi `mode = basic` thì **ẩn** param này (ngược lại với mặc định). |
| 56 | + |
| 57 | +Nhiều cha, mode khác nhau: |
| 58 | +```xml |
| 59 | +depend-on="mode|target_fs" |
| 60 | +depend-value="basic|EROFS" |
| 61 | +depend-mode="hide|show" |
| 62 | +``` |
| 63 | +→ cha 1 (`mode=basic`) → ẩn khi khớp. Cha 2 (`target_fs=EROFS`) → hiện khi khớp. |
| 64 | + |
| 65 | +--- |
| 66 | + |
| 67 | +## 4. `depend-logic` |
| 68 | + |
| 69 | +**Mục đích:** cách kết hợp nhiều điều kiện khi có ≥2 param cha. Có 5 chế độ. |
| 70 | + |
| 71 | +### `and` (mặc định) |
| 72 | +Tất cả điều kiện phải cùng "muốn hiện" (`wantShow`) thì mới hiện. |
| 73 | +```xml |
| 74 | +<param name="wifi" type="switch" title="WiFi" /> |
| 75 | +<param name="battery_saver" type="switch" title="Tiết kiệm pin" /> |
| 76 | + |
| 77 | +<param name="wifi_boost" type="switch" title="Tăng tốc WiFi" |
| 78 | + depend-on="wifi|battery_saver" |
| 79 | + depend-value="1|0" |
| 80 | + depend-mode="show|show" |
| 81 | + depend-logic="and" /> |
| 82 | +``` |
| 83 | +→ Chỉ hiện khi WiFi bật **và** tiết kiệm pin tắt. |
| 84 | + |
| 85 | +### `priority` (hoặc `or`) — ưu tiên trái → phải |
| 86 | +Xét từng điều kiện theo thứ tự khai báo; điều kiện nào **khớp trước** quyết định luôn kết quả (không xét tiếp). |
| 87 | +```xml |
| 88 | +<param name="target_fs" type="select" title="Định dạng" option-sh="..." /> |
| 89 | +<param name="images" type="select" title="Loại ảnh" option-sh="..." /> |
| 90 | + |
| 91 | +<param name="erofs_notice" type="input" title="Cảnh báo EROFS" |
| 92 | + depend-on="target_fs|images" |
| 93 | + depend-value="EXT4,F2FS|(erofs)" |
| 94 | + depend-mode="hide|show" |
| 95 | + depend-logic="priority" /> |
| 96 | +``` |
| 97 | +→ Nếu `target_fs = EXT4/F2FS` → **ẩn ngay** (không xét `images` nữa). |
| 98 | +→ Nếu `target_fs` không khớp → xét tiếp `images`; nếu `images = (erofs)` → **hiện**. |
| 99 | +→ Nếu không điều kiện nào khớp → dùng `depend-default`. |
| 100 | + |
| 101 | +### `priority-rtl` (hoặc `or-rtl`) |
| 102 | +Giống `priority` nhưng xét từ phải sang trái (điều kiện cuối cùng trong `depend-on` được xét trước). |
| 103 | +```xml |
| 104 | +depend-on="a|b|c" |
| 105 | +depend-value="1|2|3" |
| 106 | +depend-logic="priority-rtl" |
| 107 | +``` |
| 108 | +→ Xét `c` trước, rồi `b`, rồi `a`. |
| 109 | + |
| 110 | +### `xor` |
| 111 | +Chỉ hiện khi **đúng một** điều kiện khớp (không hơn, không kém). |
| 112 | +```xml |
| 113 | +<param name="use_manual" type="switch" title="Nhập tay" /> |
| 114 | +<param name="use_auto" type="switch" title="Tự động" /> |
| 115 | + |
| 116 | +<param name="conflict_warning" type="input" title="⚠ Xung đột cấu hình" |
| 117 | + depend-on="use_manual|use_auto" |
| 118 | + depend-value="1|1" |
| 119 | + depend-logic="xor" /> |
| 120 | +``` |
| 121 | +→ Nếu **cả hai** hoặc **không cái nào** bật → không hiện cảnh báo. Chỉ hiện khi đúng 1 trong 2 bật. |
| 122 | + |
| 123 | +### `nand` |
| 124 | +Phủ định của `and` — hiện khi **không phải tất cả** điều kiện đều thỏa (ít nhất 1 cái không thỏa thì hiện). |
| 125 | +```xml |
| 126 | +depend-on="a|b" |
| 127 | +depend-value="1|1" |
| 128 | +depend-logic="nand" |
| 129 | +``` |
| 130 | +→ Hiện trừ khi cả `a=1` và `b=1` cùng lúc. |
| 131 | + |
| 132 | +--- |
| 133 | + |
| 134 | +## 5. `depend-default` |
| 135 | + |
| 136 | +**Mục đích:** kết quả khi **không có điều kiện nào khớp** (chỉ có ý nghĩa rõ với `priority`/`priority-rtl`, hoặc khi param cha không tìm thấy). Giá trị: `"show"` (mặc định) hoặc `"hide"`. |
| 137 | + |
| 138 | +```xml |
| 139 | +<param name="advanced_toggle" type="input" title="Tuỳ chọn hiếm gặp" |
| 140 | + depend-on="mode" |
| 141 | + depend-value="expert" |
| 142 | + depend-logic="priority" |
| 143 | + depend-default="hide" /> |
| 144 | +``` |
| 145 | +→ Chỉ hiện khi `mode = expert`; mọi trường hợp khác (kể cả không xác định được `mode`) → **ẩn** thay vì mặc định hiện như trước. |
| 146 | + |
| 147 | +--- |
| 148 | + |
| 149 | +## 6. `depend-initial` (hoặc `depend-initial-state`) |
| 150 | + |
| 151 | +**Mục đích:** trạng thái ẩn/hiện **ngay lúc mở dialog**, trước khi kịp đánh giá điều kiện thật (tránh nhấp nháy khi load). Giá trị: `"auto"` (mặc định — dựa theo `depend-default`), `"show"`, `"hide"`. |
| 152 | + |
| 153 | +```xml |
| 154 | +<param name="big_form" type="input" title="Form phức tạp" |
| 155 | + depend-on="enable_big_form" |
| 156 | + depend-value="1" |
| 157 | + depend-initial="hide" /> |
| 158 | +``` |
| 159 | +→ Ngay khi dialog vừa mở, `big_form` bị ẩn tức thì (không đợi 1 nhịp `evaluateDependencies()` chạy xong mới ẩn), tránh hiện rồi biến mất gây giật màn hình. |
| 160 | + |
| 161 | +--- |
| 162 | + |
| 163 | +## 7. `depend-negate` |
| 164 | + |
| 165 | +**Mục đích:** đảo ngược toàn bộ kết quả cuối cùng của khối điều kiện (áp dụng sau khi đã tính `and`/`nand`/`xor`). Giá trị: `"true"`/`"1"`/`"negate"`. |
| 166 | + |
| 167 | +```xml |
| 168 | +<param name="admin_mode" type="switch" title="Admin" /> |
| 169 | + |
| 170 | +<param name="user_only_hint" type="input" title="Chỉ dành cho người dùng thường" |
| 171 | + depend-on="admin_mode" |
| 172 | + depend-value="1" |
| 173 | + depend-negate="true" /> |
| 174 | +``` |
| 175 | +→ Bình thường `depend-value="1"` nghĩa là hiện khi `admin_mode=1`. Có `depend-negate="true"` → đảo lại: hiện khi `admin_mode != 1`. |
| 176 | + |
| 177 | +Kết hợp với `xor` để bắt "cả 2 cùng bật": |
| 178 | +```xml |
| 179 | +depend-on="use_manual|use_auto" |
| 180 | +depend-value="1|1" |
| 181 | +depend-logic="xor" |
| 182 | +depend-negate="true" |
| 183 | +``` |
| 184 | +→ `xor` gốc chỉ đúng khi *đúng một* cái bật → `negate` đảo lại → hiện khi **cả hai cùng bật hoặc cùng tắt** (loại trừ trường hợp chỉ 1 cái bật). |
| 185 | + |
| 186 | +--- |
| 187 | + |
| 188 | +## 8. `depend-threshold` |
| 189 | + |
| 190 | +**Mục đích:** chỉ áp dụng cho `depend-logic="and"` (mặc định) — quy định **% tối thiểu** số điều kiện cần thỏa thay vì bắt buộc 100%. Giá trị: số nguyên 0–100 (mặc định `-1` = 100%). |
| 191 | + |
| 192 | +```xml |
| 193 | +<param name="cond_a" type="switch" title="Điều kiện A" /> |
| 194 | +<param name="cond_b" type="switch" title="Điều kiện B" /> |
| 195 | +<param name="cond_c" type="switch" title="Điều kiện C" /> |
| 196 | + |
| 197 | +<param name="need_2_of_3" type="input" title="Cần ít nhất 2/3 điều kiện" |
| 198 | + depend-on="cond_a|cond_b|cond_c" |
| 199 | + depend-value="1|1|1" |
| 200 | + depend-threshold="67" /> |
| 201 | +``` |
| 202 | +→ 3 điều kiện, ngưỡng 67% ≈ tối thiểu 2/3 → chỉ cần 2 trong 3 công tắc bật là hiện, không cần cả 3. |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +## 9. `depend-include-hidden` |
| 207 | + |
| 208 | +**Mục đích:** khi param bị **ẩn**, mặc định nó vẫn được đưa vào kết quả `readParamsValue()` nếu có giá trị (không bắt buộc `required`). Thuộc tính này hiện chỉ ảnh hưởng tới việc bỏ qua kiểm tra `required` khi ẩn — không phải "loại bỏ khỏi params". Giá trị: `"true"`/`"1"`. |
| 209 | + |
| 210 | +```xml |
| 211 | +<param name="hidden_debug_flag" type="input" title="Debug flag" value="0" |
| 212 | + depend-on="dev_mode" |
| 213 | + depend-value="1" |
| 214 | + depend-include-hidden="true" /> |
| 215 | +``` |
| 216 | +→ Dùng khi bạn muốn 1 giá trị mặc định (`value="0"`) vẫn được gửi lên script kể cả khi param đang ẩn (ví dụ 1 cờ ẩn luôn đi kèm mọi lần chạy, người dùng chỉ chỉnh nó khi bật `dev_mode`). |
| 217 | + |
| 218 | +> ⚠️ Nếu muốn **loại hẳn giá trị của param ẩn khỏi kết quả**, cần sửa thêm đoạn `readParamsValue()` (chưa áp dụng trong bản hiện tại). |
| 219 | +
|
| 220 | +--- |
| 221 | + |
| 222 | +## 10. `depend-cascade` — cha ẩn thì con ẩn theo *(mới)* |
| 223 | + |
| 224 | +**Mặc định: BẬT SẴN (`true`)** — không cần khai báo gì cũng đã có hiệu lực. |
| 225 | + |
| 226 | +**Mục đích:** nếu **bất kỳ param cha nào** trong `depend-on` đang **bị ẩn** (do chính `depend-on` của nó, hoặc do một chuỗi phụ thuộc nhiều cấp phía trên), param con này **tự động ẩn theo luôn**, bất kể giá trị hiện tại của cha có khớp `depend-value` hay không. |
| 227 | + |
| 228 | +Việc tính toán chạy qua **nhiều lượt (fixed-point)** cho tới khi trạng thái ổn định, nên hoạt động đúng dù param cha được khai báo **trước hay sau** param con trong file XML, và lan truyền đúng qua **chuỗi nhiều cấp** (con của con của con...). |
| 229 | + |
| 230 | +### Ví dụ: chuỗi phụ thuộc 3 cấp |
| 231 | + |
| 232 | +```xml |
| 233 | +<param name="mode" type="select" title="Chế độ" |
| 234 | + option-sh="echo -e 'basic\nadvanced'" /> |
| 235 | + |
| 236 | +<!-- Cấp 2: chỉ hiện khi mode = advanced --> |
| 237 | +<param name="target_fs" type="select" title="Định dạng đích" |
| 238 | + option-sh="echo -e 'EXT4\nF2FS\nEROFS'" |
| 239 | + depend-on="mode" depend-value="advanced" /> |
| 240 | + |
| 241 | +<!-- Cấp 3: phụ thuộc vào target_fs (là param cấp 2, có thể đang ẩn) --> |
| 242 | +<param name="erofs_readonly_hint" type="input" title="⚠ EROFS chỉ đọc" |
| 243 | + depend-on="target_fs" depend-value="EROFS" /> |
| 244 | +``` |
| 245 | + |
| 246 | +- Khi `mode = basic` → `target_fs` bị ẩn (không khớp `advanced`). |
| 247 | +- Vì `depend-cascade` mặc định bật, `erofs_readonly_hint` **cũng tự ẩn theo `target_fs`**, dù giá trị đang lưu trong `target_fs` (widget đã bị ẩn, người dùng không sửa được nữa) tình cờ vẫn là `EROFS` từ trước đó. Nếu không có cascade, `erofs_readonly_hint` sẽ hiện ra một cách vô lý dù `target_fs` (cha trực tiếp của nó) đang không hiển thị trên màn hình. |
| 248 | +- Chỉ khi `mode = advanced` (→ `target_fs` hiện) **và** người dùng chọn `target_fs = EROFS`, thì `erofs_readonly_hint` mới thực sự hiện. |
| 249 | + |
| 250 | +### Tắt cascade cho 1 param cụ thể |
| 251 | + |
| 252 | +Trường hợp hiếm gặp — bạn muốn param con vẫn tự đánh giá theo **giá trị** của cha ngay cả khi hàng chứa cha đang ẩn khỏi màn hình: |
| 253 | + |
| 254 | +```xml |
| 255 | +<param name="always_evaluate" type="input" title="Luôn tự xét theo giá trị cha" |
| 256 | + depend-on="target_fs" depend-value="EROFS" |
| 257 | + depend-cascade="false" /> |
| 258 | +``` |
| 259 | +→ `always_evaluate` sẽ hiện khi `target_fs` **có giá trị** `EROFS`, kể cả khi bản thân dòng `target_fs` đang bị ẩn khỏi giao diện. |
| 260 | + |
| 261 | +--- |
| 262 | + |
| 263 | +## 11. `depend-onchange` (hoặc `depend-on-change`, `depend-callback`) — đã thực thi *(mới)* |
| 264 | + |
| 265 | +**Mục đích:** chạy 1 đoạn shell script mỗi khi trạng thái ẩn/hiện của param này **thực sự đổi** (từ hiện → ẩn hoặc ẩn → hiện). Script chạy trên **luồng nền riêng** (không chạy trên UI thread, tránh treo giao diện khi lệnh root mất thời gian). |
| 266 | + |
| 267 | +Script nhận được 2 biến môi trường: |
| 268 | + |
| 269 | +| Biến | Ý nghĩa | |
| 270 | +|---|---| |
| 271 | +| `$PARAM_NAME` | Tên param vừa đổi trạng thái | |
| 272 | +| `$PARAM_VISIBLE` | `1` nếu param vừa chuyển sang **hiện**, `0` nếu vừa chuyển sang **ẩn** | |
| 273 | + |
| 274 | +### Ví dụ 1: ghi log mỗi khi 1 param đổi trạng thái |
| 275 | + |
| 276 | +```xml |
| 277 | +<param name="mode" type="select" title="Chế độ" |
| 278 | + option-sh="echo -e 'basic\nadvanced'" /> |
| 279 | + |
| 280 | +<param name="advanced_opt" type="input" title="Tùy chọn nâng cao" |
| 281 | + depend-on="mode" depend-value="advanced" |
| 282 | + depend-onchange="log -t krscript "advanced_opt doi trang thai: visible=$PARAM_VISIBLE"" /> |
| 283 | +``` |
| 284 | +→ Mỗi lần `advanced_opt` chuyển ẩn ↔ hiện (do người dùng đổi `mode`), dòng log được ghi ra logcat. |
| 285 | + |
| 286 | +### Ví dụ 2: tự dọn dẹp giá trị khi param bị ẩn đi |
| 287 | + |
| 288 | +```xml |
| 289 | +<param name="temp_folder" type="folder" title="Thư mục tạm" value="" |
| 290 | + depend-on="use_temp_folder" depend-value="1" |
| 291 | + depend-onchange="if [ "$PARAM_VISIBLE" = "0" ]; then rm -rf /sdcard/krscript_temp; fi" /> |
| 292 | +``` |
| 293 | +→ Khi `temp_folder` bị ẩn đi (người dùng tắt `use_temp_folder`), script tự xoá thư mục tạm tương ứng — hữu ích để dọn rác khi 1 tính năng bị tắt. |
| 294 | + |
| 295 | +### Ví dụ 3: kết hợp `depend-cascade` + `depend-onchange` |
| 296 | + |
| 297 | +```xml |
| 298 | +<param name="root_access" type="switch" title="Quyền Root" /> |
| 299 | + |
| 300 | +<param name="deep_clean" type="switch" title="Dọn dẹp sâu" |
| 301 | + depend-on="root_access" depend-value="1" /> |
| 302 | + |
| 303 | +<param name="deep_clean_confirm" type="input" title="Xác nhận dọn dẹp sâu" |
| 304 | + depend-on="deep_clean" depend-value="1" |
| 305 | + depend-onchange="log -t krscript "deep_clean_confirm: $PARAM_VISIBLE"" /> |
| 306 | +``` |
| 307 | +→ Nếu `root_access` tắt → `deep_clean` tự ẩn (điều kiện `depend-value` không khớp) → nhờ `depend-cascade`, `deep_clean_confirm` **cũng ẩn theo `deep_clean`** dù `deep_clean` từng có giá trị `1` trước đó → `depend-onchange` của `deep_clean_confirm` được gọi đúng 1 lần với `PARAM_VISIBLE=0`. |
| 308 | + |
| 309 | +> ⚠️ **Lưu ý:** callback chạy bằng quyền root qua `ScriptEnvironmen.executeResultRoot`, hiện **chưa được truyền** `PAGE_CONFIG_DIR` / `PAGE_WORK_DIR` (biến môi trường chỉ có khi chạy trong ngữ cảnh của 1 trang cấu hình cụ thể). Nếu cần các biến này trong script `depend-onchange`, cần bổ sung thêm việc truyền `NodeInfoBase` vào `ActionParamsLayoutRender`. |
| 310 | +> |
| 311 | +> Callback **không** gọi khi dialog vừa mở (chỉ gọi khi có thay đổi thật so với lần đánh giá trước). |
| 312 | +
|
| 313 | +--- |
| 314 | + |
| 315 | +## Bảng tóm tắt nhanh |
| 316 | + |
| 317 | +| Thuộc tính | Áp dụng cho | Giá trị | Mặc định | |
| 318 | +|---|---|---|---| |
| 319 | +| `depend-on` / `depend` | tên param cha | text, `\|` cách nhau | — | |
| 320 | +| `depend-value` | điều kiện khớp | text, `,` OR trong 1 vị trí, `\|` cách vị trí | — | |
| 321 | +| `depend-mode` | show/hide khi khớp | `show`/`hide`, có thể riêng từng cha | `show` | |
| 322 | +| `depend-logic` | cách kết hợp nhiều cha | `and`, `priority`/`or`, `priority-rtl`/`or-rtl`, `xor`, `nand` | `and` | |
| 323 | +| `depend-default` | kết quả khi không match | `show`/`hide` | `show` | |
| 324 | +| `depend-initial` / `depend-initial-state` | trạng thái lúc mở dialog | `auto`/`show`/`hide` | `auto` | |
| 325 | +| `depend-negate` | đảo kết quả cuối | `true`/`1`/`negate` | `false` | |
| 326 | +| `depend-threshold` | % điều kiện cần thỏa (chỉ `and`) | 0–100 | -1 (=100%) | |
| 327 | +| `depend-include-hidden` | có bỏ qua required khi ẩn | `true`/`1` | `false` | |
| 328 | +| `depend-cascade` | cha ẩn thì con ẩn theo | `true`/`false` | `true` | |
| 329 | +| `depend-onchange` / `depend-on-change` / `depend-callback` | script chạy khi đổi trạng thái | text (shell script), có `$PARAM_NAME`/`$PARAM_VISIBLE` | — | |
0 commit comments