1.处理轨迹经纬度飘和圆滑度

2.增加围栏缩放最小值提示
3.增加map和直播显示多宠物设置
This commit is contained in:
yezhiqiu
2026-05-28 11:01:51 +08:00
parent f26e3545b2
commit fde637793e
27 changed files with 557 additions and 77 deletions

View File

@@ -57,6 +57,11 @@ abstract class BaseDialog<T : ViewBinding>
initView()
}
override fun onStart() {
super.onStart()
// ✅ 每次显示都会执行刷新数据、更新UI等
}
abstract fun initView()
/**

View File

@@ -36,12 +36,14 @@ data class PetBean(
var showDFUDialog: Boolean,//是否显示了固件升级弹窗
var showNoWifiDialog: Boolean,//是否显示了没有设置wifi弹窗
var availableOrder: Int,//判断套餐是否可用或过期1是可用 0不可用
var latitude: Double,
var longitude: Double,
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, ""
), "", 0f, 0f, "", "", "", 1, 1, "", false, false, 1, 0.0, 0.0, ""
)
override fun isSameObject(other: Any): Boolean {

View File

@@ -55,6 +55,7 @@ import androidx.annotation.StringDef
ConstantString.Upgrade,
ConstantString.Password,
ConstantString.PackageUnitDay,
ConstantString.Lists,
ConstantString.PackageUnitMonth,
ConstantString.PackageUnitYear,
ConstantString.SetMealIndex
@@ -95,6 +96,7 @@ annotation class ConstantString {
const val Latitude = "latitude"
const val Longitude = "longitude"
const val isFirstBind = "isFirstBind"
const val Lists = "list"
const val isCheck = "isCheck"
const val Amount = "amount"

View File

@@ -490,6 +490,10 @@ class FencesAddActivity : BaseActivity<ActivityFencesAddBinding>(ActivityFencesA
override fun rectStartDragClick() {
}
override fun rectMinSizeClick() {
}
override fun rectEndDragClick() {
}
@@ -504,6 +508,9 @@ class FencesAddActivity : BaseActivity<ActivityFencesAddBinding>(ActivityFencesA
}
override fun circleMinSizeClick() {
}
override fun circleEndDragClick() {
}

View File

@@ -1,6 +1,7 @@
package com.abbidot.tracker.dialog
import android.content.Context
import android.view.View
import android.widget.CompoundButton
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.list.BaseRecyclerAdapter
@@ -20,14 +21,16 @@ import com.abbidot.tracker.util.ViewUtil
class SelectMapTypeDialog(
context: Context,
adapter: BaseRecyclerAdapter<*>,
fenceCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
fenceCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
showAllPetCheckedListener: CompoundButton.OnCheckedChangeListener? = null
) : BaseDialog<DialogSelectMapTypeLayoutBinding>(
DialogSelectMapTypeLayoutBinding::inflate, context
) {
private var mAdapter = adapter
private val mFenceCheckedChangeListener = fenceCheckedChangeListener
private val mDashedCheckedChangeListener = dashedCheckedChangeListener
private val mFenceCheckedListener = fenceCheckedListener
private val mDashedCheckedListener = dashedCheckedListener
private val mShowAllPetCheckedListener = showAllPetCheckedListener
override fun initView() {
mViewBinding.apply {
@@ -35,13 +38,15 @@ class SelectMapTypeDialog(
context, rvShowMapTypeList, mAdapter, right = AppUtils.dpToPx(58)
)
cbDialogMapFencesSwitch.isChecked = Util.getShowFenceSp()
cbDialogMapDashedLineSwitch.isChecked =
MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false)
cbDialogMapFencesSwitch.setOnCheckedChangeListener(mFenceCheckedChangeListener)
cbDialogMapDashedLineSwitch.setOnCheckedChangeListener(mDashedCheckedChangeListener)
cbDialogMapDashedLineSwitch.isChecked = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
cbDialogMapAllPetSwitch.isChecked = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
cbDialogMapFencesSwitch.setOnCheckedChangeListener(mFenceCheckedListener)
cbDialogMapDashedLineSwitch.setOnCheckedChangeListener(mDashedCheckedListener)
cbDialogMapAllPetSwitch.setOnCheckedChangeListener(mShowAllPetCheckedListener)
}
}
/**
* 检测地图类型有没有改变
*/
@@ -69,4 +74,12 @@ class SelectMapTypeDialog(
fun setDashedSwitch(checked: Boolean) {
mViewBinding.cbDialogMapDashedLineSwitch.isChecked = checked
}
fun setShowAllPetSwitch(checked: Boolean) {
mViewBinding.cbDialogMapAllPetSwitch.isChecked = checked
}
fun showAllPetSwitch(isShow: Boolean) {
mViewBinding.llMapTypeShowAllPetSwitch.visibility = if (isShow) View.VISIBLE else View.GONE
}
}

View File

@@ -8,6 +8,7 @@ import android.net.Network
import android.net.NetworkCapabilities
import android.net.NetworkRequest
import android.text.TextUtils
import android.view.Gravity
import android.view.MenuItem
import android.view.View
import androidx.activity.viewModels
@@ -92,7 +93,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
var mSelectPetPosition = 0
//宠物数据
lateinit var mPetList: MutableList<PetBean>
val mPetList = mutableListOf<PetBean>()
private var isFirst = true
//是否请求过宠物数据
@@ -163,7 +164,6 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
it.itemIconTintList = null
it.setOnItemSelectedListener(this)
}
mPetList = mutableListOf()
mChangePetListDialogAdapter = ChangePetListDialogAdapter(mContext, mPetList).apply {
setOnChangeClickListener(this@HomeV2Activity)
}
@@ -175,7 +175,7 @@ class HomeV2Activity : BaseActivity<ActivityHomeV2Binding>(ActivityHomeV2Binding
val isCrash = MMKVUtil.getBoolean(MMKVKey.isCrash, false)
if (isCrash) {
MMKVUtil.putBoolean(MMKVKey.isCrash, false)
showToast(R.string.txt_show_crash)
showToast(R.string.txt_show_crash, gravity = Gravity.CENTER)
mCountdownType = ConstantInt.Type0
mCountDownTimerViewModel.startCountDown(4)
} else {

View File

@@ -838,6 +838,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesBean.startDrag = true
}
override fun rectMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun rectEndDragClick() {
mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
@@ -855,6 +860,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
}
override fun circleMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun circleEndDragClick() {
mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)
@@ -868,6 +878,11 @@ open class AddAndEditFencesZoneBaseActivity :
mFencesBean.startDrag = true
}
override fun polygonMinSizeClick() {
if (isLimitClick()) return
showToast(R.string.txt_already_min_size, gravity = Gravity.CENTER)
}
override fun polygonEndDragClick() {
mFencesBean.startDrag = false
mFencesMapCommon.calculateDistanceAndGetPointLatLng(delayMillis = 0)

View File

@@ -94,6 +94,9 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
//是否显示虚线
private var isShowDashed = true
//是否显示所有宠物
private var isShowAllPet = false
//地图类型,标准和卫星地图
private var mMapType = ConstantInt.Type0
private var mShowCenterLocation = ConstantInt.PetLocationType
@@ -102,6 +105,7 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
private var isLiveJump = true
private var mLiveStatus = 1
private var mPetBean: PetBean? = null
private var mPetList: MutableList<PetBean>? = null
private var mTrackBleDevice: BleTrackDeviceBean? = null
//直播是否开始
@@ -138,10 +142,14 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
isCloseMsg = getBoolean(ConstantString.PetAuthorized)
mMapViewModel.mDeviceMsgType = getInt(ConstantString.Type, ConstantInt.SpecialType)
mPetBean = Util.getParcelableAdaptive(intent, ConstantString.Pet, PetBean::class.java)
mPetList = Util.getParcelableArrayListAdaptive(
intent, ConstantString.Lists, PetBean::class.java
)
}
isShowFence = Util.getShowFenceSp()
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false)
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
isShowAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
mViewBinding.apply {
ViewUtil.instance.viewRotationAnimator(
@@ -314,6 +322,10 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
mDeviceStateList,
mDeviceStateAdapter
)
if (MMKVUtil.getBoolean(MMKVKey.ShowAllPet)) {
setAllPetShow(true)
}
}
}
@@ -808,13 +820,43 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
} else {
mHomeMapCommon.removeUserAndPetLine()
}
})
},
{ _, isChecked ->
setAllPetShow(isChecked)
}).apply {
setOnShowListener {
mPetList?.let {
if (it.size > 1) mSelectMapTypeDialog?.showAllPetSwitch(true)
else mSelectMapTypeDialog?.showAllPetSwitch(false)
}
}
}
} else {
mSelectMapTypeDialog!!.mapTypeSpToUpdate()
}
mSelectMapTypeDialog?.show()
}
/**
* 设置多宠物显示
*/
private fun setAllPetShow(isShow: Boolean) {
isShowAllPet = isShow
MMKVUtil.putBoolean(MMKVKey.ShowAllPet, isShow)
if (isShow) {
lifecycleScope.launch {
mPetList?.apply {
mPetBean?.let { p ->
//删除当前选中宠物
mHomeMapCommon.showOtherPetHeadMarker(filter { it != p }.toMutableList())
}
}
}
} else {
mHomeMapCommon.clearOtherPetHeadMarker()
}
}
/**
* 设置地图相关数据
*/
@@ -928,7 +970,7 @@ class LiveActivityV3 : BaseActivity<ActivityLiveV3Binding>(ActivityLiveV3Binding
it.setCompoundDrawablesWithIntrinsicBounds(0, 0, 0, 0)
}
mViewBinding.llLiveV2MapTopPet.homeDataPetHeadSmall.appHeadImage.let {
ViewUtil.instance.setPetTypeHead(mContext,it, imgurl, petType)
ViewUtil.instance.setPetTypeHead(mContext, it, imgurl, petType)
ViewUtil.instance.viewAlphaAndRotationObjectAnimator(it)
}
//重新设置地图宠物头像

View File

@@ -6,9 +6,11 @@ import androidx.fragment.app.Fragment
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseMapCommon
import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MapDeviceBean
import com.abbidot.tracker.bean.PetBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding
import com.abbidot.tracker.ui.fragment.map.baidumap.HomeMapBaiduMapFragment
@@ -206,6 +208,18 @@ class HomeMapCommonV3 @Inject constructor() : BaseMapCommon() {
mHomeMapGoogleMapFragment?.removeUserAndPetLine()
}
suspend fun showOtherPetHeadMarker(
petList: MutableList<PetBean>, headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
if (petList.size > 0) {
mHomeMapGoogleMapFragment?.showOtherPetHeadMarker(petList, headBgResId)
}
}
fun clearOtherPetHeadMarker() {
mHomeMapGoogleMapFragment?.clearOtherPetHeadMarker()
}
// fun getAddressShowMarkerInfoWindow(historyDataBean: HistoryDataBean) {
// if (null != mHomeMapBaiduMapFragment) {
// mHomeMapBaiduMapFragment!!.showMarkerInfoWindow(historyDataBean)

View File

@@ -17,6 +17,7 @@ import androidx.core.view.WindowInsetsCompat
import androidx.fragment.app.Fragment
import androidx.fragment.app.commit
import androidx.fragment.app.viewModels
import androidx.lifecycle.lifecycleScope
import com.abbidot.baselibrary.constant.ConState
import com.abbidot.baselibrary.constant.EventName
import com.abbidot.baselibrary.constant.MMKVKey
@@ -61,6 +62,7 @@ import com.google.android.gms.maps.model.LatLng
import com.hjq.permissions.XXPermissions
import com.hjq.permissions.permission.PermissionLists
import dagger.hilt.android.AndroidEntryPoint
import kotlinx.coroutines.launch
import javax.inject.Inject
@@ -91,8 +93,6 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
private lateinit var mDeviceStateList: MutableList<DataBean>
private lateinit var mDeviceStateAdapter: HomeMapDeviceStateAdapter
// private lateinit var mDeviceMsgList: MutableList<DataBean>
// private lateinit var mDeviceMsgAdapter: HomeMapDeviceMsgAdapter
//启动移动地图摄像机
private var isMoveCamera = true
@@ -102,7 +102,10 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
private var isShowFence = true
//是否显示虚线
private var isShowDashed = true
private var isShowDashed = false
//是否显示所有宠物
private var isShowAllPet = false
//地图类型,标准和卫星地图
private var mMapType = ConstantInt.Type0
@@ -112,6 +115,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
//是否需要gps坐标转火星坐标
private var needGpsToGCJ02 = true
//一键定位绿波动画
private var mAnimatorSet: AnimatorSet? = null
//一键定位开始的时间戳
@@ -131,7 +136,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
}
isShowFence = Util.getShowFenceSp()
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false)
isShowDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
isShowAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
mViewBinding.apply {
getHomeV2Activity()?.edgeToEdgeAdapterBars(
@@ -220,7 +226,8 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
}
val showFence = Util.getShowFenceSp()
val showDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine, false)
val showDashed = MMKVUtil.getBoolean(MMKVKey.ShowDashedLine)
val showAllPet = MMKVUtil.getBoolean(MMKVKey.ShowAllPet)
//检测直播页面有没有修改围栏显示
if (isShowFence != showFence) {
if (null == mSelectMapTypeDialog) {
@@ -236,6 +243,13 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
mSelectMapTypeDialog?.setDashedSwitch(showDashed)
}
}
if (isShowAllPet != showAllPet) {
if (null == mSelectMapTypeDialog) {
setAllPetShow(showAllPet)
} else {
mSelectMapTypeDialog?.setShowAllPetSwitch(showDashed)
}
}
}
private fun getHomeV2Activity(): HomeV2Activity? {
@@ -600,20 +614,29 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
* 显示地图类型选择弹窗
*/
private fun showMapTypeDialog() {
if (null == mSelectMapTypeDialog) mSelectMapTypeDialog = ViewUtil.instance.getMapTypeDialog(
mContext!!, object : BaseRecyclerAdapter.OnItemClickListener {
override fun onItemClick(itemView: View?, pos: Int) {
mMapType = Util.getMapTypeSp()
if (pos == mMapType) {
return
if (null == mSelectMapTypeDialog) {
mSelectMapTypeDialog = ViewUtil.instance.getMapTypeDialog(
mContext!!, object : BaseRecyclerAdapter.OnItemClickListener {
override fun onItemClick(itemView: View?, pos: Int) {
mMapType = Util.getMapTypeSp()
if (pos == mMapType) {
return
}
mHomeMapCommon.switchSatelliteAndNormalMapType()
}
}, this, this, this
).apply {
setOnShowListener {
getHomeV2Activity()?.apply {
if (mPetList.size > 1) mSelectMapTypeDialog?.showAllPetSwitch(true)
else mSelectMapTypeDialog?.showAllPetSwitch(false)
}
mHomeMapCommon.switchSatelliteAndNormalMapType()
}
}, this, this
)
else {
}
} else {
mSelectMapTypeDialog!!.mapTypeSpToUpdate()
}
mSelectMapTypeDialog?.show()
}
@@ -645,6 +668,27 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
}
}
/**
* 设置多宠物显示
*/
private fun setAllPetShow(isShow: Boolean) {
isShowAllPet = isShow
MMKVUtil.putBoolean(MMKVKey.ShowAllPet, isShow)
if (isShow) {
lifecycleScope.launch {
getHomeV2Activity()?.apply {
getPet(false)?.let { p ->
//删除当前选中宠物
mHomeMapCommon.showOtherPetHeadMarker(mPetList.filter { it != p }
.toMutableList())
}
}
}
} else {
mHomeMapCommon.clearOtherPetHeadMarker()
}
}
/**
* 设置需要更新的标识
*/
@@ -755,6 +799,9 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
if (isShowDashed) {
mHomeMapCommon.addUserAndPetLine()
}
if (isShowAllPet) {
setAllPetShow(true)
}
}
}
@@ -951,6 +998,9 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
getPet()?.apply {
val intent = Intent(mContext, LiveActivityV3::class.java)
intent.putExtra(ConstantString.JumpActivity, isLiveJump)
intent.putParcelableArrayListExtra(
ConstantString.Lists, mPetList.toCollection(ArrayList())
)
intent.putExtra(ConstantString.Pet, this)
mMapDeviceBean?.let {
intent.putExtra(ConstantString.DeviceAuthorized, it.isCloseBattery)
@@ -1056,6 +1106,7 @@ class MapV3Fragment : BaseFragment<FragmentMapV3Binding>(FragmentMapV3Binding::i
when (v.id) {
R.id.cb_dialog_map_fences_switch -> setFencesShow(isChecked)
R.id.cb_dialog_map_dashed_line_switch -> setDashedShow(isChecked)
R.id.cb_dialog_map_all_pet_switch -> setAllPetShow(isChecked)
}
}

View File

@@ -25,6 +25,7 @@ import com.abbidot.tracker.databinding.FragmentBaseBaiduMapBinding
import com.abbidot.tracker.ui.fragment.map.googlemap.GoogleBitmapHelper
import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.LonAndLatUtil
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.abbidot.tracker.widget.TypefaceTextView
import com.baidu.location.BDAbstractLocationListener
import com.baidu.location.BDLocation
@@ -218,7 +219,7 @@ abstract class BaseBaiduMapFragment :
*/
fun setPetHeadIconUrl(petHeadUrl: String) {
mPetHeadUrl = petHeadUrl
mPetHeadIconBitmap?.recycle()
mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
}
@@ -247,20 +248,21 @@ abstract class BaseBaiduMapFragment :
headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
if (null == mPetHeadIconBitmap) {
//设置头像
mPetHeadIconBitmap =
GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
}
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
}
// val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
// else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
// var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
// if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
// if (null == mPetHeadIconBitmap) {
// //设置头像
// mPetHeadIconBitmap =
// GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
// }
// bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
// //保存在内存中,提高显示速度
// MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
// }
val bitmapDescriptor =
getMarkerBitmapDescriptor(mContext!!, mPetHeadUrl, mPetType, headBgResId)
mMarker?.remove()
val latLng = getBaiduMapLatLng(lat, lon, needConvertGCJ02)
mPetLatLng = latLng
@@ -272,6 +274,42 @@ abstract class BaseBaiduMapFragment :
mMarker = mBaiduMap!!.addOverlay(option)
}
private suspend fun getMarkerBitmapDescriptor(
context: Context,
headUrl: String,
petType: Int,
headBgResId: Int = R.drawable.pic_map_gps_avatar
): BitmapDescriptor? {
var bitmap: Bitmap? = null
var bitmapDescriptor: Any?
try {
val mapKey = if (TextUtils.isEmpty(headUrl)) {
"pet_default_head"
} else {
val idx = headUrl.lastIndexOf("/")
if (idx != -1) {
headUrl.substring(idx + 1)
} else {
headUrl
}
}
LogUtil.e("mapKey=$mapKey")
bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
bitmap = GoogleBitmapHelper.headToBitmap(context, headBgResId, headUrl, petType)
bitmap?.let {
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(it)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor as BitmapDescriptor
}
}
} finally {
bitmap.safeRecycle()
}
// 把 return 写在 finally 外面
return bitmapDescriptor as? BitmapDescriptor
}
/**
* 地图添加图片
*/
@@ -853,7 +891,8 @@ abstract class BaseBaiduMapFragment :
override fun onDestroy() {
super.onDestroy()
mSensorManager?.unregisterListener(mSensorEventListener)
mPetHeadIconBitmap?.recycle()
mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
stopLocation()
//在activity执行onDestroy时执行mMapView.onDestroy(),实现地图生命周期管理
mMapView.onDestroy()

View File

@@ -8,7 +8,6 @@ import android.graphics.Bitmap
import android.graphics.Point
import android.location.Location
import android.os.Bundle
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
@@ -19,7 +18,6 @@ import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.tracker.MyApplication
import com.abbidot.tracker.R
import com.abbidot.tracker.base.BaseFragment
import com.abbidot.tracker.bean.FencesBean
@@ -30,6 +28,7 @@ import com.abbidot.tracker.databinding.FragmentGoogleMapBinding
import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.LonAndLatUtil
import com.abbidot.tracker.util.Util
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.abbidot.tracker.util.ViewUtil
import com.abbidot.tracker.widget.MapDeviceNetView
import com.abbidot.tracker.widget.MapMarkerInfoView
@@ -42,7 +41,6 @@ import com.google.android.gms.maps.GoogleMap
import com.google.android.gms.maps.GoogleMapOptions
import com.google.android.gms.maps.MapView
import com.google.android.gms.maps.OnMapReadyCallback
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.Circle
@@ -65,6 +63,12 @@ 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
/**
@@ -97,7 +101,7 @@ abstract class BaseGoogleMapFragment :
var mMarker: Marker? = null
//自定显示用户地点信息图层
var mUserMarker: Marker? = null
private var mUserMarker: Marker? = null
//宠物大头针设置自定义头像
private var mPetHeadIconBitmap: Bitmap? = null
@@ -318,9 +322,13 @@ abstract class BaseGoogleMapFragment :
}
ConstantInt.UserLocationType -> {
mPetLatLng?.let {
if (null == mPetLatLng) {
mUserLatLng?.apply {
setLatLngZoom(mContext!!, 250, it, this)
moveCameraLocation(this)
}
} else {
mUserLatLng?.apply {
setLatLngZoom(mContext!!, 250, mPetLatLng!!, this)
cameraUpdateMove()
}
}
@@ -368,7 +376,7 @@ abstract class BaseGoogleMapFragment :
fun setPetHeadIconUrl(petHeadUrl: String, petType: Int) {
mPetHeadUrl = petHeadUrl
mPetType = petType
mPetHeadIconBitmap?.recycle()
mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
}
@@ -390,21 +398,26 @@ abstract class BaseGoogleMapFragment :
private suspend fun setPetHeadMarker(
latLng: LatLng, headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
if (null == mPetHeadIconBitmap) {
//设置头像
mPetHeadIconBitmap =
GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
}
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
}
// val mapKey = if (TextUtils.isEmpty(mPetHeadUrl)) "pet_default_head"
// else mPetHeadUrl.substring(mPetHeadUrl.lastIndexOf("/") + 1, mPetHeadUrl.length)
// var bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
// if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
// if (null == mPetHeadIconBitmap) {
// //设置头像
// mPetHeadIconBitmap =
// GoogleBitmapHelper.headToBitmap(mContext!!, headBgResId, mPetHeadUrl, mPetType)
// }
// bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(mPetHeadIconBitmap!!)
// //保存在内存中,提高显示速度
// MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor
// }
val bitmapDescriptor = GoogleBitmapHelper.getMarkerBitmapDescriptor(
mContext!!, mPetHeadUrl, mPetType, headBgResId
)
val newLatLng = toGCJ02LatLon(latLng)
val markerOptions = MarkerOptions().position(newLatLng)
//显示最上面,层级最高,默认值 0f
val markerOptions = MarkerOptions().position(newLatLng).zIndex(99f)
markerOptions.icon(bitmapDescriptor)
mMarker?.remove()
mGoogleMap?.apply {
@@ -805,7 +818,7 @@ abstract class BaseGoogleMapFragment :
*/
fun getPolylineOptions(widthDp: Float, lineColorRes: Int): PolylineOptions {
return PolylineOptions().clickable(false).width(AppUtils.dpToPx(widthDp))
.color(ContextCompat.getColor(mContext!!, lineColorRes)).geodesic(false)
.color(ContextCompat.getColor(mContext!!, lineColorRes))
}
/**
@@ -824,8 +837,21 @@ abstract class BaseGoogleMapFragment :
} else {
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 = 1.0)
// 3. 插值加密线条
val finalPoints = latLngInterpolate(smoothPoints, step = 2)
//先黄黑线
getPolylineOptions(8f, R.color.line_stroke_color).let {
it.geodesic(true)
it.addAll(newLatLngList)
googleMap.addPolyline(it).apply {
startCap = RoundCap()
@@ -841,7 +867,8 @@ abstract class BaseGoogleMapFragment :
//再画黄线
getPolylineOptions(5f, R.color.rote_line_color).let {
it.addAll(newLatLngList)
it.geodesic(true)
it.addAll(finalPoints)
googleMap.addPolyline(it).apply {
startCap = RoundCap()
endCap = RoundCap()
@@ -1267,8 +1294,118 @@ abstract class BaseGoogleMapFragment :
override fun onDestroy() {
super.onDestroy()
mPetHeadIconBitmap?.recycle()
mPetHeadIconBitmap.safeRecycle()
mPetHeadIconBitmap = null
mGoogleMapView?.onDestroy()
}
/**
* 两点间距离 单位米
*/
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

@@ -9,9 +9,12 @@ import androidx.annotation.DrawableRes
import androidx.core.content.ContextCompat
import androidx.core.content.res.ResourcesCompat
import androidx.core.graphics.drawable.DrawableCompat
import com.abbidot.baselibrary.util.LogUtil
import com.abbidot.tracker.MyApplication
import com.abbidot.tracker.R
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.util.ImageUtil
import com.abbidot.tracker.util.Util.Companion.safeRecycle
import com.google.android.gms.maps.model.BitmapDescriptor
import com.google.android.gms.maps.model.BitmapDescriptorFactory
import com.qmuiteam.qmui.util.QMUIDisplayHelper
@@ -166,4 +169,40 @@ object GoogleBitmapHelper {
vectorDrawable.draw(canvas)
return BitmapDescriptorFactory.fromBitmap(bitmap)
}
suspend fun getMarkerBitmapDescriptor(
context: Context,
headUrl: String,
petType: Int,
headBgResId: Int = R.drawable.pic_map_gps_avatar
): BitmapDescriptor? {
var bitmap: Bitmap? = null
var bitmapDescriptor: Any?
try {
val mapKey = if (TextUtils.isEmpty(headUrl)) {
"pet_default_head"
} else {
val idx = headUrl.lastIndexOf("/")
if (idx != -1) {
headUrl.substring(idx + 1)
} else {
headUrl
}
}
LogUtil.e("mapKey=$mapKey")
bitmapDescriptor = MyApplication.mapPetHeadHashMap[mapKey]
if (null == bitmapDescriptor || bitmapDescriptor !is BitmapDescriptor) {
bitmap = headToBitmap(context, headBgResId, headUrl, petType)
bitmap?.let {
bitmapDescriptor = BitmapDescriptorFactory.fromBitmap(it)
//保存在内存中,提高显示速度
MyApplication.mapPetHeadHashMap[mapKey] = bitmapDescriptor as BitmapDescriptor
}
}
} finally {
bitmap.safeRecycle()
}
// 把 return 写在 finally 外面
return bitmapDescriptor as? BitmapDescriptor
}
}

View File

@@ -302,11 +302,10 @@ class HistoryDataGoogleMapFragment : BaseGoogleMapFragment() {
if (null != mPetIconDescriptor) {
mGoogleMap?.apply {
val newLatLng = toGCJ02LatLon(latLng)
mMarkerOptions.position(newLatLng).icon(mPetIconDescriptor)
//显示最上面,层级最高,默认值 0f
mMarkerOptions.position(newLatLng).icon(mPetIconDescriptor).zIndex(99f)
mMarker?.remove()
mMarker = addMarker(mMarkerOptions)
//显示最上面,层级最高
mMarker?.zIndex = 1f
setUserMarker()
}
}

View File

@@ -9,11 +9,14 @@ import android.view.animation.LinearInterpolator
import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.fragment.app.viewModels
import com.abbidot.baselibrary.constant.MMKVKey
import com.abbidot.baselibrary.util.AppUtils
import com.abbidot.baselibrary.util.MMKVUtil
import com.abbidot.baselibrary.util.Utils
import com.abbidot.tracker.R
import com.abbidot.tracker.bean.HistoryDataBean
import com.abbidot.tracker.bean.MapDeviceBean
import com.abbidot.tracker.bean.PetBean
import com.abbidot.tracker.constant.ConstantInt
import com.abbidot.tracker.databinding.LayoutPetLocationInfoBinding
import com.abbidot.tracker.util.Util
@@ -24,6 +27,7 @@ import com.google.android.gms.maps.model.Circle
import com.google.android.gms.maps.model.CircleOptions
import com.google.android.gms.maps.model.LatLng
import com.google.android.gms.maps.model.Marker
import com.google.android.gms.maps.model.MarkerOptions
import com.google.android.gms.maps.model.Polyline
import kotlin.math.pow
@@ -59,6 +63,8 @@ class HomeMapGoogleMapFragmentV3 : BaseGoogleMapFragment() {
//是否需要反编译地理位置
private var isReverseGeocode = false
//其他没有选择的宠物
private val mOtherPetMarkerList = mutableListOf<Marker?>()
companion object {
@JvmStatic
@@ -358,6 +364,39 @@ class HomeMapGoogleMapFragmentV3 : BaseGoogleMapFragment() {
mRippleCircle = null
}
/**
* 显示其他宠物的头像Marker
*/
suspend fun showOtherPetHeadMarker(
petList: MutableList<PetBean>, headBgResId: Int = R.drawable.pic_map_gps_avatar
) {
clearOtherPetHeadMarker()
if (MMKVUtil.getBoolean(MMKVKey.ShowAllPet)) {
for (pet in petList) {
val bitmapDescriptor = GoogleBitmapHelper.getMarkerBitmapDescriptor(
mContext!!, pet.imgurl, pet.petType, headBgResId
)
val newLatLng = toGCJ02LatLon(LatLng(pet.latitude, pet.longitude))
val markerOptions = MarkerOptions().position(newLatLng)
markerOptions.icon(bitmapDescriptor)
mGoogleMap?.apply {
mOtherPetMarkerList.add(addMarker(markerOptions)?.apply {
})
}
}
}
}
/**
* 清空其他宠物的头像Marker
*/
fun clearOtherPetHeadMarker() {
for (m in mOtherPetMarkerList) {
m?.remove()
}
mOtherPetMarkerList.clear()
}
override fun onDetach() {
super.onDetach()
clearRippleCircleAnim()

View File

@@ -5,6 +5,7 @@ import android.content.ClipboardManager
import android.content.Context
import android.content.Intent
import android.content.pm.PackageManager
import android.graphics.Bitmap
import android.location.LocationManager
import android.os.Build
import android.os.Parcelable
@@ -900,6 +901,12 @@ class Util {
return this.code in 0x4E00..0x9FA5
}
/**
* 扩展函数Bitmap单独安全回收
*/
fun Bitmap?.safeRecycle() {
this?.takeIf { !it.isRecycled }?.recycle()
}
}

View File

@@ -921,8 +921,9 @@ class ViewUtil private constructor() {
fun getMapTypeDialog(
context: Context,
onItemClickListener: BaseRecyclerAdapter.OnItemClickListener? = null,
fenceCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedChangeListener: CompoundButton.OnCheckedChangeListener? = null
fenceCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
dashedCheckedListener: CompoundButton.OnCheckedChangeListener? = null,
showAllPetCheckedListener: CompoundButton.OnCheckedChangeListener? = null
): SelectMapTypeDialog {
val mapType = Util.getMapTypeSp()
val typeList = mutableListOf<DataBean>()
@@ -947,7 +948,7 @@ class ViewUtil private constructor() {
})
}
return SelectMapTypeDialog(
context, mapTypeAdapter, fenceCheckedChangeListener, dashedCheckedChangeListener
context, mapTypeAdapter, fenceCheckedListener, dashedCheckedListener,showAllPetCheckedListener
)
}

View File

@@ -9,6 +9,7 @@ interface OnCircleViewScaleClickListener {
fun circleScaleEndClick()
fun circleStartDragClick()
fun circleScalingClick()
fun circleMinSizeClick()
fun circleEndDragClick()
}
@@ -17,11 +18,13 @@ interface OnRectViewRotateScaleClickListener {
fun rectScaleEndClick()
fun rectScalingClick()
fun rectStartDragClick()
fun rectMinSizeClick()
fun rectEndDragClick()
}
interface OnPolygonViewPointPullClickListener {
fun polygonPointPullClick()
fun polygonStartDragClick()
fun polygonMinSizeClick()
fun polygonEndDragClick()
}

View File

@@ -380,6 +380,7 @@ class FencesCircleView : View {
if (mLimitRadius == 0f && circleRadius < mLocationBitmap!!.height / 2f) {
return true
} else if (circleRadius < mLimitRadius) {
mScaleClickListener?.circleMinSizeClick()
return true
}
mMaxCircleRadius = circleRadius

View File

@@ -339,6 +339,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()
@@ -401,6 +402,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()
@@ -462,6 +464,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()
@@ -523,6 +526,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()
@@ -584,6 +588,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()
@@ -645,6 +650,7 @@ class FencesPolygonView : View {
}
val tempRect = getTempRect(mTouchPointTag, mTempPoint)
if (mLimitSide > 0 && (tempRect.width() < mLimitSide || tempRect.height() < mLimitSide)) {
mPointPullClickListener?.polygonMinSizeClick()
return false
}
mOutHideRectWidth = tempRect.width()

View File

@@ -500,6 +500,7 @@ class FencesRectView : View {
return true
}
} else if (newRectWidth < mLimitSide || newRectHeight < mLimitSide) {
mRotateScaleClickListener?.rectMinSizeClick()
return true
}
mRotateScaleClickListener?.rectScalingClick()

View File

@@ -0,0 +1,22 @@
package com.abbidot.tracker.widget
import android.content.Context
import android.graphics.Canvas
import android.util.AttributeSet
import android.view.View
/**
*Created by .yzq on 2026/5/25/周一.
* @link
* @description:
*/
class TrackLineView @JvmOverloads constructor(
context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0
) : View(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
}
}

View File

@@ -66,6 +66,33 @@
android:id="@+id/cb_dialog_map_dashed_line_switch"
style="@style/my_checkbox_switch_style"
android:layout_width="@dimen/dp_47"
android:checked="true" />
android:checked="false" />
</androidx.appcompat.widget.LinearLayoutCompat>
<androidx.appcompat.widget.LinearLayoutCompat
android:id="@+id/ll_map_type_show_all_pet_switch"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_below="@id/ll_map_type_show_dashed_line_switch"
android:layout_marginTop="@dimen/dp_8"
android:background="@drawable/shape8_gray_border_transparent_bg"
android:gravity="center_vertical"
android:minHeight="@dimen/dp_47"
android:orientation="horizontal"
android:paddingHorizontal="@dimen/dp_16">
<com.abbidot.tracker.widget.TypefaceTextView
style="@style/my_TextView_style_v2"
android:layout_weight="1"
android:gravity="start"
android:text="@string/txt_show_all_pet"
android:textSize="@dimen/textSize14"
app:typeface="@string/roboto_bold_font" />
<androidx.appcompat.widget.AppCompatCheckBox
android:id="@+id/cb_dialog_map_all_pet_switch"
style="@style/my_checkbox_switch_style"
android:layout_width="@dimen/dp_47"
android:checked="false" />
</androidx.appcompat.widget.LinearLayoutCompat>
</RelativeLayout>

View File

@@ -1012,5 +1012,7 @@
<string name="txt_package_permission3">1. VIP-Prioritätssupport\n2. Schneller Austauschservice (1x inbegriffen)\n3. Erweiterter Schutz\n4. Sorgenfreier Haustierschutz</string>
<string name="txt_add_wifi_tips">Fügen Sie eine Wi-Fi-Zone hinzu, um die Betriebszeit zu verlängern.</string>
<string name="txt_add">Hinzufügen</string>
<string name="txt_show_all_pet">Alle Haustiere anzeigen</string>
<string name="txt_already_min_size">Bereits auf Minimalgröße</string>
</resources>

View File

@@ -1056,5 +1056,7 @@
<string name="txt_package_permission3">1. VIP 优先支持\n2. 快速更换服务含1次\n3. 增强型保障\n4. 无忧宠物保护</string>
<string name="txt_add_wifi_tips">添加wifi区域可延长使用时长</string>
<string name="txt_add">去添加</string>
<string name="txt_show_all_pet">显示所有宠物</string>
<string name="txt_already_min_size">已是最小尺寸</string>
</resources>

View File

@@ -1114,5 +1114,7 @@
<string name="txt_package_permission3">1.VIP priority support\n2.Fast replacement service (1 included)\n3.Enhanced protection\n4.Hassle-free pet protection</string>
<string name="txt_add_wifi_tips">Add Wi-Fi zone to prolong care time</string>
<string name="txt_add">Add</string>
<string name="txt_show_all_pet">Display All Pets</string>
<string name="txt_already_min_size">Already at minimum size</string>
</resources>

View File

@@ -38,6 +38,7 @@ import androidx.annotation.StringDef
MMKVKey.MapType,
MMKVKey.ShowFence,
MMKVKey.ShowDashedLine,
MMKVKey.ShowAllPet,
MMKVKey.isCrash,
MMKVKey.AvailableOrder,
MMKVKey.isFirstCheckBleOpen,
@@ -94,6 +95,7 @@ annotation class MMKVKey {
//map页是否显示围栏
const val ShowFence = "isShowFence"
const val ShowDashedLine = "isShowDashedLine"
const val ShowAllPet = "isShowAllPet"
//是首次打开APP
const val FirstOpen = "firstOpen"