package com.xw.common.utils
|
|
import android.content.Context
|
import android.content.Intent
|
import android.content.IntentFilter
|
import android.content.pm.PackageManager
|
import android.location.LocationManager
|
import android.os.Build
|
import android.os.Bundle
|
import android.provider.Settings
|
import androidx.core.net.toUri
|
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.LifecycleObserver
|
import androidx.lifecycle.OnLifecycleEvent
|
import com.amap.api.location.AMapLocation
|
import com.amap.api.location.AMapLocationClient
|
import com.amap.api.location.AMapLocationClientOption
|
import com.amap.api.location.AMapLocationListener
|
import com.amap.api.maps.model.LatLng
|
import com.blankj.utilcode.util.ToastUtils
|
import com.hjq.permissions.OnPermissionCallback
|
import com.hjq.permissions.Permission
|
import com.hjq.permissions.XXPermissions
|
import com.xw.common.CommonDialog
|
import com.xw.common.Constants
|
|
/**
|
* GPS
|
*
|
*/
|
class GpsHelper(
|
var context: Context,
|
var listener: OnMapLocation,
|
var isOnceLocation: Boolean, // 是否定位一次
|
var isAutomatic: Boolean = true,// isAutomatic 是否自动开启定位
|
var savedInstanceState: Bundle?,
|
var interval: Long = 2000L,//默认定位间隔
|
) : AMapLocationListener, LifecycleObserver {
|
val TAG = "zmc-GpsHelper"
|
|
// 声明AMapLocationClient类对象
|
private var mLocationClient: AMapLocationClient? = null
|
|
// 声明mLocationOption对象
|
private lateinit var mLocationOption: AMapLocationClientOption
|
|
// GPS 状态广播接收器
|
private var gpsStatusReceiver: GpsStatusReceiver? = null
|
|
// 标记是否正在监听 GPS 状态(用于避免重复注册)
|
private var isWatchingGpsStatus = false
|
|
// 是否已经发起过位置权限申请(避免每次 onResume 都重复 request 导致权限 Activity 反复拉起、onResume 死循环)
|
private var hasRequestedPermission = false
|
|
// 是否已经弹过"权限被拒绝"提示框(避免多次弹出)
|
private var hasShownDeniedDialog = false
|
|
// 定位权限组
|
private val locationPermissions =
|
arrayOf(Permission.ACCESS_FINE_LOCATION, Permission.ACCESS_COARSE_LOCATION)
|
|
@OnLifecycleEvent(Lifecycle.Event.ON_RESUME)
|
fun resume() {
|
LogUtils.i(TAG, "onResume isAutomatic = $isAutomatic")
|
if (savedInstanceState == null && isAutomatic)
|
checkAndStartLocation()
|
}
|
|
/**
|
* 检查定位状态并决定是否开始定位或监听 GPS 状态
|
*/
|
private fun checkAndStartLocation() {
|
if (isLocationEnabledModern()) {
|
// 定位已开启,停止监听(如果正在监听),直接开始定位
|
stopWatchingGpsStatus()
|
startLocation()
|
} else {
|
// 定位未开启,先回调 null 通知外部,然后开始监听 GPS 状态变化
|
LogUtils.w(TAG, "定位未开启,开始监听 GPS 状态变化")
|
listener.location(null)
|
startWatchingGpsStatus()
|
}
|
}
|
|
fun startLocation() {
|
// 已授权:直接定位,绝不再调 request()。
|
// request() 在 XXPermissions 18+ 会拉起一个透明的权限 Activity,
|
// 权限被永久拒绝时该 Activity 会立即返回并 finish,从而再次触发宿主 onResume,
|
// 若每次 onResume 都无条件 request 就会形成 onResume <-> 权限 Activity 的死循环。
|
if (XXPermissions.isGranted(context, locationPermissions)) {
|
hasShownDeniedDialog = false // 已授权,重置提示状态,便于下次被撤销时能再次提醒
|
initLocation()
|
return
|
}
|
|
// 之前已经申请过且现在仍未授权 => 已被拒绝:不再 request,避免死循环,只提示一次
|
if (hasRequestedPermission) {
|
listener.location(null)
|
showDeniedDialogOnce()
|
return
|
}
|
|
// 首次申请,最多触发一次
|
hasRequestedPermission = true
|
XXPermissions.with(context).permission(locationPermissions)
|
.request(object : OnPermissionCallback {
|
override fun onGranted(permissions: MutableList<String>, allGranted: Boolean) { // 权限同意
|
hasShownDeniedDialog = false
|
initLocation()
|
}
|
|
override fun onDenied(permissions: MutableList<String>, doNotAskAgain: Boolean) {
|
super.onDenied(permissions, doNotAskAgain)
|
//权限禁止
|
listener.location(null)
|
showDeniedDialogOnce()
|
}
|
})
|
}
|
|
/**
|
* 位置权限被拒绝后,弹框告知用户(同一生命周期内只弹一次,避免重复打扰)
|
*/
|
private fun showDeniedDialogOnce() {
|
if (hasShownDeniedDialog) return
|
hasShownDeniedDialog = true
|
CommonDialog.showLocationNotGrantedTipsDialog(context)
|
}
|
|
private fun initLocation() {
|
if (mLocationClient == null) {
|
mLocationClient = AMapLocationClient(context)
|
mLocationClient?.let { mLocationClient ->
|
// 设置定位回调监听
|
mLocationClient.setLocationListener(this)
|
// 初始化定位参数
|
mLocationOption = AMapLocationClientOption()
|
// 设置定位模式为高精度模式,Battery_Saving为低功耗模式,Device_Sensors是仅设备模式
|
mLocationOption.locationMode =
|
AMapLocationClientOption.AMapLocationMode.Hight_Accuracy
|
// 设置是否返回地址信息(默认返回地址信息)
|
mLocationOption.isNeedAddress = true
|
// 设置是否只定位一次,默认为false
|
mLocationOption.isOnceLocation = isOnceLocation
|
// 设置是否强制刷新WIFI,默认为强制刷新
|
// mLocationOption.isWifiActiveScan = true
|
// 设置是否允许模拟位置,默认为false,不允许模拟位置
|
mLocationOption.isMockEnable = false
|
// 设置定位间隔,单位毫秒,默认为2000ms
|
mLocationOption.interval = interval
|
//返回方位角
|
mLocationOption.isSensorEnable = false
|
// 给定位客户端对象设置定位参数
|
mLocationClient.setLocationOption(mLocationOption)
|
}
|
}
|
// 启动定位
|
mLocationClient?.startLocation()
|
LogUtils.w(TAG, "mLocationClient?.startLocation")
|
|
}
|
|
@OnLifecycleEvent(Lifecycle.Event.ON_PAUSE)
|
fun pause() {
|
mLocationClient?.let { mLocationClient ->
|
mLocationClient.stopLocation()
|
LogUtils.d(TAG, "onPause")
|
}
|
}
|
|
@OnLifecycleEvent(Lifecycle.Event.ON_DESTROY)
|
fun destroy() {
|
// 停止监听 GPS 状态
|
stopWatchingGpsStatus()
|
|
mLocationClient?.let { mLocationClient ->
|
mLocationClient.onDestroy()
|
LogUtils.e(TAG, "onDestroy")
|
}
|
}
|
|
/**
|
* 开始监听 GPS 状态变化
|
*/
|
private fun startWatchingGpsStatus() {
|
if (isWatchingGpsStatus) {
|
LogUtils.d(TAG, "已经在监听 GPS 状态,无需重复注册")
|
return
|
}
|
|
gpsStatusReceiver = GpsStatusReceiver { isEnabled ->
|
LogUtils.i(TAG, "GPS 状态变化: isEnabled = $isEnabled")
|
if (isEnabled) {
|
// GPS 已开启,停止监听并开始定位
|
LogUtils.i(TAG, "检测到 GPS 已开启,自动开始定位")
|
stopWatchingGpsStatus()
|
startLocation()
|
}
|
}
|
|
gpsStatusReceiver?.let { receiver ->
|
val filter = IntentFilter(LocationManager.PROVIDERS_CHANGED_ACTION)
|
context.registerReceiver(receiver, filter, Context.RECEIVER_EXPORTED)
|
isWatchingGpsStatus = true
|
LogUtils.i(TAG, "已注册 GPS 状态监听")
|
}
|
}
|
|
/**
|
* 停止监听 GPS 状态变化
|
*/
|
private fun stopWatchingGpsStatus() {
|
if (!isWatchingGpsStatus) return
|
|
gpsStatusReceiver?.let { receiver ->
|
try {
|
context.unregisterReceiver(receiver)
|
LogUtils.i(TAG, "已注销 GPS 状态监听")
|
} catch (e: IllegalArgumentException) {
|
// 接收器可能已经被注销
|
LogUtils.e(TAG, "注销 GPS 监听失败: ${e.message}")
|
}
|
}
|
gpsStatusReceiver = null
|
isWatchingGpsStatus = false
|
}
|
|
fun isLocationEnabledModern(): Boolean {
|
return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
|
// Android 9.0 (API 28) 及以上
|
val locationManager =
|
context.getSystemService(Context.LOCATION_SERVICE) as LocationManager
|
locationManager.isLocationEnabled
|
} else {
|
// 兼容旧版本
|
val mode = Settings.Secure.getInt(
|
context.contentResolver,
|
Settings.Secure.LOCATION_MODE,
|
Settings.Secure.LOCATION_MODE_OFF
|
)
|
mode != Settings.Secure.LOCATION_MODE_OFF
|
}
|
}
|
|
|
override fun onLocationChanged(location: AMapLocation?) {
|
location?.let {
|
if (it.longitude != 0.0 && it.latitude != 0.0) {
|
Constants.longitude = it.longitude
|
Constants.latitude = it.latitude
|
}
|
LogUtils.e(TAG, "----------------------------------")
|
LogUtils.w(TAG, "onLocationChanged: ${location.errorCode} ${location.errorInfo}")
|
// 定位成功,详细打印地址信息
|
LogUtils.w(TAG, "经度: ${it.longitude}")
|
LogUtils.w(TAG, "维度: ${it.latitude}")
|
LogUtils.w(TAG, "国家: ${it.country}")
|
LogUtils.w(TAG, "省份: ${it.province}")
|
LogUtils.w(TAG, "城市: ${it.city}")
|
LogUtils.w(TAG, "区县: ${it.district}")
|
LogUtils.w(TAG, "街道: ${it.street}")
|
LogUtils.w(TAG, "门牌号: ${it.streetNum}")
|
LogUtils.w(TAG, "完整地址: ${it.address}")
|
LogUtils.w(TAG, "城市编码: ${it.cityCode}")
|
LogUtils.w(TAG, "区域编码: ${it.adCode}")
|
LogUtils.w(TAG, "POI名称: ${it.poiName}")
|
LogUtils.w(TAG, "AOI名称: ${it.aoiName}")
|
LogUtils.w(TAG, "海拔: ${it.altitude} 米")
|
LogUtils.w(TAG, "定位精度: ${it.accuracy} 米")
|
LogUtils.w(TAG, "卫星数: ${it.satellites}")
|
LogUtils.w(TAG, "定位提供者: ${it.provider}")
|
LogUtils.w(TAG, "定位类型: ${it.locationType}")
|
}
|
listener.location(location)
|
}
|
|
interface OnMapLocation {
|
fun location(pLocation: AMapLocation?)
|
}
|
|
companion object {
|
val gaodePackageName = "com.autonavi.minimap" // 高德地图包名
|
fun navigate(start: LatLng? = null, end: LatLng, context: Context) {
|
if (!isInstallPackage(gaodePackageName)) {
|
ToastUtils.showLong("请安装导航应用")
|
} else {
|
val builder = StringBuilder("amapuri://route/plan?sourceApplication=maxuslife")
|
|
start?.let {
|
builder.append("&sname=").append("起点")
|
.append("&slat=").append(start.latitude)
|
.append("&slon=").append(start.longitude)
|
}
|
|
builder.append("&dlat=").append(end.latitude)
|
.append("&dlon=").append(end.longitude)
|
.append("&dname=").append("终点")
|
.append("&dev=0")
|
.append("&t=0")
|
|
val uriString = builder.toString()
|
val intent = Intent(Intent.ACTION_VIEW)
|
intent.setPackage(gaodePackageName)
|
intent.data = uriString.toUri()
|
context.startActivity(intent)
|
}
|
}
|
|
private fun isInstallPackage(packageName: String): Boolean {
|
return try {
|
AppGlobals.getApplication().packageManager.getPackageInfo(packageName, 0)
|
true
|
} catch (e: PackageManager.NameNotFoundException) {
|
try {
|
val intent =
|
AppGlobals.getApplication().packageManager.getLaunchIntentForPackage(
|
packageName
|
)
|
intent != null
|
} catch (ex: Exception) {
|
false
|
}
|
}
|
}
|
}
|
}
|