@fudiandong/trellis-android-tv 1.0.0 → 1.0.8
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/agents/TvTouchAgent/agent.yaml +17 -0
- package/agents/TvTouchAgent/prompt.md +13 -0
- package/package.json +25 -10
- package/skills/code_review_tv/SKILL.md +7 -0
- package/skills/layout_adapt/SKILL.md +9 -0
- package/skills/touch_compat/SKILL.md +10 -0
- package/skills/trellis-before-dev/SKILL.md +34 -0
- package/skills/trellis-brainstorm/SKILL.md +548 -0
- package/skills/trellis-break-loop/SKILL.md +130 -0
- package/skills/trellis-check/SKILL.md +92 -0
- package/skills/trellis-meta/SKILL.md +73 -0
- package/skills/trellis-meta/references/customize-local/add-project-local-conventions.md +83 -0
- package/skills/trellis-meta/references/customize-local/change-agents.md +54 -0
- package/skills/trellis-meta/references/customize-local/change-context-loading.md +81 -0
- package/skills/trellis-meta/references/customize-local/change-hooks.md +57 -0
- package/skills/trellis-meta/references/customize-local/change-skills-or-commands.md +78 -0
- package/skills/trellis-meta/references/customize-local/change-spec-structure.md +83 -0
- package/skills/trellis-meta/references/customize-local/change-task-lifecycle.md +90 -0
- package/skills/trellis-meta/references/customize-local/change-workflow.md +64 -0
- package/skills/trellis-meta/references/customize-local/overview.md +55 -0
- package/skills/trellis-meta/references/local-architecture/context-injection.md +68 -0
- package/skills/trellis-meta/references/local-architecture/generated-files.md +80 -0
- package/skills/trellis-meta/references/local-architecture/overview.md +51 -0
- package/skills/trellis-meta/references/local-architecture/spec-system.md +102 -0
- package/skills/trellis-meta/references/local-architecture/task-system.md +101 -0
- package/skills/trellis-meta/references/local-architecture/workflow.md +75 -0
- package/skills/trellis-meta/references/local-architecture/workspace-memory.md +71 -0
- package/skills/trellis-meta/references/platform-files/agents.md +79 -0
- package/skills/trellis-meta/references/platform-files/hooks-and-settings.md +69 -0
- package/skills/trellis-meta/references/platform-files/overview.md +59 -0
- package/skills/trellis-meta/references/platform-files/platform-map.md +74 -0
- package/skills/trellis-meta/references/platform-files/skills-and-commands.md +83 -0
- package/skills/trellis-spec-bootstarp/SKILL.md +41 -0
- package/skills/trellis-spec-bootstarp/references/mcp-setup.md +90 -0
- package/skills/trellis-spec-bootstarp/references/repository-analysis.md +59 -0
- package/skills/trellis-spec-bootstarp/references/spec-task-planning.md +61 -0
- package/skills/trellis-spec-bootstarp/references/spec-writing.md +70 -0
- package/skills/trellis-update-spec/SKILL.md +356 -0
- package/skills/tv_focus_check/SKILL.md +10 -0
- package/spec/android_tv_base.md +17 -0
- package/spec/dpad_focus_rule.md +89 -0
- package/spec/guides/code-reuse-thinking-guide.md +105 -0
- package/spec/guides/cross-layer-thinking-guide.md +162 -0
- package/spec/guides/index.md +79 -0
- package/spec/mvvm_rule.md +100 -0
- package/spec/res_resource.md +111 -0
- package/spec/tv_touch_layout.md +31 -0
- package/workflow.md +10 -0
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
# D-pad 焦点导航规范
|
|
2
|
+
|
|
3
|
+
## 核心原则
|
|
4
|
+
|
|
5
|
+
所有 TV UI 必须实现**闭环焦点导航**,确保用户按任意方向键时焦点始终在可控范围内。
|
|
6
|
+
|
|
7
|
+
## 焦点配置
|
|
8
|
+
|
|
9
|
+
### 1. 基础配置
|
|
10
|
+
```xml
|
|
11
|
+
<!-- 焦点控件必须设置 -->
|
|
12
|
+
android:focusable="true"
|
|
13
|
+
android:focusableInTouchMode="true"
|
|
14
|
+
```
|
|
15
|
+
|
|
16
|
+
### 2. 首次焦点
|
|
17
|
+
```xml
|
|
18
|
+
<!-- 页面默认焦点:根布局设置 -->
|
|
19
|
+
android:defaultFocusHighlightEnabled="true"
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## 方向导航
|
|
23
|
+
|
|
24
|
+
### 3. nextFocusDown/Up/Left/Right
|
|
25
|
+
```xml
|
|
26
|
+
<!-- 明确指定各方向的下一个焦点控件 -->
|
|
27
|
+
android:nextFocusDown="@+id/btn_confirm"
|
|
28
|
+
android:nextFocusUp="@+id/btn_cancel"
|
|
29
|
+
android:nextFocusLeft="@+id/btn_prev"
|
|
30
|
+
android:nextFocusRight="@+id/btn_next"
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
### 4. 禁止焦点越界
|
|
34
|
+
```xml
|
|
35
|
+
<!-- 边缘控件必须指定环绕方向 -->
|
|
36
|
+
<Button
|
|
37
|
+
android:id="@+id/btn_first"
|
|
38
|
+
android:nextFocusLeft="@+id/btn_last" /> <!-- 左边界环绕到最后一个 -->
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 焦点丢失处理
|
|
42
|
+
|
|
43
|
+
### 5. ViewGroup 焦点容器
|
|
44
|
+
```xml
|
|
45
|
+
<!-- 动态区域使用 FocusableRelativeLayout 包裹 -->
|
|
46
|
+
<FocusableRelativeLayout
|
|
47
|
+
android:focusable="true"
|
|
48
|
+
android:focusableInTouchMode="true">
|
|
49
|
+
<!-- 子控件 -->
|
|
50
|
+
</FocusableRelativeLayout>
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### 6. 焦点状态监听
|
|
54
|
+
```java
|
|
55
|
+
// 监听焦点变化,处理边界情况
|
|
56
|
+
view.setOnFocusChangeListener((v, hasFocus) -> {
|
|
57
|
+
if (hasFocus) {
|
|
58
|
+
// 焦点进入:放大/高亮效果
|
|
59
|
+
}
|
|
60
|
+
});
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## 常见错误
|
|
64
|
+
|
|
65
|
+
### ❌ 禁止
|
|
66
|
+
- 动态添加 View 后未设置 nextFocus 属性
|
|
67
|
+
- Recyclerview item 内嵌套可聚焦控件未指定导航
|
|
68
|
+
- 使用.requestFocus() 而不处理焦点状态
|
|
69
|
+
|
|
70
|
+
### ✅ 正确
|
|
71
|
+
- 静态布局完整配置 nextFocus 四方向
|
|
72
|
+
- 动态区域使用 FocusChangeListener 手动处理
|
|
73
|
+
- 边缘控件使用 `nextFocusUp/Down/Left/Right` 形成闭环
|
|
74
|
+
|
|
75
|
+
## 触摸兼容
|
|
76
|
+
|
|
77
|
+
### 7. 触摸点击
|
|
78
|
+
```java
|
|
79
|
+
// 触摸模式下支持点击
|
|
80
|
+
if (isInTouchMode()) {
|
|
81
|
+
performClick();
|
|
82
|
+
}
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 8. 触摸模式焦点
|
|
86
|
+
```xml
|
|
87
|
+
<!-- 触屏设备需要 -->
|
|
88
|
+
android:focusableInTouchMode="true"
|
|
89
|
+
```
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# Code Reuse Thinking Guide
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Stop and think before creating new code - does it already exist?
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Problem
|
|
8
|
+
|
|
9
|
+
**Duplicated code is the #1 source of inconsistency bugs.**
|
|
10
|
+
|
|
11
|
+
When you copy-paste or rewrite existing logic:
|
|
12
|
+
- Bug fixes don't propagate
|
|
13
|
+
- Behavior diverges over time
|
|
14
|
+
- Codebase becomes harder to understand
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Before Writing New Code
|
|
19
|
+
|
|
20
|
+
### Step 1: Search First
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
# Search for similar function names
|
|
24
|
+
grep -r "functionName" .
|
|
25
|
+
|
|
26
|
+
# Search for similar logic
|
|
27
|
+
grep -r "keyword" .
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
### Step 2: Ask These Questions
|
|
31
|
+
|
|
32
|
+
| Question | If Yes... |
|
|
33
|
+
|----------|-----------|
|
|
34
|
+
| Does a similar function exist? | Use or extend it |
|
|
35
|
+
| Is this pattern used elsewhere? | Follow the existing pattern |
|
|
36
|
+
| Could this be a shared utility? | Create it in the right place |
|
|
37
|
+
| Am I copying code from another file? | **STOP** - extract to shared |
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## Common Duplication Patterns
|
|
42
|
+
|
|
43
|
+
### Pattern 1: Copy-Paste Functions
|
|
44
|
+
|
|
45
|
+
**Bad**: Copying a validation function to another file
|
|
46
|
+
|
|
47
|
+
**Good**: Extract to shared utilities, import where needed
|
|
48
|
+
|
|
49
|
+
### Pattern 2: Similar Components
|
|
50
|
+
|
|
51
|
+
**Bad**: Creating a new component that's 80% similar to existing
|
|
52
|
+
|
|
53
|
+
**Good**: Extend existing component with props/variants
|
|
54
|
+
|
|
55
|
+
### Pattern 3: Repeated Constants
|
|
56
|
+
|
|
57
|
+
**Bad**: Defining the same constant in multiple files
|
|
58
|
+
|
|
59
|
+
**Good**: Single source of truth, import everywhere
|
|
60
|
+
|
|
61
|
+
---
|
|
62
|
+
|
|
63
|
+
## When to Abstract
|
|
64
|
+
|
|
65
|
+
**Abstract when**:
|
|
66
|
+
- Same code appears 3+ times
|
|
67
|
+
- Logic is complex enough to have bugs
|
|
68
|
+
- Multiple people might need this
|
|
69
|
+
|
|
70
|
+
**Don't abstract when**:
|
|
71
|
+
- Only used once
|
|
72
|
+
- Trivial one-liner
|
|
73
|
+
- Abstraction would be more complex than duplication
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
77
|
+
## After Batch Modifications
|
|
78
|
+
|
|
79
|
+
When you've made similar changes to multiple files:
|
|
80
|
+
|
|
81
|
+
1. **Review**: Did you catch all instances?
|
|
82
|
+
2. **Search**: Run grep to find any missed
|
|
83
|
+
3. **Consider**: Should this be abstracted?
|
|
84
|
+
|
|
85
|
+
---
|
|
86
|
+
|
|
87
|
+
## Gotcha: Asymmetric Mechanisms Producing Same Output
|
|
88
|
+
|
|
89
|
+
**Problem**: When two different mechanisms must produce the same file set (e.g., recursive directory copy for init vs. manual `files.set()` for update), structural changes (renaming, moving, adding subdirectories) only propagate through the automatic mechanism. The manual one silently drifts.
|
|
90
|
+
|
|
91
|
+
**Symptom**: Init works perfectly, but update creates files at wrong paths or misses files entirely.
|
|
92
|
+
|
|
93
|
+
**Prevention checklist**:
|
|
94
|
+
- [ ] When migrating directory structures, search for ALL code paths that reference the old structure
|
|
95
|
+
- [ ] If one path is auto-derived (glob/copy) and another is manually listed, the manual one needs updating
|
|
96
|
+
- [ ] Add a regression test that compares outputs from both mechanisms
|
|
97
|
+
|
|
98
|
+
---
|
|
99
|
+
|
|
100
|
+
## Checklist Before Commit
|
|
101
|
+
|
|
102
|
+
- [ ] Searched for existing similar code
|
|
103
|
+
- [ ] No copy-pasted logic that should be shared
|
|
104
|
+
- [ ] Constants defined in one place
|
|
105
|
+
- [ ] Similar patterns follow same structure
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
# Cross-Layer Thinking Guide
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Think through data flow across layers before implementing.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## The Problem
|
|
8
|
+
|
|
9
|
+
**Most bugs happen at layer boundaries**, not within layers.
|
|
10
|
+
|
|
11
|
+
Common cross-layer bugs:
|
|
12
|
+
- API returns format A, frontend expects format B
|
|
13
|
+
- Database stores X, service transforms to Y, but loses data
|
|
14
|
+
- Multiple layers implement the same logic differently
|
|
15
|
+
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
## Before Implementing Cross-Layer Features
|
|
19
|
+
|
|
20
|
+
### Step 1: Map the Data Flow
|
|
21
|
+
|
|
22
|
+
Draw out how data moves:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
Source → Transform → Store → Retrieve → Transform → Display
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
For each arrow, ask:
|
|
29
|
+
- What format is the data in?
|
|
30
|
+
- What could go wrong?
|
|
31
|
+
- Who is responsible for validation?
|
|
32
|
+
|
|
33
|
+
### Step 2: Identify Boundaries
|
|
34
|
+
|
|
35
|
+
| Boundary | Common Issues |
|
|
36
|
+
|----------|---------------|
|
|
37
|
+
| API ↔ Service | Type mismatches, missing fields |
|
|
38
|
+
| Service ↔ Database | Format conversions, null handling |
|
|
39
|
+
| Backend ↔ Frontend | Serialization, date formats |
|
|
40
|
+
| Component ↔ Component | Props shape changes |
|
|
41
|
+
|
|
42
|
+
### Step 3: Define Contracts
|
|
43
|
+
|
|
44
|
+
For each boundary:
|
|
45
|
+
- What is the exact input format?
|
|
46
|
+
- What is the exact output format?
|
|
47
|
+
- What errors can occur?
|
|
48
|
+
|
|
49
|
+
---
|
|
50
|
+
|
|
51
|
+
## Common Cross-Layer Mistakes
|
|
52
|
+
|
|
53
|
+
### Mistake 1: Implicit Format Assumptions
|
|
54
|
+
|
|
55
|
+
**Bad**: Assuming date format without checking
|
|
56
|
+
|
|
57
|
+
**Good**: Explicit format conversion at boundaries
|
|
58
|
+
|
|
59
|
+
### Mistake 2: Scattered Validation
|
|
60
|
+
|
|
61
|
+
**Bad**: Validating the same thing in multiple layers
|
|
62
|
+
|
|
63
|
+
**Good**: Validate once at the entry point
|
|
64
|
+
|
|
65
|
+
### Mistake 3: Leaky Abstractions
|
|
66
|
+
|
|
67
|
+
**Bad**: Component knows about database schema
|
|
68
|
+
|
|
69
|
+
**Good**: Each layer only knows its neighbors
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Checklist for Cross-Layer Features
|
|
74
|
+
|
|
75
|
+
Before implementation:
|
|
76
|
+
- [ ] Mapped the complete data flow
|
|
77
|
+
- [ ] Identified all layer boundaries
|
|
78
|
+
- [ ] Defined format at each boundary
|
|
79
|
+
- [ ] Decided where validation happens
|
|
80
|
+
|
|
81
|
+
After implementation:
|
|
82
|
+
- [ ] Tested with edge cases (null, empty, invalid)
|
|
83
|
+
- [ ] Verified error handling at each boundary
|
|
84
|
+
- [ ] Checked data survives round-trip
|
|
85
|
+
|
|
86
|
+
---
|
|
87
|
+
|
|
88
|
+
## Cross-Platform Template Consistency
|
|
89
|
+
|
|
90
|
+
In Trellis, command templates (e.g., `record-session.md`) exist in **multiple platforms** with identical or near-identical content. This is a cross-layer boundary.
|
|
91
|
+
|
|
92
|
+
### Checklist: After Modifying Any Command Template
|
|
93
|
+
|
|
94
|
+
- [ ] Find all platforms with the same command: `find src/templates/*/commands/trellis/ -name "<command>.*"`
|
|
95
|
+
- [ ] Update all platform copies (Markdown `.md` and TOML `.toml`)
|
|
96
|
+
- [ ] For Gemini TOML: adapt line continuations (`\\` vs `\`) and triple-quoted strings
|
|
97
|
+
- [ ] Run `/trellis:check-cross-layer` to verify nothing was missed
|
|
98
|
+
|
|
99
|
+
**Real-world example**: Updated `record-session.md` in Claude to use `--mode record`, but forgot iFlow, Kilo, OpenCode, and Gemini — caught by cross-layer check.
|
|
100
|
+
|
|
101
|
+
---
|
|
102
|
+
|
|
103
|
+
## Generated Runtime Template Upgrade Consistency
|
|
104
|
+
|
|
105
|
+
Some generated files are both documentation and runtime input. In Trellis,
|
|
106
|
+
`.trellis/workflow.md` is parsed by `get_context.py`, `workflow_phase.py`,
|
|
107
|
+
SessionStart filters, and per-turn hooks. Template changes must be validated
|
|
108
|
+
against both fresh init and upgrade paths.
|
|
109
|
+
|
|
110
|
+
### Checklist: After Modifying A Runtime-Parsed Template
|
|
111
|
+
|
|
112
|
+
- [ ] Identify every runtime parser that reads the template, not just the file
|
|
113
|
+
writer that installs it
|
|
114
|
+
- [ ] Check whether relevant syntax lives outside obvious managed regions
|
|
115
|
+
such as tag blocks
|
|
116
|
+
- [ ] Verify fresh `init` output and a versioned `update` scenario that writes
|
|
117
|
+
the older `.trellis/.version`
|
|
118
|
+
- [ ] Add an upgrade regression using an older pristine template fixture, then
|
|
119
|
+
assert the installed file reaches the current packaged shape
|
|
120
|
+
- [ ] Update the backend spec that owns the runtime contract
|
|
121
|
+
|
|
122
|
+
**Real-world example**: Codex inline mode changed workflow platform markers from
|
|
123
|
+
`[Codex]` / `[Kilo, Antigravity, Windsurf]` to `[codex-sub-agent]` /
|
|
124
|
+
`[codex-inline, Kilo, Antigravity, Windsurf]`. Fresh init was correct, but
|
|
125
|
+
`trellis update` only merged `[workflow-state:*]` blocks and preserved stale
|
|
126
|
+
markers outside those blocks. Result: upgraded projects got new hook scripts
|
|
127
|
+
but old workflow routing, so `get_context.py --mode phase --platform codex`
|
|
128
|
+
could return empty Phase 2.1 detail.
|
|
129
|
+
|
|
130
|
+
---
|
|
131
|
+
|
|
132
|
+
## Mode-Detection Probe Checklist
|
|
133
|
+
|
|
134
|
+
When a CLI auto-detects a mode by probing a remote resource (e.g., checking if `index.json` exists to decide marketplace vs direct download):
|
|
135
|
+
|
|
136
|
+
### Before implementing:
|
|
137
|
+
- [ ] Probe runs in **ALL** code paths that use the result (interactive, `-y`, `--flag` combos)
|
|
138
|
+
- [ ] 404 vs transient error are distinguished — don't treat both as "not found"
|
|
139
|
+
- [ ] Transient errors **abort or retry**, never silently switch modes
|
|
140
|
+
- [ ] Shared state (caches, prefetched data) is **reset** when context changes (e.g., user switches source)
|
|
141
|
+
- [ ] **Shortcut paths** (e.g., `--template` skipping picker) must have the same error-handling quality as the probed path — check that downstream functions don't call catch-all wrappers
|
|
142
|
+
|
|
143
|
+
### After implementing:
|
|
144
|
+
- [ ] Trace every path from probe result to the mode-decision branch — no fallthrough
|
|
145
|
+
- [ ] External format contracts (giget URI, raw URLs) are tested or at least documented as comments
|
|
146
|
+
- [ ] Metadata reads consume a complete response or use a streaming parser — never parse a fixed-size prefix as full JSON
|
|
147
|
+
- [ ] When reconstructing a composite identifier from parsed parts, verify **all** fields are included and in the **correct position** (e.g., `provider:repo/path#ref` not `provider:repo#ref/path`)
|
|
148
|
+
- [ ] Verify that **action functions** called after a shortcut don't internally use the old catch-all fetch — they must use the probe-quality variant when error distinction matters
|
|
149
|
+
|
|
150
|
+
**Real-world example**: Custom registry flow had 8 bugs across 3 review rounds: (1) probe only ran in interactive mode, (2) transient errors fell through to wrong mode, (3) giget URI had `#ref` in wrong position, (4) prefetched templates leaked across source switches, (5) `--template` shortcut bypassed probe but `downloadTemplateById` internally used catch-all `fetchTemplateIndex`, turning timeouts into "Template not found".
|
|
151
|
+
|
|
152
|
+
**Real-world example**: Agent-session update hints fetched npm `latest` metadata with `response.read(4096)` and then parsed it as complete JSON. The `@mindfoldhq/trellis` package metadata exceeded 4 KB, so the JSON was truncated, parse failed silently, and the first session injection showed no update hint. Fix: read the complete response before parsing, and add a regression where `version` is followed by an 8 KB metadata tail.
|
|
153
|
+
|
|
154
|
+
---
|
|
155
|
+
|
|
156
|
+
## When to Create Flow Documentation
|
|
157
|
+
|
|
158
|
+
Create detailed flow docs when:
|
|
159
|
+
- Feature spans 3+ layers
|
|
160
|
+
- Multiple teams are involved
|
|
161
|
+
- Data format is complex
|
|
162
|
+
- Feature has caused bugs before
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
# Thinking Guides
|
|
2
|
+
|
|
3
|
+
> **Purpose**: Expand your thinking to catch things you might not have considered.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## Why Thinking Guides?
|
|
8
|
+
|
|
9
|
+
**Most bugs and tech debt come from "didn't think of that"**, not from lack of skill:
|
|
10
|
+
|
|
11
|
+
- Didn't think about what happens at layer boundaries → cross-layer bugs
|
|
12
|
+
- Didn't think about code patterns repeating → duplicated code everywhere
|
|
13
|
+
- Didn't think about edge cases → runtime errors
|
|
14
|
+
- Didn't think about future maintainers → unreadable code
|
|
15
|
+
|
|
16
|
+
These guides help you **ask the right questions before coding**.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## Available Guides
|
|
21
|
+
|
|
22
|
+
| Guide | Purpose | When to Use |
|
|
23
|
+
|-------|---------|-------------|
|
|
24
|
+
| [Code Reuse Thinking Guide](./code-reuse-thinking-guide.md) | Identify patterns and reduce duplication | When you notice repeated patterns |
|
|
25
|
+
| [Cross-Layer Thinking Guide](./cross-layer-thinking-guide.md) | Think through data flow across layers | Features spanning multiple layers |
|
|
26
|
+
|
|
27
|
+
---
|
|
28
|
+
|
|
29
|
+
## Quick Reference: Thinking Triggers
|
|
30
|
+
|
|
31
|
+
### When to Think About Cross-Layer Issues
|
|
32
|
+
|
|
33
|
+
- [ ] Feature touches 3+ layers (API, Service, Component, Database)
|
|
34
|
+
- [ ] Data format changes between layers
|
|
35
|
+
- [ ] Multiple consumers need the same data
|
|
36
|
+
- [ ] You're not sure where to put some logic
|
|
37
|
+
|
|
38
|
+
→ Read [Cross-Layer Thinking Guide](./cross-layer-thinking-guide.md)
|
|
39
|
+
|
|
40
|
+
### When to Think About Code Reuse
|
|
41
|
+
|
|
42
|
+
- [ ] You're writing similar code to something that exists
|
|
43
|
+
- [ ] You see the same pattern repeated 3+ times
|
|
44
|
+
- [ ] You're adding a new field to multiple places
|
|
45
|
+
- [ ] **You're modifying any constant or config**
|
|
46
|
+
- [ ] **You're creating a new utility/helper function** ← Search first!
|
|
47
|
+
|
|
48
|
+
→ Read [Code Reuse Thinking Guide](./code-reuse-thinking-guide.md)
|
|
49
|
+
|
|
50
|
+
---
|
|
51
|
+
|
|
52
|
+
## Pre-Modification Rule (CRITICAL)
|
|
53
|
+
|
|
54
|
+
> **Before changing ANY value, ALWAYS search first!**
|
|
55
|
+
|
|
56
|
+
```bash
|
|
57
|
+
# Search for the value you're about to change
|
|
58
|
+
grep -r "value_to_change" .
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
This single habit prevents most "forgot to update X" bugs.
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## How to Use This Directory
|
|
66
|
+
|
|
67
|
+
1. **Before coding**: Skim the relevant thinking guide
|
|
68
|
+
2. **During coding**: If something feels repetitive or complex, check the guides
|
|
69
|
+
3. **After bugs**: Add new insights to the relevant guide (learn from mistakes)
|
|
70
|
+
|
|
71
|
+
---
|
|
72
|
+
|
|
73
|
+
## Contributing
|
|
74
|
+
|
|
75
|
+
Found a new "didn't think of that" moment? Add it to the relevant guide.
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
**Core Principle**: 30 minutes of thinking saves 3 hours of debugging.
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
# MVVM 架构规范
|
|
2
|
+
|
|
3
|
+
## 项目架构
|
|
4
|
+
|
|
5
|
+
基于 Google Android Architecture Components,采用 **MVVM + ViewBinding** 模式。
|
|
6
|
+
|
|
7
|
+
## 目录结构
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
com.stb.duer.apps.xtvott
|
|
11
|
+
├── ui/ # Activity/Fragment/Adapter
|
|
12
|
+
│ ├── main/
|
|
13
|
+
│ ├── player/
|
|
14
|
+
│ └── common/
|
|
15
|
+
├── vm/ # ViewModel
|
|
16
|
+
├── model/ # 数据模型
|
|
17
|
+
│ ├── entity/ # 数据库实体
|
|
18
|
+
│ └── data/ # 数据类
|
|
19
|
+
├── repository/ # 数据仓库
|
|
20
|
+
├── data/ # 数据层
|
|
21
|
+
│ ├── local/ # 本地存储
|
|
22
|
+
│ └── remote/ # 远程接口
|
|
23
|
+
└── util/ # 工具类
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
## ViewModel 规范
|
|
27
|
+
|
|
28
|
+
### 1. ViewModel 创建
|
|
29
|
+
```java
|
|
30
|
+
// 使用 ViewModelProvider
|
|
31
|
+
ViewModelProvider provider = new ViewModelProvider(this);
|
|
32
|
+
MyViewModel vm = provider.get(MyViewModel.class);
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
### 2. LiveData 使用
|
|
36
|
+
```java
|
|
37
|
+
// 暴露 LiveData
|
|
38
|
+
private final MutableLiveData<String> statusLiveData = new MutableLiveData<>();
|
|
39
|
+
public LiveData<String> getStatus() {
|
|
40
|
+
return statusLiveData;
|
|
41
|
+
}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### 3. 生命周期感知
|
|
45
|
+
```java
|
|
46
|
+
// 使用 observe 绑定到 lifecycleOwner
|
|
47
|
+
viewModel.getData().observe(this, data -> {
|
|
48
|
+
// 自动在 onDestroy 时解绑
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## 数据绑定
|
|
53
|
+
|
|
54
|
+
### 4. ViewBinding 使用
|
|
55
|
+
```java
|
|
56
|
+
// Activity 中
|
|
57
|
+
private ActivityMainBinding binding;
|
|
58
|
+
|
|
59
|
+
@Override
|
|
60
|
+
protected void onCreate(Bundle savedInstanceState) {
|
|
61
|
+
super.onCreate(savedInstanceState);
|
|
62
|
+
binding = ActivityMainBinding.inflate(getLayoutInflater());
|
|
63
|
+
setContentView(binding.getRoot());
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### 5. 双向绑定(少量使用)
|
|
68
|
+
```xml
|
|
69
|
+
<!-- 仅用于简单表单 -->
|
|
70
|
+
android:text="@={viewModel.inputText}"
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Repository 模式
|
|
74
|
+
|
|
75
|
+
### 6. 数据仓库封装
|
|
76
|
+
```java
|
|
77
|
+
public class UserRepository {
|
|
78
|
+
private final UserApi api;
|
|
79
|
+
private final UserDao dao;
|
|
80
|
+
|
|
81
|
+
public LiveData<User> getUser(int id) {
|
|
82
|
+
// 优先使用本地缓存,支持离线
|
|
83
|
+
return dao.getUser(id);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## 常见错误
|
|
89
|
+
|
|
90
|
+
### ❌ 禁止
|
|
91
|
+
- 在 ViewModel 中直接操作 View
|
|
92
|
+
- 使用 LiveData 而不 observe
|
|
93
|
+
- ViewModel 中使用 Context(使用 Application Context)
|
|
94
|
+
- 在主线程进行网络/数据库操作
|
|
95
|
+
|
|
96
|
+
### ✅ 正确
|
|
97
|
+
- ViewModel 通过 LiveData 通知 View
|
|
98
|
+
- 使用 Executor/AsyncTask 处理异步
|
|
99
|
+
- 使用 Application Context 或 LiveDataScope
|
|
100
|
+
- 数据操作在 Repository 层统一处理
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
# 资源文件规范
|
|
2
|
+
|
|
3
|
+
## AutoSize 框架适配
|
|
4
|
+
|
|
5
|
+
项目使用 **Android AutoSize** 框架实现屏幕适配,无需编写多套 dimens 文件。
|
|
6
|
+
|
|
7
|
+
### 1. 启用 AutoSize
|
|
8
|
+
```xml
|
|
9
|
+
<!-- AndroidManifest.xml -->
|
|
10
|
+
<manifest>
|
|
11
|
+
<application>
|
|
12
|
+
<meta-data
|
|
13
|
+
android:name="design_width_in_dp"
|
|
14
|
+
android:value="1920" />
|
|
15
|
+
<meta-data
|
|
16
|
+
android:name="design_height_in_dp"
|
|
17
|
+
android:value="1080" />
|
|
18
|
+
</application>
|
|
19
|
+
</manifest>
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
### 2. 单位使用
|
|
23
|
+
```xml
|
|
24
|
+
<!-- 优先使用 dp/sp,禁止使用 px -->
|
|
25
|
+
<dimen name="text_size_title">48sp</dimen>
|
|
26
|
+
<dimen name="margin_standard">24dp</dimen>
|
|
27
|
+
|
|
28
|
+
<!-- 禁止 -->
|
|
29
|
+
<!-- <dimen name="wrong_size">100px</dimen> -->
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
### 3. 布局约束
|
|
33
|
+
```xml
|
|
34
|
+
<!-- 优先使用约束布局 + wrap_content/match_constraint -->
|
|
35
|
+
<TextView
|
|
36
|
+
android:layout_width="0dp"
|
|
37
|
+
app:layout_constraintWidth_default="wrap"
|
|
38
|
+
android:textSize="@dimen/text_size_title" />
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
## 布局文件
|
|
42
|
+
|
|
43
|
+
### 4. 命名规范
|
|
44
|
+
```
|
|
45
|
+
activity_xxx_tv.xml # TV Activity 布局
|
|
46
|
+
fragment_xxx_tv.xml # TV Fragment 布局
|
|
47
|
+
item_xxx.xml # 列表项布局
|
|
48
|
+
layout_xxx.xml # 可复用布局
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
### 5. TV/触屏共用
|
|
52
|
+
```xml
|
|
53
|
+
<!-- 同一布局文件,AutoSize 自动适配 -->
|
|
54
|
+
<!-- 不需要 values-sw720dp 等额外适配 -->
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## 字体适配
|
|
58
|
+
|
|
59
|
+
### 6. sp 单位字体
|
|
60
|
+
```xml
|
|
61
|
+
<!-- 所有字体使用 sp 单位,AutoSize 自动缩放 -->
|
|
62
|
+
<TextView
|
|
63
|
+
android:textSize="@dimen/text_size_body"
|
|
64
|
+
android:textColor="@color/text_primary" />
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
## 颜色资源
|
|
68
|
+
|
|
69
|
+
### 7. 颜色定义
|
|
70
|
+
```xml
|
|
71
|
+
<!-- res/values/colors.xml -->
|
|
72
|
+
<color name="primary">#FF6200EE</color>
|
|
73
|
+
<color name="text_primary">#FFFFFFFF</color>
|
|
74
|
+
<color name="text_secondary">#B3FFFFFF</color>
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## 字符串资源
|
|
78
|
+
|
|
79
|
+
### 8. 字符串规范
|
|
80
|
+
```xml
|
|
81
|
+
<!-- 禁止硬编码字符串 -->
|
|
82
|
+
<!-- 错误:android:text="确定" -->
|
|
83
|
+
<!-- 正确 -->
|
|
84
|
+
android:text="@string/btn_confirm"
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
## Selector 选择器
|
|
88
|
+
|
|
89
|
+
### 9. 焦点/按压状态
|
|
90
|
+
```xml
|
|
91
|
+
<!-- 必须同时定义遥控器焦点和触屏按压状态 -->
|
|
92
|
+
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
|
93
|
+
<item android:state_focused="true" android:drawable="@drawable/bg_focused"/>
|
|
94
|
+
<item android:state_pressed="true" android:drawable="@drawable/bg_pressed"/>
|
|
95
|
+
<item android:drawable="@drawable/bg_normal"/>
|
|
96
|
+
</selector>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## 常见错误
|
|
100
|
+
|
|
101
|
+
### ❌ 禁止
|
|
102
|
+
- 使用 px 单位
|
|
103
|
+
- 硬编码尺寸数值
|
|
104
|
+
- 硬编码字符串
|
|
105
|
+
- 布局中缺少 focusable 属性
|
|
106
|
+
|
|
107
|
+
### ✅ 正确
|
|
108
|
+
- 使用 dp/sp 单位
|
|
109
|
+
- 统一管理颜色/字符串资源
|
|
110
|
+
- TV 控件设置 focusable="true"
|
|
111
|
+
- 使用 ConstraintLayout 减少嵌套
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# TV+触屏双端布局规范
|
|
2
|
+
|
|
3
|
+
## 1. 布局结构
|
|
4
|
+
根布局优先ConstraintLayout,杜绝多层LinearLayout嵌套(TV渲染卡顿)
|
|
5
|
+
|
|
6
|
+
## 2. 焦点配置
|
|
7
|
+
焦点控件默认设置focusable=true,触屏环境focusableInTouchMode=true
|
|
8
|
+
|
|
9
|
+
## 3. 控件尺寸
|
|
10
|
+
TV端控件偏大(最小高48dp起),触屏复用同套layout,使用 AutoSize 框架自动适配各尺寸屏幕
|
|
11
|
+
- 基准设计稿:1920x1080dp
|
|
12
|
+
- 单位:dp/sp(禁止px)
|
|
13
|
+
|
|
14
|
+
## 4. 点击状态
|
|
15
|
+
selector必须同时配置state_focused(遥控器选中)、state_pressed(触屏按压)
|
|
16
|
+
|
|
17
|
+
## 5. 布局约束
|
|
18
|
+
禁止写死宽高match_parent+固定dp混用,优先wrap_content+layout_constraint约束
|
|
19
|
+
|
|
20
|
+
## 6. 列表控件
|
|
21
|
+
RecyclerView统一封装TvRecyclerView,内置焦点跳转+触屏滑动兼容
|
|
22
|
+
|
|
23
|
+
## 7. AutoSize 配置
|
|
24
|
+
```xml
|
|
25
|
+
<meta-data
|
|
26
|
+
android:name="design_width_in_dp"
|
|
27
|
+
android:value="1920" />
|
|
28
|
+
<meta-data
|
|
29
|
+
android:name="design_height_in_dp"
|
|
30
|
+
android:value="1080" />
|
|
31
|
+
```
|
package/workflow.md
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# Android TV+触屏 标准化开发工作流
|
|
2
|
+
1. 需求录入:解析页面功能,生成标准目录结构(Activity + Fragment + ViewModel + 布局文件)
|
|
3
|
+
2. 布局编写:基于 ConstraintLayout 实现布局,默认开启焦点、触屏双适配属性
|
|
4
|
+
3. 业务代码:采用 Java + AndroidX + MVVM 架构,强制继承项目基础基类,启用 ViewBinding
|
|
5
|
+
4. 自动化校验:依次执行内置技能
|
|
6
|
+
- tv_focus_check:校验并修复 Dpad 焦点问题
|
|
7
|
+
- touch_compat:补全触屏兼容逻辑与交互效果
|
|
8
|
+
- layout_adapt:抽离硬编码尺寸,完成多分辨率适配
|
|
9
|
+
5. 代码评审:code_review_tv 全量检查编码规范、架构、适配规则
|
|
10
|
+
6. 最终输出:可直接编译运行的完整代码
|