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,447 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool 8 – material3_expressive
|
|
3
|
+
*
|
|
4
|
+
* Complete reference for Material 3 Expressive — Google's design system
|
|
5
|
+
* announced May 2025, GA on Android 16 / QPR1 September 2025.
|
|
6
|
+
*
|
|
7
|
+
* Covers: new components, MaterialExpressiveTheme, MotionScheme,
|
|
8
|
+
* MaterialShapes, shape morphing, typography updates, migration from M3,
|
|
9
|
+
* Wear OS M3 Expressive, and anti-patterns.
|
|
10
|
+
*
|
|
11
|
+
* All knowledge sourced from:
|
|
12
|
+
* - developer.android.com/develop/ui/compose/designsystems/material3
|
|
13
|
+
* - developer.android.com/jetpack/androidx/releases/compose-material3
|
|
14
|
+
* - android-developers.googleblog.com (Androidify I/O 2025 post)
|
|
15
|
+
* - m3.material.io/blog/building-with-m3-expressive
|
|
16
|
+
*/
|
|
17
|
+
import { secureFetch, extractPageText } from "../http.js";
|
|
18
|
+
// ── Knowledge base ───────────────────────────────────────────────────────────
|
|
19
|
+
const M3E_OVERVIEW = `
|
|
20
|
+
## Material 3 Expressive (M3E) — Full Reference
|
|
21
|
+
|
|
22
|
+
**Announced:** Google I/O, May 2025
|
|
23
|
+
**GA on device:** Android 16 QPR1, September 2025 (Pixel 6+, Pixel Tablet)
|
|
24
|
+
**Compose dependency:** \`androidx.compose.material3:material3:1.4.x\`
|
|
25
|
+
**Opt-in annotation:** \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`
|
|
26
|
+
|
|
27
|
+
M3 Expressive is NOT a new Material version ("Material 4").
|
|
28
|
+
It is an extension of Material You (M3 / Material Design 3) — same library,
|
|
29
|
+
new components and motion primitives layered on top.
|
|
30
|
+
|
|
31
|
+
### What's new vs plain M3
|
|
32
|
+
| Area | M3 | M3 Expressive |
|
|
33
|
+
|------|-----|--------------|
|
|
34
|
+
| Theme entry point | \`MaterialTheme\` | \`MaterialExpressiveTheme\` (wraps MaterialTheme + expressive MotionScheme by default) |
|
|
35
|
+
| Motion | Static tweens | Physics-based \`MotionScheme\` (standard vs expressive) |
|
|
36
|
+
| Shapes | 5-step scale | 35-shape \`MaterialShapes\` library + shape morphing |
|
|
37
|
+
| Typography | Fixed weights | Variable fonts (weight + width axes) |
|
|
38
|
+
| New components | — | ButtonGroup, FloatingToolbar, DockedToolbar, FlexibleBottomAppBar, LoadingIndicator, SplitButtonLayout, FABMenu, ShortNavigationBar, WideNavigationRail |
|
|
39
|
+
| Bottom bar | \`BottomAppBar\` | \`DockedToolbar\` (BottomAppBar deprecated in favour of it) |
|
|
40
|
+
`;
|
|
41
|
+
const THEME_SETUP = `
|
|
42
|
+
## Setting Up MaterialExpressiveTheme
|
|
43
|
+
|
|
44
|
+
### Gradle dependency
|
|
45
|
+
\`\`\`kotlin
|
|
46
|
+
// build.gradle.kts (app)
|
|
47
|
+
implementation("androidx.compose.material3:material3:1.4.0-beta01") // check latest
|
|
48
|
+
\`\`\`
|
|
49
|
+
|
|
50
|
+
### Theme wrapper
|
|
51
|
+
\`\`\`kotlin
|
|
52
|
+
// Source: android-developers.googleblog.com (Androidify, Google I/O 2025)
|
|
53
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
54
|
+
@Composable
|
|
55
|
+
fun AppTheme(content: @Composable () -> Unit) {
|
|
56
|
+
MaterialExpressiveTheme( // ← replaces MaterialTheme
|
|
57
|
+
colorScheme = dynamicColorScheme(LocalContext.current),
|
|
58
|
+
typography = Typography,
|
|
59
|
+
shapes = Shapes(),
|
|
60
|
+
motionScheme = MotionScheme.expressive(), // or .standard()
|
|
61
|
+
content = content
|
|
62
|
+
)
|
|
63
|
+
}
|
|
64
|
+
\`\`\`
|
|
65
|
+
|
|
66
|
+
### MotionScheme tokens — use in custom animations
|
|
67
|
+
\`\`\`kotlin
|
|
68
|
+
// Spatial (position/size/shape changes)
|
|
69
|
+
val spatialSpec = MaterialTheme.motionScheme.defaultSpatialSpec<Float>()
|
|
70
|
+
// Effects (opacity, color, blur)
|
|
71
|
+
val effectsSpec = MaterialTheme.motionScheme.defaultEffectsSpec<Float>()
|
|
72
|
+
|
|
73
|
+
// Example: animate a shape change with the themed spec
|
|
74
|
+
val animatedSpec = MaterialTheme.motionScheme.defaultSpatialSpec<Float>()
|
|
75
|
+
val animatedCorner by animateFloatAsState(
|
|
76
|
+
targetValue = if (selected) 50f else 12f,
|
|
77
|
+
animationSpec = animatedSpec
|
|
78
|
+
)
|
|
79
|
+
\`\`\`
|
|
80
|
+
|
|
81
|
+
> ✅ GROUNDING GATE: Use \`MotionScheme.expressive()\` for playful consumer apps,
|
|
82
|
+
> \`MotionScheme.standard()\` for productivity/enterprise apps.
|
|
83
|
+
`;
|
|
84
|
+
const NEW_COMPONENTS = `
|
|
85
|
+
## New & Updated M3 Expressive Components
|
|
86
|
+
|
|
87
|
+
### 1. ButtonGroup
|
|
88
|
+
Groups related buttons with fluid shape-morphing on selection.
|
|
89
|
+
Requires \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`.
|
|
90
|
+
|
|
91
|
+
\`\`\`kotlin
|
|
92
|
+
// Single-select ButtonGroup
|
|
93
|
+
// Source: proandroiddev.com M3 Expressive series + composables.com docs
|
|
94
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
95
|
+
@Composable
|
|
96
|
+
fun FilterButtonGroup() {
|
|
97
|
+
val options = listOf("All", "Unread", "Starred")
|
|
98
|
+
var selected by remember { mutableIntStateOf(0) }
|
|
99
|
+
|
|
100
|
+
ButtonGroup(overflowIndicator = {}) {
|
|
101
|
+
options.forEachIndexed { index, label ->
|
|
102
|
+
toggleableItem(
|
|
103
|
+
checked = selected == index,
|
|
104
|
+
onCheckedChange = { selected = index },
|
|
105
|
+
label = label
|
|
106
|
+
)
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// Multi-select with custom connected shapes
|
|
112
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
113
|
+
@Composable
|
|
114
|
+
fun MultiSelectButtonGroup() {
|
|
115
|
+
val checked = remember { mutableStateListOf(false, false, false) }
|
|
116
|
+
Row(horizontalArrangement = Arrangement.spacedBy(ButtonGroupDefaults.ConnectedSpaceBetween)) {
|
|
117
|
+
listOf("Work", "Home", "Café").forEachIndexed { i, label ->
|
|
118
|
+
ToggleButton(
|
|
119
|
+
checked = checked[i],
|
|
120
|
+
onCheckedChange = { checked[i] = it },
|
|
121
|
+
shapes = when (i) {
|
|
122
|
+
0 -> ButtonGroupDefaults.connectedLeadingButtonShapes()
|
|
123
|
+
2 -> ButtonGroupDefaults.connectedTrailingButtonShapes()
|
|
124
|
+
else -> ButtonGroupDefaults.connectedMiddleButtonShapes()
|
|
125
|
+
}
|
|
126
|
+
) { Text(label) }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
\`\`\`
|
|
131
|
+
|
|
132
|
+
### 2. HorizontalFloatingToolbar / VerticalFloatingToolbar
|
|
133
|
+
Replaces the old FAB + BottomAppBar pattern. Collapses on scroll.
|
|
134
|
+
|
|
135
|
+
\`\`\`kotlin
|
|
136
|
+
// Source: proandroiddev.com M3 Expressive Part 2
|
|
137
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
138
|
+
@Composable
|
|
139
|
+
fun MainScreen() {
|
|
140
|
+
var expanded by rememberSaveable { mutableStateOf(true) }
|
|
141
|
+
Scaffold { padding ->
|
|
142
|
+
Box(Modifier.fillMaxSize()) {
|
|
143
|
+
LazyColumn(
|
|
144
|
+
modifier = Modifier.floatingToolbarVerticalNestedScroll(
|
|
145
|
+
expanded = expanded,
|
|
146
|
+
onExpand = { expanded = true },
|
|
147
|
+
onCollapse = { expanded = false }
|
|
148
|
+
),
|
|
149
|
+
contentPadding = padding
|
|
150
|
+
) { /* items */ }
|
|
151
|
+
|
|
152
|
+
HorizontalFloatingToolbar(
|
|
153
|
+
modifier = Modifier.align(Alignment.BottomCenter),
|
|
154
|
+
expanded = expanded,
|
|
155
|
+
floatingActionButton = {
|
|
156
|
+
FloatingToolbarDefaults.VibrantFloatingActionButton(onClick = { /*...*/ }) {
|
|
157
|
+
Icon(Icons.Filled.Add, contentDescription = "Add")
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
colors = FloatingToolbarDefaults.vibrantFloatingToolbarColors()
|
|
161
|
+
) {
|
|
162
|
+
IconButton(onClick = { }) { Icon(Icons.Filled.Edit, "Edit") }
|
|
163
|
+
IconButton(onClick = { }) { Icon(Icons.Filled.Share, "Share") }
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
\`\`\`
|
|
169
|
+
|
|
170
|
+
### 3. DockedToolbar (replaces BottomAppBar)
|
|
171
|
+
\`\`\`kotlin
|
|
172
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
173
|
+
@Composable
|
|
174
|
+
fun AppScaffold() {
|
|
175
|
+
Scaffold(
|
|
176
|
+
bottomBar = {
|
|
177
|
+
DockedToolbar { // ← use this, not BottomAppBar
|
|
178
|
+
IconButton(onClick = {}) { Icon(Icons.Filled.Home, "Home") }
|
|
179
|
+
IconButton(onClick = {}) { Icon(Icons.Filled.Search, "Search") }
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
) { /* content */ }
|
|
183
|
+
}
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
### 4. LoadingIndicator (replaces CircularProgressIndicator for indeterminate)
|
|
187
|
+
\`\`\`kotlin
|
|
188
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
189
|
+
@Composable
|
|
190
|
+
fun LoadingScreen() {
|
|
191
|
+
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
|
192
|
+
LoadingIndicator() // animated wavy M3E indicator
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
\`\`\`
|
|
196
|
+
|
|
197
|
+
### 5. SplitButtonLayout
|
|
198
|
+
\`\`\`kotlin
|
|
199
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
200
|
+
@Composable
|
|
201
|
+
fun SaveSplitButton() {
|
|
202
|
+
SplitButtonLayout(
|
|
203
|
+
leadingButton = {
|
|
204
|
+
SplitButtonDefaults.LeadingButton(onClick = { /* primary action */ }) {
|
|
205
|
+
Text("Save")
|
|
206
|
+
}
|
|
207
|
+
},
|
|
208
|
+
trailingButton = {
|
|
209
|
+
SplitButtonDefaults.TrailingButton(onClick = { /* show dropdown */ }) {
|
|
210
|
+
Icon(Icons.Filled.ArrowDropDown, "More options")
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
)
|
|
214
|
+
}
|
|
215
|
+
\`\`\`
|
|
216
|
+
|
|
217
|
+
### 6. FloatingActionButtonMenu (FABMenu)
|
|
218
|
+
\`\`\`kotlin
|
|
219
|
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
|
220
|
+
@Composable
|
|
221
|
+
fun SpeedDialFab() {
|
|
222
|
+
var expanded by rememberSaveable { mutableStateOf(false) }
|
|
223
|
+
FloatingActionButtonMenu(
|
|
224
|
+
expanded = expanded,
|
|
225
|
+
button = {
|
|
226
|
+
ToggleFloatingActionButton(
|
|
227
|
+
checked = expanded,
|
|
228
|
+
onCheckedChange = { expanded = it }
|
|
229
|
+
) {
|
|
230
|
+
Icon(
|
|
231
|
+
if (expanded) Icons.Filled.Close else Icons.Filled.Add,
|
|
232
|
+
contentDescription = "Menu"
|
|
233
|
+
)
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
) {
|
|
237
|
+
FloatingActionButtonMenuItem(onClick = {}, icon = { Icon(Icons.Filled.Edit, null) }, text = { Text("Edit") })
|
|
238
|
+
FloatingActionButtonMenuItem(onClick = {}, icon = { Icon(Icons.Filled.Share, null) }, text = { Text("Share") })
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
\`\`\`
|
|
242
|
+
`;
|
|
243
|
+
const MATERIALSHAPES = `
|
|
244
|
+
## MaterialShapes — 35 Preset Morphable Shapes
|
|
245
|
+
|
|
246
|
+
M3 Expressive ships a \`MaterialShapes\` object with 35 named shapes
|
|
247
|
+
designed for smooth morphing between each other.
|
|
248
|
+
|
|
249
|
+
\`\`\`kotlin
|
|
250
|
+
// Source: android-developers.googleblog.com (Androidify I/O 2025)
|
|
251
|
+
import androidx.compose.material3.MaterialShapes
|
|
252
|
+
|
|
253
|
+
// Preset shapes (selection):
|
|
254
|
+
// MaterialShapes.Circle
|
|
255
|
+
// MaterialShapes.Square
|
|
256
|
+
// MaterialShapes.Pill
|
|
257
|
+
// MaterialShapes.Cookie4Sided
|
|
258
|
+
// MaterialShapes.Cookie6Sided
|
|
259
|
+
// MaterialShapes.Cookie9Sided ← used in Androidify
|
|
260
|
+
// MaterialShapes.Clover4Leaf
|
|
261
|
+
// MaterialShapes.Flower
|
|
262
|
+
// MaterialShapes.Burst8
|
|
263
|
+
// MaterialShapes.Slanted ...and more
|
|
264
|
+
|
|
265
|
+
// Convert to Compose Shape for clipping:
|
|
266
|
+
Box(
|
|
267
|
+
Modifier
|
|
268
|
+
.size(96.dp)
|
|
269
|
+
.clip(MaterialShapes.Cookie9Sided.toShape())
|
|
270
|
+
.background(MaterialTheme.colorScheme.primaryContainer)
|
|
271
|
+
)
|
|
272
|
+
|
|
273
|
+
// Morphing between shapes with animation:
|
|
274
|
+
val morph = remember { Morph(MaterialShapes.Circle, MaterialShapes.Square) }
|
|
275
|
+
val progress by animateFloatAsState(if (selected) 1f else 0f,
|
|
276
|
+
animationSpec = MaterialTheme.motionScheme.defaultSpatialSpec())
|
|
277
|
+
Canvas(Modifier.size(64.dp)) {
|
|
278
|
+
drawPath(morph.toPath(progress, size), color = primaryColor)
|
|
279
|
+
}
|
|
280
|
+
\`\`\`
|
|
281
|
+
`;
|
|
282
|
+
const TYPOGRAPHY = `
|
|
283
|
+
## M3 Expressive Typography — Variable Fonts
|
|
284
|
+
|
|
285
|
+
M3 Expressive uses variable fonts with weight and width axes.
|
|
286
|
+
The type scale is unchanged (displayLarge → labelSmall) but
|
|
287
|
+
weights and sizes are more expressive.
|
|
288
|
+
|
|
289
|
+
\`\`\`kotlin
|
|
290
|
+
// Custom typography with variable font weight
|
|
291
|
+
val Typography = Typography(
|
|
292
|
+
headlineLarge = TextStyle(
|
|
293
|
+
fontFamily = FontFamily(Font(R.font.roboto_flex)), // variable font
|
|
294
|
+
fontVariationSettings = FontVariationSettings(
|
|
295
|
+
FontVariation.weight(700f),
|
|
296
|
+
FontVariation.width(100f)
|
|
297
|
+
),
|
|
298
|
+
fontSize = 32.sp,
|
|
299
|
+
lineHeight = 40.sp
|
|
300
|
+
)
|
|
301
|
+
// ... other styles
|
|
302
|
+
)
|
|
303
|
+
|
|
304
|
+
// In Compose — animated font weight (M3E pattern):
|
|
305
|
+
val fontWeight by animateFloatAsState(
|
|
306
|
+
if (isActive) 700f else 400f,
|
|
307
|
+
animationSpec = MaterialTheme.motionScheme.defaultEffectsSpec()
|
|
308
|
+
)
|
|
309
|
+
Text(
|
|
310
|
+
text = label,
|
|
311
|
+
fontVariationSettings = FontVariationSettings(FontVariation.weight(fontWeight))
|
|
312
|
+
)
|
|
313
|
+
\`\`\`
|
|
314
|
+
`;
|
|
315
|
+
const ANTI_PATTERNS = `
|
|
316
|
+
## M3 Expressive Anti-Patterns
|
|
317
|
+
|
|
318
|
+
| ❌ Don't | ✅ Do Instead |
|
|
319
|
+
|---------|-------------|
|
|
320
|
+
| Use \`BottomAppBar\` for new screens | Use \`DockedToolbar\` |
|
|
321
|
+
| Mix \`MaterialTheme\` + expressive components | Wrap with \`MaterialExpressiveTheme\` |
|
|
322
|
+
| Use \`CircularProgressIndicator\` (indeterminate) where wavy fits | Use \`LoadingIndicator\` |
|
|
323
|
+
| Use \`SegmentedButton\` for filter pills | Use \`ButtonGroup\` with \`toggleableItem\` |
|
|
324
|
+
| Hardcode animation specs (\`spring()\`, \`tween()\`) | Pull from \`MaterialTheme.motionScheme.defaultSpatialSpec()\` |
|
|
325
|
+
| Use FAB + BottomAppBar combo | Use \`HorizontalFloatingToolbar\` with FAB slot |
|
|
326
|
+
| Skip \`@OptIn\` annotation | Always add \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\` |
|
|
327
|
+
|
|
328
|
+
### BottomAppBar migration
|
|
329
|
+
\`\`\`kotlin
|
|
330
|
+
// ❌ Old — still works but deprecated in M3E context
|
|
331
|
+
Scaffold(bottomBar = {
|
|
332
|
+
BottomAppBar { /* actions */ }
|
|
333
|
+
})
|
|
334
|
+
|
|
335
|
+
// ✅ New
|
|
336
|
+
Scaffold(bottomBar = {
|
|
337
|
+
DockedToolbar { /* actions */ }
|
|
338
|
+
})
|
|
339
|
+
\`\`\`
|
|
340
|
+
`;
|
|
341
|
+
const WEAR_M3E = `
|
|
342
|
+
## Wear OS — Material 3 Expressive
|
|
343
|
+
|
|
344
|
+
\`\`\`kotlin
|
|
345
|
+
// Wear Compose M3 dependency (source: developer.android.com/jetpack/androidx/releases/wear-compose-m3)
|
|
346
|
+
implementation("androidx.wear.compose:compose-material3:1.0.0-beta01")
|
|
347
|
+
\`\`\`
|
|
348
|
+
|
|
349
|
+
### Key Wear M3E components
|
|
350
|
+
- **EdgeButton** — hugs the bottom edge of the round screen
|
|
351
|
+
- **ButtonGroup** — shape-morphs on touch, designed for round displays
|
|
352
|
+
- **TransformingLazyColumn** — built-in scroll animations that trace display curves
|
|
353
|
+
- **ConfirmationDialog** — success/failure/open-on-phone variants with timeout
|
|
354
|
+
- **Shape morphing** — IconButton, TextButton, IconToggleButton animate on press
|
|
355
|
+
- **Dynamic Color** — theme adapts to the user's chosen watch face
|
|
356
|
+
|
|
357
|
+
\`\`\`kotlin
|
|
358
|
+
// MotionScheme for Wear
|
|
359
|
+
val motionScheme = MotionScheme.expressive // companion object (Wear API)
|
|
360
|
+
// or
|
|
361
|
+
val motionScheme = MotionScheme.standard
|
|
362
|
+
// Set via MaterialTheme(motionScheme = ...) on Wear
|
|
363
|
+
\`\`\`
|
|
364
|
+
`;
|
|
365
|
+
const MIGRATION = `
|
|
366
|
+
## Migrating from M3 to M3 Expressive
|
|
367
|
+
|
|
368
|
+
1. **Update dependency** to \`material3:1.4.x\`
|
|
369
|
+
2. **Replace \`MaterialTheme\`** with \`MaterialExpressiveTheme\`
|
|
370
|
+
3. **Replace \`BottomAppBar\`** with \`DockedToolbar\`
|
|
371
|
+
4. **Replace FAB + BottomAppBar combos** with \`HorizontalFloatingToolbar\`
|
|
372
|
+
5. **Replace \`SegmentedButton\`** filter rows with \`ButtonGroup\`
|
|
373
|
+
6. **Replace \`CircularProgressIndicator\` (indeterminate)** with \`LoadingIndicator\`
|
|
374
|
+
7. **Replace hardcoded animation specs** with \`MaterialTheme.motionScheme.*\`
|
|
375
|
+
8. **Add \`@OptIn\`** annotations to all files using new components
|
|
376
|
+
|
|
377
|
+
> 📚 Official migration guide: https://developer.android.com/develop/ui/compose/designsystems/material3
|
|
378
|
+
`;
|
|
379
|
+
const TOPICS = [
|
|
380
|
+
{ keywords: ["overview", "what is", "intro", "m3e", "expressive"], content: M3E_OVERVIEW },
|
|
381
|
+
{ keywords: ["theme", "setup", "motionscheme", "materialexpressivetheme", "getting started", "dependency", "gradle"], content: THEME_SETUP },
|
|
382
|
+
{ keywords: ["component", "buttongroup", "floatingtoolbar", "dockedtoolbar", "loadingindicator", "splitbutton", "fabmenu", "floatingactionbutton", "new"], content: NEW_COMPONENTS },
|
|
383
|
+
{ keywords: ["shape", "materialshapes", "morph", "morphing", "cookie", "squircle", "35"], content: MATERIALSHAPES },
|
|
384
|
+
{ keywords: ["typography", "font", "variable", "weight", "width", "type"], content: TYPOGRAPHY },
|
|
385
|
+
{ keywords: ["anti-pattern", "antipattern", "wrong", "avoid", "don't", "migrate", "migration", "from m3"], content: ANTI_PATTERNS + "\n\n" + MIGRATION },
|
|
386
|
+
{ keywords: ["wear", "wearos", "watch", "pixel watch", "edgebutton", "transforminglazy"], content: WEAR_M3E },
|
|
387
|
+
];
|
|
388
|
+
function findTopic(query) {
|
|
389
|
+
const lower = query.toLowerCase();
|
|
390
|
+
for (const topic of TOPICS) {
|
|
391
|
+
if (topic.keywords.some(k => lower.includes(k)))
|
|
392
|
+
return topic.content;
|
|
393
|
+
}
|
|
394
|
+
return null;
|
|
395
|
+
}
|
|
396
|
+
const INDEX = `
|
|
397
|
+
## AndroJack — Material 3 Expressive Reference
|
|
398
|
+
|
|
399
|
+
**Query topics available:**
|
|
400
|
+
|
|
401
|
+
| Topic | Example query |
|
|
402
|
+
|-------|--------------|
|
|
403
|
+
| Overview & what's new | "m3 expressive overview" |
|
|
404
|
+
| Theme setup & MotionScheme | "MaterialExpressiveTheme setup" |
|
|
405
|
+
| New components | "ButtonGroup", "FloatingToolbar", "LoadingIndicator" |
|
|
406
|
+
| MaterialShapes & morphing | "shape morphing", "MaterialShapes" |
|
|
407
|
+
| Typography / variable fonts | "expressive typography" |
|
|
408
|
+
| Anti-patterns & migration | "migrate from M3", "anti-pattern" |
|
|
409
|
+
| Wear OS M3 Expressive | "wear expressive", "EdgeButton" |
|
|
410
|
+
|
|
411
|
+
**Official sources:**
|
|
412
|
+
- https://developer.android.com/develop/ui/compose/designsystems/material3
|
|
413
|
+
- https://m3.material.io/blog/building-with-m3-expressive
|
|
414
|
+
- https://developer.android.com/jetpack/androidx/releases/compose-material3
|
|
415
|
+
`;
|
|
416
|
+
// ── Main handler ─────────────────────────────────────────────────────────────
|
|
417
|
+
export async function material3Expressive(topic) {
|
|
418
|
+
const trimmed = topic.trim();
|
|
419
|
+
if (!trimmed || trimmed.toLowerCase() === "list" || trimmed.toLowerCase() === "help") {
|
|
420
|
+
return INDEX;
|
|
421
|
+
}
|
|
422
|
+
// Try local knowledge first (instant, no network)
|
|
423
|
+
const local = findTopic(trimmed);
|
|
424
|
+
if (local) {
|
|
425
|
+
return (local.trim() +
|
|
426
|
+
`\n\n---\n` +
|
|
427
|
+
`**Official docs:** https://developer.android.com/develop/ui/compose/designsystems/material3\n` +
|
|
428
|
+
`> 🎨 GROUNDING GATE: All M3 Expressive code must use \`MaterialExpressiveTheme\` and \`@OptIn(ExperimentalMaterial3ExpressiveApi::class)\`.`);
|
|
429
|
+
}
|
|
430
|
+
// Fallback: live fetch from official source
|
|
431
|
+
const url = `https://developer.android.com/s/results?q=${encodeURIComponent("material 3 expressive " + trimmed)}`;
|
|
432
|
+
try {
|
|
433
|
+
const html = await secureFetch(url);
|
|
434
|
+
const text = extractPageText(html, 2000);
|
|
435
|
+
return (`## M3 Expressive: "${trimmed}"\n\n` +
|
|
436
|
+
`No built-in entry found. Live results from developer.android.com:\n\n` +
|
|
437
|
+
text +
|
|
438
|
+
`\n\n**Search URL:** ${url}` +
|
|
439
|
+
`\n\n> 🎨 Verify against https://developer.android.com/develop/ui/compose/designsystems/material3`);
|
|
440
|
+
}
|
|
441
|
+
catch {
|
|
442
|
+
return (`## M3 Expressive: "${trimmed}"\n\n` +
|
|
443
|
+
`No built-in entry. Search manually:\n` +
|
|
444
|
+
`- https://developer.android.com/develop/ui/compose/designsystems/material3\n` +
|
|
445
|
+
`- https://m3.material.io/blog/building-with-m3-expressive`);
|
|
446
|
+
}
|
|
447
|
+
}
|