@hungvv/super-ads-skills 1.0.0 → 1.1.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.
- package/README.md +71 -13
- package/dist/cli.js +8 -2
- package/package.json +1 -1
- package/skills/README.md +4 -0
- package/skills/adspro-to-superads/SKILL.md +157 -732
- package/skills/adspro-to-superads/references/global-invariant-check.md +49 -0
- package/skills/adspro-to-superads/references/migration-contract.md +57 -0
- package/skills/adspro-to-superads/references/phase-0-inventory.md +39 -0
- package/skills/adspro-to-superads/references/phase-1-2-gradle-manifest.md +85 -0
- package/skills/adspro-to-superads/references/phase-3-config-migration.md +159 -0
- package/skills/adspro-to-superads/references/phase-4-wrapper-observability.md +70 -0
- package/skills/adspro-to-superads/references/phase-5-7-callsites-layout-cleanup.md +73 -0
- package/skills/adspro-to-superads/references/principles-and-authority.md +73 -0
- package/skills/adspro-to-superads/references/user-questions.md +53 -0
- package/skills/adspro-to-superads/snippets/ads-api-quick.md +20 -0
- package/skills/adspro-to-superads/templates/migration-inventory.md +42 -0
- package/skills/super-ads-ab-test/SKILL.md +91 -23
- package/skills/super-ads-adb-test/SKILL.md +85 -36
- package/skills/super-ads-adb-test/{references → examples}/scenarios-sample.md +5 -5
- package/skills/super-ads-add-placement/SKILL.md +145 -50
- package/skills/super-ads-add-placement/references/call-site-api.md +33 -0
- package/skills/super-ads-add-placement/snippets/app-open.md +25 -0
- package/skills/super-ads-add-placement/snippets/fullscreen-nav.md +14 -0
- package/skills/super-ads-add-placement/snippets/inline-banner-native.md +19 -0
- package/skills/super-ads-add-placement/snippets/preload-event.md +13 -0
- package/skills/super-ads-add-placement/snippets/reward.md +17 -0
- package/skills/super-ads-config/SKILL.md +94 -36
- package/skills/super-ads-debug/SKILL.md +87 -37
- package/skills/super-ads-debug/references/logging-setup.md +2 -0
- package/skills/super-ads-debug/scripts/filter-logads.sh +21 -0
- package/skills/super-ads-debug-override/SKILL.md +154 -81
- package/skills/super-ads-debug-override/snippets/arm-override.md +9 -0
- package/skills/super-ads-debug-override/snippets/check-module.md +8 -0
- package/skills/super-ads-debug-override/snippets/clear-override.md +9 -0
- package/skills/super-ads-debug-override/snippets/confirm-active.md +16 -0
- package/skills/super-ads-guide/SKILL.md +129 -0
- package/skills/super-ads-guide/references/compatibility.md +26 -0
- package/skills/super-ads-guide/references/sdk-version-check.md +50 -0
- package/skills/super-ads-integrate/SKILL.md +145 -47
- package/skills/super-ads-integrate/references/application-setup.md +2 -21
- package/skills/super-ads-integrate/references/gradle-setup.md +1 -13
- package/skills/super-ads-integrate/snippets/consent-request.md +12 -0
- package/skills/super-ads-integrate/templates/application-oncreate.md +17 -0
- package/skills/super-ads-integrate/templates/gradle-dependencies.md +17 -0
- package/skills/super-ads-release-check/SKILL.md +85 -32
- package/skills/super-ads-show-rate/SKILL.md +87 -36
- package/skills/super-ads-show-rate/references/collect-snippet.md +2 -36
- package/skills/super-ads-show-rate/scripts/show-rate.sh +25 -0
- package/skills/super-ads-show-rate/snippets/show-events-logger.md +26 -0
- package/skills/super-ads-upgrade/SKILL.md +137 -0
- package/skills/super-ads-upgrade/references/upgrade-hops.md +26 -0
- package/skills/super-ads-upgrade/templates/changelog-template.md +39 -0
- package/skills/super-ads-add-placement/references/call-site-snippets.md +0 -90
- /package/skills/super-ads-ab-test/{references → templates}/firebase-handoff-template.md +0 -0
- /package/skills/super-ads-config/{references → examples}/patch-examples.md +0 -0
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# Global Invariant Check
|
|
2
|
+
|
|
3
|
+
Chạy sau Phase 7, **trước** build. Đọc [verification.md](verification.md).
|
|
4
|
+
|
|
5
|
+
```text
|
|
6
|
+
COUNT(unique AdsPro spaceName) <= COUNT(SuperAds placements)
|
|
7
|
+
AdsPro spaceName \ SuperAds placements = rỗng
|
|
8
|
+
SuperAds placements \ AdsPro spaceName ⊆ Bảng D reload extras ∪ Documented exclusions
|
|
9
|
+
COUNT(AdsPro ids toàn bộ) == COUNT(SuperAds units toàn bộ)
|
|
10
|
+
(gộp unit trùng network+id chỉ khi product yêu cầu; DUPLICATE_UNIT_ID = warning)
|
|
11
|
+
|
|
12
|
+
COUNT(call site load() cũ) == COUNT(hàng Bảng B đã map)
|
|
13
|
+
mọi chuỗi preload.eventActive ⊆ tập EventScreen app này thực sự emit
|
|
14
|
+
|
|
15
|
+
mọi adItem NATIVE_COLLAPSIBLE có đủ 2 key layout, và collapsed != expanded
|
|
16
|
+
mọi ui.layoutAdName* trỏ tới file XML tồn tại
|
|
17
|
+
mọi network trong adItem.units có key layout tương ứng
|
|
18
|
+
|
|
19
|
+
mọi reload.*.placementID trỏ tới placement tồn tại
|
|
20
|
+
mọi placement có show.timeAutoReloadMs đều có reload.auto.placementID
|
|
21
|
+
không placement chain nào mang reload.auto hoặc reload.dismiss
|
|
22
|
+
mọi space reload AdsPro **có call site** là đích của đúng một reload.*.placementID
|
|
23
|
+
zero-call-site reload → Documented exclusions
|
|
24
|
+
nếu Bảng D: layout đích == layout show; unit == unit AdsPro lúc click
|
|
25
|
+
không gộp ≥2 layout nguồn khác nhau vào một adItem reload
|
|
26
|
+
|
|
27
|
+
file Ads.init / configProvider == file JSON đang migrate
|
|
28
|
+
nếu có chain: len(native) + firstShowAt/showEvery khớp đo app này
|
|
29
|
+
mọi INTER fullscreen: mode.navigate từ callback app, không default SDK
|
|
30
|
+
|
|
31
|
+
mọi file màn hình bị sửa có lý do thuộc bảng 5b
|
|
32
|
+
mọi tham số inert đã ghi vào bảng Inert parameters
|
|
33
|
+
không có file utils ads mới nào được tạo ngoài quyết định nhóm C
|
|
34
|
+
|
|
35
|
+
ba mối observability đã nối: logger sink, tracker collector, config provider
|
|
36
|
+
không dispatchEvent / app open nào trước callback consent
|
|
37
|
+
mọi tên event analytics cũ đều có nhà mới (tracking.event* trong JSON, hoặc collector app-side)
|
|
38
|
+
không tên event analytics nào bị mất mà không ghi vào Decisions
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Ngoại lệ chỉ hợp lệ khi ghi vào "Documented exclusions".
|
|
42
|
+
|
|
43
|
+
Sau invariant: build → Toast `UpdateJson` version (bước đầu tiên) → `ConfigRegistryCheck` sạch →
|
|
44
|
+
fill từng placement → semantic matrix → 13 chuỗi `adType` analytics không đổi.
|
|
45
|
+
|
|
46
|
+
13 chuỗi khoá bởi `AdProfileMappingTest`: `INTER`, `NATIVE`, `NATIVE_COLLAPSIBLE`,
|
|
47
|
+
`NATIVE_FULL_ACTIVITY`, `APP_OPEN`, `BANNER`, `BANNER320x250`, `BANNER_COLLAPSIBLE_TOP`,
|
|
48
|
+
`BANNER_COLLAPSIBLE_BOTTOM`, `REWARD`, `REWARD_INTER`, `NATIVE_CHAIN`,
|
|
49
|
+
`INTERSTITIAL_NATIVE_CHAIN`. Đổi chuỗi ⇒ dashboard doanh thu trắng, không có gì fail.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Migration Contract và Stop Conditions
|
|
2
|
+
|
|
3
|
+
MUST:
|
|
4
|
+
|
|
5
|
+
- Placement name giữ **nguyên văn** spaceName cũ, kể cả sai chính tả
|
|
6
|
+
(`Native_Trennding`, `Native_Litst_Face_Filter`, `Native_WellcomeBack`).
|
|
7
|
+
**MUST NOT CHANGE.** Dashboard doanh thu, Firebase RC và analytics đang query đúng chuỗi đó.
|
|
8
|
+
- `adsType` preserve đúng format semantics theo bảng trong `api-mapping.md`.
|
|
9
|
+
- Mediation adapter `com.google.ads.mediation:*` hiện có **giữ nguyên**.
|
|
10
|
+
- App open setup **đúng một lần** trong wrapper `init`.
|
|
11
|
+
- Firebase RC key ads giữ nguyên chuỗi.
|
|
12
|
+
- Mỗi thời điểm preload cũ ứng **đúng một** màn/thời điểm trong `preload.eventActive` (một chuỗi,
|
|
13
|
+
hoặc cặp `_fo` + `_fn` của **cùng** thời điểm nếu code cũ preload vô điều kiện — xem 3b).
|
|
14
|
+
- Consent (CMP) **trước** mọi preload / show / app open: wrapper chỉ dispatch event và mở app open
|
|
15
|
+
sau callback `showCmp` (`MIGRATION.md:126-127` — core không tự chặn request khi chưa có consent).
|
|
16
|
+
- `mode.navigate` theo **từng** placement, đọc callback AdsPro của **app này**. Default SDK `NAVIGATE_ON_DISMISS` — không copy template/SlideShow.
|
|
17
|
+
- Reload → `reload.*` trên `placements`. Layout + unit là của **placement đích** (`ReloadCoordinator`). Bảng D khi có click reload.
|
|
18
|
+
- File JSON migrate **trùng** `Ads.init` / `configProvider` của app này.
|
|
19
|
+
- Nếu có native-sau-inter: `firstShowAt` / `showEvery` / `adItems.native[]` đo từ counter + số dialog **production của app này** (công thức SDK `ChainAdActivity`), không copy app khác / không đếm space JSON.
|
|
20
|
+
- Hàm gọi ad gốc **giữ nguyên chữ ký**; wrapper ở đúng file cũ.
|
|
21
|
+
- Nối đủ ba mối observability trước khi verify: logger sink, tracker collector, config provider.
|
|
22
|
+
|
|
23
|
+
MUST NOT:
|
|
24
|
+
|
|
25
|
+
- Đổi / chuẩn hoá / sửa chính tả placement name.
|
|
26
|
+
- Tạo abstraction mới, hay tách wrapper thành nhiều file helper.
|
|
27
|
+
- Port *logic* của `AdsUtils.kt` cũ (chữ ký thì giữ).
|
|
28
|
+
- Xoá mediation adapter vì "SuperAds đã có sẵn".
|
|
29
|
+
- Gọi `Ads.preload*` thủ công thay `preload.eventActive`.
|
|
30
|
+
- Tự viết timer / listener reload ở app — `ReloadCoordinator` trong core đã làm.
|
|
31
|
+
- Sửa màn hình khi không có lý do thuộc danh sách Phase 5b.
|
|
32
|
+
- Dọn tham số inert trong cùng commit migration.
|
|
33
|
+
- Tạo `LegacyAdCallbackAdapter` hay bất kỳ lớp ép 14 callback AdsPro thành 7 callback SuperAds.
|
|
34
|
+
- Gán `mode.navigate` hàng loạt từ template / default SDK.
|
|
35
|
+
- Gộp nhiều layout click khác nhau vào một adItem reload (P2) — hoặc tách bừa khi AdsPro đã tách space.
|
|
36
|
+
- `adItems.native[]` / `showEvery` copy từ template hoặc từ app đã migrate khác.
|
|
37
|
+
- Reload đích = space zero call site chỉ vì tên có `Reload`.
|
|
38
|
+
- Sửa JSON A trong khi init / RC đọc JSON B.
|
|
39
|
+
|
|
40
|
+
## Stop Conditions
|
|
41
|
+
|
|
42
|
+
Agent **dừng và hỏi user** (theo catalog bên dưới), không đoán, khi:
|
|
43
|
+
|
|
44
|
+
- Gặp API AdsPro không có trong bảng mapping.
|
|
45
|
+
- Placement không có format mapping xác định.
|
|
46
|
+
- Thiếu module provider SuperAds cho network trong config.
|
|
47
|
+
- Callback semantics không thể bảo toàn.
|
|
48
|
+
- Config có placement trùng tên.
|
|
49
|
+
- Không xác định được hành vi của RC key ads.
|
|
50
|
+
- Call site `load()` không xác định được ứng với screen event nào.
|
|
51
|
+
- App chưa có `screenName()` hoặc registry tracking screen.
|
|
52
|
+
- AdItem `NATIVE_COLLAPSIBLE` không đủ cặp layout collapsed + expanded.
|
|
53
|
+
- Không xác định được code cũ nav từ callback show hay dismiss.
|
|
54
|
+
- Click reload: cùng space AdsPro nhưng **khác** layout / unit — không biết tách bao nhiêu target (P2).
|
|
55
|
+
- Flag RC / AB / counter ads không diễn đạt được bằng một JSON tĩnh (P4).
|
|
56
|
+
- Reload cũ không xác định được trigger là click, dismiss hay timer.
|
|
57
|
+
- Wrapper không thể giữ chữ ký cũ (hàm cũ trả về giá trị SuperAds không cung cấp).
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Phase 0 — Inventory matrix và checkpoint git
|
|
2
|
+
|
|
3
|
+
## Checkpoint git
|
|
4
|
+
|
|
5
|
+
Trước Phase 1, tạo checkpoint và **luôn biết mình đang ở checkpoint nào**. Không bắt buộc commit
|
|
6
|
+
tự động — nhưng rollback phải biết điểm trở về.
|
|
7
|
+
|
|
8
|
+
| Checkpoint | Khi nào |
|
|
9
|
+
|---|---|
|
|
10
|
+
| `migration/00-inventory` | Sau khi `migration-inventory.md` đầy đủ |
|
|
11
|
+
| `migration/01-gradle` | Sau Phase 1 |
|
|
12
|
+
| `migration/02-manifest` | Sau Phase 2 |
|
|
13
|
+
| `migration/03-config` | Sau Phase 3 |
|
|
14
|
+
| `migration/04-wrapper` | Sau Phase 4 |
|
|
15
|
+
| `migration/05-call-sites` | Sau Phase 5 |
|
|
16
|
+
| `migration/06-layout` | Sau Phase 6 |
|
|
17
|
+
| `migration/07-cleanup` | Sau Phase 7, trước Global Invariant Check |
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
## Inventory
|
|
21
|
+
|
|
22
|
+
Tạo file `migration-inventory.md` trong repo **app** trước khi sửa dòng code nào. Không có file
|
|
23
|
+
này thì không được sang Phase 1.
|
|
24
|
+
|
|
25
|
+
Nguồn: `rg -l "com.android.fullhd.adssdk"`, `rg -n "\.load\("`, mọi `spaceName` trong
|
|
26
|
+
`assets/*.json`, layout native XML + view id, RC key ads.
|
|
27
|
+
|
|
28
|
+
Bảng A-D, Decisions, Inert parameters, Documented exclusions: dùng [templates/migration-inventory.md](../templates/migration-inventory.md).
|
|
29
|
+
|
|
30
|
+
## Verify Phase 0
|
|
31
|
+
|
|
32
|
+
- [ ] Mọi hàng có `status`
|
|
33
|
+
- [ ] Không hàng nào trống `format`
|
|
34
|
+
- [ ] Số hàng Bảng B = số call site `load()` đếm được
|
|
35
|
+
- [ ] Mọi space reload **có call site** xuất hiện ở Bảng C
|
|
36
|
+
- [ ] Nếu có click reload: Bảng D; layout/unit đích khớp AdsPro lúc click
|
|
37
|
+
- [ ] Đã đọc [migration-pitfalls.md](migration-pitfalls.md) — đo **app này**, không copy số liệu app khác
|
|
38
|
+
- [ ] Nhóm câu hỏi B đã hỏi và ghi vào Decisions
|
|
39
|
+
- [ ] Checkpoint `migration/00-inventory`
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
# Phase 1-2 — Gradle và Manifest
|
|
2
|
+
|
|
3
|
+
## Phase 1 — Gradle
|
|
4
|
+
|
|
5
|
+
Bỏ:
|
|
6
|
+
|
|
7
|
+
```gradle
|
|
8
|
+
implementation 'com.android.fullhd.adssdk:AdsPro:...'
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
Thêm (phiên bản hiện tại của core và mọi plugin: `4.0.0` — `super-ads/build.gradle:6`,
|
|
12
|
+
`MIGRATION.md:7-15`; **đọc lại source / maven hiện tại, không hardcode từ skill nếu source khác**):
|
|
13
|
+
|
|
14
|
+
```gradle
|
|
15
|
+
def superAds = '4.0.0' // core + MỌI plugin cùng một version (SPI đổi package ở 4.0.0 — trộn 3.x là NoClassDefFoundError lúc chạy)
|
|
16
|
+
implementation "com.android.fullhd.ads:SuperAds:$superAds"
|
|
17
|
+
// Bộ nền: LUÔN khai (kể cả config chưa có unit InHouse)
|
|
18
|
+
implementation "com.android.fullhd.ads:SuperAdsInHouse:$superAds"
|
|
19
|
+
implementation "com.android.fullhd.ads:SuperAdsDebug:$superAds" // có cả ở release
|
|
20
|
+
debugImplementation "com.android.fullhd.ads:SuperAdsDebugOverride:$superAds" // chỉ debug, không lọt release
|
|
21
|
+
implementation "com.android.fullhd.ads:SuperAdsAdmob:$superAds" // BẮT BUỘC nếu config có network Admob (kéo theo SuperAdsUmp)
|
|
22
|
+
implementation "com.android.fullhd.ads:SuperAdsTracking:$superAds" // ad_impression / ad_click Firebase — thiếu là mất doanh thu
|
|
23
|
+
implementation 'com.android.fullhd.layout:SuperAdsLayout:1.0.0-alpha27'
|
|
24
|
+
|
|
25
|
+
// Chỉ thêm khi config có network tương ứng:
|
|
26
|
+
implementation "com.android.fullhd.ads:SuperAdsCloudX:$superAds"
|
|
27
|
+
implementation "com.android.fullhd.ads:SuperAdsApplovin:$superAds"
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
Wrapper (Phase 4) viết theo API 4.0.0 (vd `com.android.fullhd.super_ads.api.lifecycle.AppLifecycleMonitor`,
|
|
31
|
+
`MIGRATION.md:26`). Pin bản 3.x trở xuống thì wrapper không compile.
|
|
32
|
+
|
|
33
|
+
`SuperAdsTracking` tự cài qua `TrackingInstaller` (ContentProvider), không gọi code. Thiếu module ⇒
|
|
34
|
+
**mất `ad_impression` (doanh thu) và `ad_click`** dù config bật (`MIGRATION.md:452`). Cờ bật/tắt là
|
|
35
|
+
key phẳng trong `global`: `"global": { "tracking.revenue": true }` (mặc định `true`,
|
|
36
|
+
`SA/domain/config/model/AdsConfig.kt:85`).
|
|
37
|
+
|
|
38
|
+
**Thiếu `SuperAdsAdmob` = zero ads im lặng, không crash.** `ConfigRegistryCheck` log
|
|
39
|
+
`ERROR_AND_CRASH` lúc init — chỉ thấy được nếu đã nối logger sink (Phase 4).
|
|
40
|
+
|
|
41
|
+
Giữ nguyên mọi `com.google.ads.mediation:*` hiện có. CloudX adapter (`io.cloudx:adapter-*`) chỉ
|
|
42
|
+
thêm khi user chọn dùng CloudX (nhóm D).
|
|
43
|
+
|
|
44
|
+
Repo SuperAds artifacts: `http://repo.volio.vn/repository/maven-s3/` (`allowInsecureProtocol = true`).
|
|
45
|
+
|
|
46
|
+
Kiểm version **trước khi khai** (mỗi artifact sẽ dùng, `<A>` = `SuperAds`, `SuperAdsAdmob`, `SuperAdsUmp`, `SuperAdsTracking`…):
|
|
47
|
+
|
|
48
|
+
```bash
|
|
49
|
+
curl -sL https://repo.volio.vn/repository/maven-s3/com/android/fullhd/ads/<A>/maven-metadata.xml | grep -o "<version>[^<]*</version>"
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Trạng thái đã kiểm ngày 2026-10-27: repo có `4.0.0` của cả 11 artifact SDK (`SuperAds`, `SuperAdsAdmob`, `SuperAdsAdmobNextGen`, `SuperAdsApplovin`, `SuperAdsArbiter`, `SuperAdsCloudX`, `SuperAdsInHouse`, `SuperAdsTracking`, `SuperAdsUmp`, `SuperAdsDebug`, `SuperAdsDebugOverride`); `SuperAdsContract:1.0.0-alpha` đã có. Trạng thái đổi theo thời gian: luôn chạy lệnh trên, đừng tin con số này. Version cần dùng chưa có trên repo ⇒ Stop, hỏi user: chờ nhóm SDK publish (`scripts/publish-modules.sh --remote`, script đã gồm `ump` và `admob-next-gen`), hay dùng tạm `mavenLocal` / project dependency. Không tự hạ xuống bản cũ: wrapper và config theo API 4.0.0.
|
|
53
|
+
|
|
54
|
+
### Verify Phase 1
|
|
55
|
+
|
|
56
|
+
- [ ] `./gradlew :app:dependencies --configuration releaseRuntimeClasspath` thấy `SuperAds`,
|
|
57
|
+
`SuperAdsAdmob`, `SuperAdsTracking` cùng `4.0.0`, và **không còn** `AdsPro`
|
|
58
|
+
- [ ] Mediation adapter cũ vẫn còn
|
|
59
|
+
- [ ] Checkpoint `migration/01-gradle`
|
|
60
|
+
|
|
61
|
+
## Phase 2 — Manifest
|
|
62
|
+
|
|
63
|
+
Giữ:
|
|
64
|
+
|
|
65
|
+
```xml
|
|
66
|
+
<meta-data
|
|
67
|
+
android:name="com.google.android.gms.ads.APPLICATION_ID"
|
|
68
|
+
android:value="@string/admob_app_id" />
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
Thêm **chỉ khi** config có network tương ứng:
|
|
72
|
+
|
|
73
|
+
```xml
|
|
74
|
+
<meta-data android:name="io.cloudx.APP_KEY" android:value="..." />
|
|
75
|
+
<meta-data android:name="applovin.sdk.key" android:value="..." />
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
Không khai activity / provider ads thủ công — mỗi module SuperAds tự register bằng
|
|
79
|
+
`ContentProvider` (`AdmobInstaller`, `InHouseInstaller`, `CloudXInstaller`, `AppLovinInstaller`).
|
|
80
|
+
|
|
81
|
+
### Verify Phase 2
|
|
82
|
+
|
|
83
|
+
- [ ] AdMob APPLICATION_ID còn
|
|
84
|
+
- [ ] Key CloudX / AppLovin khớp quyết định nhóm D
|
|
85
|
+
- [ ] Checkpoint `migration/02-manifest`
|
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
# Phase 3 — Config migration
|
|
2
|
+
|
|
3
|
+
Đọc [config-template.md](config-template.md),
|
|
4
|
+
[api-mapping.md](api-mapping.md) và
|
|
5
|
+
[migration-pitfalls.md](migration-pitfalls.md) **trước** khi viết JSON. Hỏi
|
|
6
|
+
nhóm A và D nếu chưa hỏi.
|
|
7
|
+
|
|
8
|
+
Bảy bảng con 3a–3g + Bảng D. Cập nhật cột `status` của Bảng A/B/C/D khi xong từng hàng.
|
|
9
|
+
|
|
10
|
+
## 3a — Identity
|
|
11
|
+
|
|
12
|
+
- 1 `spaceName` → 1 `placement` **giữ nguyên tên**.
|
|
13
|
+
- `ids[]` (waterfall high-floor) → nhiều `units` entry, **giữ đúng thứ tự** trong `adItem.units[]`.
|
|
14
|
+
- `adsType` → `format` theo bảng 15 giá trị. Không map được → stop, hỏi nhóm A.
|
|
15
|
+
|
|
16
|
+
## 3b — Preload: từ `load()` sang event
|
|
17
|
+
|
|
18
|
+
```text
|
|
19
|
+
BaseFragment.onCreate -> EventScreen.pushEventCreateScreen(screenName())
|
|
20
|
+
BaseFragment.onDestroy -> EventScreen.pushEventDestroyScreen(screenName())
|
|
21
|
+
-> emit "create_{screenName}_{fo|fn}" / "destroy_{screenName}_{fo|fn}"
|
|
22
|
+
-> setUpPreloadTrigger() collect -> Ads.waitAdSDKReady { Ads.dispatchEvent(activity, event) }
|
|
23
|
+
-> khớp "preload.eventActive" của adItem -> preload
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
SlideShow gọi `pushEventCreateScreen` trong `onCreate`, `pushEventDestroyScreen` trong
|
|
27
|
+
`onDestroy` — không phải `onViewCreated` / `onDestroyView`. Khớp source app đang migrate, không
|
|
28
|
+
đoán lifecycle.
|
|
29
|
+
|
|
30
|
+
- Chuỗi event là **convention của app**, không phải hằng số SDK. JSON khớp **từng ký tự**; lệch
|
|
31
|
+
một ký tự là **im lặng không preload**, không log lỗi.
|
|
32
|
+
- `_fo` / `_fn` (first-open / quay lại) là **hai chuỗi khác nhau**. Code cũ preload vô điều kiện
|
|
33
|
+
thì khai **cả hai**, như SlideShow: `["create_open_screen_5_fo", "create_open_screen_5_fn"]`.
|
|
34
|
+
- Chỉ màn **đã hook** `pushEventCreateScreen` mới emit (Activity thường sót). `isFo` nếu
|
|
35
|
+
`lazy` thì lock lần đầu. Event = màn **đang đứng** lúc `load()`, không đoán màn kế.
|
|
36
|
+
- Key thuộc tầng **`adItems`**; đặt lên `placements` bị bỏ qua.
|
|
37
|
+
- `preload.enabled` default **true** (`AdItemConfig.isPreloadEnabled()`). Lật false trên adItem
|
|
38
|
+
`SHOW_LOADED` giết âm thầm placement vì cache luôn rỗng.
|
|
39
|
+
- Key phụ: `preload.max`, `preload.delayLoadFistMs` (đúng chính tả source — `Fist` không phải
|
|
40
|
+
`First`), `preload.delayLoadAfterShowMs`, `preload.autoPreloadAfterDismiss`.
|
|
41
|
+
- Mỗi hàng Bảng B → một chuỗi thêm vào `preload.eventActive` của adItem tương ứng.
|
|
42
|
+
- **Không** thay `load()` bằng `Ads.preload.preloadNow` (API thủ công, đã `@Deprecated` —
|
|
43
|
+
`SA/api/AdsFeatures.kt:90`).
|
|
44
|
+
|
|
45
|
+
Phase 3b và Phase 4 khớp nhau **qua chuỗi event** — coupling duy nhất được phép.
|
|
46
|
+
|
|
47
|
+
## 3c — Layout
|
|
48
|
+
|
|
49
|
+
- Native / banner thường: **một** key `ui.layoutAdName.{network}`.
|
|
50
|
+
- Collapsible (`NATIVE_COLLAPSIBLE`): **hai** key —
|
|
51
|
+
`ui.layoutAdName.{network}` = **collapsed / small**,
|
|
52
|
+
`ui.layoutAdNameExpand.{network}` = **expanded / large**.
|
|
53
|
+
- Từ AdsPro: `AdmobNativeCollapsible.show(..., layoutRes, collapsibleLayoutRes, ...)` →
|
|
54
|
+
`layoutRes` vào key thường, `collapsibleLayoutRes` vào key Expand.
|
|
55
|
+
**Đảo hai cái thì ad vẫn fill, vẫn có impression, build vẫn pass, chỉ hiện sai cỡ.**
|
|
56
|
+
- Suffix network **chữ thường, phân biệt hoa thường**: `.admob`, `.cloudx`, `.applovin`, `.meta`.
|
|
57
|
+
AdItem trộn network phải khai key cho **từng** network, thiếu là `RenderError.LayoutNotFound`.
|
|
58
|
+
- Giá trị là **tên file XML không đuôi**, resolve trong res app hoặc `SuperAdsLayout`.
|
|
59
|
+
- Loading: `ui.loading.layoutName` + `ui.loading.enable`.
|
|
60
|
+
- `NativeConfig(resNativeMeta)` (layout riêng khi mediation AdMob trả về Meta) → **loại B**:
|
|
61
|
+
`ui.layoutAdName.meta` (+ `ui.layoutAdNameExpand.meta` cho collapsible). Plugin AdMob chọn layout
|
|
62
|
+
meta khi response là Meta mediation và file XML tồn tại, ngược lại dùng layout thường
|
|
63
|
+
(`SA/presentation/render/NativeRenderer.kt:97`, `SA/presentation/render/CollapsibleNativeRenderer.kt:80`;
|
|
64
|
+
`super-ads-admob/src/main/java/com/android/fullhd/super_ads/plugin/admob/nativead/NativeLayoutResolver.kt:40`). `meta` chỉ là **hậu tố key**, không phải
|
|
65
|
+
network — không khai unit `"network": "Meta"` (`SA/domain/config/key/AdConfigKey.kt:42`).
|
|
66
|
+
- **Một** `ui.layoutAdName` / adItem / network. AdsPro xoay `index % N` layout → P5, hỏi product.
|
|
67
|
+
- Native full chain: XML app **hoặc** catalog SuperAdsLayout — product chọn, ghi Decisions. Không tự coi lệch XML là bug, cũng không mặc định catalog.
|
|
68
|
+
|
|
69
|
+
## 3d — Chuyển màn: theo behavior thật của app
|
|
70
|
+
|
|
71
|
+
- `mode.show`: `LOAD_AND_SHOW` (default) ứng `loadAndShow(...)`; `SHOW_LOADED` ứng `show(...)`
|
|
72
|
+
**có gate `getStatusBySpace == LOADED`**. Vì vậy `getStatusBySpace` làm gate là loại **B**.
|
|
73
|
+
- `mode.navigate`: chỉ `NAVIGATE_ON_SHOW` / `NAVIGATE_ON_DISMISS`. Default
|
|
74
|
+
`NAVIGATE_ON_DISMISS` (`AdItemConfig.getModeNavigate()`; giá trị lạ bị ép về default).
|
|
75
|
+
Xác định **bắt buộc đọc từ app**: nav trong `onAdShowedFullScreenContent` → `NAVIGATE_ON_SHOW`;
|
|
76
|
+
trong `onAdDismissedFullScreenContent` → `NAVIGATE_ON_DISMISS`. Không suy ra được thì hỏi nhóm B.
|
|
77
|
+
- Sau migration app **vẫn sở hữu nav đi đâu** qua `NavAdCallback`, chỉ không còn sở hữu *thời điểm*.
|
|
78
|
+
- Không áp một `mode.navigate` chung. Splash / leftover / in-app / chain đo riêng (P1).
|
|
79
|
+
- Placement chain: một giá trị không cover hai path AdsPro (nav lúc show vs lúc đóng native). Ghi Decisions nếu lệch.
|
|
80
|
+
|
|
81
|
+
## 3e — Reload: đưa hết vào `placements`
|
|
82
|
+
|
|
83
|
+
SDK tự chạy — `ReloadCoordinator.Plan.from(placement, surface)` trong core đọc config. App
|
|
84
|
+
**không viết một dòng reload nào**. Wrapper **không** có hàm reload. SlideShow từng viết
|
|
85
|
+
`showAdViewWithReload` rồi comment bỏ toàn bộ — đó là bằng chứng, không phải code cần copy.
|
|
86
|
+
|
|
87
|
+
| Trigger | Key | Timing kèm theo |
|
|
88
|
+
|---|---|---|
|
|
89
|
+
| click vào ad | `reload.click.placementID` | - |
|
|
90
|
+
| đóng ad (collapsible dismiss) | `reload.dismiss.placementID` | `show.timeDelayShowAfterDismissMs` |
|
|
91
|
+
| chu kỳ tự động | `reload.auto.placementID` | `show.timeAutoReloadMs` |
|
|
92
|
+
|
|
93
|
+
Mẫu chuẩn: cặp placement sinh đôi `X` / `X_Reload`, khớp họ space `*_Reload` của app cũ.
|
|
94
|
+
|
|
95
|
+
Layout + unit thuộc **đích**. Gộp / tách theo Bảng D của **app này** (P2) — không hardcode một twin hay N twin. Click lại cùng space AdsPro → self-click trên twin. Chain: `reload.click` trên placement chain.
|
|
96
|
+
|
|
97
|
+
`ConfigRegistryCheck` log issue lúc init (key vẫn bị bỏ qua, không crash):
|
|
98
|
+
|
|
99
|
+
- `show.timeAutoReloadMs` mà **thiếu** `reload.auto.placementID` ⇒ auto-reload **không chạy**.
|
|
100
|
+
- Placement gốc INTER / REWARD / APP_OPEN mang `reload.*` ⇒ bỏ qua (không có container để thay tại
|
|
101
|
+
chỗ, `SA/config/validate/ConfigRegistryCheck.kt:182`).
|
|
102
|
+
- Placement đích không tồn tại / tắt / priority rỗng / **khác loại bề mặt** với gốc ⇒ bỏ qua
|
|
103
|
+
(`SA/config/validate/ConfigRegistryCheck.kt:202`).
|
|
104
|
+
- Placement **chain** không nhận `reload.auto` (tự advance slot, timer tranh render).
|
|
105
|
+
- Placement **chain** không nhận `reload.dismiss` (đóng slot = sang bước kế). Chain chỉ reload bằng click.
|
|
106
|
+
|
|
107
|
+
Ngoài `ConfigRegistryCheck`:
|
|
108
|
+
|
|
109
|
+
- Chu kỳ auto bị `ReloadCoordinator` kẹp sàn `MIN_AUTO_RELOAD_MS = 5_000L`
|
|
110
|
+
(`SA/show/ReloadCoordinator.kt:23`, `SA/show/ReloadCoordinator.kt:82`).
|
|
111
|
+
- Key `reload.placementID` (thiếu `.click` / `.dismiss` / `.auto`) **không tồn tại** trong
|
|
112
|
+
`AdConfigKey.Reload` — có trong config demo nhưng **vô hiệu**, SDK không báo gì. Không được dùng.
|
|
113
|
+
|
|
114
|
+
## 3f — Analytics: tên event vào JSON
|
|
115
|
+
|
|
116
|
+
| Key (tầng `adItems`) | Bắn khi | Tương ứng AdsPro |
|
|
117
|
+
|---|---|---|
|
|
118
|
+
| `tracking.eventImpress` | impression | `onAdImpression` + `logPairValue*` |
|
|
119
|
+
| `tracking.eventLoaded` | load xong | `onAdLoaded` |
|
|
120
|
+
| `tracking.eventPaid` | paid event | `onAdPaidValueListener` |
|
|
121
|
+
| `tracking.mode` | `ALWAYS` / `ONCE_PER_PROCESS` / `ONCE_PER_LIFETIME` | `Set` chống trùng tự viết |
|
|
122
|
+
|
|
123
|
+
Giá trị là **mảng** tên event. Event bắn ra `TrackerEvent.TrackingEvent` trên `Ads.tracker.events`;
|
|
124
|
+
app forward sang Firebase / Meta ở **một** chỗ trong wrapper.
|
|
125
|
+
|
|
126
|
+
Doanh thu tự động: artifact `com.android.fullhd.ads:SuperAdsTracking` (Phase 1; tự cài khi có
|
|
127
|
+
dependency, không gọi code; bật/tắt bằng key phẳng `tracking.enable` / `tracking.revenue` /
|
|
128
|
+
`tracking.perf` / `tracking.arbiter` trong object `global`) và
|
|
129
|
+
`Ads.tracking.enableAppsflyer(app, appsflyerId)` — thay `pushAppsflyerRevenue` thủ công.
|
|
130
|
+
|
|
131
|
+
Event JSON **không mang** được cặp `previous_screen` / `next_screen`. Hỏi nhóm B.
|
|
132
|
+
|
|
133
|
+
`global.version` là nhãn để soi config nào đang chạy (Toast `UpdateJson`). Bump version mỗi lần
|
|
134
|
+
sửa JSON để Toast chứng minh file đang chạy (O1).
|
|
135
|
+
|
|
136
|
+
## 3g — Native sau inter (`INTERSTITIAL_NATIVE_CHAIN`)
|
|
137
|
+
|
|
138
|
+
Chỉ khi **app này** có dialog / flow native sau inter. Công thức SDK:
|
|
139
|
+
`nextPos >= firstShowAt && (nextPos - firstShowAt) % showEvery == 0`.
|
|
140
|
+
Điền số từ counter AdsPro của app — template `1`/`2`/`5000` / 1–2 native **không** phải chuẩn sản phẩm.
|
|
141
|
+
|
|
142
|
+
- `adItems.native[]` = số native production (kèm RC/AB **tên key của app này**), không = số space JSON.
|
|
143
|
+
- Countdown = timer dialog app này.
|
|
144
|
+
- `reload.click` trên placement chain. Xoá dialog Kotlin song song (Phase 5b).
|
|
145
|
+
- adItem chain `SHOW_LOADED` cần cache: `preload.enabled` (mặc định true) **không** được tắt và phải
|
|
146
|
+
có `preload.eventActive`. Tắt preload thì không preload, không vào event map, nhánh `SHOW_LOADED`
|
|
147
|
+
luôn `ModeShowedButNotHaveAdCached` ⇒ placement không bao giờ show
|
|
148
|
+
(`SA/engine/load/Preloader.kt:225`, `SA/config/repository/AdsConfigRepositoryImpl.kt:180`, `SA/engine/load/PriorityLoader.kt:163`).
|
|
149
|
+
Không preload được thì dùng `LOAD_AND_SHOW`.
|
|
150
|
+
|
|
151
|
+
`forceLoadNewAdIfShowed` **không** suy ra `mode.show`. Đọc `show` vs `loadAndShow` + gate `LOADED`.
|
|
152
|
+
|
|
153
|
+
## Verify Phase 3
|
|
154
|
+
|
|
155
|
+
- [ ] Checklist referential integrity trong `config-template.md` pass
|
|
156
|
+
- [ ] Cột `status` Bảng A/B/C cập nhật; nếu có click reload thì Bảng D layout/unit khớp AdsPro
|
|
157
|
+
- [ ] `Ads.init` / RC khớp file JSON vừa viết (P0)
|
|
158
|
+
- [ ] Nếu có chain: `adItems.native` + gate khớp đo Phase 0 của **app này**
|
|
159
|
+
- [ ] Checkpoint `migration/03-config`
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Phase 4 — Wrapper, EventScreen, observability
|
|
2
|
+
|
|
3
|
+
Hỏi nhóm C trước. Mặc định khuyến nghị: **moi ruột file `AdsUtils.kt` cũ**, giữ path, giữ chữ ký
|
|
4
|
+
mọi hàm public, thay ruột bằng delegate xuống `Ads.show`.
|
|
5
|
+
|
|
6
|
+
Đọc [wrapper-template.md](wrapper-template.md).
|
|
7
|
+
|
|
8
|
+
Bề mặt cần có: `init / setPremium / isPremium / showCmp / setUpAppOpen / setUpPreloadTrigger /
|
|
9
|
+
dispatcherEvent` + 4 extension `showAdView`, `showAdFull`, `showAdFullInApp`, `showAdRewardFull`
|
|
10
|
+
(extension có thể private nội bộ file nếu màn hình vẫn gọi chữ ký cũ).
|
|
11
|
+
|
|
12
|
+
`EventScreen`: `MutableSharedFlow` replay 0, extraBufferCapacity 64. Hook base fragment:
|
|
13
|
+
`onCreate` → `pushEventCreateScreen(screenName())`, `onDestroy` →
|
|
14
|
+
`pushEventDestroyScreen(screenName())`. Collector của `EventScreen` phải subscribe **trước** màn đầu
|
|
15
|
+
emit (replay 0 ⇒ emit khi chưa có subscriber là mất): launch bằng `Dispatchers.Main.immediate`.
|
|
16
|
+
|
|
17
|
+
**Consent trước preload / app open.** Core không tự chặn request khi chưa có consent
|
|
18
|
+
(`MIGRATION.md:126-127`). Wrapper giữ cờ `consentDone`: `showCmp` set cờ trong callback; preload
|
|
19
|
+
trigger chỉ `Ads.dispatchEvent` khi cờ bật (event màn đầu nhận trong lúc chờ CMP được giữ lại, phát
|
|
20
|
+
sau callback); `setUpAppOpen` bỏ qua khi cờ tắt. Xem wrapper-template.
|
|
21
|
+
|
|
22
|
+
Ba mối observability — **bắt buộc trước khi verify**, không phải tuỳ chọn:
|
|
23
|
+
|
|
24
|
+
**(a) Logger sink** — `Ads.logger.init(LogConfig(enabled = true, minLevel = LogLevel.FLOW),
|
|
25
|
+
debug = isDebug, externalSink)` (`LogLevel` không có `VERBOSE`; `FLOW` là mức thấp nhất,
|
|
26
|
+
`SA/domain/model/log/LogLevel.kt:6`). Logcat chỉ ở debug; `ERROR` / `ERROR_AND_CRASH` thì debug Toast (post lên
|
|
27
|
+
main — sink chạy đồng bộ trên luồng đang log, thường là IO; Toast ngoài main thread ném exception và
|
|
28
|
+
bị SDK nuốt trong `runCatching`, `SA/infra/logger/SdkAdLogger.kt:50`, nên Toast không hiện), release
|
|
29
|
+
`Firebase.crashlytics.recordException(Exception(event.toJsonString()))`. Đây là đường duy nhất
|
|
30
|
+
thấy issue `ConfigRegistryCheck`.
|
|
31
|
+
|
|
32
|
+
**(b) Tracker collector** — collect `Ads.tracker.events` một lần trên `Dispatchers.Main`:
|
|
33
|
+
|
|
34
|
+
| Event | Việc làm |
|
|
35
|
+
|---|---|
|
|
36
|
+
| `TrackingEvent` | forward `event.eventName` sang Firebase; thêm hậu tố `_oke` / `_fail` theo `FirebaseRemoteConfig.getValue(key).source == VALUE_SOURCE_REMOTE` |
|
|
37
|
+
| `StartRequest` / `Impression` | event bespoke theo `event.data.adItemId` (`logOnce` cho impression) |
|
|
38
|
+
| `UpdateJson` | DEBUG: Toast `"Load version ${event.version}"` |
|
|
39
|
+
| `Click` | DEBUG: Toast `unitId.takeLast(4)` + `adItemId` |
|
|
40
|
+
| `FailedToLoad` / `FailedToShowFull` | DEBUG: Toast kèm `error.code` |
|
|
41
|
+
|
|
42
|
+
`global.tracking.item_events` mặc định bật (không khai = `true`, từ 3.0.6) ⇒ `SuperAdsTracking` tự forward `TrackingEvent` (kèm `ab_variant`): **bỏ** dòng forward `TrackingEvent` ở trên (hoặc khai `false`), nếu không event bị gửi đôi. Dự án chạy Firebase A/B bằng RC: nạp lại config bằng `Ads.loadFromJson(json, firebaseActivated = true)` **chỉ** sau `activate()` (`loadFromJson(json)` và config provider không push activation event) — xem `MIGRATION.md`, mục A/B test bằng remote config.
|
|
43
|
+
|
|
44
|
+
**(c) Config provider** — overload `Ads.init(application, configProvider = { ... }, isDebug)`:
|
|
45
|
+
|
|
46
|
+
```text
|
|
47
|
+
assets = readAssetString(file, timeout 2000)
|
|
48
|
+
remote = getRemoteConfigString(key, fallbackToOldValue = true, timeout 3500)
|
|
49
|
+
remote hợp lệ (parse được JSON object có "placements") ? remote : assets
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
JSON RC hỏng mà vẫn trả về ⇒ SDK parse ra `null` ⇒ init **FAILED**, mất sạch ad, không có bản dự
|
|
53
|
+
phòng (`SA/runtime/SdkRuntime.kt:226`, `SA/runtime/SdkRuntime.kt:275`). Vì vậy provider phải kiểm
|
|
54
|
+
JSON trước khi trả. Muốn có bản dự phòng: `Ads.init(application, fileNameAsset)` chạy asset ngay, rồi nạp
|
|
55
|
+
giá trị RC sau `fetchAndActivate()` bằng `Ads.loadFromJson(json, firebaseActivated = true)` (`SA/api/Ads.kt:197`);
|
|
56
|
+
JSON hỏng bị từ chối, giữ bản đang chạy — hỏi nhóm C. (`initRemote` mới mở lại, chỉ app mẫu dùng; chưa kiểm trên thiết bị.)
|
|
57
|
+
|
|
58
|
+
`minimumFetchIntervalInSeconds = 0` như SlideShow là rất hung — hỏi nhóm D. RC key phụ
|
|
59
|
+
`keyTimeBetweenAdInter` không có tương đương.
|
|
60
|
+
|
|
61
|
+
Cấm thêm vào wrapper: state timing, cache riêng, retry, adapter callback SDK, preload thủ công,
|
|
62
|
+
driver reload, tách file helper theo loại ad.
|
|
63
|
+
|
|
64
|
+
## Verify Phase 4
|
|
65
|
+
|
|
66
|
+
- [ ] Chữ ký public của wrapper cũ không đổi
|
|
67
|
+
- [ ] EventScreen emit chuỗi khớp `preload.eventActive` đã viết ở Phase 3b
|
|
68
|
+
- [ ] Ba mối observability đã nối
|
|
69
|
+
- [ ] Không event preload / app open nào chạy trước callback `showCmp`
|
|
70
|
+
- [ ] Checkpoint `migration/04-wrapper`
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Phase 5-7 — Call sites, Layout, Cleanup
|
|
2
|
+
|
|
3
|
+
# Phase 5 — Call sites, chia hai nhóm
|
|
4
|
+
|
|
5
|
+
## 5a — Không sửa màn hình
|
|
6
|
+
|
|
7
|
+
Mọi lời gọi mà wrapper giữ được chữ ký thì **màn hình không đổi một ký tự**:
|
|
8
|
+
`showAdsNative(...)`, `showAdsInterGeneral(...)`, `showNativeCollapsible(...)`,
|
|
9
|
+
`showInterAdsSplash(...)`, `showInterSplashLate(...)`.
|
|
10
|
+
|
|
11
|
+
## 5b — Danh sách đóng: chỉ những lý do này mới được sửa màn hình
|
|
12
|
+
|
|
13
|
+
| Lý do | Hành động |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `AdmobX.load(space)` gọi trực tiếp trong màn | Xoá call site (đã ở `preload.eventActive`) |
|
|
16
|
+
| `getStatusBySpace(...) == LOADED` làm gate trước show | Xoá gate (đã ở `mode.show: SHOW_LOADED`) |
|
|
17
|
+
| Gọi lại `showAdsNative("X_Reload", ...)` trong `onAdClicked` | Xoá (đã ở `reload.click.placementID`) |
|
|
18
|
+
| Timer reload trong Fragment | Xoá (đã ở `reload.auto.placementID` + `show.timeAutoReloadMs`) |
|
|
19
|
+
| `AdmobOpenResume.loadAndAutoShowIfAvailable(...)` rải rác | Xoá, dồn về `setUpAppOpen(...)` một lần trong init |
|
|
20
|
+
| Dialog native-sau-inter tự viết | Xoá, dùng `INTERSTITIAL_NATIVE_CHAIN` + `chain.native.showEvery` |
|
|
21
|
+
| Pseudo-reward bằng native dialog | Đổi sang placement `REWARD` / `NATIVE_FULL_ACTIVITY` thật |
|
|
22
|
+
| Màn gọi trực tiếp `Admob*` không qua wrapper | Đổi sang chữ ký wrapper |
|
|
23
|
+
| Base fragment | Thêm hook `EventScreen` |
|
|
24
|
+
| Override `onAdPaidValueListener` / `logPairValue*` trong màn hoặc dialog | Xoá, gom về tracker collector (tên event ở `tracking.event*`) |
|
|
25
|
+
|
|
26
|
+
Không có lý do trong bảng này thì **không sửa file đó**.
|
|
27
|
+
|
|
28
|
+
## Verify Phase 5
|
|
29
|
+
|
|
30
|
+
- [ ] `rg "com.android.fullhd.adssdk"` rỗng
|
|
31
|
+
- [ ] Không còn `.load(` trên code ads
|
|
32
|
+
- [ ] Không còn lời gọi tay tới placement `*_Reload`
|
|
33
|
+
- [ ] Mọi file màn hình bị sửa truy được về đúng một dòng bảng 5b
|
|
34
|
+
- [ ] Ba bảng inventory `status = DONE`
|
|
35
|
+
- [ ] Checkpoint `migration/05-call-sites`
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
# Phase 6 — Layout
|
|
40
|
+
|
|
41
|
+
Layout AdMob chuẩn giữ nguyên. Sửa tay:
|
|
42
|
+
|
|
43
|
+
- Id badge `ivAdMark` (AdsPro) → `tvAdMark` (SuperAds `ProtectedAssets`).
|
|
44
|
+
- `iv_blur` chỉ còn ý nghĩa với AdMob.
|
|
45
|
+
|
|
46
|
+
Xoá `R.layout.*` khỏi **thân wrapper** (call site vẫn truyền vào — tham số inert). Đối chiếu
|
|
47
|
+
ngược: mỗi giá trị `ui.layoutAdName*` có file XML tồn tại thật (app res hoặc SuperAdsLayout).
|
|
48
|
+
|
|
49
|
+
View id bắt buộc: `ad_media`, `ad_app_icon`, `ad_headline`, `ad_body`, `ad_call_to_action`,
|
|
50
|
+
`ad_choices_container`, `tvAdMark`. Optional: `ad_price`, `ad_store`, `ad_stars`, `ad_advertiser`.
|
|
51
|
+
|
|
52
|
+
## Verify Phase 6
|
|
53
|
+
|
|
54
|
+
- [ ] Không layout collapsible nào đảo collapsed / expanded
|
|
55
|
+
- [ ] Mọi `ui.layoutAdName*` resolve được
|
|
56
|
+
- [ ] Checkpoint `migration/06-layout`
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
# Phase 7 — Cleanup
|
|
61
|
+
|
|
62
|
+
Xoá dialog ads tự viết và timer đã bị JSON thay thế. Xoá import chết.
|
|
63
|
+
|
|
64
|
+
**Không** xoá file wrapper. **Không** dọn tham số inert (commit riêng).
|
|
65
|
+
|
|
66
|
+
Cách SlideShow comment lại 650 dòng `AdsUtils.kt` là điều **không** nên copy — hoặc moi ruột,
|
|
67
|
+
hoặc xoá, không để code chết.
|
|
68
|
+
|
|
69
|
+
## Verify Phase 7
|
|
70
|
+
|
|
71
|
+
- [ ] Không còn dialog ads tự viết đã bị `INTERSTITIAL_NATIVE_CHAIN` thay
|
|
72
|
+
- [ ] Không còn file utils ads mới ngoài quyết định nhóm C
|
|
73
|
+
- [ ] Checkpoint `migration/07-cleanup`
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Authority Order
|
|
2
|
+
|
|
3
|
+
Khi các nguồn xung đột, thứ tự thẩm quyền:
|
|
4
|
+
|
|
5
|
+
1. Source SuperAds hiện tại (`super-ads/src/main/.../api/`, `domain/config/key/AdConfigKey.kt`, `core/flow/`)
|
|
6
|
+
2. Migration production của SlideShow — **cho API và JSON**, không cho chiến lược diff
|
|
7
|
+
3. Source AdsPro
|
|
8
|
+
4. Template / example trong skill này
|
|
9
|
+
5. Suy luận của agent
|
|
10
|
+
|
|
11
|
+
Không dùng example/template trong skill để suy ra API nếu source thực tế khác. **Không được tự
|
|
12
|
+
suy luận nếu source chưa chứng minh.** Không copy `showEvery`, số native chain, countdown, RC
|
|
13
|
+
key, hay `mode.navigate` từ SlideShow / app đã migrate khác — đo app hiện tại.
|
|
14
|
+
|
|
15
|
+
# Ba nguyên tắc
|
|
16
|
+
|
|
17
|
+
Thứ tự khi xung đột: **JSON-first > wrapper-first > minimal-diff**. Giữ diff nhỏ không bao giờ
|
|
18
|
+
là lý do để giữ behavior trong Kotlin.
|
|
19
|
+
|
|
20
|
+
# 1. JSON-first
|
|
21
|
+
|
|
22
|
+
Một dòng behavior mà JSON diễn đạt được thì **không được tồn tại trong Kotlin**. Ba ví dụ điển
|
|
23
|
+
hình (bảng đầy đủ behavior → key JSON, theo từng tầng `adItems`/`placements`/`wrapper`: xem
|
|
24
|
+
[config-template.md](config-template.md) §"Key theo tầng" và
|
|
25
|
+
[api-mapping.md](api-mapping.md)):
|
|
26
|
+
|
|
27
|
+
| Behavior | Kotlin cũ (AdsPro) | JSON mới |
|
|
28
|
+
|---|---|---|
|
|
29
|
+
| preload theo thời điểm | `AdmobX.load(space)` rải rác | `preload.eventActive` |
|
|
30
|
+
| gate "chỉ show khi đã load" | `getStatusBySpace == LOADED` | `mode.show: SHOW_LOADED` |
|
|
31
|
+
| auto reload chu kỳ | timer trong Fragment | `reload.auto.placementID` + `show.timeAutoReloadMs` |
|
|
32
|
+
|
|
33
|
+
Code smell sau migration — còn thấy là chưa xong: `Handler`/`delay` quanh ads, biến `countShowAd`,
|
|
34
|
+
`System.currentTimeMillis()` so khoảng cách ad, `Ads.preload*` thủ công, `if (status == LOADED)`
|
|
35
|
+
trước show, `R.layout.*` trong **thân wrapper**, timer reload trong Fragment.
|
|
36
|
+
|
|
37
|
+
# 2. Wrapper-first
|
|
38
|
+
|
|
39
|
+
**Giữ vỏ, bỏ ruột.** Hàm gọi ad gốc của app được giữ **nguyên chữ ký** và trở thành lớp wrapper
|
|
40
|
+
mỏng delegate xuống `Ads.show`. Behavior bên trong bị bỏ hết vì đã sang JSON. Màn hình **không bị
|
|
41
|
+
sửa** nếu chữ ký giữ được.
|
|
42
|
+
|
|
43
|
+
Giữ chữ ký ≠ port behavior. Điều bị cấm là mang *logic* cũ sang, không phải mang *tên hàm* sang.
|
|
44
|
+
|
|
45
|
+
Một số tham số trở thành **inert** (`idLayoutAds`, `layoutLoadingRes`, `forceLoadNew`, …). Rủi
|
|
46
|
+
ro: sau này có người đổi `R.layout.x` ở call site và không có gì xảy ra. Xử lý:
|
|
47
|
+
|
|
48
|
+
- Mỗi tham số inert ghi vào bảng "Inert parameters" trong `migration-inventory.md`.
|
|
49
|
+
- Trong thân wrapper có **một** dòng comment nêu tham số đó giờ do JSON quyết định.
|
|
50
|
+
- Dọn tham số inert là **commit riêng sau migration**, không nằm trong phạm vi này.
|
|
51
|
+
|
|
52
|
+
# 3. Minimal-diff
|
|
53
|
+
|
|
54
|
+
- Không tạo file utils mới nếu đã có nhà. File `AdsUtils.kt` (hoặc tương đương) cũ được **giữ lại
|
|
55
|
+
làm nhà cho wrapper** — moi ruột, giữ path và tên. Giữ path nghĩa là ~40 màn hình không đổi
|
|
56
|
+
một dòng `import`.
|
|
57
|
+
- Một file wrapper duy nhất. Không tách `NativeAdsHelper` / `InterAdsHelper`.
|
|
58
|
+
- Không sửa màn hình nếu wrapper giữ được chữ ký. Mỗi lần sửa màn hình phải có lý do thuộc đúng
|
|
59
|
+
danh sách Phase 5b.
|
|
60
|
+
- Code mới (`EventScreen`, hook base fragment) ưu tiên thêm vào file đã có trong package base.
|
|
61
|
+
- Mục tiêu diff: `≈ 1 file config JSON + 1 file wrapper + 1 file base + n màn hình có lý do 5b`.
|
|
62
|
+
Không phải 40 màn hình.
|
|
63
|
+
|
|
64
|
+
SlideShow (commit `4e36e791`) tạo `AdsSP.kt` mới và comment bỏ 650 dòng `AdsUtils.kt`, sửa ~158
|
|
65
|
+
file. Skill dùng SlideShow làm authority cho **API và JSON**, nhưng **không** copy chiến lược
|
|
66
|
+
diff của nó.
|
|
67
|
+
|
|
68
|
+
```text
|
|
69
|
+
AdsPro: 40 màn --space + R.layout + timing--> AdsUtils (650 dòng) --> Admob*.load/show
|
|
70
|
+
SuperAds: 40 màn KHÔNG ĐỔI --chữ ký cũ--> AdsUtils moi ruột --> Ads.show
|
|
71
|
+
EventScreen --> Ads.dispatchEvent
|
|
72
|
+
JSON (format, layout, mode, preload, reload, chain)
|
|
73
|
+
```
|