All checks were successful
continuous-integration/drone/push Build is passing
- Created comprehensive Android app requirements document - Built complete Android project with Kotlin and Jetpack Compose - Implemented task management with control and display screens - Added Android widget with 5-minute auto-refresh capability - Integrated Room database for offline support - Set up MVVM architecture with Hilt dependency injection - Configured Retrofit for API communication - Added Material Design 3 theming and UI components
11 KiB
11 KiB
Calendar Widget Android 应用需求文档
项目概述
将现有的 Calendar Widget 任务管理系统迁移到 Android 平台,保持核心功能的同时提供原生移动应用体验。
技术栈
- 开发语言: Kotlin
- UI框架: Jetpack Compose
- 架构模式: MVVM (Model-View-ViewModel)
- 网络请求: Retrofit + OkHttp
- 本地存储: Room Database
- 依赖注入: Hilt
- 小组件: Android App Widget API
应用结构
1. 控制页面 (MainActivity)
功能需求
-
任务列表展示
- 显示所有任务的列表视图
- 每个任务项显示:名称、状态颜色、最小间隔天数、上次执行时间
- 支持下拉刷新更新任务列表
-
任务管理
- 添加新任务:浮动操作按钮触发对话框
- 编辑任务:长按任务项进入编辑模式
- 删除任务:侧滑删除或长按菜单删除
- 拖拽排序:长按拖动重新排列任务优先级
-
任务执行
- 点击"执行"按钮记录任务执行时间
- 成功执行后自动刷新状态颜色
UI设计要点
// 主界面布局结构
@Composable
fun ControlScreen() {
Scaffold(
topBar = { /* 应用标题栏 */ },
floatingActionButton = { /* 添加任务按钮 */ }
) {
LazyColumn {
items(tasks) { task ->
TaskCard(
task = task,
onExecute = { /* 执行任务 */ },
onEdit = { /* 编辑任务 */ },
onDelete = { /* 删除任务 */ }
)
}
}
}
}
2. 展示页面 (DisplayActivity)
功能需求
-
任务状态可视化
- 网格布局展示任务卡片
- 动态适配屏幕大小(横屏4列,竖屏2列)
- 每个卡片显示任务名称和颜色条
- 点击卡片快速执行任务
-
视图模式
- 紧凑模式:仅显示名称和颜色
- 详细模式:显示名称、颜色、剩余天数
UI设计要点
@Composable
fun DisplayScreen() {
val configuration = LocalConfiguration.current
val columns = if (configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) 4 else 2
LazyVerticalGrid(
columns = GridCells.Fixed(columns),
contentPadding = PaddingValues(16.dp)
) {
items(tasks) { task ->
TaskDisplayCard(
task = task,
onClick = { /* 执行任务 */ }
)
}
}
}
3. Android 小组件 (Widget)
功能需求
-
尺寸支持
- 小型 (2x1): 显示1个任务
- 中型 (2x2): 显示4个任务
- 大型 (4x2): 显示8个任务
-
交互功能
- 点击任务执行并刷新小组件
- 自动每5分钟更新一次
- 支持手动刷新按钮
实现要点
class TaskWidgetProvider : AppWidgetProvider() {
override fun onUpdate(context: Context, appWidgetManager: AppWidgetManager, appWidgetIds: IntArray) {
for (appWidgetId in appWidgetIds) {
val size = getWidgetSize(appWidgetManager, appWidgetId)
val views = createRemoteViews(context, size)
appWidgetManager.updateAppWidget(appWidgetId, views)
}
// 设置下次更新时间为5分钟后
scheduleNextUpdate(context)
}
private fun scheduleNextUpdate(context: Context) {
val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
val intent = Intent(context, TaskWidgetProvider::class.java).apply {
action = AppWidgetManager.ACTION_APPWIDGET_UPDATE
}
val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, PendingIntent.FLAG_UPDATE_CURRENT)
// 5分钟后更新
val updateInterval = 5 * 60 * 1000L // 5分钟转换为毫秒
alarmManager.setExactAndAllowWhileIdle(
AlarmManager.RTC,
System.currentTimeMillis() + updateInterval,
pendingIntent
)
}
}
小组件布局 (仿照 widget.js 样式)
<!-- widget_layout.xml -->
<LinearLayout android:background="#f5f5f9"
android:padding="10dp">
<GridLayout android:columnCount="@integer/widget_columns"
android:rowCount="@integer/widget_rows">
<!-- 任务卡片 -->
<LinearLayout android:background="#f2f2f2"
android:padding="10dp"
android:gravity="center">
<TextView android:id="@+id/task_name"
android:textSize="14sp"
android:textColor="@android:color/black"/>
<View android:id="@+id/color_bar"
android:layout_height="10dp"
android:layout_marginTop="8dp"/>
</LinearLayout>
</GridLayout>
</LinearLayout>
数据管理
API 接口层
interface TaskApiService {
@GET("tasks")
suspend fun getTasks(@Header("X-Api-Key") apiKey: String): List<Task>
@POST("tasks")
suspend fun createTask(@Header("X-Api-Key") apiKey: String, @Body task: CreateTaskRequest): Task
@PUT("tasks/{id}")
suspend fun updateTask(@Header("X-Api-Key") apiKey: String, @Path("id") id: Int, @Body task: UpdateTaskRequest): Task
@DELETE("tasks/{id}")
suspend fun deleteTask(@Header("X-Api-Key") apiKey: String, @Path("id") id: Int)
@POST("tasks/{id}/schedule")
suspend fun scheduleTask(@Header("X-Api-Key") apiKey: String, @Path("id") id: Int)
@POST("tasks/reorder")
suspend fun reorderTasks(@Header("X-Api-Key") apiKey: String, @Body request: ReorderRequest)
}
本地缓存
@Entity(tableName = "tasks")
data class TaskEntity(
@PrimaryKey val id: Int,
val name: String,
val color: String,
val minIntervalDays: Int,
val lastExecutionTime: String?,
val priority: Int
)
@Dao
interface TaskDao {
@Query("SELECT * FROM tasks ORDER BY priority")
fun getAllTasks(): Flow<List<TaskEntity>>
@Insert(onConflict = OnConflictStrategy.REPLACE)
suspend fun insertTasks(tasks: List<TaskEntity>)
@Query("DELETE FROM tasks")
suspend fun deleteAllTasks()
}
状态管理
ViewModel 实现
@HiltViewModel
class TaskViewModel @Inject constructor(
private val repository: TaskRepository
) : ViewModel() {
private val _uiState = MutableStateFlow(TaskUiState())
val uiState: StateFlow<TaskUiState> = _uiState.asStateFlow()
fun loadTasks() {
viewModelScope.launch {
repository.getTasks()
.catch { /* 处理错误,使用缓存 */ }
.collect { tasks ->
_uiState.update { it.copy(tasks = tasks) }
}
}
}
fun executeTask(taskId: Int) {
viewModelScope.launch {
repository.scheduleTask(taskId)
loadTasks() // 刷新列表
updateWidget() // 更新小组件
}
}
}
应用设置
SharedPreferences 配置
object AppSettings {
private const val PREF_NAME = "calendar_widget_prefs"
private const val KEY_API_KEY = "api_key"
private const val KEY_API_HOST = "api_host"
private const val KEY_AUTO_REFRESH = "auto_refresh"
private const val KEY_REFRESH_INTERVAL = "refresh_interval"
fun saveApiKey(context: Context, apiKey: String) {
context.getSharedPreferences(PREF_NAME, Context.MODE_PRIVATE)
.edit()
.putString(KEY_API_KEY, apiKey)
.apply()
}
}
设置页面功能
- API密钥配置
- 服务器地址配置
- 小组件自动刷新间隔(默认5分钟,可选1/5/10/15/30/60分钟)
- 主题选择(浅色/深色)
- 通知设置
UI/UX 设计规范
颜色主题
val CalendarWidgetTheme = lightColorScheme(
primary = Color(0xFF667EEA),
secondary = Color(0xFF764BA2),
background = Color(0xFFF5F5F9),
surface = Color(0xFFF2F2F2),
error = Color(0xFFFF4757),
onPrimary = Color.White,
onSecondary = Color.White,
onBackground = Color.Black,
onSurface = Color.Black
)
// 任务状态颜色
object TaskColors {
val Green = Color(0xFF00B894)
val Yellow = Color(0xFFFDCB6E)
val Red = Color(0xFFFF4757)
}
Material Design 3 组件
- 使用 Material You 动态主题
- 圆角卡片设计(16dp corner radius)
- 浮动操作按钮
- 底部导航栏
- Snackbar 提示信息
权限需求
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.VIBRATE" />
构建配置
build.gradle.kts (Module)
android {
compileSdk = 34
defaultConfig {
applicationId = "com.tunpok.calendarwidget"
minSdk = 24
targetSdk = 34
versionCode = 1
versionName = "1.0.0"
}
buildFeatures {
compose = true
}
}
dependencies {
// Compose
implementation("androidx.compose.ui:ui:1.5.4")
implementation("androidx.compose.material3:material3:1.1.2")
// Navigation
implementation("androidx.navigation:navigation-compose:2.7.5")
// Network
implementation("com.squareup.retrofit2:retrofit:2.9.0")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
// Database
implementation("androidx.room:room-runtime:2.6.0")
implementation("androidx.room:room-ktx:2.6.0")
// Dependency Injection
implementation("com.google.dagger:hilt-android:2.48")
// Widget
implementation("androidx.glance:glance-appwidget:1.0.0")
}
测试策略
单元测试
- Repository 层测试
- ViewModel 逻辑测试
- 日期计算和颜色映射测试
UI 测试
- Compose UI 测试
- 小组件更新测试
- 网络请求模拟测试
发布准备
ProGuard 规则
-keep class com.tunpok.calendarwidget.data.model.** { *; }
-keepattributes Signature
-keepattributes *Annotation*
版本管理
- 使用语义化版本号
- 维护 CHANGELOG.md
- 配置自动化构建和发布流程
后续优化建议
-
性能优化
- 实现图片和数据懒加载
- 使用 WorkManager 进行后台同步
- 实现智能更新策略(活动时5分钟更新,空闲时降低频率以节省电量)
-
功能扩展
- 添加任务提醒通知
- 支持任务分类和标签
- 实现任务统计图表
- 添加批量操作功能
-
用户体验
- 实现手势操作
- 添加动画效果
- 支持快捷方式
- 深色模式自动切换
-
国际化
- 支持多语言
- 适配不同地区日期格式
- 时区自动转换