bbf7a020d4
- 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) — зелёные
75 lines
2.9 KiB
Kotlin
75 lines
2.9 KiB
Kotlin
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
|
|
} |