androjack-mcp 1.3.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/.github/ISSUE_TEMPLATE/bug_report.md +40 -0
- package/.github/ISSUE_TEMPLATE/feature_request.md +34 -0
- package/.github/pull_request_template.md +16 -0
- package/CONTRIBUTING.md +27 -0
- package/LICENSE +21 -0
- package/README.md +592 -0
- package/SECURITY.md +26 -0
- package/assets/AndroJack banner.png +0 -0
- package/assets/killer_argument.png +0 -0
- package/build/constants.js +412 -0
- package/build/http-server.js +163 -0
- package/build/http.js +151 -0
- package/build/index.js +553 -0
- package/build/install.js +379 -0
- package/build/logger.js +57 -0
- package/build/tools/api-level.js +170 -0
- package/build/tools/api36-compliance.js +282 -0
- package/build/tools/architecture.js +75 -0
- package/build/tools/build-publish.js +362 -0
- package/build/tools/component.js +90 -0
- package/build/tools/debugger.js +82 -0
- package/build/tools/gradle.js +234 -0
- package/build/tools/kmp.js +348 -0
- package/build/tools/kotlin-patterns.js +500 -0
- package/build/tools/large-screen.js +366 -0
- package/build/tools/m3-expressive.js +447 -0
- package/build/tools/navigation3.js +331 -0
- package/build/tools/ondevice-ai.js +283 -0
- package/build/tools/permissions.js +404 -0
- package/build/tools/play-policy.js +221 -0
- package/build/tools/scalability.js +621 -0
- package/build/tools/search.js +89 -0
- package/build/tools/testing.js +439 -0
- package/build/tools/wear.js +337 -0
- package/build/tools/xr.js +274 -0
- package/config/antigravity_mcp.json +32 -0
- package/config/claude_desktop_config.json +17 -0
- package/config/cursor_mcp.json +21 -0
- package/config/jetbrains_mcp.json +28 -0
- package/config/kiro_mcp.json +40 -0
- package/config/vscode_mcp.json +24 -0
- package/config/windsurf_mcp.json +18 -0
- package/package.json +51 -0
- package/src/constants.ts +436 -0
- package/src/http-server.ts +186 -0
- package/src/http.ts +190 -0
- package/src/index.ts +702 -0
- package/src/install.ts +441 -0
- package/src/logger.ts +67 -0
- package/src/tools/api-level.ts +198 -0
- package/src/tools/api36-compliance.ts +289 -0
- package/src/tools/architecture.ts +94 -0
- package/src/tools/build-publish.ts +379 -0
- package/src/tools/component.ts +106 -0
- package/src/tools/debugger.ts +111 -0
- package/src/tools/gradle.ts +288 -0
- package/src/tools/kmp.ts +352 -0
- package/src/tools/kotlin-patterns.ts +534 -0
- package/src/tools/large-screen.ts +391 -0
- package/src/tools/m3-expressive.ts +473 -0
- package/src/tools/navigation3.ts +338 -0
- package/src/tools/ondevice-ai.ts +287 -0
- package/src/tools/permissions.ts +445 -0
- package/src/tools/play-policy.ts +229 -0
- package/src/tools/scalability.ts +646 -0
- package/src/tools/search.ts +112 -0
- package/src/tools/testing.ts +460 -0
- package/src/tools/wear.ts +343 -0
- package/src/tools/xr.ts +278 -0
- package/tsconfig.json +17 -0
|
@@ -0,0 +1,366 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool 12 – android_large_screen_guide
|
|
3
|
+
*
|
|
4
|
+
* Complete reference for Android large screen and adaptive layout development.
|
|
5
|
+
* Google Play Store quality guidelines require apps to properly support
|
|
6
|
+
* tablets, foldables, and ChromeOS — this tool enforces that standard.
|
|
7
|
+
*
|
|
8
|
+
* Sources:
|
|
9
|
+
* - developer.android.com/guide/topics/large-screens
|
|
10
|
+
* - developer.android.com/develop/ui/compose/layouts/adaptive
|
|
11
|
+
* - developer.android.com/develop/ui/views/layout/responsive-ui
|
|
12
|
+
*/
|
|
13
|
+
// ── Knowledge base ─────────────────────────────────────────────────────────────
|
|
14
|
+
const OVERVIEW = `
|
|
15
|
+
## Android Large Screen & Adaptive Layout Overview
|
|
16
|
+
|
|
17
|
+
### Why this matters — Play Store quality bar (2024+)
|
|
18
|
+
Google Play now surfaces large-screen-optimized apps preferentially on tablets and foldables.
|
|
19
|
+
Apps that don't support large screens receive lower quality ratings that reduce discovery.
|
|
20
|
+
|
|
21
|
+
### The three device classes (WindowSizeClass)
|
|
22
|
+
|
|
23
|
+
| Class | Width | Typical Device |
|
|
24
|
+
|-------|-------|---------------|
|
|
25
|
+
| Compact | < 600dp | Phone portrait |
|
|
26
|
+
| Medium | 600–840dp | Phone landscape, foldable, small tablet |
|
|
27
|
+
| Expanded | > 840dp | Tablet, large foldable unfolded, ChromeOS |
|
|
28
|
+
|
|
29
|
+
Rule: **Design for Compact first, adapt for Medium and Expanded.**
|
|
30
|
+
|
|
31
|
+
### Key Jetpack libraries
|
|
32
|
+
|
|
33
|
+
\`\`\`kotlin
|
|
34
|
+
// libs.versions.toml
|
|
35
|
+
window = { group = "androidx.window", name = "window", version = "1.4.0" }
|
|
36
|
+
adaptive = { group = "androidx.compose.material3.adaptive", name = "adaptive", version = "1.1.0" }
|
|
37
|
+
adaptive-nav = { group = "androidx.compose.material3.adaptive", name = "adaptive-navigation", version = "1.1.0" }
|
|
38
|
+
|
|
39
|
+
// build.gradle.kts
|
|
40
|
+
implementation(libs.window)
|
|
41
|
+
implementation(libs.adaptive)
|
|
42
|
+
implementation(libs.adaptive.nav)
|
|
43
|
+
\`\`\`
|
|
44
|
+
|
|
45
|
+
**Official guide:** https://developer.android.com/guide/topics/large-screens/get-started-with-large-screens
|
|
46
|
+
`;
|
|
47
|
+
const WINDOW_SIZE_CLASS = `
|
|
48
|
+
## WindowSizeClass — Adaptive Layout Foundation
|
|
49
|
+
|
|
50
|
+
\`\`\`kotlin
|
|
51
|
+
// Source: developer.android.com/develop/ui/compose/layouts/adaptive/use-window-size-classes
|
|
52
|
+
|
|
53
|
+
// ── In your Activity / entry Composable ──────────────────────────────
|
|
54
|
+
@Composable
|
|
55
|
+
fun App() {
|
|
56
|
+
// currentWindowAdaptiveInfo provides WindowSizeClass + FoldingFeature
|
|
57
|
+
val adaptiveInfo = currentWindowAdaptiveInfo()
|
|
58
|
+
val windowSizeClass = adaptiveInfo.windowSizeClass
|
|
59
|
+
|
|
60
|
+
AppContent(windowSizeClass = windowSizeClass)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
@Composable
|
|
64
|
+
fun AppContent(windowSizeClass: WindowSizeClass) {
|
|
65
|
+
when {
|
|
66
|
+
windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_EXPANDED_LOWER_BOUND) -> {
|
|
67
|
+
// Expanded — show two-pane layout (list + detail side-by-side)
|
|
68
|
+
TwoPaneLayout()
|
|
69
|
+
}
|
|
70
|
+
windowSizeClass.isWidthAtLeastBreakpoint(WindowSizeClass.WIDTH_DP_MEDIUM_LOWER_BOUND) -> {
|
|
71
|
+
// Medium — show navigation rail
|
|
72
|
+
NavigationRailLayout()
|
|
73
|
+
}
|
|
74
|
+
else -> {
|
|
75
|
+
// Compact — show bottom navigation
|
|
76
|
+
BottomNavLayout()
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
\`\`\`
|
|
81
|
+
|
|
82
|
+
### WindowSizeClass breakpoints (dp)
|
|
83
|
+
|
|
84
|
+
| Breakpoint constant | Value |
|
|
85
|
+
|---------------------|-------|
|
|
86
|
+
| WIDTH_DP_COMPACT_LOWER_BOUND | 0dp |
|
|
87
|
+
| WIDTH_DP_MEDIUM_LOWER_BOUND | 600dp |
|
|
88
|
+
| WIDTH_DP_EXPANDED_LOWER_BOUND | 840dp |
|
|
89
|
+
| HEIGHT_DP_COMPACT_LOWER_BOUND | 0dp |
|
|
90
|
+
| HEIGHT_DP_MEDIUM_LOWER_BOUND | 480dp |
|
|
91
|
+
| HEIGHT_DP_EXPANDED_LOWER_BOUND | 900dp |
|
|
92
|
+
`;
|
|
93
|
+
const NAVIGATION_PATTERNS = `
|
|
94
|
+
## Adaptive Navigation — M3 NavigationSuiteScaffold
|
|
95
|
+
|
|
96
|
+
The correct M3 pattern adapts navigation component type automatically based on window size.
|
|
97
|
+
|
|
98
|
+
\`\`\`kotlin
|
|
99
|
+
// Source: developer.android.com/develop/ui/compose/layouts/adaptive/adaptive-navigation-suite-scaffold
|
|
100
|
+
|
|
101
|
+
// ── Setup ─────────────────────────────────────────────────────────────
|
|
102
|
+
// Dependency:
|
|
103
|
+
// implementation("androidx.compose.material3:material3-adaptive-navigation-suite:1.4.0")
|
|
104
|
+
|
|
105
|
+
enum class AppDestination(val icon: ImageVector, val label: String) {
|
|
106
|
+
HOME(Icons.Filled.Home, "Home"),
|
|
107
|
+
SEARCH(Icons.Filled.Search, "Search"),
|
|
108
|
+
PROFILE(Icons.Filled.Person, "Profile"),
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
@Composable
|
|
112
|
+
fun AdaptiveApp() {
|
|
113
|
+
var currentDest by remember { mutableStateOf(AppDestination.HOME) }
|
|
114
|
+
|
|
115
|
+
// NavigationSuiteScaffold automatically switches between:
|
|
116
|
+
// - BottomNavigationBar (Compact width)
|
|
117
|
+
// - NavigationRail (Medium width)
|
|
118
|
+
// - NavigationDrawer (Expanded width)
|
|
119
|
+
NavigationSuiteScaffold(
|
|
120
|
+
navigationSuiteItems = {
|
|
121
|
+
AppDestination.entries.forEach { dest ->
|
|
122
|
+
item(
|
|
123
|
+
icon = { Icon(dest.icon, contentDescription = dest.label) },
|
|
124
|
+
label = { Text(dest.label) },
|
|
125
|
+
selected = currentDest == dest,
|
|
126
|
+
onClick = { currentDest = dest }
|
|
127
|
+
)
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
) {
|
|
131
|
+
when (currentDest) {
|
|
132
|
+
AppDestination.HOME -> HomeScreen()
|
|
133
|
+
AppDestination.SEARCH -> SearchScreen()
|
|
134
|
+
AppDestination.PROFILE -> ProfileScreen()
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
\`\`\`
|
|
139
|
+
`;
|
|
140
|
+
const TWO_PANE = `
|
|
141
|
+
## Two-Pane List-Detail Layout
|
|
142
|
+
|
|
143
|
+
The canonical large screen pattern: list on left, detail on right.
|
|
144
|
+
Use ListDetailPaneScaffold from the Adaptive library.
|
|
145
|
+
|
|
146
|
+
\`\`\`kotlin
|
|
147
|
+
// Source: developer.android.com/develop/ui/compose/layouts/adaptive/list-detail-pane-scaffold
|
|
148
|
+
|
|
149
|
+
@Composable
|
|
150
|
+
fun UserListDetailScreen() {
|
|
151
|
+
val navigator = rememberListDetailPaneScaffoldNavigator<Int>()
|
|
152
|
+
|
|
153
|
+
BackHandler(navigator.canNavigateBack()) {
|
|
154
|
+
navigator.navigateBack()
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
ListDetailPaneScaffold(
|
|
158
|
+
directive = navigator.scaffoldDirective,
|
|
159
|
+
value = navigator.scaffoldValue,
|
|
160
|
+
listPane = {
|
|
161
|
+
AnimatedPane {
|
|
162
|
+
UserList(
|
|
163
|
+
onUserClick = { userId ->
|
|
164
|
+
navigator.navigateTo(ListDetailPaneScaffoldRole.Detail, userId)
|
|
165
|
+
}
|
|
166
|
+
)
|
|
167
|
+
}
|
|
168
|
+
},
|
|
169
|
+
detailPane = {
|
|
170
|
+
AnimatedPane {
|
|
171
|
+
navigator.currentDestination?.content?.let { userId ->
|
|
172
|
+
UserDetailScreen(userId = userId)
|
|
173
|
+
} ?: run {
|
|
174
|
+
// Placeholder shown when no item is selected
|
|
175
|
+
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
|
176
|
+
Text("Select a user")
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
)
|
|
182
|
+
}
|
|
183
|
+
\`\`\`
|
|
184
|
+
|
|
185
|
+
### Key behaviors
|
|
186
|
+
- **Compact** — full-screen list; detail replaces list on navigation
|
|
187
|
+
- **Expanded** — list and detail shown side-by-side simultaneously
|
|
188
|
+
- **BackHandler** required — system back should navigate back to list on compact
|
|
189
|
+
`;
|
|
190
|
+
const FOLDABLES = `
|
|
191
|
+
## Foldables — Hinge & Fold Awareness
|
|
192
|
+
|
|
193
|
+
\`\`\`kotlin
|
|
194
|
+
// Source: developer.android.com/guide/topics/large-screens/learn-about-foldables
|
|
195
|
+
|
|
196
|
+
@Composable
|
|
197
|
+
fun FoldAwareLayout() {
|
|
198
|
+
val foldingFeature = currentWindowAdaptiveInfo().windowPosture.hingeList.firstOrNull()
|
|
199
|
+
|
|
200
|
+
if (foldingFeature != null && foldingFeature.isSeparating) {
|
|
201
|
+
// Device is folded at the hinge — show content on each half
|
|
202
|
+
val hingeLeft = foldingFeature.bounds.left
|
|
203
|
+
val hingeRight = foldingFeature.bounds.right
|
|
204
|
+
// Place content avoiding the hinge area
|
|
205
|
+
TwoHalvesLayout(leftBound = hingeLeft, rightBound = hingeRight)
|
|
206
|
+
} else {
|
|
207
|
+
// Unfolded or non-foldable — standard layout
|
|
208
|
+
StandardLayout()
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
// ── WindowInfoTracker for non-Compose contexts ────────────────────────
|
|
213
|
+
// In an Activity or Fragment:
|
|
214
|
+
lifecycleScope.launch {
|
|
215
|
+
lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
|
216
|
+
WindowInfoTracker.getOrCreate(this@MainActivity)
|
|
217
|
+
.windowLayoutInfo(this@MainActivity)
|
|
218
|
+
.collect { layoutInfo ->
|
|
219
|
+
val foldingFeatures = layoutInfo.displayFeatures
|
|
220
|
+
.filterIsInstance<FoldingFeature>()
|
|
221
|
+
// React to fold state changes
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
\`\`\`
|
|
226
|
+
|
|
227
|
+
### FoldingFeature properties
|
|
228
|
+
| Property | Values | Meaning |
|
|
229
|
+
|----------|--------|---------|
|
|
230
|
+
| \`state\` | FLAT, HALF_OPENED | Current fold angle |
|
|
231
|
+
| \`orientation\` | HORIZONTAL, VERTICAL | Hinge direction |
|
|
232
|
+
| \`isSeparating\` | true/false | Whether hinge occludes content |
|
|
233
|
+
| \`occlusionType\` | NONE, FULL | Whether hinge is physically present |
|
|
234
|
+
`;
|
|
235
|
+
const CONTINUITY = `
|
|
236
|
+
## Continuity — Surviving Configuration Changes
|
|
237
|
+
|
|
238
|
+
Large screen apps get more configuration changes (fold/unfold, rotation, window resize).
|
|
239
|
+
|
|
240
|
+
\`\`\`kotlin
|
|
241
|
+
// ── ViewModel survives ALL config changes ─────────────────────────────
|
|
242
|
+
// (already the correct pattern — nothing extra needed)
|
|
243
|
+
|
|
244
|
+
// ── Compose state that survives process death ─────────────────────────
|
|
245
|
+
// Use rememberSaveable + a custom saver for complex types:
|
|
246
|
+
@Composable
|
|
247
|
+
fun PersistentScreen() {
|
|
248
|
+
var selectedId by rememberSaveable { mutableStateOf<Int?>(null) }
|
|
249
|
+
// selectedId survives rotation, fold, and process death
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// ── For complex types, write a Saver: ─────────────────────────────────
|
|
253
|
+
val UiStateSaver = Saver<UiState, Int>(
|
|
254
|
+
save = { state -> (state as? UiState.Success)?.data?.id ?: -1 },
|
|
255
|
+
restore = { id -> if (id >= 0) UiState.Success(User(id, "")) else UiState.Loading }
|
|
256
|
+
)
|
|
257
|
+
var state by rememberSaveable(stateSaver = UiStateSaver) { mutableStateOf(UiState.Loading) }
|
|
258
|
+
|
|
259
|
+
// ── Test config changes in unit test ──────────────────────────────────
|
|
260
|
+
// ActivityScenario handles rotation:
|
|
261
|
+
activityScenario.onActivity { activity ->
|
|
262
|
+
activity.requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
|
|
263
|
+
}
|
|
264
|
+
// Verify ViewModel state persists across rotation
|
|
265
|
+
\`\`\`
|
|
266
|
+
|
|
267
|
+
### Manifest — do NOT disable config changes for large screen
|
|
268
|
+
\`\`\`xml
|
|
269
|
+
<!-- ❌ Anti-pattern — breaks system-managed resizing on large screens -->
|
|
270
|
+
<activity android:configChanges="orientation|screenSize|smallestScreenSize|screenLayout">
|
|
271
|
+
|
|
272
|
+
<!-- ✅ Let the system handle it; use ViewModel + rememberSaveable -->
|
|
273
|
+
<!-- No configChanges needed for most apps -->
|
|
274
|
+
\`\`\`
|
|
275
|
+
`;
|
|
276
|
+
const PLAY_QUALITY = `
|
|
277
|
+
## Google Play Large Screen Quality Guidelines
|
|
278
|
+
|
|
279
|
+
**Source:** developer.android.com/docs/quality-guidelines/large-screen-app-quality
|
|
280
|
+
|
|
281
|
+
### Tier 1 — Large Screen Optimized (required for Featured placement)
|
|
282
|
+
- [ ] App functional in landscape orientation on tablet
|
|
283
|
+
- [ ] No hardcoded portrait-only orientation in manifest
|
|
284
|
+
- [ ] No distortion or pixel-scaling artifacts on large screen
|
|
285
|
+
- [ ] Correctly handles multi-window (split-screen) mode
|
|
286
|
+
- [ ] UI is not stretched/letter-boxed — uses full width on tablets
|
|
287
|
+
- [ ] \`resizeableActivity\` not set to \`false\`
|
|
288
|
+
|
|
289
|
+
### Tier 2 — Large Screen Ready
|
|
290
|
+
- [ ] NavigationSuiteScaffold or equivalent (Rail on medium, Drawer on expanded)
|
|
291
|
+
- [ ] Two-pane list-detail layout for list → detail flows on expanded
|
|
292
|
+
- [ ] No input devices missing (keyboard, mouse support for ChromeOS)
|
|
293
|
+
- [ ] Keyboard navigation works via Tab and arrow keys
|
|
294
|
+
- [ ] Predictive back gesture implemented
|
|
295
|
+
|
|
296
|
+
### Manifest requirements
|
|
297
|
+
\`\`\`xml
|
|
298
|
+
<manifest>
|
|
299
|
+
<!-- Support all screen sizes -->
|
|
300
|
+
<supports-screens
|
|
301
|
+
android:smallScreens="true"
|
|
302
|
+
android:normalScreens="true"
|
|
303
|
+
android:largeScreens="true"
|
|
304
|
+
android:xlargeScreens="true"
|
|
305
|
+
android:resizeable="true" />
|
|
306
|
+
|
|
307
|
+
<application>
|
|
308
|
+
<!-- CRITICAL: Do NOT set android:screenOrientation="portrait" for any Activity -->
|
|
309
|
+
<!-- CRITICAL: Do NOT set android:resizeableActivity="false" -->
|
|
310
|
+
<activity android:name=".MainActivity"
|
|
311
|
+
android:windowSoftInputMode="adjustResize" />
|
|
312
|
+
</application>
|
|
313
|
+
</manifest>
|
|
314
|
+
\`\`\`
|
|
315
|
+
|
|
316
|
+
**Checklist tool:** https://developer.android.com/docs/quality-guidelines/large-screen-app-quality
|
|
317
|
+
`;
|
|
318
|
+
// ── Topic routing ──────────────────────────────────────────────────────────────
|
|
319
|
+
const TOPICS = [
|
|
320
|
+
{ keywords: ["overview", "intro", "what is", "large screen", "adaptive", "why"], content: OVERVIEW },
|
|
321
|
+
{ keywords: ["windowsizeclass", "window size", "breakpoint", "compact", "medium", "expanded", "class"], content: WINDOW_SIZE_CLASS },
|
|
322
|
+
{ keywords: ["navigation", "rail", "drawer", "bottom nav", "navigationsuitescaffold", "adaptive nav"], content: NAVIGATION_PATTERNS },
|
|
323
|
+
{ keywords: ["two pane", "twopane", "list detail", "split", "listdetailpane", "detail pane"], content: TWO_PANE },
|
|
324
|
+
{ keywords: ["fold", "foldable", "hinge", "unfold", "half", "windowinfotracker", "foldingfeature"], content: FOLDABLES },
|
|
325
|
+
{ keywords: ["config", "continuity", "rotation", "orientation", "rememberSaveable", "configChanges", "survive", "persist"], content: CONTINUITY },
|
|
326
|
+
{ keywords: ["play", "quality", "tier", "checklist", "guideline", "requirement", "manifest", "optimize"], content: PLAY_QUALITY },
|
|
327
|
+
];
|
|
328
|
+
const INDEX = `
|
|
329
|
+
## Android Large Screen & Adaptive Layout Guide
|
|
330
|
+
|
|
331
|
+
**Query topics available:**
|
|
332
|
+
|
|
333
|
+
| Topic | Example query |
|
|
334
|
+
|-------|----|
|
|
335
|
+
| Overview & device classes | "large screen overview" |
|
|
336
|
+
| WindowSizeClass | "windowsizeclass breakpoints" |
|
|
337
|
+
| Adaptive navigation | "NavigationSuiteScaffold" |
|
|
338
|
+
| List-detail two-pane | "ListDetailPaneScaffold" |
|
|
339
|
+
| Foldables & hinge | "foldable hinge FoldingFeature" |
|
|
340
|
+
| Config change continuity | "survive rotation rememberSaveable" |
|
|
341
|
+
| Play Store quality bar | "Play quality checklist" |
|
|
342
|
+
|
|
343
|
+
**Official sources:**
|
|
344
|
+
- https://developer.android.com/guide/topics/large-screens
|
|
345
|
+
- https://developer.android.com/develop/ui/compose/layouts/adaptive
|
|
346
|
+
- https://developer.android.com/docs/quality-guidelines/large-screen-app-quality
|
|
347
|
+
`;
|
|
348
|
+
export async function androidLargeScreenGuide(topic) {
|
|
349
|
+
const trimmed = topic.trim();
|
|
350
|
+
if (!trimmed || trimmed.toLowerCase() === "list" || trimmed.toLowerCase() === "help") {
|
|
351
|
+
return INDEX;
|
|
352
|
+
}
|
|
353
|
+
const lower = trimmed.toLowerCase();
|
|
354
|
+
const found = TOPICS.find(t => t.keywords.some(k => lower.includes(k)));
|
|
355
|
+
if (found) {
|
|
356
|
+
return (found.content.trim() +
|
|
357
|
+
`\n\n---\n` +
|
|
358
|
+
`**Official docs:** https://developer.android.com/guide/topics/large-screens\n` +
|
|
359
|
+
`> 📐 GROUNDING GATE: Large screen code must use WindowSizeClass, NavigationSuiteScaffold, and ListDetailPaneScaffold — not hardcoded pixel values or orientation locks.`);
|
|
360
|
+
}
|
|
361
|
+
return (`## Large Screen: "${trimmed}"\n\n` +
|
|
362
|
+
`No built-in entry found. Check:\n` +
|
|
363
|
+
`- https://developer.android.com/guide/topics/large-screens\n` +
|
|
364
|
+
`- https://developer.android.com/develop/ui/compose/layouts/adaptive\n\n` +
|
|
365
|
+
INDEX);
|
|
366
|
+
}
|