Files
habo/docs/04-ASSETS.md
dazhuang aa69f2a91e feat: initial commit - Habo habit tracking app
- Complete MVP with Repository Pattern, SQLite storage
- Provider + ChangeNotifier state management
- Navigation 2.0 with deep link support
- Habit CRUD with twoDayRule, notifications, categories
- Backup/Restore via JSON
- Statistics with streak tracking
- Material You theme support
- Biometric lock support
- Desktop widget support
- 27 languages i18n structure
- Comprehensive test suite (87/89 passing)
2026-04-13 15:02:30 +00:00

548 lines
20 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Habo 可复用素材清单
> 从原项目提取的静态资源、国际化文本、测试用例,作为 AI 复刻项目的起点素材
---
## 1. 静态资源清单
### 1.1 图片资源 (`assets/images/`)
| 文件 | 用途 | 格式 |
|------|------|------|
| `icon.png` | Android 应用图标 | PNG |
| `ios_icon.jpg` | iOS 应用图标 | JPG |
| `macos_icon.png` | macOS 应用图标 | PNG |
| `app_icon.png` | 通用应用图标 | PNG |
| `splash_icon.png` | 启动画面图标 | PNG |
| `splash_icon2.png` | 备用启动图标 | PNG |
| `android_foreground.png` | Android 自适应图标前景 | PNG |
| `android_background.png` | Android 自适应图标背景 | PNG |
| `android_monochrome.svg` | Android 单色图标 | SVG |
| `emptyList.svg` | 空列表占位图 | SVG |
| `noDataStatistics.svg` | 统计页空数据占位图 | SVG |
### 1.2 引导页图片 (`assets/images/onboard/`)
| 文件 | 用途 |
|------|------|
| `1.svg` | 第 1 步: 定义习惯 (空列表插图) |
| `2.svg` | 第 2 步: 记录天数 (习惯追踪插图) |
| `3.svg` | 第 3 步: 观察进步 (进度追踪插图) |
### 1.3 音效资源 (`assets/sounds/`)
| 文件 | 用途 | 说明 |
|------|------|------|
| `check.wav` | 打卡完成音效 | 成功完成的正向音效 |
| `click.wav` | 通用点击音效 | 失败/跳过等操作的反馈音效 |
| `sound_sources.txt` | 音效来源说明 | 开源协议信息 |
### 1.4 字体资源 (`assets/google_fonts/`)
**字体族**: Nunito18 个字重/样式变体)
| 文件 | 字重 |
|------|------|
| Nunito-ExtraLight.ttf | 200 |
| Nunito-Light.ttf | 300 |
| Nunito-Regular.ttf | 400 |
| Nunito-Medium.ttf | 500 |
| Nunito-SemiBold.ttf | 600 |
| Nunito-Bold.ttf | 700 |
| Nunito-ExtraBold.ttf | 800 |
| Nunito-Black.ttf | 900 |
| 以及对应的 Italic 变体 | |
许可证: OFL (SIL Open Font License)
---
## 2. 国际化文本 (英文基准 ARB)
> 完整的 `intl_en.arb`,包含 **354 个键**,可直接复制为项目的国际化基准文件
```json
{
"@@locale": "en",
"habits": "Habits:",
"statistics": "Statistics",
"emptyList": "Empty list",
"noDataAboutHabits": "There is no data about habits.",
"topStreak": "Top streak",
"currentStreak": "Current streak",
"total": "Total",
"unknown": "Unknown",
"warning": "Warning",
"allHabitsWillBeReplaced": "All habits will be replaced with habits from backup.",
"restore": "Restore",
"cancel": "Cancel",
"settings": "Settings",
"theme": "Theme",
"firstDayOfWeek": "First day of the week",
"notifications": "Notifications",
"notificationTime": "Notification time",
"soundEffects": "Sound effects",
"showMonthName": "Show month name",
"setColors": "Set colors",
"backup": "Backup",
"create": "Create",
"onboarding": "Onboarding",
"about": "About",
"habo": "Habo",
"copyright": "©2023 Habo",
"termsAndConditions": "Terms and Conditions",
"privacyPolicy": "Privacy Policy",
"disclaimer": "Disclaimer",
"sourceCode": "Source code (GitHub)",
"ifYouWantToSupport": "If you want to support Habo you can:",
"buyMeACoffee": "Buy me a coffee",
"reset": "Reset",
"done": "Done",
"congratulationsReward": "Congratulations! Your reward:",
"ohNoSanction": "Oh no! Your sanction:",
"month": "Month",
"week": "Week",
"habitLoop": "Habit loop",
"habitLoopDescription": "Habit Loop is a psychological model describing the process of habit formation. It consists of three components: Cue, Routine, and Reward. The Cue triggers the Routine (habitual action), which is then reinforced by the Reward, creating a loop that makes the habit more ingrained and likely to be repeated.",
"cue": "Cue",
"cueDescription": "is the trigger that initiates your habit. It could be a specific time, location, feeling, or an event.",
"routine": "Routine",
"routineDescription": "is the action you take in response to the cue. This is the habit itself.",
"reward": "Reward",
"rewardDescription": "is the benefit or positive feeling you experience after performing the routine. It reinforces the habit.",
"editHabit": "Edit Habit",
"createHabit": "Create Habit",
"delete": "Delete",
"habitTitleEmptyError": "The habit title can not be empty.",
"save": "Save",
"exercise": "Exercise",
"habit": "Habit",
"useTwoDayRule": "Use Two day rule",
"twoDayRule": "Two day rule",
"twoDayRuleDescription": "With two day rule, you can miss one day and do not lose a streak if the next day is successful.",
"advancedHabitBuilding": "Advanced habit building",
"advancedHabitBuildingDescription": "This section helps you better define your habits utilizing the Habit loop. You should define cues, routines, and rewards for every habit.",
"at7AM": "At 7:00AM",
"do50PushUps": "Do 50 push ups",
"fifteenMinOfVideoGames": "15 min. of video games",
"showReward": "Show reward",
"remainderOfReward": "The reminder of the reward after a successful routine.",
"habitContract": "Habit contract",
"habitContractDescription": "While positive reinforcement is recommended, some people may opt for a habit contract. A habit contract allows you to specify a sanction that will be imposed if you miss your habit, and may involve an accountability partner who helps supervise your goals.",
"donateToCharity": "Donate 10$ to charity",
"sanction": "Sanction",
"showSanction": "Show sanction",
"remainderOfSanction": "The reminder of the sanction after a unsuccessful routine.",
"dan": "Dan",
"accountabilityPartner": "Accountability partner",
"add": "Add",
"haboNeedsPermission": "Habo needs permission to send notifications to work properly.",
"allow": "Allow",
"date": "Date",
"check": "Check",
"fail": "Fail",
"skip": "Skip",
"note": "Note",
"yourCommentHere": "Your note here",
"close": "Close",
"createYourFirstHabit": "Create your first habit.",
"modify": "Modify",
"backupFailedError": "ERROR: Creating backup failed.",
"restoreFailedError": "ERROR: Restoring backup failed.",
"habitDeleted": "Habit deleted.",
"undo": "Undo",
"appNotifications": "App notifications",
"appNotificationsChannel": "Notification channel for application notifications",
"habitNotifications": "Habit notifications",
"habitNotificationsChannel": "Notification channel for habit notifications",
"doNotForgetToCheckYourHabits": "Do not forget to check your habits.",
"themeSelect": "{theme, select, device {Device} light {Light} dark {Dark} oled {OLED black} materialYou {Material You} other{Device}}",
"@themeSelect": {
"placeholders": {
"theme": {
"type": "String"
}
}
},
"defineYourHabits": "Define your habits",
"defineYourHabitsDescription": "To better stick to your habits, you can define:",
"cueNumbered": "1. Cue",
"routineNumbered": "2. Routine",
"rewardNumbered": "3. Reward",
"logYourDays": "Log your days",
"successful": "Successful",
"notSoSuccessful": "Not so successful",
"skipDoesNotAffectStreaks": "Skip (does not affect streaks)",
"observeYourProgress": "Observe your progress",
"trackYourProgress": "You can track your progress through the calendar view in every habit or on the statistics page.",
"backupCreatedSuccessfully": "Backup created successfully!",
"backupFailed": "Backup failed!",
"restoreCompletedSuccessfully": "Restore completed successfully!",
"restoreFailed": "Restore failed!",
"fileNotFound": "File not found",
"fileTooLarge": "File too large (max 10MB)",
"invalidBackupFile": "Invalid backup file",
"progress": "Progress",
"enterAmount": "Enter amount",
"complete": "Complete",
"saveProgress": "Save Progress",
"currentProgress": "Current: {current} {unit}",
"@currentProgress": {
"placeholders": {
"current": { "type": "String" },
"unit": { "type": "String" }
}
},
"targetProgress": "Target: {target} {unit}",
"@targetProgress": {
"placeholders": {
"target": { "type": "String" },
"unit": { "type": "String" }
}
},
"progressOf": "{current} / {target} {unit}",
"@progressOf": {
"placeholders": {
"current": { "type": "String" },
"target": { "type": "String" },
"unit": { "type": "String" }
}
},
"numericHabit": "Progressive",
"targetValue": "Target value",
"partialValue": "Partial value",
"unit": "Unit",
"habitType": "Habit type",
"booleanHabit": "Checkable (Yes/No)",
"slider": "Slider",
"input": "Input",
"numericHabitDescription": "Numeric habits let you track progress in increments throughout the day.",
"partialValueDescription": "To track progress in smaller increments",
"categories": "Categories",
"addCategory": "Add Category",
"editCategory": "Edit Category",
"category": "Category",
"noCategoriesYet": "No categories yet",
"createFirstCategory": "Create your first category to organize your habits",
"pleaseEnterCategoryTitle": "Please enter a category title",
"categoryAlreadyExists": "Category \"{title}\" already exists",
"@categoryAlreadyExists": { "placeholders": { "title": { "type": "String" } } },
"categoryCreatedSuccessfully": "Category \"{title}\" created successfully",
"@categoryCreatedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
"categoryUpdatedSuccessfully": "Category \"{title}\" updated successfully",
"@categoryUpdatedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
"categoryDeletedSuccessfully": "Category \"{title}\" deleted successfully",
"@categoryDeletedSuccessfully": { "placeholders": { "title": { "type": "String" } } },
"failedToSaveCategory": "Failed to save category: {error}",
"@failedToSaveCategory": { "placeholders": { "error": { "type": "String" } } },
"failedToDeleteCategory": "Failed to delete category: {error}",
"@failedToDeleteCategory": { "placeholders": { "error": { "type": "String" } } },
"selectCategories": "Select Categories",
"selectedCategories": "Selected Categories ({count})",
"@selectedCategories": { "placeholders": { "count": { "type": "int" } } },
"allCategories": "All Categories",
"deleteCategory": "Delete Category",
"deleteCategoryConfirmation": "Are you sure you want to delete \"{title}\"?\n\nThis will remove the category from all habits that use it.",
"@deleteCategoryConfirmation": { "placeholders": { "title": { "type": "String" } } },
"noHabitsInCategory": "No habits in \"{title}\"",
"@noHabitsInCategory": { "placeholders": { "title": { "type": "String" } } },
"createHabitForCategory": "Create a habit and assign it to this category",
"showCategories": "Show Categories",
"archive": "Archive",
"unarchive": "Unarchive",
"archiveHabit": "Archive habit",
"unarchiveHabit": "Unarchive habit",
"archivedHabits": "Archived Habits",
"noArchivedHabits": "No archived habits",
"viewArchivedHabits": "View archived habits",
"habitArchived": "Habit archived",
"habitUnarchived": "Habit unarchived",
"biometric": "Biometric",
"biometricLockEnabled": "Biometric lock enabled",
"biometricLockDisabled": "Biometric lock disabled",
"authenticationError": "Authentication error",
"biometricAuthenticationRequired": "Biometric authentication required",
"setupFingerprintFaceUnlock": "Please set up your fingerprint or face unlock in device settings",
"touchSensor": "Touch sensor",
"biometricNotRecognized": "Biometric not recognized, try again",
"biometricRequired": "Biometric required",
"biometricAuthenticationSucceeded": "Biometric authentication succeeded",
"deviceCredentialsRequired": "Device credentials required",
"setupDeviceCredentials": "Please set up device credentials in settings",
"setupTouchIdFaceId": "Please set up your Touch ID or Face ID in device settings",
"reenableTouchIdFaceId": "Please reenable your Touch ID or Face ID",
"biometricLock": "Biometric Lock",
"biometricLockDescription": "Secure app with {authMethod}",
"@biometricLockDescription": { "placeholders": { "authMethod": { "type": "String" } } },
"authenticateToEnable": "Authenticate to enable biometric lock",
"authenticateToAccess": "Please authenticate to access Habo",
"authenticationRequired": "Authentication Required",
"authenticationFailedMessage": "Please authenticate to access Habo using {authMethod}",
"@authenticationFailedMessage": { "placeholders": { "authMethod": { "type": "String" } } },
"tryAgain": "Try Again",
"authenticating": "Authenticating…",
"authenticate": "Authenticate",
"buildingBetterHabits": "Building Better Habits",
"authenticationPrompt": "Please authenticate using {authMethod} to access your habits",
"@authenticationPrompt": { "placeholders": { "authMethod": { "type": "String" } } },
"devicePinPatternPassword": "Device PIN, Pattern, or Password",
"fingerprint": "Fingerprint",
"iris": "Iris",
"whatsNewTitle": "What's New",
"whatsNewVersion": "Version {version}",
"@whatsNewVersion": { "placeholders": { "version": { "type": "String" } } },
"featureNumericTitle": "Numeric values in habits",
"featureNumericDesc": "Track counts like glasses of water or pages read",
"featureDeepLinksTitle": "URL scheme (deep links)",
"featureDeepLinksDesc": "Open Habo directly to screens like settings or create",
"featureCategoriesTitle": "Categories",
"featureCategoriesDesc": "Organize habits with category filters",
"featureArchiveTitle": "Archive",
"featureArchiveDesc": "Hide habits you no longer track without deleting",
"featureMaterialYouTitle": "Material You theme (Android)",
"featureMaterialYouDesc": "Dynamic colors that match your wallpaper",
"featureSoundTitle": "New sound engine",
"featureSoundDesc": "Adjustable volume",
"featureLockTitle": "Lock feature",
"featureLockDesc": "Secure the app with Face ID / Touch ID / biometrics",
"featureIosSoundMixingTitle": "Fixed sound mixing",
"featureIosSoundMixingDesc": "Habo sounds no longer interrupt your music or podcasts",
"featureHomescreenWidgetTitle": "Homescreen widget",
"featureHomescreenWidgetDesc": "View your habit progress at a glance from your home screen (experimental)",
"featureLongpressCheckTitle": "Longpress check",
"featureLongpressCheckDesc": "Longpress on habit buttons to quickly change status",
"haboSyncComingSoon": "Coming Soon",
"haboSyncDescription": "Sync your habits across all your devices with Habo's end-to-end encrypted cloud service.",
"haboSyncLearnMore": "Learn more at habo.space/sync",
"habitsToday": "Habits today",
"or": "or",
"oneTapCheck": "Single tap to check",
"tapCheckLongPressMenu": "Tap to check, long press for menu",
"categoryName": "Category name",
"createCategory": "Create category",
"all": "All",
"selectIcon": "Pick an icon",
"searchIcons": "Search"
}
```
---
## 3. 支持的语言 (27 种)
| 代码 | 语言 |
|------|------|
| `en` | English (基准) |
| `zh_Hans` | 中文简体 |
| `zh_Hant` | 中文繁體 |
| `es` | Español |
| `fr` | Français |
| `de` | Deutsch |
| `it` | Italiano |
| `pt` | Português |
| `pt_BR` | Português (Brasil) |
| `ru` | Русский |
| `ja` | (可能缺失, 需确认) |
| `ar` | العربية |
| `he` | עברית |
| `pl` | Polski |
| `nl` | Nederlands |
| `sv` | Svenska |
| `cs` | Čeština |
| `sk` | Slovenčina |
| `uk` | Українська |
| `vi` | Tiếng Việt |
| `id` | Bahasa Indonesia |
| `tr` | Türkçe |
| `ca` | Català |
| `ta` | தமிழ் |
| `nb_NO` | Norsk bokmål |
| `eo` | Esperanto |
| `ia` | Interlingua |
| `ckb` | کوردی |
---
## 4. 测试用例清单
> 13 个测试文件,覆盖核心业务逻辑
### 4.1 单元测试
| 文件 | 测试目标 | 测试用例数 |
|------|----------|-----------|
| `test/habits/habits_manager_test.dart` | HabitsManager CRUD | 13 |
| `test/habits/habits_manager_updated_test.dart` | Repository 模式集成 | 8 |
| `test/habits/habits_manager_fixed_test.dart` | 归档功能 | 8 |
| `test/habits/habits_manager_notifications_test.dart` | 通知调度 | 3 |
| `test/habits/backup_enhancement_test.dart` | 备份格式 | 4 |
| `test/services/backup_service_test.dart` | 备份服务 | 7 |
| `test/services/backup_feature_comprehensive_test.dart` | 备份完整性 | 18 |
| `test/services/notification_service_test.dart` | 通知服务 | 11 |
| `test/app_test.dart` | 应用初始化 | 4 |
| `test/repositories/repository_test.dart` | Repository 模式 | 6 |
### 4.2 Widget 测试
| 文件 | 测试目标 | 测试用例数 |
|------|----------|-----------|
| `test/widgets/habit_details_widget_test.dart` | 习惯详情组件 | 3 |
| `test/widgets/habit_list_widget_test.dart` | 习惯列表组件 | 2 |
### 4.3 集成测试
| 文件 | 测试目标 | 测试用例数 |
|------|----------|-----------|
| `test/integration/habit_crud_integration_test.dart` | 完整 CRUD 流程 | 2 |
### 4.4 测试 Mock 基础设施
| 文件 | 内容 |
|------|------|
| `test/mocks/mock_repositories.dart` | MockHabitRepository, MockEventRepository, MockCategoryRepository, MockBackupRepository + InMemory 实现用于集成测试 |
### 4.5 关键测试场景摘要
**HabitsManager 测试**:
- 初始化时从 Repository 加载习惯
- 空列表正确处理
- 创建习惯并分配正确位置
- 编辑更新已有习惯
- 删除习惯并支持 Undo
- 归档/取消归档切换
- 活跃/归档习惯正确过滤
- 位置排序更新
- 通知调度触发
**备份测试**:
- 正确的 JSON 结构验证
- 时间戳格式验证
- 空数据备份处理
- 事件类型保留
- 分类关联保留
- 大数据集处理
- 损坏数据检测
- 10MB 文件大小限制
- 并发操作处理
**通知测试**:
- 空习惯列表不崩溃
- 习惯通知调度
- 事件添加/删除触发通知
- 多习惯批量通知
- 当日/非当日事件区分
---
## 5. 备份文件格式规格
### 5.1 当前格式 (Version 3)
```json
{
"version": 3,
"habits": [
{
"id": 1,
"position": 0,
"title": "Exercise",
"twoDayRule": false,
"cue": "At 7:00AM",
"routine": "Do 50 push ups",
"reward": "15 min. of video games",
"showReward": true,
"advanced": true,
"notification": true,
"notTime": {"hour": 7, "minute": 0},
"events": {
"2024-01-01 00:00:00.000": [1, ""],
"2024-01-02 00:00:00.000": [2, "Tired"]
},
"sanction": "Donate 10$ to charity",
"showSanction": true,
"accountant": "Dan",
"habitType": 0,
"targetValue": 1.0,
"partialValue": 1.0,
"unit": ""
}
],
"categories": [
{ "id": 1, "title": "Health", "iconCodePoint": 58718, "fontFamily": "fontAwesomeFlutter" }
],
"habit_categories": [
{ "habit_id": 1, "category_id": 1 }
],
"metadata": {
"import_timestamp": "2024-01-15T10:30:00.000Z"
}
}
```
### 5.2 旧版兼容格式 (数组)
```json
[
{
"id": 1,
"title": "Exercise",
"position": 0,
"events": {},
...
}
]
```
### 5.3 事件类型编码
| 值 | DayType | 含义 |
|----|---------|------|
| 0 | clear | 清除/无事件 |
| 1 | check | 完成 |
| 2 | fail | 失败 |
| 3 | skip | 跳过 |
| 4 | progress | 进度(部分完成) |
---
## 6. pubspec.yaml 关键配置
```yaml
name: habo
version: 3.1.2+5115
environment:
sdk: ">=3.11.0 <4.0.0"
flutter: ">=3.41.1"
flutter:
uses-material-design: true
generate: true
assets:
- assets/
- assets/images/
- assets/images/onboard/
- assets/sounds/
- assets/google_fonts/
flutter_intl:
enabled: true
flutter_native_splash:
color: "#FAFAFA"
color_dark: "#000000"
image: assets/images/splash_icon.png
image_dark: assets/images/splash_icon.png
ios_content_mode: center
android_gravity: center
fullscreen: false
android_12:
color: "#FAFAFA"
color_dark: "#000000"
image: assets/images/splash_icon.png
android: true
ios: true
web: false
```