From 0a128cb8a623e037eeeb15f8761cbbe37a40762f Mon Sep 17 00:00:00 2001 From: yezhiqiu <123456@qq.com> Date: Fri, 24 Jul 2026 10:18:46 +0800 Subject: [PATCH] =?UTF-8?q?1.=E4=BF=AE=E5=A4=8D=E8=BD=A8=E8=BF=B9=E5=92=8C?= =?UTF-8?q?=E5=AE=A0=E7=89=A9=E6=9C=89=E6=97=B6=E4=B8=8D=E5=9C=A8=E4=B8=80?= =?UTF-8?q?=E6=9D=A1=E7=BA=BF=E4=B8=8A=E7=9A=84bug=202.=E5=A2=9E=E5=8A=A0p?= =?UTF-8?q?et=E9=A1=B5=E5=88=87=E6=8D=A2=E5=88=B7=E6=96=B0=E8=BF=90?= =?UTF-8?q?=E5=8A=A8=E6=95=B0=E6=8D=AE=203.=E4=BF=AE=E5=A4=8D=E7=BB=91?= =?UTF-8?q?=E5=AE=9A=E8=AE=BE=E5=A4=87=E6=BF=80=E6=B4=BB=E9=87=8D=E5=90=AF?= =?UTF-8?q?=EF=BC=8C=E6=AD=A4=E6=97=B6=E7=82=B9=E5=87=BB=E5=A4=AA=E5=BF=AB?= =?UTF-8?q?=E8=BF=9B=E5=85=A5wifi=20=E6=90=9C=E7=B4=A2=E4=B8=8D=E5=88=B0bu?= =?UTF-8?q?g=204.=E8=93=9D=E7=89=99=E9=80=8F=E4=BC=A0=E9=80=9A=E8=B7=AF?= =?UTF-8?q?=E7=94=B1Raw=20TCP=E6=94=B9=E4=B8=BAHttps?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/build.gradle | 6 +- .../java/com/abbidot/tracker/bean/PetBean.kt | 4 +- .../tracker/retrofit2/INetworkService.kt | 16 ++ .../abbidot/tracker/retrofit2/NetworkApi.kt | 7 + .../tracker/ui/activity/HomeV2Activity.kt | 43 +++- .../device/AddPairedSuccessActivity.kt | 4 +- .../device/InitializingDeviceActivity.kt | 19 ++ .../subscribe/SureSubscriptionPlanActivity.kt | 8 +- .../ui/fragment/data/ActivityV2Fragment.kt | 2 +- .../ui/fragment/data/RouteV3Fragment.kt | 157 +++++++++++- .../map/googlemap/BaseGoogleMapFragment.kt | 235 ++++++++---------- .../tracker/ui/fragment/pet/PetV2Fragment.kt | 3 +- .../tracker/util/bluetooth/SRBleCmdUtil.kt | 3 + .../tracker/util/bluetooth/SRBleUtil.kt | 6 +- .../com/abbidot/tracker/vm/DataViewModel.kt | 52 ++++ .../layout/activity_initializing_device.xml | 1 + .../abbidot/baselibrary/constant/EventName.kt | 3 + .../network/base/BaseNetworkApi.kt | 15 +- .../com/abbidot/baselibrary/util/AppUtils.kt | 5 +- 19 files changed, 434 insertions(+), 155 deletions(-) diff --git a/app/build.gradle b/app/build.gradle index b7839ae..fe9ce5b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -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" diff --git a/app/src/main/java/com/abbidot/tracker/bean/PetBean.kt b/app/src/main/java/com/abbidot/tracker/bean/PetBean.kt index 0123e89..e84182c 100644 --- a/app/src/main/java/com/abbidot/tracker/bean/PetBean.kt +++ b/app/src/main/java/com/abbidot/tracker/bean/PetBean.kt @@ -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 { diff --git a/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt b/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt index 2ca5981..8d8bbb8 100644 --- a/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt +++ b/app/src/main/java/com/abbidot/tracker/retrofit2/INetworkService.kt @@ -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 + /** * 获取国家代码号 */ @@ -1362,6 +1377,7 @@ interface INetworkService { @POST("map/setupRefreshLocation") suspend fun setupRefreshLocation(@Field("deviceId") deviceId: String): BaseResponse + /*******************************************以下是国内版本接口***********************************/ /** * 获取微信登录的OpedId diff --git a/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt b/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt index acf38bc..4362a3f 100644 --- a/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt +++ b/app/src/main/java/com/abbidot/tracker/retrofit2/NetworkApi.kt @@ -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.BASE_URL) { service.getRechargeHistory(deviceId) } + /** + * 蓝牙透传机制从TCP迁移到https + */ + suspend fun blePassThrough(url: String, body: RequestBody) = + service.blePassThrough(url, body) + /** * 取消自动续费/自动订阅 */ diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/HomeV2Activity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/HomeV2Activity.kt index 92bbdcb..41897e0 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/HomeV2Activity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/HomeV2Activity.kt @@ -185,7 +185,7 @@ class HomeV2Activity : BaseActivity(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 } } - override fun liveDataObserve() {/* + override fun liveDataObserve() { + + /* Dispatchers.IO 在Android中主要用于执行输入输出操作,如文件读写和网络请求 Dispatchers.Main‌:适用于处理与UI相关的操作,如更新UI界面、响应用户输入等。 Dispatchers.Default‌:适用于执行CPU密集型任务,如算法计算、数据处理等 @@ -358,6 +360,21 @@ class HomeV2Activity : BaseActivity(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 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 */ 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 } 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调试页面 */ diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/device/AddPairedSuccessActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/device/AddPairedSuccessActivity.kt index 9600922..550102b 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/device/AddPairedSuccessActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/device/AddPairedSuccessActivity.kt @@ -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) } } diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/device/InitializingDeviceActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/device/InitializingDeviceActivity.kt index 856fd35..df9492d 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/device/InitializingDeviceActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/device/InitializingDeviceActivity.kt @@ -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::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() { diff --git a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/SureSubscriptionPlanActivity.kt b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/SureSubscriptionPlanActivity.kt index a16450f..d811962 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/SureSubscriptionPlanActivity.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/activity/subscribe/SureSubscriptionPlanActivity.kt @@ -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() } diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/ActivityV2Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/ActivityV2Fragment.kt index a917937..5944524 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/ActivityV2Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/ActivityV2Fragment.kt @@ -284,7 +284,7 @@ class ActivityV2Fragment : */ fun showPetNameAndHead(position: Int) { getHomeV2Activity()?.let { - if (it.mPetList.size == 0) { + if (it.mPetList.isEmpty()) { return } mCurrentShowPetPos = position diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV3Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV3Fragment.kt index 23e88ee..3470944 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV3Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/data/RouteV3Fragment.kt @@ -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(FragmentRouteV3Bind */ fun showPetNameAndHead(position: Int) { getHomeV2Activity()?.apply { - if (mPetList.size == 0) { + if (mPetList.isEmpty()) { return } mCurrentShowPetPos = position @@ -503,7 +508,19 @@ class RouteV3Fragment : BaseFragment(FragmentRouteV3Bind private fun setLatLngData(data: MutableList) { 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(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(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, epsilon: Double = 1.0 + ): List { + 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, + step: Int = 2 + ): List { + if (points.size < 2) return points + val result = mutableListOf() + 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, + minDis: Double = 5.0, + maxDis: Double = 60.0 + ): List { + if (data.isEmpty()) return emptyList() + val res = mutableListOf().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 + } } \ No newline at end of file diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt index e581283..4cbe9c3 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/map/googlemap/BaseGoogleMapFragment.kt @@ -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, epsilon: Double = 1.0): List { - 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, step: Int = 2): List { - if (points.size < 2) return points - val result = mutableListOf() - 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, minDis: Double = 5.0, maxDis: Double = 60.0 - ): List { - if (points.isEmpty()) return emptyList() - val res = mutableListOf().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, epsilon: Double = 1.0): List { +// 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, step: Int = 2): List { +// if (points.size < 2) return points +// val result = mutableListOf() +// 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, minDis: Double = 5.0, maxDis: Double = 60.0 +// ): List { +// if (points.isEmpty()) return emptyList() +// val res = mutableListOf().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 +// } } diff --git a/app/src/main/java/com/abbidot/tracker/ui/fragment/pet/PetV2Fragment.kt b/app/src/main/java/com/abbidot/tracker/ui/fragment/pet/PetV2Fragment.kt index 1f0f015..63391e0 100644 --- a/app/src/main/java/com/abbidot/tracker/ui/fragment/pet/PetV2Fragment.kt +++ b/app/src/main/java/com/abbidot/tracker/ui/fragment/pet/PetV2Fragment.kt @@ -119,12 +119,13 @@ class PetV2Fragment : BaseFragment(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() } } diff --git a/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleCmdUtil.kt b/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleCmdUtil.kt index 585c604..84a5325 100644 --- a/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleCmdUtil.kt +++ b/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleCmdUtil.kt @@ -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)) diff --git a/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleUtil.kt b/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleUtil.kt index e42def1..d68fd6e 100644 --- a/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleUtil.kt +++ b/app/src/main/java/com/abbidot/tracker/util/bluetooth/SRBleUtil.kt @@ -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) { diff --git a/app/src/main/java/com/abbidot/tracker/vm/DataViewModel.kt b/app/src/main/java/com/abbidot/tracker/vm/DataViewModel.kt index b1f91a8..eb822fb 100644 --- a/app/src/main/java/com/abbidot/tracker/vm/DataViewModel.kt +++ b/app/src/main/java/com/abbidot/tracker/vm/DataViewModel.kt @@ -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>>() val mHomeDataLiveData = MutableLiveData>() @@ -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}") + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/res/layout/activity_initializing_device.xml b/app/src/main/res/layout/activity_initializing_device.xml index 70840a3..de6078e 100644 --- a/app/src/main/res/layout/activity_initializing_device.xml +++ b/app/src/main/res/layout/activity_initializing_device.xml @@ -55,6 +55,7 @@ diff --git a/baselibrary/src/main/java/com/abbidot/baselibrary/constant/EventName.kt b/baselibrary/src/main/java/com/abbidot/baselibrary/constant/EventName.kt index 001bb6a..413f5c8 100644 --- a/baselibrary/src/main/java/com/abbidot/baselibrary/constant/EventName.kt +++ b/baselibrary/src/main/java/com/abbidot/baselibrary/constant/EventName.kt @@ -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" } } diff --git a/baselibrary/src/main/java/com/abbidot/baselibrary/network/base/BaseNetworkApi.kt b/baselibrary/src/main/java/com/abbidot/baselibrary/network/base/BaseNetworkApi.kt index f41e76c..7d10e45 100644 --- a/baselibrary/src/main/java/com/abbidot/baselibrary/network/base/BaseNetworkApi.kt +++ b/baselibrary/src/main/java/com/abbidot/baselibrary/network/base/BaseNetworkApi.kt @@ -28,7 +28,7 @@ abstract class BaseNetworkApi(private val baseUrl: String) : IService { 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(private val baseUrl: String) : IService { .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 { val genType = javaClass.genericSuperclass as ParameterizedType return genType.actualTypeArguments[0] as Class diff --git a/baselibrary/src/main/java/com/abbidot/baselibrary/util/AppUtils.kt b/baselibrary/src/main/java/com/abbidot/baselibrary/util/AppUtils.kt index ecc5c10..be36697 100644 --- a/baselibrary/src/main/java/com/abbidot/baselibrary/util/AppUtils.kt +++ b/baselibrary/src/main/java/com/abbidot/baselibrary/util/AppUtils.kt @@ -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)) { //只启用谷歌地图