package com.whitefish.ring import android.annotation.SuppressLint import android.app.AlertDialog import android.bluetooth.BluetoothManager import android.bluetooth.BluetoothProfile import android.content.Context import androidx.lifecycle.MutableLiveData import com.whitefish.app.bt.BleDevice import com.whitefish.ring.bt.OnBleConnectionListener import com.whitefish.ring.bt.OnBleScanCallback import com.whitefish.ring.bean.ui.Device import com.whitefish.ring.device.IDeviceManager import io.github.aakira.napier.Napier import kotlinx.coroutines.CoroutineScope import kotlinx.coroutines.Dispatchers import lib.linktop.nexring.api.BATTERY_STATE_CHARGING import lib.linktop.nexring.api.LOAD_DATA_EMPTY import lib.linktop.nexring.api.LOAD_DATA_STATE_COMPLETED import lib.linktop.nexring.api.LOAD_DATA_STATE_PROCESSING import lib.linktop.nexring.api.LOAD_DATA_STATE_START import lib.linktop.nexring.api.NexRingManager import lib.linktop.nexring.api.OnSleepDataLoadListener import lib.linktop.nexring.api.SleepData class DeviceManager() : IDeviceManager(), OnBleConnectionListener, OnSleepDataLoadListener { companion object{ const val STATE_DEVICE_CHARGING = 1 const val STATE_DEVICE_DISCHARGING = 0 const val STATE_DEVICE_DISCONNECTED = -3 const val STATE_DEVICE_CONNECTING = -2 const val STATE_DEVICE_CONNECTED = -1 } private val app = Application.INSTANTS!! private var isRegisterBattery = false val batteryLevel = MutableLiveData(STATE_DEVICE_DISCONNECTED to 0) private val sycProgress = MutableLiveData(0) var isSyncingData: Boolean = false private lateinit var context: Context private val scope = CoroutineScope(Dispatchers.IO) // var homeViewModel: demo.linktop.nexring.ui.HomeViewModel? = null // var workoutDetailViewModel: demo.linktop.nexring.ui.workout.WorkoutDetailViewModel? = null fun init(context: Context){ registerCb() this.context = context } override fun onBleState(state: Int) { bleStateListeners().forEach { it.invoke(state) } when (state) { BluetoothProfile.STATE_DISCONNECTED -> { isRegisterBattery = false batteryLevel.postValue(STATE_DEVICE_DISCONNECTED to 0) } BluetoothProfile.STATE_CONNECTED -> { batteryLevel.postValue(STATE_DEVICE_CONNECTED to 0) } } } override fun onBleReady() { bleReadyStateFlow.value = true postDelay { NexRingManager.get() .deviceApi() .getBatteryInfo { if (it.state == BATTERY_STATE_CHARGING) { batteryLevel.postValue(STATE_DEVICE_CHARGING to 0) } else { batteryLevel.postValue(STATE_DEVICE_DISCHARGING to it.level) } if (!isRegisterBattery) { isRegisterBattery = true postDelay { NexRingManager.get() .sleepApi() .syncDataFromDev() } } } } } override fun onSyncDataFromDevice(state: Int, progress: Int) { Napier.i( "onSyncDataFromDevice state: $state, progress: $progress" ) when (state) { LOAD_DATA_EMPTY -> { Napier.e("Empty data") //TODO Callback when no data is received from the device. } LOAD_DATA_STATE_START -> { isSyncingData = true sycProgress.postValue(progress) } LOAD_DATA_STATE_PROCESSING -> sycProgress.postValue(progress) LOAD_DATA_STATE_COMPLETED -> { sycProgress.postValue(progress) isSyncingData = false //todo sync data complete } } } override fun onSyncDataError(errorCode: Int) { context.cmdErrorTip(errorCode) } override fun onOutputNewSleepData(sleepData: ArrayList?) { sleepData.also { if (it.isNullOrEmpty()) { Napier.i( "onOutputNewSleepData NULL" ) } else { Napier.i( "onOutputNewSleepData size ${it.size}" ) it.forEachIndexed { index, data -> Napier.i( "onOutputNewSleepData $index sleep from ${data.startTs} to ${data.endTs}" ) } } } } fun registerCb() { app.bleManager.addOnBleConnectionListener(this) NexRingManager.get().sleepApi().setOnSleepDataLoadListener(this) } fun unregisterCb() { NexRingManager.get().sleepApi().setOnSleepDataLoadListener(null) app.bleManager.removeOnBleConnectionListener(this) } override fun connect(address: String) { with(app.bleManager) { when (bleState.value) { BluetoothProfile.STATE_DISCONNECTED -> { batteryLevel.postValue(STATE_DEVICE_CONNECTING to 0) if (!connect(address)) { startScan( 20 * 1000L, object : OnBleScanCallback { override fun onScanning(result: BleDevice) { if (result.device.address == address) { connect(result.device) } } override fun onScanFinished() { } }) } } BluetoothProfile.STATE_CONNECTED -> { onBleState(bleState.value) onBleReady() } } } } override fun bind(onBound: () -> Unit) { NexRingManager.get() .deviceApi() .getBindState { if (it) { //todo bind dialog AlertDialog.Builder(context) .setCancelable(false) .setTitle(R.string.dialog_title_restricted_mode) .setMessage(R.string.dialog_msg_restricted_mode) .setNegativeButton(android.R.string.cancel) { _, _ -> }.setPositiveButton(android.R.string.ok) { _, _ -> onResetDeviceWhenBind.invoke() NexRingManager.get() .deviceApi() .factoryReset() }.create().show() } else { NexRingManager.get() .deviceApi() .bind { onBound.invoke() //todo bind result } } } } override fun getCurrentMac(): String? { return app.bleManager.connectedDevice?.address } override fun startScan() { val bluetoothAdapter = (context.getSystemService(Context.BLUETOOTH_SERVICE) as BluetoothManager).adapter if (bluetoothAdapter.isEnabled) { if (!app.bleManager.isScanning) { app.bleManager.startScan(20 * 1000L, object : OnBleScanCallback { @SuppressLint("MissingPermission") override fun onScanning(result: BleDevice) { Napier.i("scanned device:${result}") val newDevices = arrayListOf().apply { addAll(_deviceList.value) add(Device(result.device.name,result.device.address)) } _deviceList.value = newDevices } override fun onScanFinished() { } }) } } } override fun stopScan() { } }