2022-01-06 高德定位记录

        package com.lcw.routel.ui.patrol
        
        import android.Manifest
        import android.annotation.TargetApi
        import android.app.Notification
        import android.app.NotificationChannel
        import android.app.NotificationManager
        import android.app.PendingIntent
        import android.content.Context.NOTIFICATION_SERVICE
        import android.content.Intent
        import android.graphics.Color
        import android.os.Build
        import android.os.Bundle
        import android.view.LayoutInflater
        import android.view.View
        import android.view.ViewGroup
        import androidx.activity.addCallback
        import androidx.lifecycle.Observer
        import androidx.lifecycle.ViewModelProvider
        import androidx.navigation.fragment.findNavController
        import com.amap.api.location.AMapLocationClient
        import com.amap.api.location.AMapLocationClientOption
        import com.amap.api.location.AMapLocationClientOption.AMapLocationMode
        import com.amap.api.location.AMapLocationClientOption.AMapLocationProtocol
        import com.amap.api.maps.AMap
        import com.amap.api.maps.AMap.CancelableCallback
        import com.amap.api.maps.CameraUpdateFactory
        import com.amap.api.maps.Projection
        import com.amap.api.maps.TextureMapView
        import com.amap.api.maps.model.*
        import com.amap.api.track.AMapTrackClient
        import com.amap.api.track.ErrorCode
        import com.amap.api.track.OnTrackLifecycleListener
        import com.amap.api.track.TrackParam
        import com.amap.api.track.query.model.*
        import com.hjq.permissions.OnPermissionCallback
        import com.hjq.permissions.XXPermissions
        import com.lcw.routel.App
        import com.lcw.routel.MainActivity
        import com.lcw.routel.R
        import com.lcw.routel.base.BaseFragment
        import com.lcw.routel.base.Constants
        import com.lcw.routel.databinding.FragmentPatrolBinding
        import com.lcw.routel.entity.LocationEntity
        import com.lcw.routel.entity.PostPatrolRecordEntity
        import com.lcw.routel.entity.PostPatrolRecordStatusEntity
        import com.lcw.routel.net.easyhttp.http.EasyLog
        import com.lcw.routel.util.*
        import com.lcw.routel.widget.dialog.RequestDialog
        import dagger.hilt.android.AndroidEntryPoint
        import javax.inject.Inject
        
        
        /**
         * desc: 一键巡查
         * 

* create by lcz on 2021-12-14 */ @AndroidEntryPoint class PatrolFragment : BaseFragment(), PatrolActionHandler { @Inject lateinit var prefs: PreferenceStorage private lateinit var binding: FragmentPatrolBinding private val mViewModel: PatrolViewModel by lazy { ViewModelProvider(this)[PatrolViewModel::class.java] } private var mAMap: AMap? = null private var mMapView: TextureMapView? = null private var postEntity: PostPatrolRecordEntity = PostPatrolRecordEntity() private var postStatusEntity: PostPatrolRecordStatusEntity = PostPatrolRecordStatusEntity() private var singleLocationClient: AMapLocationClient? = null//单次定位 private var continueLocationClient: AMapLocationClient? = null//连续定位 private var singleLocationOption: AMapLocationClientOption? = null private var continueLocationOption: AMapLocationClientOption? = null private var locationMarker: Marker? = null //自定义定位小蓝点的Marker private var projection: Projection? = null//坐标和经纬度转换工具 private var useMoveToLocationWithMapMode = true private var myCancelCallback: MyCancelCallback = MyCancelCallback() private var locationAddress = "" private lateinit var locationLatLng: LatLng private var latitude = 0.0 private var longitude = 0.0 private var isServiceRunning = false//viewModel 暂存 private var aMapTrackClient: AMapTrackClient? = null private var terminalId: Long = 0 private var trackId: Long = 0 private val needPermissions = arrayOf( Manifest.permission.ACCESS_COARSE_LOCATION, Manifest.permission.ACCESS_FINE_LOCATION ) override fun initContentView(inflater: LayoutInflater, container: ViewGroup?): View { binding = FragmentPatrolBinding.inflate(inflater, container, false).apply { lifecycleOwner = viewLifecycleOwner viewModel = mViewModel } binding.actionHandler = this mMapView = binding.mMapView return binding.root } override fun process(savedInstanceState: Bundle?) { // 不要使用Activity作为Context传入 aMapTrackClient = AMapTrackClient(App.appContext) aMapTrackClient?.setInterval(4, 8) mMapView?.onCreate(savedInstanceState) if (mAMap == null) { mAMap = mMapView?.map setUpMap() } initLocation() XXPermissions.with(this).permission(needPermissions) .request(object : OnPermissionCallback { override fun onGranted(permissions: MutableList?, all: Boolean) { if (all) { // startSingleLocation() // 启用地图内置定位 mAMap?.moveCamera(CameraUpdateFactory.zoomTo(17f)) mAMap?.isMyLocationEnabled = true mAMap?.myLocationStyle = MyLocationStyle().interval(3000) .myLocationIcon(BitmapDescriptorFactory.fromResource(R.mipmap.location)) .myLocationType(MyLocationStyle.LOCATION_TYPE_FOLLOW) .strokeColor(Color.argb(0, 0, 0, 0)) .radiusFillColor(Color.argb(0, 0, 0, 0)) mAMap?.addOnMyLocationChangeListener { val location = GsonUtil.json2Bean( GsonUtil.bean2Json(it), LocationEntity::class.java ) latitude = location.latitude!! //纬 度 longitude = location.longitude!!//经 度 locationAddress = location.address.toString() locationLatLng = LatLng(latitude, longitude) EasyLog.print("经度---$longitude 纬度---$latitude 地址---$locationAddress") // if (location.errorCode == 0) { // EasyLog.print("定位成功") // latitude = location.latitude//纬 度 // longitude = location.longitude//经 度 // locationAddress = location.address // EasyLog.print("经 度---" + longitude + " 纬 度---" + latitude + " 地 址---" + location.address) // locationLatLng = LatLng(latitude, longitude) // if (locationMarker == null) { // locationMarker = mAMap?.addMarker( // MarkerOptions() // .position(locationLatLng) // .icon(BitmapDescriptorFactory.fromResource(R.mipmap.location)) // .anchor(0.5f, 0.5f) // ) // mAMap?.moveCamera( // CameraUpdateFactory.newLatLngZoom( // locationLatLng, // 17f // ) // ) // } else { // if (useMoveToLocationWithMapMode) { // startMoveLocationAndMap(locationLatLng) // } else { // startChangeLocation(locationLatLng) // } // } // } else { // EasyLog.print("定位失败---" + location.errorCode + " 错误信息---" + location.errorInfo + " 错误描述---" + location.locationDetail) // } } } else { "部分权限获取失败".showToast() } } override fun onDenied(permissions: MutableList?, never: Boolean) { if (never) { "被永久拒绝授权,请手动授予权限".showToast() } else { "权限获取失败".showToast() } } }) mViewModel.isServiceRunning.observe(this, Observer { EasyLog.print("isServiceRunning--------------$it") isServiceRunning = it if (it) { mViewModel.startRefreshTimer() binding.mTrackAction.text = "停止巡查" binding.mTrackAction.setBackgroundResource(R.drawable.stop_track_selector) // 开启巡查持续时间统计 onActionLocation() } else { mViewModel.stopRefreshTimer() binding.mTrackAction.text = "开始巡查" binding.mTrackAction.setBackgroundResource(R.drawable.track_selector) if (isPressBack) { findNavController().popBackStack() } } }) mViewModel.terminalId.observe(this, EventObserver { EasyLog.print("savedStateHandle terminal_id--------------$it") terminalId = it }) mViewModel.trackId.observe(this, EventObserver { EasyLog.print("savedStateHandle trackId--------------$it") trackId = it }) } private fun setUpMap() { mAMap?.uiSettings?.isZoomControlsEnabled = false//缩放按钮 } /*开启单次定位*/ private fun startSingleLocation() { try { // 启动定位 singleLocationClient!!.startLocation() } catch (e: java.lang.Exception) { e.printStackTrace() } } /*开启连续定位*/ private fun startContinueLocation() { try { // 启动定位 continueLocationClient!!.startLocation() } catch (e: java.lang.Exception) { e.printStackTrace() } } /*关闭连续定位*/ private fun stopContinueLocation() { try { // 启动定位 continueLocationClient!!.stopLocation() } catch (e: java.lang.Exception) { e.printStackTrace() } } private fun initLocation() { try { //单次定位 singleLocationClient = AMapLocationClient(App.appContext) singleLocationOption = getDefaultOption(true) singleLocationClient?.setLocationOption(singleLocationOption) singleLocationClient?.setLocationListener { location -> if (location.errorCode == 0) { EasyLog.print("定位成功") latitude = location.latitude//纬 度 longitude = location.longitude//经 度 locationAddress = location.address EasyLog.print("经 度---" + longitude + " 纬 度---" + latitude + " 地 址---" + location.address) locationLatLng = LatLng(latitude, longitude) if (locationMarker == null) { locationMarker = mAMap?.addMarker( MarkerOptions() .position(locationLatLng) .icon(BitmapDescriptorFactory.fromResource(R.mipmap.location)) .anchor(0.5f, 0.5f) ) mAMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(locationLatLng, 17f)) } else { if (useMoveToLocationWithMapMode) { startMoveLocationAndMap(locationLatLng) } else { startChangeLocation(locationLatLng) } } } else { EasyLog.print("定位失败---" + location.errorCode + " 错误信息---" + location.errorInfo + " 错误描述---" + location.locationDetail) } } //连续定位 continueLocationClient = AMapLocationClient(App.appContext) continueLocationOption = getDefaultOption(false) continueLocationClient?.setLocationOption(continueLocationOption) continueLocationClient?.setLocationListener { location -> if (location.errorCode == 0) { EasyLog.print("定位成功") latitude = location.latitude//纬 度 longitude = location.longitude//经 度 locationAddress = location.address EasyLog.print("经 度---" + longitude + " 纬 度---" + latitude + " 地 址---" + location.address) locationLatLng = LatLng(latitude, longitude) if (locationMarker == null) { locationMarker = mAMap?.addMarker( MarkerOptions() .position(locationLatLng) .icon(BitmapDescriptorFactory.fromResource(R.mipmap.location)) .anchor(0.5f, 0.5f) ) mAMap?.moveCamera(CameraUpdateFactory.newLatLngZoom(locationLatLng, 17f)) } else { if (useMoveToLocationWithMapMode) { startMoveLocationAndMap(locationLatLng) } else { startChangeLocation(locationLatLng) } } } else { EasyLog.print("定位失败---" + location.errorCode + " 错误信息---" + location.errorInfo + " 错误描述---" + location.locationDetail) } } } catch (e: Exception) { e.printStackTrace() } } /** * 默认的定位参数 单次定位 连续定位 */ private fun getDefaultOption(isOnceLocation: Boolean): AMapLocationClientOption? { val mOption = AMapLocationClientOption() mOption.locationMode = AMapLocationMode.Hight_Accuracy //可选,设置定位模式,可选的模式有高精度、仅设备、仅网络。默认为高精度模式 mOption.isGpsFirst = false //可选,设置是否gps优先,只在高精度模式下有效。默认关闭 mOption.httpTimeOut = 30000 //可选,设置网络请求超时时间。默认为30秒。在仅设备模式下无效 mOption.interval = 2000 //可选,设置定位间隔。默认为2秒 mOption.isNeedAddress = true //可选,设置是否返回逆地理地址信息。默认是true mOption.isOnceLocation = isOnceLocation //可选,设置是否单次定位。默认是false mOption.isOnceLocationLatest = false //可选,设置是否等待wifi刷新,默认为false.如果设置为true,会自动变为单次定位,持续定位时不要使用 AMapLocationClientOption.setLocationProtocol(AMapLocationProtocol.HTTP) //可选, 设置网络请求的协议。可选HTTP或者HTTPS。默认为HTTP mOption.isSensorEnable = false //可选,设置是否使用传感器。默认是false mOption.isWifiScan = true //可选,设置是否开启wifi扫描。默认为true,如果设置为false会同时停止主动刷新,停止以后完全依赖于系统刷新,定位位置可能存在误差 mOption.isLocationCacheEnable = true //可选,设置是否使用缓存定位,默认为true mOption.geoLanguage = AMapLocationClientOption.GeoLanguage.DEFAULT //可选,设置逆地理信息的语言,默认值为默认语言(根据所在地区选择语言) return mOption } /** * 修改自定义定位小蓝点的位置 * @param latLng */ private fun startChangeLocation(latLng: LatLng) { if (locationMarker != null) { val curLatlng = locationMarker!!.position if (curLatlng == null || curLatlng != latLng) { locationMarker!!.position = latLng } } } /** * 监控地图动画移动情况,如果结束或者被打断,都需要执行响应的操作 */ inner class MyCancelCallback : CancelableCallback { private var targetLatlng: LatLng? = null fun setTargetLatlng(latlng: LatLng?) { targetLatlng = latlng } override fun onFinish() { if (locationMarker != null && targetLatlng != null) { locationMarker!!.position = targetLatlng } } override fun onCancel() { if (locationMarker != null && targetLatlng != null) { locationMarker!!.position = targetLatlng } } } /** * 同时修改自定义定位小蓝点和地图的位置 * @param latLng */ private fun startMoveLocationAndMap(latLng: LatLng) { //将小蓝点提取到屏幕上 if (projection == null) { projection = mAMap?.projection } if (locationMarker != null && projection != null) { val markerLocation = locationMarker!!.position val screenPosition: android.graphics.Point? = mAMap?.projection?.toScreenLocation(markerLocation) locationMarker!!.setPositionByPixels(screenPosition!!.x, screenPosition!!.y) } //移动地图,移动结束后,将小蓝点放到放到地图上 myCancelCallback.setTargetLatlng(latLng) //动画移动的时间,最好不要比定位间隔长,如果定位间隔2000ms 动画移动时间最好小于2000ms,可以使用1000ms //如果超过了,需要在myCancelCallback中进行处理被打断的情况 // mAMap?.animateCamera(CameraUpdateFactory.changeLatLng(latLng), 1000, myCancelCallback) mAMap?.animateCamera(CameraUpdateFactory.newLatLngZoom(latLng, 17f), 1000, myCancelCallback) } override fun onResume() { super.onResume() mMapView?.onResume() EasyLog.print("onResume---------------") if (isServiceRunning) {//页面被销毁,重新 bindService startTrack() } } override fun onPause() { super.onPause() EasyLog.print("onPause---------------") mMapView?.onPause() } override fun onStop() { super.onStop() EasyLog.print("onStop---------------") } override fun onSaveInstanceState(outState: Bundle) { super.onSaveInstanceState(outState) mMapView?.onSaveInstanceState(outState) } override fun onDestroyView() { super.onDestroyView() EasyLog.print("onDestroyView---------------") mMapView?.onDestroy() if (isServiceRunning) { aMapTrackClient?.stopTrack( TrackParam(Constants.SERVICE_ID, terminalId), SimpleOnTrackLifecycleListener() ) } destroyLocation() //fix Navigation 销毁 Fragment 导致 mMapView?.onSaveInstanceState(outState) 失败 mAMap?.clear() mAMap = null locationMarker = null } private fun destroyLocation() { if (null != singleLocationClient) { /** * 如果AMapLocationClient是在当前Activity实例化的, * 在Activity的onDestroy中一定要执行AMapLocationClient的onDestroy */ singleLocationClient!!.onDestroy() singleLocationClient = null singleLocationOption = null } if (null != continueLocationClient) { /** * 如果AMapLocationClient是在当前Activity实例化的, * 在Activity的onDestroy中一定要执行AMapLocationClient的onDestroy */ continueLocationClient!!.onDestroy() continueLocationClient = null continueLocationOption = null } } override fun onActionStartPatrol() { if (isServiceRunning) { stopTrackAction("确定要结束巡查吗?") } else { startTrack() } } /*开始巡查 上报巡查记录到高德*/ private fun addPatrolServiceRecord() { postEntity.terminal_id = terminalId.toString() postEntity.track_id = trackId.toString() postEntity.detail_address = locationAddress mViewModel.submitMapPatrolRecord(postEntity) } /*结束巡查 更新巡查记录状态*/ private fun updatePatrolServiceStatus() { // stopContinueLocation() postStatusEntity.track_id = trackId.toString() postStatusEntity.detail_address = locationAddress mViewModel.updateMapPatrolRecordStatus(postStatusEntity) } private fun stopTrack() { aMapTrackClient!!.stopTrack( TrackParam(Constants.SERVICE_ID, terminalId), onTrackListener ) } private fun startTrack() { // startContinueLocation() // 先根据Terminal名称查询Terminal ID,如果Terminal还不存在,就尝试创建,拿到Terminal ID后, // 用Terminal ID开启轨迹服务 aMapTrackClient?.queryTerminal( QueryTerminalRequest( Constants.SERVICE_ID, prefs.userId ), object : SimpleOnTrackListener() { override fun onQueryTerminalCallback(queryTerminalResponse: QueryTerminalResponse) { if (queryTerminalResponse.isSuccess) { if (queryTerminalResponse.isTerminalExist) { // 当前终端已经创建过,直接使用查询到的terminal id terminalId = queryTerminalResponse.tid mViewModel.saveTerminalId(terminalId) aMapTrackClient!!.addTrack( AddTrackRequest( Constants.SERVICE_ID, terminalId ), object : SimpleOnTrackListener() { override fun onAddTrackCallback(addTrackResponse: AddTrackResponse) { if (addTrackResponse.isSuccess) { // trackId需要在启动服务后设置才能生效,因此这里不设置,而是在startGather之前设置了track id if (trackId == 0L) { trackId = addTrackResponse.trid mViewModel.saveTrackId(trackId) } val trackParam = TrackParam(Constants.SERVICE_ID, terminalId) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { trackParam.notification = createNotification() } //已经启动过的终端,重新bind aMapTrackClient!!.startTrack( trackParam, onTrackListener ) } else { "网络请求失败".showToast() } } }) } else { // 当前终端是新终端,还未创建过,创建该终端并使用新生成的terminal id aMapTrackClient!!.addTerminal( AddTerminalRequest( prefs.userId, Constants.SERVICE_ID ), object : SimpleOnTrackListener() { override fun onCreateTerminalCallback(addTerminalResponse: AddTerminalResponse) { if (addTerminalResponse.isSuccess) { terminalId = addTerminalResponse.tid mViewModel.saveTerminalId(terminalId) val trackParam = TrackParam(Constants.SERVICE_ID, terminalId) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { trackParam.notification = createNotification() } aMapTrackClient!!.startTrack( trackParam, onTrackListener ) } else { "网络请求失败".showToast() } } }) } } else { "网络请求失败".showToast() } } }) } private val onTrackListener: OnTrackLifecycleListener = object : SimpleOnTrackLifecycleListener() { override fun onBindServiceCallback(status: Int, msg: String) { EasyLog.print("onBindServiceCallback, status: $status, msg: $msg") } override fun onStartTrackCallback(status: Int, msg: String) { when (status) { ErrorCode.TrackListen.START_TRACK_SUCEE, ErrorCode.TrackListen.START_TRACK_SUCEE_NO_NETWORK -> { // 成功启动 "巡查服务启动成功".showToast() if (!isServiceRunning) { addPatrolServiceRecord() } mViewModel.isRunningServiceStatusChanged(true) startGather() } ErrorCode.TrackListen.START_TRACK_ALREADY_STARTED -> { // 已经启动 "巡查服务已经启动".showToast() startGather() } else -> { EasyLog.print("error onStartTrackCallback, status: $status, msg: $msg") "error onStartTrackCallback, status: $status, msg: $msg".showToast() } } } override fun onStopTrackCallback(status: Int, msg: String) { if (status == ErrorCode.TrackListen.STOP_TRACK_SUCCE) { // 成功停止 "巡查服务已结束".showToast() mViewModel.isRunningServiceStatusChanged(false) updatePatrolServiceStatus() } else { EasyLog.print("error onStopTrackCallback, status: $status, msg: $msg") "error onStopTrackCallback, status: $status, msg: $msg".showToast() } } override fun onStartGatherCallback(status: Int, msg: String) { when (status) { ErrorCode.TrackListen.START_GATHER_SUCEE -> { "定位采集开启成功".showToast() } ErrorCode.TrackListen.START_GATHER_ALREADY_STARTED -> { "定位采集已经开启".showToast() } else -> { EasyLog.print("error onStartGatherCallback, status: $status, msg: $msg") "error onStartGatherCallback, status: $status, msg: $msg".showToast() } } } override fun onStopGatherCallback(status: Int, msg: String) { if (status == ErrorCode.TrackListen.STOP_GATHER_SUCCE) { "定位采集停止成功".showToast() stopTrack() } else { EasyLog.print("error onStopGatherCallback, status: $status, msg: $msg") "error onStopGatherCallback, status: $status, msg: $msg".showToast() } } } private fun startGather() { aMapTrackClient?.startGather(onTrackListener) } private fun stopGather() { aMapTrackClient?.trackId = trackId aMapTrackClient?.stopGather(onTrackListener) } /** * 在8.0以上手机,如果app切到后台,系统会限制定位相关接口调用频率 * 可以在启动轨迹上报服务时提供一个通知,这样Service启动时会使用该通知成为前台Service,可以避免此限制 */ @TargetApi(Build.VERSION_CODES.JELLY_BEAN) private fun createNotification(): Notification? { val builder: Notification.Builder = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val nm = mContext.getSystemService(NOTIFICATION_SERVICE) as NotificationManager? val channel = NotificationChannel( "CHANNEL_ID_SERVICE_RUNNING", "app service", NotificationManager.IMPORTANCE_LOW ) nm!!.createNotificationChannel(channel) Notification.Builder(App.appContext, "CHANNEL_ID_SERVICE_RUNNING") } else { Notification.Builder(App.appContext) } val nfIntent = Intent(context, MainActivity::class.java) nfIntent.flags = Intent.FLAG_ACTIVITY_SINGLE_TOP or Intent.FLAG_ACTIVITY_CLEAR_TOP builder.setContentIntent( PendingIntent.getActivity( context, 0, nfIntent, 0 ) ) .setSmallIcon(R.mipmap.ic_launcher) .setContentTitle("道路云养护运行中") .setContentText("道路云养护运行中") return builder.build() } private fun stopTrackAction(tip: String) { EasyLog.print("stopTrackAction-------------$terminalId") // 弹框提示是否结束 RequestDialog(mContext, tip) { stopGather() } } override fun onActionEditReport() { // if (!isServiceRunning) { // "请先开始巡查".showToast() // return // } findNavController().safeNavigate( PatrolFragmentDirections.actionPatrolFragmentToMapReportFragment( locationAddress, latitude.toFloat(), longitude.toFloat(), trackId.toString() ) ) } private var isPressBack = false override fun onActionBack() { isPressBack = true if (isServiceRunning) { stopTrackAction("确定要结束巡查吗?") } else { findNavController().popBackStack() } } override fun onActionLocation() { } override fun initViewListener() { activity?.onBackPressedDispatcher?.addCallback(this) { isPressBack = true if (isServiceRunning) { stopTrackAction("确定要结束巡查吗?") } else { findNavController().popBackStack() } } } companion object { fun newInstance() = PatrolFragment() } }

你可能感兴趣的:(2022-01-06 高德定位记录)