feat(ota): OTA APK 下载更新模块

REQ-20260430-0041

新增 UpdateManager:
- POST newAppVersion/queryWatch 检查版本(5分钟最小间隔)
- OkHttp 下载 APK(进度回调,独立超时配置)
- FileProvider + Intent ACTION_VIEW 触发系统安装
- 非 .apk 链接自动跳过

新增 UpdateDialogView:
- 全屏覆盖弹窗,三种状态(发现新版本/下载中/下载失败)
- 进度条支持百分比和未知大小两种模式

配置变更:
- AndroidManifest: REQUEST_INSTALL_PACKAGES + FileProvider
- CommonApi.checkVersion: GET→POST,传 version 参数

集成点:
- HomeFragment.onResume → MainActivity.checkForUpdate()
- MainActivity 管理弹窗交互和下载流程
- 更新时停止蓝牙扫描 + 保持屏幕常亮

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
dongliang
2026-04-30 21:20:06 +09:30
parent 76997f4830
commit 6e8b210f8e
9 changed files with 521 additions and 3 deletions

View File

@@ -9,9 +9,13 @@ import androidx.appcompat.app.AppCompatActivity
import com.xiaoqu.watch.databinding.ActivityMainBinding
import com.xiaoqu.watch.event.AppEvent
import com.xiaoqu.watch.event.EventBus
import com.xiaoqu.watch.device.screen.ScreenController
import com.xiaoqu.watch.device.sensor.AccelerometerWakeController
import com.xiaoqu.watch.service.manager.BluetoothScanManager
import com.xiaoqu.watch.service.manager.NotificationManager
import com.xiaoqu.watch.service.manager.SystemStateMonitor
import com.xiaoqu.watch.service.manager.UpdateManager
import com.xiaoqu.watch.ui.widget.UpdateDialogView
import com.xiaoqu.watch.ui.widget.NotificationBannerView
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.CoroutineScope
@@ -37,6 +41,12 @@ class MainActivity : AppCompatActivity() {
@Inject lateinit var eventBus: EventBus
/** 加速度计抬手亮屏控制器 */
@Inject lateinit var accelerometerWake: AccelerometerWakeController
/** OTA 更新管理器 */
@Inject lateinit var updateManager: UpdateManager
@Inject lateinit var screenController: ScreenController
@Inject lateinit var bluetoothScanManager: BluetoothScanManager
/** OTA 更新弹窗 */
lateinit var updateDialog: UpdateDialogView
lateinit var notificationBanner: NotificationBannerView
private val activityScope = CoroutineScope(Dispatchers.Main + SupervisorJob())
@@ -70,6 +80,10 @@ class MainActivity : AppCompatActivity() {
// 初始化通知横幅
notificationBanner = binding.notificationBanner
// 初始化 OTA 更新弹窗
updateDialog = binding.updateDialog
setupUpdateDialog()
// 监听 MQTT type=1 → 处理通知 + 显示横幅
observeMqttMessages()
@@ -166,4 +180,64 @@ class MainActivity : AppCompatActivity() {
}
})
}
// ===== OTA 更新 =====
/** 设置更新弹窗按钮回调 */
private fun setupUpdateDialog() {
updateDialog.onActionClick = {
startDownloadAndInstall()
}
}
/**
* 检查版本更新(由 HomeFragment.onResume 调用)
* 有更新时显示弹窗,停止蓝牙扫描
*/
fun checkForUpdate() {
if (updateManager.isUpdating) return
activityScope.launch {
val info = updateManager.checkUpdate() ?: return@launch
// 停止蓝牙扫描
bluetoothScanManager.stop()
// 显示更新弹窗
updateDialog.showDiscover()
// 保存下载地址
pendingUpdateUrl = info.url
}
}
/** 待下载的更新 URL */
private var pendingUpdateUrl: String? = null
/** 开始下载并安装 APK */
private fun startDownloadAndInstall() {
val url = pendingUpdateUrl ?: return
updateManager.isUpdating = true
// 保持屏幕常亮
screenController.turnOn()
// 切换到下载状态
updateDialog.showDownloading()
activityScope.launch {
val file = updateManager.downloadApk(url) { progress, bytes ->
// 进度回调在 IO 线程,切回主线程更新 UI
launch(kotlinx.coroutines.Dispatchers.Main) {
updateDialog.updateProgress(progress, bytes)
}
}
// 回到主线程处理结果
kotlinx.coroutines.withContext(kotlinx.coroutines.Dispatchers.Main) {
if (file != null) {
// 下载成功 → 触发安装
updateManager.installApk(file)
} else {
// 下载失败 → 显示错误
updateManager.isUpdating = false
updateDialog.showError()
}
}
}
}
}