Ассистент Порфирий: тула времени, сессии, чат-UI, голосовой цикл; BT-транспорт очки↔телефон
- BT (RFCOMM SPP 00001105) вместо WiFi: BtGlassesTransport/BtServerTransport, реконнект 3с - PlaybackService (foreground, WAKE_LOCK) против морозов HONOR - Ассистент: AgentTools (TimeTool ISO 8601), TOOL_CALL-протокол, сессии SQLite (UUID) - Чат-UI: создание/сброс/переключение сессий, автообновление ленты 2с - LlmClient на прямом OkHttp (Koog давал пустой content на длинных историях) - Голосовой цикл: тройной тап на очках → STT → активная сессия → ShowText - Тесты: AgentToolsTest (3), GlassesServerTest переписан (6) — зелёные
This commit is contained in:
@@ -0,0 +1,75 @@
|
||||
package pw.binom.viewmate.phone
|
||||
|
||||
import android.app.NotificationChannel
|
||||
import android.app.NotificationManager
|
||||
import android.app.Service
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.wifi.WifiManager
|
||||
import android.os.IBinder
|
||||
import android.os.PowerManager
|
||||
import androidx.core.app.NotificationCompat
|
||||
import androidx.core.content.ContextCompat
|
||||
|
||||
/**
|
||||
* Foreground service типа mediaPlayback: держит процесс телефона живым (WS-сервер +
|
||||
* аудио-плеер) при выключенном экране. Без него HONOR замораживает/убивает процесс
|
||||
* в фоне → очки теряют связь (видео паузится) и звук встаёт. Является спутником
|
||||
* WS-сервера: стартует в PhoneApp.onCreate, останавливается в onTerminate.
|
||||
*/
|
||||
class PlaybackService : Service() {
|
||||
companion object {
|
||||
const val CHANNEL_ID = "viewmate_playback"
|
||||
const val NOTIFICATION_ID = 1
|
||||
|
||||
fun start(context: Context) {
|
||||
ContextCompat.startForegroundService(
|
||||
context, Intent(context, PlaybackService::class.java)
|
||||
)
|
||||
}
|
||||
|
||||
fun stop(context: Context) {
|
||||
context.stopService(Intent(context, PlaybackService::class.java))
|
||||
}
|
||||
}
|
||||
|
||||
private var wakeLock: PowerManager.WakeLock? = null
|
||||
private var wifiLock: WifiManager.WifiLock? = null
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
val channel = NotificationChannel(
|
||||
CHANNEL_ID, "ViewMate playback",
|
||||
NotificationManager.IMPORTANCE_LOW,
|
||||
).apply { setShowBadge(false) }
|
||||
getSystemService(NotificationManager::class.java).createNotificationChannel(channel)
|
||||
|
||||
val notification = NotificationCompat.Builder(this, CHANNEL_ID)
|
||||
.setSmallIcon(android.R.drawable.ic_media_play)
|
||||
.setContentTitle("ViewMate")
|
||||
.setContentText("Звук на телефоне при просмотре на очках")
|
||||
.setOngoing(true)
|
||||
.build()
|
||||
startForeground(NOTIFICATION_ID, notification)
|
||||
|
||||
// держим CPU и WiFi живыми при выключенном экране
|
||||
wakeLock = (getSystemService(PowerManager::class.java) as PowerManager)
|
||||
.newWakeLock(PowerManager.PARTIAL_WAKE_LOCK, "viewmate:audio")
|
||||
?.apply { acquire() }
|
||||
wifiLock = (getApplicationContext().getSystemService(WIFI_SERVICE) as WifiManager)
|
||||
.createWifiLock(WifiManager.WIFI_MODE_FULL_HIGH_PERF, "viewmate:wifi")
|
||||
?.apply { acquire() }
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
|
||||
START_STICKY
|
||||
|
||||
override fun onDestroy() {
|
||||
wakeLock?.takeIf { it.isHeld }?.release()
|
||||
wifiLock?.takeIf { it.isHeld }?.release()
|
||||
super.onDestroy()
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? = null
|
||||
}
|
||||
Reference in New Issue
Block a user