1.修复轨迹和宠物有时不在一条线上的bug

2.增加pet页切换刷新运动数据
3.修复绑定设备激活重启,此时点击太快进入wifi 搜索不到bug
4.蓝牙透传通路由Raw TCP改为Https
This commit is contained in:
2026-07-24 10:18:46 +08:00
parent f51e8f24de
commit 0a128cb8a6
19 changed files with 434 additions and 155 deletions

View File

@@ -28,9 +28,9 @@ android {
applicationId "com.abbidot.tracker"
minSdkVersion 24
targetSdkVersion 35
versionCode 2216
// versionName "2.2.16"
versionName "2.2.16-Beta1"
versionCode 2300
// versionName "2.3.0"
versionName "2.3.0-Beta1"
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"

View File

@@ -5,6 +5,7 @@ import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.base.BaseDiffBean
import com.abbidot.tracker.retrofit2.INetworkService
import kotlinx.parcelize.Parcelize
/**
@@ -38,12 +39,13 @@ data class PetBean(
var availableOrder: Int,//判断套餐是否可用或过期1是可用 0不可用
var latitude: Double,
var longitude: Double,
var serverDomain: String,
var deviceId: String
) : Parcelable, BaseDiffBean() {
constructor() : this(
"", "", "", "", 0, 1, MMKVUtil.getString(MMKVKey.UserId), "", 0, Utils.formatTime(
System.currentTimeMillis(), Utils.DATE_FORMAT_PATTERN_CN
), "", 0f, 0f, "", "", "", 1, 1, "", false, false, 1, 0.0, 0.0, ""
), "", 0f, 0f, "", "", "", 1, 1, "", false, false, 1, 0.0, 0.0, INetworkService.BASE_URL_BLE, ""
)
override fun isSameObject(other: Any): Boolean {

View File

@@ -37,6 +37,10 @@ import com.abbidot.tracker.bean.UserInfoBean
import com.abbidot.tracker.bean.WXPayOrderBean
import com.abbidot.tracker.bean.WiFiZoneListBean
import okhttp3.MultipartBody
import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.Response
import retrofit2.http.Body
import retrofit2.http.Field
import retrofit2.http.FormUrlEncoded
import retrofit2.http.GET
@@ -44,6 +48,7 @@ import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import retrofit2.http.Query
import retrofit2.http.Url
interface INetworkService {
@@ -57,6 +62,9 @@ interface INetworkService {
//亚马逊服务器(国外ip)
const val BASE_URL_ABROAD = "https://aws.abbidot.com/abbidot/"
//设备透传
const val BASE_URL_BLE = "https://ab-server.abbidot.cn/"
//测试服务器
const val BASE_URL_TEST = "https://aws.abbidot.com:8443/abbidot/"
// const val BASE_URL_TEST = "http://aws.abbidot.com:8089/abbidot/"
@@ -80,6 +88,13 @@ interface INetworkService {
// const val BASE_URL_CN1 = "$SERVER_IP_CN/abbidotServer/"
}
/**
* 蓝牙透传机制从TCP迁移到https
*/
@POST
// @Url 一定要传入以“/”结尾注解会替换 baseUrl每次调用可传入完整 URL
suspend fun blePassThrough(@Url url: String, @Body body: RequestBody): Response<ResponseBody>
/**
* 获取国家代码号
*/
@@ -1362,6 +1377,7 @@ interface INetworkService {
@POST("map/setupRefreshLocation")
suspend fun setupRefreshLocation(@Field("deviceId") deviceId: String): BaseResponse<String>
/*******************************************以下是国内版本接口***********************************/
/**
* 获取微信登录的OpedId

View File

@@ -7,6 +7,7 @@ import com.abbidot.tracker.bean.SetMealBean
import com.abbidot.tracker.bean.SubscriptionsOrderBean
import com.abbidot.tracker.constant.ConstantInt
import okhttp3.MultipartBody
import okhttp3.RequestBody
/**
@@ -803,6 +804,12 @@ object NetworkApi : BaseNetworkApi<INetworkService>(INetworkService.BASE_URL) {
service.getRechargeHistory(deviceId)
}
/**
* 蓝牙透传机制从TCP迁移到https
*/
suspend fun blePassThrough(url: String, body: RequestBody) =
service.blePassThrough(url, body)
/**
* 取消自动续费/自动订阅
*/

View File

@@ -185,7 +185,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
// 当网络可用时调用
super.onAvailable(network)
LogUtil.e("当网络可用时,onAvailable")
if (mNetworkRequestsFailRetryCount == mNetworkRequestsFailMaxCount && mPetList.size == 0) {
if (mNetworkRequestsFailRetryCount == mNetworkRequestsFailMaxCount && mPetList.isEmpty()) {
mNetworkRequestsFailRetryCount = 0
isFirst = false
mDataViewModel.getHomeBindPetList(this@HomeV2Activity)
@@ -239,7 +239,9 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
}
}
override fun liveDataObserve() {/*
override fun liveDataObserve() {
/*
Dispatchers.IO 在Android中主要用于执行输入输出操作如文件读写和网络请求
Dispatchers.Main适用于处理与UI相关的操作如更新UI界面、响应用户输入等。
Dispatchers.Default适用于执行CPU密集型任务如算法计算、数据处理等
@@ -358,6 +360,21 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
}
}
}
//蓝牙透传
XEventBus.observe(this, EventName.BlePassThrough) { receiveData: ReceiveDeviceData ->
getPet(false)?.apply {
receiveData.data?.let { d ->
val sendData = d.sliceArray(8..d.size - 2)
if (macID == receiveData.mac) {
mDataViewModel.blePassThrough(this, sendData)
} else {
getMacPet(receiveData.mac)?.let { p ->
mDataViewModel.blePassThrough(p, sendData)
}
}
}
}
}
//蓝牙上报的设备日志
mLogBleReportViewModel.apply {
@@ -392,7 +409,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
override fun onResult(any: Any) {
LogUtil.e("HomeV2Activity------>>获取所有绑定宠物数据")
isRequestPetData = true
if (mPetList.size > 0) {
if (mPetList.isNotEmpty()) {
it.getOrNull()?.apply {
for (oPet in mPetList) {
for (nPet in this) {
@@ -597,7 +614,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
*/
fun selectPetDialog(checkPackageExpire: Boolean = true) {
isCheckPackageExpire = checkPackageExpire
if (mPetList.size == 0) {
if (mPetList.isEmpty()) {
showToast(R.string.no_bind_pet)
return
}
@@ -608,13 +625,29 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
}
fun getPet(showNoPetTip: Boolean = true): PetBean? {
return if (mPetList.size > 0) mPetList[mSelectPetPosition]
return if (mPetList.isNotEmpty()) mPetList[mSelectPetPosition]
else {
if (isRequestPetData && showNoPetTip) showToast(R.string.no_bind_pet)
null
}
}
/**
* 获取指定mac宠物
*/
fun getMacPet(mac: String): PetBean? {
return if (mPetList.isNotEmpty()) {
for (p in mPetList) {
if (p.macID == mac) {
return p
}
}
null
} else {
null
}
}
/**
* 跳转debug调试页面
*/

View File

@@ -24,7 +24,6 @@ import com.abbidot.tracker.constant.ConstantString
import com.abbidot.tracker.constant.GetResultCallback
import com.abbidot.tracker.databinding.ActivityAddPairedSuccessBinding
import com.abbidot.tracker.deprecated.ui.activity.vm.AddTrackerDeviceViewModel
import com.abbidot.tracker.ui.activity.device.wifi.AddWifiPowerZone1Activity
import com.abbidot.tracker.ui.activity.subscribe.SubscriptionPlanActivity
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.ViewUtil
@@ -238,9 +237,10 @@ class AddPairedSuccessActivity :
if (isFirstBind) {
mPetBean?.apply {
val intent =
Intent(mContext, AddWifiPowerZone1Activity::class.java)
Intent(mContext, InitializingDeviceActivity::class.java)
intent.putExtra(ConstantString.Pet, this)
intent.putExtra(ConstantString.isFirstBind, isFirstBind)
intent.putExtra(ConstantString.Type, mType)
startActivity(intent)
}
}

View File

@@ -1,6 +1,7 @@
package com.abbidot.tracker.ui.activity.device
import android.content.Intent
import androidx.activity.viewModels
import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.bean.PetBean
@@ -9,10 +10,12 @@ import com.abbidot.tracker.constant.ConstantString
import com.abbidot.tracker.databinding.ActivityInitializingDeviceBinding
import com.abbidot.tracker.ui.activity.device.wifi.AddWifiPowerZone1Activity
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.vm.CountDownTimerViewModel
class InitializingDeviceActivity :
BaseActivity<ActivityInitializingDeviceBinding>(ActivityInitializingDeviceBinding::inflate) {
private val mCountDownTimerViewModel: CountDownTimerViewModel by viewModels()
private var mPetBean: PetBean? = null
private var isFirstBind = false
private var mDeviceType = ConstantInt.Type1
@@ -43,6 +46,22 @@ class InitializingDeviceActivity :
startActivityFinish(intent)
}
}
mCountDownTimerViewModel.startCountDown(6)
}
override fun liveDataObserve() {
mCountDownTimerViewModel.apply {
mCountDowningLiveData.observe(this@InitializingDeviceActivity) {
val string = getString(R.string.txt_continue) + " (${it}s)"
mViewBinding.btnPaySuccessInitDeviceNext.text = string
}
mCountDownEndLiveData.observe(this@InitializingDeviceActivity) {
mViewBinding.btnPaySuccessInitDeviceNext.let { v ->
v.setText(R.string.txt_continue)
setButtonEnabled(v, ConstantInt.Type1)
}
}
}
}
override fun leftBackOnClick() {

View File

@@ -469,10 +469,10 @@ class SureSubscriptionPlanActivity :
planTimeMonthsCount = cPlan.planTimeMonthsCount
mealUnit = cPlan.mealUnit
planCategory = cPlan.planCategory
autoRenewPrice =
Utils.formatDecimal(cPlan.autoRenewPrice + cPlan.autoRenewPrice * mTaxRate, 2)
.toDouble()
// autoRenewPrice = cPlan.autoRenewPrice
// autoRenewPrice =
// Utils.formatDecimal(cPlan.autoRenewPrice + cPlan.autoRenewPrice * mTaxRate, 2)
// .toDouble()
autoRenewPrice = cPlan.autoRenewPrice
mealDesc =
mViewBinding.ilSubscribePlanDetail.tvSureSubscribePlanMoney.text.toString() + mViewBinding.ilSubscribePlanDetail.tvSureSubscribePlanPer.text.toString()
}

View File

@@ -284,7 +284,7 @@ class ActivityV2Fragment :
*/
fun showPetNameAndHead(position: Int) {
getHomeV2Activity()?.let {
if (it.mPetList.size == 0) {
if (it.mPetList.isEmpty()) {
return
}
mCurrentShowPetPos = position

View File

@@ -38,6 +38,11 @@ import com.abbidot.tracker.widget.NoClickSlideSeekBar
import com.abbidot.tracker.widget.TypefaceTextView
import kotlinx.coroutines.launch
import kotlin.math.abs
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
/**
@@ -482,7 +487,7 @@ class RouteV3Fragment : BaseFragment<FragmentRouteV3Binding>(FragmentRouteV3Bind
*/
fun showPetNameAndHead(position: Int) {
getHomeV2Activity()?.apply {
if (mPetList.size == 0) {
if (mPetList.isEmpty()) {
return
}
mCurrentShowPetPos = position
@@ -503,7 +508,19 @@ class RouteV3Fragment : BaseFragment<FragmentRouteV3Binding>(FragmentRouteV3Bind
private fun setLatLngData(data: MutableList<HistoryDataBean>) {
viewLifecycleOwner.lifecycleScope.launch {
mHistoryDataList = data
/*慢速步行epsilon=0.5,插值 2 个
日常骑行epsilon=1.0,插值 2 个
机动车行驶epsilon=1.5~2.0,插值 3 个
**/
// 1. 降噪
val filterPoints = filterNoisePoint(data)
// 2. RDP平滑抽稀
val smoothPoints = rdpSmooth(filterPoints, epsilon = 0.5)
// 3. 插值加密线条
val finalPoints = latLngInterpolate(smoothPoints, step = 2)
mHistoryDataList = finalPoints.toMutableList()
getHomeV2Activity()?.getPet(false)?.let {
mHistoryDataMapCommon.setPetBean(it)
}
@@ -517,17 +534,17 @@ class RouteV3Fragment : BaseFragment<FragmentRouteV3Binding>(FragmentRouteV3Bind
}
}
}
if (data.size == 0) {
if (finalPoints.isEmpty()) {
setSeekBarShowHide(false)
mHistoryDataMapCommon.setPetUserLatLng()
return@launch
}
setSeekBarShowHide(true)
val max = data.size - 1
val max = finalPoints.size - 1
setSeekBarMax(max)
mHistoryDataMapCommon.setLatLngData(data)
mHistoryDataMapCommon.setLatLngData(finalPoints.toMutableList())
}
}
@@ -699,4 +716,134 @@ class RouteV3Fragment : BaseFragment<FragmentRouteV3Binding>(FragmentRouteV3Bind
}
}
}
/**
* 两点间距离 单位米
*/
private fun distanceBetween(p1: HistoryDataBean, p2: HistoryDataBean): Double {
val r = 6371000.0
val lat1 = p1.latitude * Math.PI / 180
val lng1 = p1.longitude * Math.PI / 180
val lat2 = p2.latitude * Math.PI / 180
val lng2 = p2.longitude * Math.PI / 180
val dLat = lat2 - lat1
val dLng = lng2 - lng1
val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLng / 2).pow(2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return r * c
}
/**
* Ramer-Douglas-Peucker 轨迹抽稀平滑
* @param epsilon 阈值 建议0.5~2.0
*/
private fun rdpSmooth(
points: List<HistoryDataBean>, epsilon: Double = 1.0
): List<HistoryDataBean> {
if (points.size < 3) return points
var maxDist = 0.0
var index = 0
val start = points.first()
val end = points.last()
for (i in 1 until points.lastIndex) {
val dist = pointToLineDist(p = points[i], a = start, b = end)
if (dist > maxDist) {
maxDist = dist
index = i
}
}
return if (maxDist > epsilon) {
val left = rdpSmooth(points.subList(0, index + 1), epsilon)
val right = rdpSmooth(points.subList(index, points.size), epsilon)
left.dropLast(1) + right
} else {
listOf(start, end)
}
}
/**
* 点到线段距离
*/
private fun pointToLineDist(
p: HistoryDataBean,
a: HistoryDataBean,
b: HistoryDataBean
): Double {
val ax = a.longitude
val ay = a.latitude
val bx = b.longitude
val by = b.latitude
val px = p.longitude
val py = p.latitude
val cross = (px - ax) * (bx - ax) + (py - ay) * (by - ay)
if (cross <= 0.0) return distanceBetween(p1 = p, p2 = a)
val len2 = (bx - ax).pow(2) + (by - ay).pow(2)
if (cross >= len2) return distanceBetween(p1 = p, p2 = b)
val t = cross / len2
val nearX = ax + t * (bx - ax)
val nearY = ay + t * (by - ay)
return distanceBetween(
p1 = p,
p2 = HistoryDataBean().apply {
latitude = nearY
longitude = nearX
}
)
}
/**
* 相邻两点插值补点,让线条更顺滑
* @param step 插值个数 推荐2~3
*/
private fun latLngInterpolate(
points: List<HistoryDataBean>,
step: Int = 2
): List<HistoryDataBean> {
if (points.size < 2) return points
val result = mutableListOf<HistoryDataBean>()
for (i in 0 until points.size - 1) {
val p0 = points[i]
val p1 = points[i + 1]
result.add(p0)
for (s in 1..step) {
val ratio = s.toDouble() / (step + 1)
val lat = p0.latitude + (p1.latitude - p0.latitude) * ratio
val lng = p0.longitude + (p1.longitude - p0.longitude) * ratio
result.add(HistoryDataBean().apply {
latitude = lat
longitude = lng
})
}
}
result.add(points.last())
return result
}
/**
* GPS降噪过滤跳点漂移
*/
private fun filterNoisePoint(
data: List<HistoryDataBean>,
minDis: Double = 5.0,
maxDis: Double = 60.0
): List<HistoryDataBean> {
if (data.isEmpty()) return emptyList()
val res = mutableListOf<HistoryDataBean>().apply { add(data.first()) }
for (i in 1 until data.size) {
val last = res.last()
val d = data[i]
val dis = distanceBetween(p1 = last, p2 = d)
if (dis in minDis..maxDis) {
res.add(d)
}
}
return res
}
}

View File

@@ -63,12 +63,6 @@ import com.google.android.gms.tasks.CancellationTokenSource
import com.google.android.gms.tasks.Task
import com.qmuiteam.qmui.util.QMUIDisplayHelper
import kotlinx.coroutines.launch
import kotlin.math.PI
import kotlin.math.atan2
import kotlin.math.cos
import kotlin.math.pow
import kotlin.math.sin
import kotlin.math.sqrt
/**
@@ -838,21 +832,10 @@ abstract class BaseGoogleMapFragment :
latLngList
}
/*慢速步行epsilon=0.5,插值 2 个
日常骑行epsilon=1.0,插值 2 个
机动车行驶epsilon=1.5~2.0,插值 3 个
**/
// 1. 降噪
val filterPoints = filterNoisePoint(newLatLngList)
// 2. RDP平滑抽稀
val smoothPoints = rdpSmooth(filterPoints, epsilon = 0.5)
// 3. 插值加密线条
val finalPoints = latLngInterpolate(smoothPoints, step = 2)
//先黄黑线
getPolylineOptions(8f, R.color.line_stroke_color).let {
it.geodesic(true)
it.addAll(finalPoints)
it.addAll(newLatLngList)
googleMap.addPolyline(it).apply {
startCap = RoundCap()
endCap = RoundCap()
@@ -868,7 +851,7 @@ abstract class BaseGoogleMapFragment :
//再画黄线
getPolylineOptions(5f, R.color.rote_line_color).let {
it.geodesic(true)
it.addAll(finalPoints)
it.addAll(newLatLngList)
googleMap.addPolyline(it).apply {
startCap = RoundCap()
endCap = RoundCap()
@@ -1300,112 +1283,112 @@ abstract class BaseGoogleMapFragment :
}
/**
* 两点间距离 单位米
*/
private fun distanceBetween(p1: LatLng, p2: LatLng): Double {
val r = 6371000.0
val lat1 = p1.latitude * PI / 180
val lng1 = p1.longitude * PI / 180
val lat2 = p2.latitude * PI / 180
val lng2 = p2.longitude * PI / 180
val dLat = lat2 - lat1
val dLng = lng2 - lng1
val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLng / 2).pow(2)
val c = 2 * atan2(sqrt(a), sqrt(1 - a))
return r * c
}
/**
* Ramer-Douglas-Peucker 轨迹抽稀平滑
* @param epsilon 阈值 建议0.5~2.0
*/
private fun rdpSmooth(points: List<LatLng>, epsilon: Double = 1.0): List<LatLng> {
if (points.size < 3) return points
var maxDist = 0.0
var index = 0
val start = points.first()
val end = points.last()
for (i in 1 until points.lastIndex) {
val dist = pointToLineDist(points[i], start, end)
if (dist > maxDist) {
maxDist = dist
index = i
}
}
return if (maxDist > epsilon) {
val left = rdpSmooth(points.subList(0, index + 1), epsilon)
val right = rdpSmooth(points.subList(index, points.size), epsilon)
left.dropLast(1) + right
} else {
listOf(start, end)
}
}
/**
* 点到线段距离
*/
private fun pointToLineDist(p: LatLng, a: LatLng, b: LatLng): Double {
val ax = a.longitude
val ay = a.latitude
val bx = b.longitude
val by = b.latitude
val px = p.longitude
val py = p.latitude
val cross = (px - ax) * (bx - ax) + (py - ay) * (by - ay)
if (cross <= 0.0) return distanceBetween(p, a)
val len2 = (bx - ax).pow(2) + (by - ay).pow(2)
if (cross >= len2) return distanceBetween(p, b)
val t = cross / len2
val nearX = ax + t * (bx - ax)
val nearY = ay + t * (by - ay)
return distanceBetween(p, LatLng(nearY, nearX))
}
/**
* 相邻两点插值补点,让线条更顺滑
* @param step 插值个数 推荐2~3
*/
private fun latLngInterpolate(points: List<LatLng>, step: Int = 2): List<LatLng> {
if (points.size < 2) return points
val result = mutableListOf<LatLng>()
for (i in 0 until points.size - 1) {
val p0 = points[i]
val p1 = points[i + 1]
result.add(p0)
for (s in 1..step) {
val ratio = s.toDouble() / (step + 1)
val lat = p0.latitude + (p1.latitude - p0.latitude) * ratio
val lng = p0.longitude + (p1.longitude - p0.longitude) * ratio
result.add(LatLng(lat, lng))
}
}
result.add(points.last())
return result
}
/**
* GPS降噪过滤跳点漂移
*/
private fun filterNoisePoint(
points: List<LatLng>, minDis: Double = 5.0, maxDis: Double = 60.0
): List<LatLng> {
if (points.isEmpty()) return emptyList()
val res = mutableListOf<LatLng>().apply { add(points.first()) }
for (i in 1 until points.size) {
val last = res.last()
val dis = distanceBetween(last, points[i])
if (dis in minDis..maxDis) {
res.add(points[i])
}
}
return res
}
// /**
// * 两点间距离 单位米
// */
// private fun distanceBetween(p1: LatLng, p2: LatLng): Double {
// val r = 6371000.0
// val lat1 = p1.latitude * PI / 180
// val lng1 = p1.longitude * PI / 180
// val lat2 = p2.latitude * PI / 180
// val lng2 = p2.longitude * PI / 180
//
// val dLat = lat2 - lat1
// val dLng = lng2 - lng1
// val a = sin(dLat / 2).pow(2) + cos(lat1) * cos(lat2) * sin(dLng / 2).pow(2)
// val c = 2 * atan2(sqrt(a), sqrt(1 - a))
// return r * c
// }
//
// /**
// * Ramer-Douglas-Peucker 轨迹抽稀平滑
// * @param epsilon 阈值 建议0.5~2.0
// */
// private fun rdpSmooth(points: List<LatLng>, epsilon: Double = 1.0): List<LatLng> {
// if (points.size < 3) return points
// var maxDist = 0.0
// var index = 0
// val start = points.first()
// val end = points.last()
//
// for (i in 1 until points.lastIndex) {
// val dist = pointToLineDist(points[i], start, end)
// if (dist > maxDist) {
// maxDist = dist
// index = i
// }
// }
//
// return if (maxDist > epsilon) {
// val left = rdpSmooth(points.subList(0, index + 1), epsilon)
// val right = rdpSmooth(points.subList(index, points.size), epsilon)
// left.dropLast(1) + right
// } else {
// listOf(start, end)
// }
// }
//
// /**
// * 点到线段距离
// */
// private fun pointToLineDist(p: LatLng, a: LatLng, b: LatLng): Double {
// val ax = a.longitude
// val ay = a.latitude
// val bx = b.longitude
// val by = b.latitude
// val px = p.longitude
// val py = p.latitude
//
// val cross = (px - ax) * (bx - ax) + (py - ay) * (by - ay)
// if (cross <= 0.0) return distanceBetween(p, a)
//
// val len2 = (bx - ax).pow(2) + (by - ay).pow(2)
// if (cross >= len2) return distanceBetween(p, b)
//
// val t = cross / len2
// val nearX = ax + t * (bx - ax)
// val nearY = ay + t * (by - ay)
// return distanceBetween(p, LatLng(nearY, nearX))
// }
//
// /**
// * 相邻两点插值补点,让线条更顺滑
// * @param step 插值个数 推荐2~3
// */
// private fun latLngInterpolate(points: List<LatLng>, step: Int = 2): List<LatLng> {
// if (points.size < 2) return points
// val result = mutableListOf<LatLng>()
// for (i in 0 until points.size - 1) {
// val p0 = points[i]
// val p1 = points[i + 1]
// result.add(p0)
// for (s in 1..step) {
// val ratio = s.toDouble() / (step + 1)
// val lat = p0.latitude + (p1.latitude - p0.latitude) * ratio
// val lng = p0.longitude + (p1.longitude - p0.longitude) * ratio
// result.add(LatLng(lat, lng))
// }
// }
// result.add(points.last())
// return result
// }
//
// /**
// * GPS降噪过滤跳点漂移
// */
// private fun filterNoisePoint(
// points: List<LatLng>, minDis: Double = 5.0, maxDis: Double = 60.0
// ): List<LatLng> {
// if (points.isEmpty()) return emptyList()
// val res = mutableListOf<LatLng>().apply { add(points.first()) }
// for (i in 1 until points.size) {
// val last = res.last()
// val dis = distanceBetween(last, points[i])
// if (dis in minDis..maxDis) {
// res.add(points[i])
// }
// }
// return res
// }
}

View File

@@ -119,12 +119,13 @@ class PetV2Fragment : BaseFragment<FragmentPetV2Binding>(FragmentPetV2Binding::i
override fun onResume() {
super.onResume()
mNotificationsViewModel.getMessageUnReadCount()
(mFragments[1] as HomeTrackFragment).onResume()
// (mFragments[1] as HomeTrackFragment).onResume()
getHomeV2Activity()?.apply {
//其他页面是否选择了宠物
if (mCurrentShowPetPos != mSelectPetPosition) {
showPetNameAndHead(mSelectPetPosition)
} else {
(mFragments[0] as HomePetFragment).getPetAllInfoData()
(mFragments[1] as HomeTrackFragment).getPetTrackerInfoData()
}
}

View File

@@ -375,6 +375,9 @@ class SRBleCmdUtil private constructor() {
return getCrc8Cmd(byteArray)
}
/**
* 蓝牙透传上报设备信息
*/
fun setProxy(result: Int, data: ByteArray?): ByteArray {
if (null == data) {
return getCrc8Cmd(byteArrayOf(0x24, result.toByte(), 0))

View File

@@ -242,10 +242,8 @@ class SRBleUtil private constructor() {
XEventBus.post(EventName.BleGetLocation, bleDevice.mac)
} else if (data0 == 0x24) {
LogUtil.e("${bleDevice.mac},蓝牙透传消息")
if (null == mSocketUtilManage) mSocketUtilManage = SocketUtilManageV2()
mContext?.let {
mSocketUtilManage?.initEasySocket(it, bleDevice.mac, data)
}
val deviceData = ReceiveDeviceData(bleDevice, data, bleDevice.mac)
XEventBus.post(EventName.BlePassThrough, deviceData)
} else if (data0 == 0x09 && data1 == 0x00) {
val deviceData = ReceiveDeviceData(bleDevice, data, bleDevice.mac)
if (isNotifyFirmware) {

View File

@@ -4,12 +4,22 @@ import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.tracker.base.BaseActivity
import com.abbidot.tracker.bean.HomeDataBean
import com.abbidot.tracker.bean.PetBean
import com.abbidot.tracker.retrofit2.INetworkService
import com.abbidot.tracker.retrofit2.NetworkApi
import com.abbidot.tracker.util.bluetooth.SRBleCmdUtil
import com.abbidot.tracker.util.bluetooth.SRBleUtil
import com.mapbox.core.utils.TextUtils
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import okhttp3.MediaType.Companion.toMediaType
import okhttp3.RequestBody.Companion.toRequestBody
/**
*Created by .yzq on 2022/3/11/011.
@@ -18,6 +28,8 @@ import kotlinx.coroutines.launch
*/
class DataViewModel : ViewModel() {
private val mMutex = Mutex()
val mHomePetLiveData = MutableLiveData<Result<MutableList<PetBean>>>()
val mHomeDataLiveData = MutableLiveData<Result<HomeDataBean>>()
@@ -50,4 +62,44 @@ class DataViewModel : ViewModel() {
}
}
fun blePassThrough(petBean: PetBean, binaryData: ByteArray) {
viewModelScope.launch(Dispatchers.IO) {
mMutex.withLock {
try {
var baseUrl =
if (TextUtils.isEmpty(petBean.serverDomain)) INetworkService.BASE_URL_BLE
else petBean.serverDomain
if (!baseUrl.startsWith("https")) baseUrl = "https://$baseUrl"
if (!baseUrl.endsWith("/")) baseUrl = "$baseUrl/"
val requestBody =
binaryData.toRequestBody("application/octet-stream".toMediaType())
val response =
NetworkApi.blePassThrough("${baseUrl}BlePassThrough", requestBody)
if (response.isSuccessful) {
// 读取响应二进制
val b = response.body()
if (null == b) {
SRBleUtil.instance.isConnectBleSendCmdData(
petBean.macID, SRBleCmdUtil.instance.setProxy(1, null)
)
} else {
val result = b.bytes()
SRBleUtil.instance.isConnectBleSendCmdData(
petBean.macID, SRBleCmdUtil.instance.setProxy(0, result)
)
}
} else {
SRBleUtil.instance.isConnectBleSendCmdData(
petBean.macID, SRBleCmdUtil.instance.setProxy(1, null)
)
}
} catch (e: Exception) {
SRBleUtil.instance.isConnectBleSendCmdData(
petBean.macID, SRBleCmdUtil.instance.setProxy(1, null)
)
LogUtil.e("蓝牙透传解析异常:${e.message}")
}
}
}
}
}

View File

@@ -55,6 +55,7 @@
<com.abbidot.tracker.widget.TypefaceButton
android:id="@+id/btn_pay_success_init_device_next"
style="@style/my_match_RoundRect_Button_style"
android:enabled="false"
android:text="@string/txt_continue"
app:qmui_radius="@dimen/dp_64"
app:typeface="@string/roboto_bold_font" />

View File

@@ -42,6 +42,7 @@ import androidx.annotation.StringDef
EventName.BleGetLocation,
EventName.BleReportData,
EventName.LogReport,
EventName.BlePassThrough,
EventName.ActionConDeviceState
)
@Retention(AnnotationRetention.SOURCE)
@@ -112,5 +113,7 @@ annotation class EventName {
//设备固件版本
const val BleFirmwareVersion = "firmwareVersion"
//蓝牙透传
const val BlePassThrough = "blePassThrough"
}
}

View File

@@ -28,7 +28,7 @@ abstract class BaseNetworkApi<I>(private val baseUrl: String) : IService<I> {
getRetrofit().create(getServiceClass())
}
protected open fun getRetrofit(): Retrofit {
protected fun getRetrofit(): Retrofit {
//添加gson解析null拦截器
val gson = GsonBuilder().registerTypeAdapter(String::class.java, StringNullAdapter())
.registerTypeAdapter(Double::class.java, DoubleDefault0Adapter()).create()
@@ -37,6 +37,19 @@ abstract class BaseNetworkApi<I>(private val baseUrl: String) : IService<I> {
.addConverterFactory(GsonConverterFactory.create(gson)).build()
}
/**
* 二进制上传专用 独立 Retrofit 配置
*/
// protected val binaryService: I by lazy {
// binaryRetrofit().create(getServiceClass())
// }
// 无 Gson 解析,只处理原始流/二进制
// private fun binaryRetrofit(): Retrofit {
// return Retrofit.Builder().baseUrl(baseUrl).client(getOkHttpClient())
// .build()
// }
private fun getServiceClass(): Class<I> {
val genType = javaClass.genericSuperclass as ParameterizedType
return genType.actualTypeArguments[0] as Class<I>

View File

@@ -18,7 +18,8 @@ class AppUtils {
companion object {
private var isDebug: Boolean? = true
// private var isDebug: Boolean? = true
private var isDebug: Boolean? = null
/**
* 屏幕密度,系统源码注释不推荐使用
@@ -93,7 +94,7 @@ class AppUtils {
* 是否是国内
*/
fun isChina(type: String = SWITCH_PAGE_TYPE): Boolean {
return true
return false
if (isDebug()) {
if (type == SWITCH_MAP_TYPE && MMKVUtil.getBoolean(MMKVKey.OnlyGoogleMap, false)) {
//只启用谷歌地图