feat: управление яркостью/WiFi очков через Mercury + накопленные доработки
- Mercury (app-phone): управление яркостью (с авто-выкл. перед применением) и WiFi (скан/подключение/список сетей с паролем); добавлено право BLUETOOTH_ADVERTISE в манифест и runtime-запрос (без него GATT-сервер не рекламируется → очки не подключаются) - логирование (lib-core core.log): BatchingLogCollector + LogSink + LogSpool + LokiLogSink; цепочка очки→телефон→Loki; lib-core log() переадресован в коллектор - STT: Qwen3-ASR через pw.binom.asr:asr-qwen3-android, убран in-house whisper-фоллбек - audio-sync: P-регулятор с насыщением вместо фиксированной скорости - media-split: очки только video, телефон только audio (UI-чипы) - Components: форматирование размера через Locale.ROOT
This commit is contained in:
@@ -4,7 +4,13 @@ import android.app.Application
|
||||
import android.content.Context
|
||||
import com.rayneo.arsdk.android.MercurySDK
|
||||
import java.io.File
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import pw.binom.viewmate.core.log.BatchingLogCollector
|
||||
import pw.binom.viewmate.core.log.FileLogSpool
|
||||
import pw.binom.viewmate.core.log.SystemClock
|
||||
|
||||
/** Один элемент диалога в оверлее очков (роль wire: "user"/"assistant"). */
|
||||
data class ChatEntry(val role: String, val text: String)
|
||||
@@ -45,6 +51,13 @@ class GlassesApp : Application() {
|
||||
lateinit var downloadManager: GlassesDownloadManager
|
||||
private set
|
||||
|
||||
/** Лог-коллектор очков: пачки → телефон, при обрыве связи — спул в filesDir. */
|
||||
lateinit var logCollector: BatchingLogCollector
|
||||
private set
|
||||
|
||||
/** Скоуп фоновых корутин (таймер лог-коллектора). */
|
||||
private val bgScope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
|
||||
|
||||
private val prefs by lazy {
|
||||
getSharedPreferences("viewmate_prefs", Context.MODE_PRIVATE)
|
||||
}
|
||||
@@ -123,6 +136,20 @@ class GlassesApp : Application() {
|
||||
)
|
||||
hostConnection.start()
|
||||
log("app", "HostConnection стартует: mDNS-поиск, fallback: ${GlassesConfig.HOST_URL}")
|
||||
|
||||
// Лог-коллектор: пачки → телефон; обрыв связи — спул в filesDir, ретраи таймером.
|
||||
logCollector = BatchingLogCollector(
|
||||
device = "glasses",
|
||||
sink = GlassesLinkLogSink(hostConnection),
|
||||
spool = FileLogSpool(File(filesDir, "logspool").absolutePath, 32),
|
||||
clock = SystemClock(),
|
||||
flushIntervalMs = 60_000,
|
||||
maxBatchEntries = 500,
|
||||
)
|
||||
logCollector.start(bgScope)
|
||||
attachLogCollector(logCollector)
|
||||
pw.binom.viewmate.core.logCollector = logCollector
|
||||
log("app", "лог-коллектор запущен (пачки → телефон, спул: filesDir/logspool)")
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@@ -99,7 +99,8 @@ internal class GlassesStateStore(private val file: File) {
|
||||
|
||||
/**
|
||||
* Менеджер скачивания контента в память очков (аналог телефонного DownloadManager).
|
||||
* Каталог: filesDir/viewmate/{itemId}/video.mkv + audio-N.ogg. Скачивание пишется в .part
|
||||
* Каталог: filesDir/viewmate/{itemId}/video.mkv — на очки уходит только видео,
|
||||
* звук (audio-N.ogg) не скачивается, он лежит на телефоне. Скачивание пишется в .part
|
||||
* и докачивается с места обрыва (Range). Прогресс шлём на телефон через [send].
|
||||
*/
|
||||
class GlassesDownloadManager(
|
||||
|
||||
@@ -0,0 +1,20 @@
|
||||
package pw.binom.viewmate.glasses
|
||||
|
||||
import pw.binom.viewmate.core.log.LogBatch
|
||||
import pw.binom.viewmate.core.log.LogSink
|
||||
import pw.binom.viewmate.core.protocol.LogBatchMsg
|
||||
|
||||
/**
|
||||
* LogSink коллектора очков: пачка уходит на телефон по связи очков↔телефон
|
||||
* ([LogBatchMsg]). Связи нет → false: пачка прихраняется в споуле очков
|
||||
* (filesDir) и ретраится после восстановления связи.
|
||||
*/
|
||||
class GlassesLinkLogSink(
|
||||
private val connection: HostConnection,
|
||||
) : LogSink {
|
||||
override suspend fun send(batch: LogBatch): Boolean {
|
||||
if (!connection.connected.value) return false
|
||||
connection.sendToHost(LogBatchMsg(entries = batch.entries))
|
||||
return true
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,21 @@
|
||||
package pw.binom.viewmate.glasses
|
||||
|
||||
/** Единый лог очков: простой println (работает и в unit-тестах). */
|
||||
import pw.binom.viewmate.core.log.BatchingLogCollector
|
||||
|
||||
/**
|
||||
* Единый лог очков: println (видно в юнит-тестах) + лог-коллектор — пачки
|
||||
* уходят на телефон ([GlassesLinkLogSink]); при обрыве связи пачки
|
||||
* прихраняются в споуле очков (filesDir) и ретраятся позже.
|
||||
*/
|
||||
@Volatile
|
||||
private var logCollector: BatchingLogCollector? = null
|
||||
|
||||
/** Подключить коллектор (GlassesApp.onCreate, после HostConnection). */
|
||||
fun attachLogCollector(collector: BatchingLogCollector) {
|
||||
logCollector = collector
|
||||
}
|
||||
|
||||
fun log(tag: String, message: String) {
|
||||
println("[view-mate] $tag $message")
|
||||
logCollector?.record(tag, message)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user