@leejungkiin/awkit 1.0.5 β 1.0.7
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/VERSION +1 -1
- package/package.json +2 -2
- package/skills/smali-to-kotlin/SKILL.md +85 -331
- package/skills/smali-to-kotlin/phase-0-discovery.md +129 -0
- package/skills/smali-to-kotlin/phase-1-architecture.md +157 -0
- package/skills/smali-to-kotlin/phase-2-blueprint-ui.md +347 -0
- package/skills/smali-to-kotlin/phase-3-logic-build.md +268 -0
- package/skills/smali-to-swift/SKILL.md +91 -532
- package/skills/smali-to-swift/phase-0-discovery.md +154 -0
- package/skills/smali-to-swift/phase-1-architecture.md +173 -0
- package/skills/smali-to-swift/phase-2-blueprint-ui.md +348 -0
- package/skills/smali-to-swift/phase-3-logic-build.md +312 -0
- package/workflows/mobile/reverse-android-build.md +119 -79
- package/workflows/mobile/reverse-android-design.md +10 -7
- package/workflows/mobile/reverse-android.md +52 -46
- package/workflows/mobile/reverse-ios-build.md +161 -50
- package/workflows/mobile/reverse-ios-design.md +10 -1
- package/workflows/mobile/reverse-ios.md +49 -37
|
@@ -0,0 +1,312 @@
|
|
|
1
|
+
# π¨ Phase 3: Logic Build (Per Feature) β iOS
|
|
2
|
+
|
|
3
|
+
> **Zoom Level:** 3 β Code Implementation
|
|
4
|
+
> **Goal:** Code logic behind the APPROVED UI shell for ONE feature.
|
|
5
|
+
> **Input:** Approved Blueprint + Working SwiftUI Shell from Phase 2.
|
|
6
|
+
> **Output:** Feature fully wired β UI + logic connected.
|
|
7
|
+
|
|
8
|
+
---
|
|
9
|
+
|
|
10
|
+
## β
PREREQUISITES
|
|
11
|
+
|
|
12
|
+
Before writing ANY logic code, confirm:
|
|
13
|
+
- [ ] Phase 0 App Map: approved
|
|
14
|
+
- [ ] Phase 1 Architecture Blueprint: approved
|
|
15
|
+
- [ ] Phase 2 Contracts: approved for THIS feature
|
|
16
|
+
- [ ] Phase 2 UI Shell: approved, runs in Preview/Simulator
|
|
17
|
+
|
|
18
|
+
> β οΈ If UI is not approved yet, STOP. Go back to Phase 2.
|
|
19
|
+
|
|
20
|
+
---
|
|
21
|
+
|
|
22
|
+
## π Implementation Order
|
|
23
|
+
|
|
24
|
+
### 3.1: Domain Layer
|
|
25
|
+
|
|
26
|
+
Implement from contracts defined in Phase 2:
|
|
27
|
+
|
|
28
|
+
1. **Models** β structs (already drafted in 2.2, create actual files)
|
|
29
|
+
2. **Repository protocols** β (already drafted in 2.3, create files)
|
|
30
|
+
3. **UseCases** β implement execute() with repository calls
|
|
31
|
+
|
|
32
|
+
```swift
|
|
33
|
+
struct LoginUseCase {
|
|
34
|
+
private let authRepo: AuthRepository
|
|
35
|
+
|
|
36
|
+
func execute(email: String, password: String) async throws -> User {
|
|
37
|
+
try await authRepo.login(email: email, password: password)
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
### 3.2: Data Layer
|
|
43
|
+
|
|
44
|
+
1. **DTOs** β Codable structs matching API JSON
|
|
45
|
+
2. **API Client** β async URLSession with proper error handling
|
|
46
|
+
3. **SwiftData @Model** classes (if applicable)
|
|
47
|
+
4. **Keychain / UserDefaults** wrappers (if applicable)
|
|
48
|
+
5. **Repository implementation** β offline-first pattern
|
|
49
|
+
|
|
50
|
+
```swift
|
|
51
|
+
final class AuthRepositoryImpl: AuthRepository {
|
|
52
|
+
private let apiClient: APIClient
|
|
53
|
+
private let tokenStore: TokenStore
|
|
54
|
+
|
|
55
|
+
func login(email: String, password: String) async throws -> User {
|
|
56
|
+
let dto = try await apiClient.request(
|
|
57
|
+
AuthEndpoint.login(email: email, password: password),
|
|
58
|
+
responseType: LoginResponseDTO.self
|
|
59
|
+
)
|
|
60
|
+
await tokenStore.save(dto.accessToken)
|
|
61
|
+
return dto.user.toDomain()
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func isLoggedIn() -> Bool {
|
|
65
|
+
tokenStore.hasToken
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 3.3: DI Container
|
|
71
|
+
|
|
72
|
+
```swift
|
|
73
|
+
@MainActor
|
|
74
|
+
final class AppContainer {
|
|
75
|
+
// Singletons
|
|
76
|
+
lazy var apiClient = APIClient(baseURL: Config.apiBaseURL)
|
|
77
|
+
lazy var tokenStore = TokenStore()
|
|
78
|
+
|
|
79
|
+
// Repositories
|
|
80
|
+
lazy var authRepository: AuthRepository = AuthRepositoryImpl(
|
|
81
|
+
apiClient: apiClient,
|
|
82
|
+
tokenStore: tokenStore
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
// UseCases
|
|
86
|
+
func makeLoginUseCase() -> LoginUseCase {
|
|
87
|
+
LoginUseCase(authRepo: authRepository)
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// ViewModels
|
|
91
|
+
func makeLoginViewModel() -> LoginViewModel {
|
|
92
|
+
LoginViewModel(loginUseCase: makeLoginUseCase())
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
### 3.4: ViewModel
|
|
98
|
+
|
|
99
|
+
Implement using ViewState + Events + Actions from Phase 2.6:
|
|
100
|
+
|
|
101
|
+
```swift
|
|
102
|
+
@Observable
|
|
103
|
+
final class LoginViewModel {
|
|
104
|
+
var state = LoginViewState()
|
|
105
|
+
|
|
106
|
+
private let loginUseCase: LoginUseCase
|
|
107
|
+
private var onEvent: ((LoginEvent) -> Void)?
|
|
108
|
+
|
|
109
|
+
init(loginUseCase: LoginUseCase) {
|
|
110
|
+
self.loginUseCase = loginUseCase
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
func setEventHandler(_ handler: @escaping (LoginEvent) -> Void) {
|
|
114
|
+
self.onEvent = handler
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
func handle(_ action: LoginAction) {
|
|
118
|
+
switch action {
|
|
119
|
+
case .updateEmail(let email):
|
|
120
|
+
state.email = email
|
|
121
|
+
case .updatePassword(let password):
|
|
122
|
+
state.password = password
|
|
123
|
+
case .togglePasswordVisibility:
|
|
124
|
+
state.isPasswordVisible.toggle()
|
|
125
|
+
case .submit:
|
|
126
|
+
login()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private func login() {
|
|
131
|
+
state.isLoading = true
|
|
132
|
+
state.error = nil
|
|
133
|
+
Task {
|
|
134
|
+
do {
|
|
135
|
+
let _ = try await loginUseCase.execute(
|
|
136
|
+
email: state.email,
|
|
137
|
+
password: state.password
|
|
138
|
+
)
|
|
139
|
+
state.isLoading = false
|
|
140
|
+
onEvent?(.navigateToHome)
|
|
141
|
+
} catch {
|
|
142
|
+
state.isLoading = false
|
|
143
|
+
state.error = error.localizedDescription
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
### 3.5: Wire UI β Logic β (NOT "code new UI")
|
|
151
|
+
|
|
152
|
+
> **This step does NOT create new UI.** The UI already exists from Phase 2.8.
|
|
153
|
+
> Only CONNECT the existing UI shell to the real ViewModel.
|
|
154
|
+
|
|
155
|
+
**Changes needed on the UI shell:**
|
|
156
|
+
|
|
157
|
+
```swift
|
|
158
|
+
// Phase 2 code stays as the STATELESS "Content" view (for Preview):
|
|
159
|
+
struct LoginScreenContent: View {
|
|
160
|
+
var state: LoginViewState = .normal
|
|
161
|
+
var onAction: (LoginAction) -> Void = { _ in }
|
|
162
|
+
|
|
163
|
+
var body: some View {
|
|
164
|
+
// ... all UI code from Phase 2.8 β DO NOT MODIFY
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Previews still work:
|
|
169
|
+
#Preview("Normal") { LoginScreenContent(state: .normal) }
|
|
170
|
+
#Preview("Loading") { LoginScreenContent(state: .loading) }
|
|
171
|
+
|
|
172
|
+
// ADD a NEW wrapper that wires ViewModel:
|
|
173
|
+
struct LoginScreen: View {
|
|
174
|
+
@State private var viewModel: LoginViewModel
|
|
175
|
+
var onNavigateToHome: () -> Void
|
|
176
|
+
|
|
177
|
+
init(container: AppContainer, onNavigateToHome: @escaping () -> Void) {
|
|
178
|
+
_viewModel = State(initialValue: container.makeLoginViewModel())
|
|
179
|
+
self.onNavigateToHome = onNavigateToHome
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
var body: some View {
|
|
183
|
+
LoginScreenContent(
|
|
184
|
+
state: viewModel.state,
|
|
185
|
+
onAction: viewModel.handle
|
|
186
|
+
)
|
|
187
|
+
.onAppear {
|
|
188
|
+
viewModel.setEventHandler { event in
|
|
189
|
+
switch event {
|
|
190
|
+
case .navigateToHome:
|
|
191
|
+
onNavigateToHome()
|
|
192
|
+
case .showError(let msg):
|
|
193
|
+
// handle
|
|
194
|
+
break
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
**Wire Checklist:**
|
|
203
|
+
- [ ] Wrapper view injects ViewModel from DI container
|
|
204
|
+
- [ ] State bindings: ViewModel.state β Content view
|
|
205
|
+
- [ ] Action bindings: Content onAction β ViewModel.handle()
|
|
206
|
+
- [ ] Event handling: navigation, alerts
|
|
207
|
+
- [ ] Previews still work (they use the stateless Content view)
|
|
208
|
+
|
|
209
|
+
### 3.6: Integration Test β
|
|
210
|
+
|
|
211
|
+
Verify UI + logic end-to-end:
|
|
212
|
+
|
|
213
|
+
```markdown
|
|
214
|
+
### π§ͺ Integration: [Feature]
|
|
215
|
+
|
|
216
|
+
Functional:
|
|
217
|
+
- [ ] API calls succeed, data displays on UI
|
|
218
|
+
- [ ] Loading state shows/hides at right time
|
|
219
|
+
- [ ] Error state displays correct message
|
|
220
|
+
- [ ] Navigation works as expected
|
|
221
|
+
- [ ] Form validation works
|
|
222
|
+
|
|
223
|
+
Data:
|
|
224
|
+
- [ ] Request format matches original app
|
|
225
|
+
- [ ] Response parses correctly
|
|
226
|
+
- [ ] Token/session stored properly
|
|
227
|
+
- [ ] Crypto output matches original (if applicable)
|
|
228
|
+
|
|
229
|
+
Edge Cases:
|
|
230
|
+
- [ ] Empty input handling
|
|
231
|
+
- [ ] Network error handling
|
|
232
|
+
- [ ] Back navigation
|
|
233
|
+
- [ ] App backgrounding/foregrounding
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
---
|
|
237
|
+
|
|
238
|
+
## π CRYPTO UTILS (Special Handling)
|
|
239
|
+
|
|
240
|
+
If the feature involves encryption/hashing:
|
|
241
|
+
|
|
242
|
+
1. Read disassembly carefully β exact algorithm, padding, encoding
|
|
243
|
+
2. Implement in Swift β preserve exact input/output
|
|
244
|
+
3. Unit test IMMEDIATELY with known pairs
|
|
245
|
+
|
|
246
|
+
```swift
|
|
247
|
+
import CryptoKit
|
|
248
|
+
import CommonCrypto
|
|
249
|
+
|
|
250
|
+
enum CryptoUtils {
|
|
251
|
+
static func md5Hash(_ input: String) -> String {
|
|
252
|
+
let data = Data(input.utf8)
|
|
253
|
+
var digest = [UInt8](repeating: 0, count: Int(CC_MD5_DIGEST_LENGTH))
|
|
254
|
+
data.withUnsafeBytes { CC_MD5($0.baseAddress, CC_LONG(data.count), &digest) }
|
|
255
|
+
return digest.map { String(format: "%02x", $0) }.joined()
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
// MANDATORY test
|
|
260
|
+
final class CryptoUtilsTests: XCTestCase {
|
|
261
|
+
func testMD5MatchesOriginal() {
|
|
262
|
+
XCTAssertEqual("expected_hash", CryptoUtils.md5Hash("known_input"))
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
> β οΈ Crypto functions MUST produce identical output. Any mismatch breaks server communication.
|
|
268
|
+
|
|
269
|
+
---
|
|
270
|
+
|
|
271
|
+
## β
Checkpoint
|
|
272
|
+
|
|
273
|
+
```markdown
|
|
274
|
+
## β
Feature Complete: [Feature Name]
|
|
275
|
+
|
|
276
|
+
### Files created:
|
|
277
|
+
- Domain/Models/User.swift
|
|
278
|
+
- Domain/Repositories/AuthRepository.swift
|
|
279
|
+
- Domain/UseCases/LoginUseCase.swift
|
|
280
|
+
- Data/Network/Endpoints/AuthEndpoint.swift
|
|
281
|
+
- Data/Network/DTOs/LoginRequestDTO.swift, LoginResponseDTO.swift
|
|
282
|
+
- Data/Repositories/AuthRepositoryImpl.swift
|
|
283
|
+
- DI/AppContainer.swift (updated)
|
|
284
|
+
- Presentation/Screens/Auth/LoginViewModel.swift
|
|
285
|
+
- Presentation/Screens/Auth/LoginScreen.swift β wired (from Phase 2)
|
|
286
|
+
|
|
287
|
+
### Tests:
|
|
288
|
+
- [ ] Crypto utils verified (if applicable)
|
|
289
|
+
- [ ] API contract matches original
|
|
290
|
+
- [ ] UI + Logic e2e works
|
|
291
|
+
|
|
292
|
+
### βοΈ Next Feature: [Name]
|
|
293
|
+
β Return to Phase 2 (Blueprint + UI Design)
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
---
|
|
297
|
+
|
|
298
|
+
## π Feature Loop
|
|
299
|
+
|
|
300
|
+
```
|
|
301
|
+
Phase 2 (Blueprint + UI for Feature X) β GATE β Phase 3 (Logic for X) β Checkpoint
|
|
302
|
+
β
|
|
303
|
+
Phase 2 (Blueprint + UI for Feature Y) β GATE β Phase 3 (Logic for Y) β Checkpoint
|
|
304
|
+
β
|
|
305
|
+
... (repeat for all features from Architecture Build Order)
|
|
306
|
+
β
|
|
307
|
+
Phase 4: Final Parity Check & Quality Gate
|
|
308
|
+
```
|
|
309
|
+
|
|
310
|
+
---
|
|
311
|
+
|
|
312
|
+
*Phase 3: Logic Build β Wire logic behind approved UI*
|
|
@@ -1,170 +1,210 @@
|
|
|
1
1
|
---
|
|
2
|
-
description: π¨ RE Android Phase 2+3 β
|
|
2
|
+
description: π¨ RE Android Phase 2+3+4 β Blueprint + UI Shell β Logic Build β Final Parity (UI-First)
|
|
3
3
|
parent: reverse-android
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# /re-android-build β Blueprint
|
|
6
|
+
# /re-android-build β Blueprint + UI + Build (Per Feature)
|
|
7
7
|
|
|
8
|
-
> **Parent:** [`/reverse-android`](reverse-android.md) β Phase 2+3
|
|
8
|
+
> **Parent:** [`/reverse-android`](reverse-android.md) β Phase 2+3+4
|
|
9
9
|
> **Prerequisite:** Completed Architecture from [`/re-android-design`](reverse-android-design.md)
|
|
10
|
-
> **Skill:** `smali-to-kotlin` β `phase-2-blueprint.md` + `phase-3-build.md`
|
|
10
|
+
> **Skill:** `smali-to-kotlin` β `phase-2-blueprint-ui.md` + `phase-3-logic-build.md`
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## π Feature Loop
|
|
14
|
+
## π Feature Loop (UI-First)
|
|
15
15
|
|
|
16
16
|
```
|
|
17
17
|
For each feature (from Architecture Build Order):
|
|
18
|
-
Phase 2: Blueprint (
|
|
19
|
-
β
|
|
20
|
-
Phase 3:
|
|
21
|
-
β
|
|
18
|
+
Phase 2: Blueprint + UI (contracts + visual shell)
|
|
19
|
+
β π¦ GATE: User approves UI + contracts
|
|
20
|
+
Phase 3: Logic Build (domain β data β wire)
|
|
21
|
+
β π CHECKPOINT
|
|
22
22
|
β Next feature
|
|
23
23
|
```
|
|
24
24
|
|
|
25
25
|
---
|
|
26
26
|
|
|
27
|
-
##
|
|
27
|
+
## ππ¨ Phase 2: Blueprint + UI Design
|
|
28
28
|
|
|
29
|
-
> **Output:**
|
|
29
|
+
> **Output:** Approved contracts + Working Compose UI shell with mock data.
|
|
30
30
|
|
|
31
|
-
###
|
|
31
|
+
### Part A: Contracts (signatures only)
|
|
32
32
|
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
- Control flow β business rules (document, don't code)
|
|
37
|
-
|
|
38
|
-
### 2.2: Contracts
|
|
39
|
-
|
|
40
|
-
Define for this feature:
|
|
33
|
+
#### 2.1: Deep Smali Reading
|
|
34
|
+
Read Smali files for the chosen feature. Extract class hierarchy, fields, method signatures,
|
|
35
|
+
string constants, control flow. See `smali-reading-guide.md`.
|
|
41
36
|
|
|
37
|
+
#### 2.2β2.5: Define Contracts
|
|
42
38
|
```kotlin
|
|
43
|
-
// Domain Model
|
|
44
|
-
data class [Model](val
|
|
39
|
+
// 2.2 Domain Model
|
|
40
|
+
data class [Model](val field: Type, ...)
|
|
45
41
|
|
|
46
|
-
// Repository Interface
|
|
42
|
+
// 2.3 Repository Interface
|
|
47
43
|
interface [Feature]Repository {
|
|
48
44
|
suspend fun [method](...): Result<[Type]>
|
|
49
|
-
fun [stream](): Flow<[Type]>
|
|
50
45
|
}
|
|
51
46
|
|
|
52
|
-
// API Interface
|
|
47
|
+
// 2.4 API Interface
|
|
53
48
|
interface [Feature]Api {
|
|
54
49
|
@[METHOD]("[endpoint]")
|
|
55
50
|
suspend fun [method](...): [Response]
|
|
56
51
|
}
|
|
57
52
|
|
|
58
|
-
// UseCase
|
|
53
|
+
// 2.5 UseCase
|
|
59
54
|
class [Action]UseCase(repo: [Feature]Repository) {
|
|
60
55
|
suspend operator fun invoke(...): Result<[Type]> // TODO()
|
|
61
56
|
}
|
|
62
57
|
```
|
|
63
58
|
|
|
64
|
-
|
|
65
|
-
|
|
59
|
+
#### 2.6: UI State Design
|
|
66
60
|
```kotlin
|
|
67
61
|
data class [Screen]UiState(
|
|
68
62
|
val field: Type = default,
|
|
69
63
|
val isLoading: Boolean = false,
|
|
70
64
|
val error: String? = null
|
|
71
65
|
)
|
|
72
|
-
|
|
73
66
|
sealed interface [Screen]Event { /* navigation, snackbar */ }
|
|
74
67
|
sealed interface [Screen]Action { /* user interactions */ }
|
|
75
68
|
```
|
|
76
69
|
|
|
77
|
-
###
|
|
70
|
+
### Part B: UI Visual Shell
|
|
71
|
+
|
|
72
|
+
#### 2.7: Resource Extraction β (BEFORE UI code!)
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
# Only resources for THIS screen
|
|
76
|
+
grep -o '@drawable/[a-z_]*' [apktool_dir]/res/layout/activity_[screen].xml | sort -u
|
|
77
|
+
grep -o '@color/[a-z_]*' [apktool_dir]/res/layout/activity_[screen].xml | sort -u
|
|
78
|
+
```
|
|
78
79
|
|
|
79
|
-
|
|
80
|
+
Copy ONLY needed resources. Verify they compile.
|
|
80
81
|
|
|
81
|
-
|
|
82
|
+
#### 2.8: UI Implementation β (Visual shell with mock data)
|
|
82
83
|
|
|
84
|
+
- Use UiState from 2.6 with hardcoded defaults
|
|
85
|
+
- Use REAL resources from 2.7
|
|
86
|
+
- Create `[Screen]Content` composable (stateless)
|
|
87
|
+
- Match visual parity with original app
|
|
88
|
+
- NO ViewModel connection, NO real API calls
|
|
89
|
+
|
|
90
|
+
```kotlin
|
|
91
|
+
@Composable
|
|
92
|
+
fun [Screen]Content(
|
|
93
|
+
uiState: [Screen]UiState = [Screen]UiState(), // Mock
|
|
94
|
+
onAction: ([Screen]Action) -> Unit = {} // No-op
|
|
95
|
+
) {
|
|
96
|
+
// Full Compose UI matching original app
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
@Preview @Composable
|
|
100
|
+
private fun NormalPreview() { AppTheme { [Screen]Content() } }
|
|
101
|
+
|
|
102
|
+
@Preview @Composable
|
|
103
|
+
private fun LoadingPreview() { AppTheme { [Screen]Content([Screen]UiState(isLoading = true)) } }
|
|
104
|
+
|
|
105
|
+
@Preview @Composable
|
|
106
|
+
private fun ErrorPreview() { AppTheme { [Screen]Content([Screen]UiState(error = "Error")) } }
|
|
83
107
|
```
|
|
84
|
-
|
|
108
|
+
|
|
109
|
+
#### 2.9: Visual Parity Check β
|
|
110
|
+
|
|
111
|
+
Compare with original app:
|
|
112
|
+
- [ ] Layout structure matches
|
|
113
|
+
- [ ] Colors, typography, spacing correct
|
|
114
|
+
- [ ] Icons/images positioned correctly
|
|
115
|
+
- [ ] All states: normal, loading, error, empty
|
|
116
|
+
|
|
117
|
+
### π¦ UI + Contracts Gate (MANDATORY)
|
|
118
|
+
|
|
119
|
+
```
|
|
120
|
+
"ππ¨ Blueprint + UI cho [Feature] xong:
|
|
121
|
+
π Contracts: [N] models, [N] repos, [N] use cases, [N] APIs
|
|
122
|
+
π¨ UI: [Screen] with [N] state previews
|
|
123
|
+
πΈ Visual Parity: [OK / needs adjustment]
|
|
124
|
+
|
|
125
|
+
β β
Approve β Phase 3 (Logic Build)
|
|
126
|
+
β π¨ Adjust UI β fix then re-check
|
|
127
|
+
β π Adjust contracts β revise"
|
|
85
128
|
```
|
|
86
129
|
|
|
130
|
+
> β οΈ **DO NOT proceed to Phase 3 without user approval.**
|
|
131
|
+
|
|
87
132
|
---
|
|
88
133
|
|
|
89
|
-
## π¨ Phase 3:
|
|
134
|
+
## π¨ Phase 3: Logic Build
|
|
90
135
|
|
|
91
|
-
> **Output:** Full production-quality Kotlin code
|
|
136
|
+
> **Output:** Full production-quality Kotlin code. Wire logic to EXISTING UI.
|
|
92
137
|
|
|
93
138
|
### 3.1: Domain Layer
|
|
94
|
-
|
|
95
|
-
- Models (data classes from Blueprint)
|
|
96
|
-
- Repository interfaces (from Blueprint)
|
|
97
|
-
- UseCases (implement invoke with repo calls)
|
|
139
|
+
Models + Repository interfaces + UseCases (implement invoke with repo calls)
|
|
98
140
|
|
|
99
141
|
### 3.2: Data Layer
|
|
100
|
-
|
|
101
|
-
- DTOs (@Serializable)
|
|
102
|
-
- Retrofit API interface
|
|
103
|
-
- Room entities + DAOs (if applicable)
|
|
104
|
-
- Repository implementation (offline-first)
|
|
142
|
+
DTOs + Retrofit API + Room (if applicable) + Repository implementation
|
|
105
143
|
|
|
106
144
|
### 3.3: DI Module
|
|
107
|
-
|
|
108
|
-
- Hilt @Module with @Binds for repository
|
|
145
|
+
Hilt @Module with @Binds for repository
|
|
109
146
|
|
|
110
147
|
### 3.4: ViewModel
|
|
148
|
+
@HiltViewModel with StateFlow + SharedFlow, implements onAction()
|
|
111
149
|
|
|
112
|
-
|
|
113
|
-
- Implement onAction() handler from Blueprint actions
|
|
114
|
-
|
|
115
|
-
### 3.5: Compose Screen
|
|
150
|
+
### 3.5: Wire UI β Logic β (NOT "code new UI")
|
|
116
151
|
|
|
117
|
-
|
|
118
|
-
- UI from wireframe
|
|
119
|
-
- LaunchedEffect for events
|
|
152
|
+
The UI already exists from Phase 2.8. Only CONNECT it:
|
|
120
153
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
154
|
+
```kotlin
|
|
155
|
+
// Keep stateless Content composable from Phase 2 (for Preview):
|
|
156
|
+
// [Screen]Content(uiState, onAction) β DO NOT MODIFY
|
|
157
|
+
|
|
158
|
+
// ADD wrapper that wires ViewModel:
|
|
159
|
+
@Composable
|
|
160
|
+
fun [Screen](
|
|
161
|
+
viewModel: [Screen]ViewModel = hiltViewModel(),
|
|
162
|
+
onNavigate: () -> Unit
|
|
163
|
+
) {
|
|
164
|
+
val uiState by viewModel.uiState.collectAsStateWithLifecycle()
|
|
165
|
+
LaunchedEffect(Unit) {
|
|
166
|
+
viewModel.events.collect { event -> /* handle navigation */ }
|
|
167
|
+
}
|
|
168
|
+
[Screen]Content(uiState = uiState, onAction = viewModel::onAction)
|
|
169
|
+
}
|
|
126
170
|
```
|
|
127
171
|
|
|
128
|
-
###
|
|
172
|
+
### 3.6: Integration Test β
|
|
173
|
+
- [ ] API calls succeed, data displays
|
|
174
|
+
- [ ] Loading/error states work
|
|
175
|
+
- [ ] Navigation correct
|
|
176
|
+
- [ ] Crypto output matches (if applicable)
|
|
129
177
|
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
3. Unit test IMMEDIATELY with known pairs
|
|
134
|
-
|
|
135
|
-
> β οΈ Crypto MUST produce identical output to original app.
|
|
178
|
+
### π Crypto Utils (Special)
|
|
179
|
+
If crypto involved: implement + unit test IMMEDIATELY with known pairs.
|
|
180
|
+
> β οΈ MUST produce identical output.
|
|
136
181
|
|
|
137
182
|
### β
Feature Checkpoint
|
|
138
183
|
|
|
139
184
|
```markdown
|
|
140
185
|
## β
Feature Complete: [Name]
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
### Resources extracted: [only needed]
|
|
144
|
-
### Tests: [crypto verified? API matches?]
|
|
145
|
-
|
|
146
|
-
### βοΈ Next Feature: [Name]
|
|
147
|
-
β Return to Phase 2 (Blueprint) for next feature
|
|
186
|
+
Files: [list] | Resources: [count] | Tests: [status]
|
|
187
|
+
βοΈ Next Feature: [Name] β Return to Phase 2
|
|
148
188
|
```
|
|
149
189
|
|
|
150
190
|
---
|
|
151
191
|
|
|
152
|
-
## β
Final Parity Check (After ALL features)
|
|
192
|
+
## β
Phase 4: Final Parity Check (After ALL features)
|
|
153
193
|
|
|
154
194
|
### Checklist
|
|
155
195
|
- [ ] API Parity β all endpoints match (headers, body, encoding)
|
|
156
196
|
- [ ] Data Parity β crypto/hash output identical
|
|
157
197
|
- [ ] UI Parity β screen-by-screen comparison
|
|
158
198
|
- [ ] Edge Cases β empty states, errors, offline, lifecycle
|
|
159
|
-
- [ ] Build
|
|
199
|
+
- [ ] Build: `./gradlew assembleDebug && ./gradlew test && ./gradlew lint`
|
|
160
200
|
|
|
161
201
|
### π Final Summary
|
|
162
202
|
|
|
163
203
|
```markdown
|
|
164
204
|
## β
Reverse Engineering Complete!
|
|
165
|
-
- Screens: [
|
|
166
|
-
- Libs reused: [
|
|
167
|
-
- Tests: [pass/fail] | Lint: [
|
|
205
|
+
- Screens: [N] | Features: [N]
|
|
206
|
+
- Libs reused: [N] | Replaced: [N]
|
|
207
|
+
- Tests: [pass/fail] | Lint: [warnings]
|
|
168
208
|
|
|
169
209
|
βοΈ Next: /test β /deploy β /code-janitor
|
|
170
210
|
```
|
|
@@ -175,8 +215,8 @@ If feature involves crypto/hashing:
|
|
|
175
215
|
|
|
176
216
|
- **Parent:** [`/reverse-android`](reverse-android.md)
|
|
177
217
|
- **Previous:** [`/re-android-design`](reverse-android-design.md) (Phase 1)
|
|
178
|
-
- **Skill:** `smali-to-kotlin` β `phase-2-blueprint.md` + `phase-3-build.md`
|
|
218
|
+
- **Skill:** `smali-to-kotlin` β `phase-2-blueprint-ui.md` + `phase-3-logic-build.md`
|
|
179
219
|
|
|
180
220
|
---
|
|
181
221
|
|
|
182
|
-
*re-android-build
|
|
222
|
+
*re-android-build v4.0.0 β UI-First Blueprint + Build*
|
|
@@ -84,16 +84,19 @@ app/src/main/java/[package]/
|
|
|
84
84
|
βββ util/
|
|
85
85
|
```
|
|
86
86
|
|
|
87
|
-
## π’ Step 6: Build Order
|
|
87
|
+
## π’ Step 6: Build Order (UI-First) β
|
|
88
88
|
|
|
89
89
|
| # | Phase | Scope | Complexity |
|
|
90
90
|
|---|-------|-------|-----------|
|
|
91
|
-
| 1 | π’
|
|
92
|
-
| 2 | π’
|
|
93
|
-
| 3 |
|
|
94
|
-
| 4 |
|
|
95
|
-
| 5 |
|
|
96
|
-
|
|
|
91
|
+
| 1 | π’ Foundation | Project + DI skeleton + Theme/Design System | Low |
|
|
92
|
+
| 2 | π’ Navigation | Routes, tab bar, NavGraph | Low |
|
|
93
|
+
| 3 | π΅ Per Feature | Blueprint (contracts) + UI Shell (visual) | Medium |
|
|
94
|
+
| 4 | π¦ UI Gate | User approves UI before logic | β |
|
|
95
|
+
| 5 | π‘ Per Feature | Logic Build (Domain β Data β VM β Wire UI) | High |
|
|
96
|
+
| 6 | π¦ SDKs | Third-party SDK + Native libs integration | Medium |
|
|
97
|
+
| N | π΄ Final | Full Parity Check | High |
|
|
98
|
+
|
|
99
|
+
> **UI-First:** Design and approve UI before coding logic for each feature.
|
|
97
100
|
|
|
98
101
|
## π§ Step 7: Tech Stack Confirmation
|
|
99
102
|
|