Merge branch 'master' of gitlab.navinfo.com:CollectVehicle/OneMapQS

This commit is contained in:
xiaoyan 2023-04-27 13:37:37 +08:00
commit 61ba1d1d2f
13 changed files with 437 additions and 14 deletions

View File

@ -0,0 +1,50 @@
package com.navinfo.omqs.bean
import com.google.gson.annotations.SerializedName
data class EvaluationInfo(
@SerializedName("evaluationTaskId")
val evaluationTaskId: String = "",//测评任务id
@SerializedName("linkPid")
val linkPid: String = "",//Link号
@SerializedName("linkStatus")
val linkStatus: String = "",//Link状态
@SerializedName("markId")
val markId: String = "",//Link状态
@SerializedName("trackPhotoNumber")
val trackPhotoNumber: String = "",//轨迹照片编号 多个分号隔开
@SerializedName("markGeometry")
val markGeometry: String = "",//MARK_几何坐标
@SerializedName("featureName")
val featureName: String = "",//问题类型
@SerializedName("problemType")
val problemType: String = "",//问题现象
@SerializedName("problemPhenomenon")
val problemPhenomenon: String = "",//问题现象
@SerializedName("problemDesc")
val problemDesc: String = "",//问题描述
@SerializedName("problemLink")
val problemLink: String = "",//问题环节
@SerializedName("problemReason")
val problemReason: String = "",//问题原因
@SerializedName("evaluatorName")
val evaluatorName: String = "",//测评人名称
@SerializedName("evaluationDate")
val evaluationDate: String = "",//测评日期(yyyy-mm-dd)
@SerializedName("evaluationWay")
val evaluationWay: String = ""//测评方式
)

View File

@ -2,6 +2,7 @@ package com.navinfo.omqs.bean
import com.google.gson.annotations.SerializedName
import com.navinfo.omqs.Constant
import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import io.realm.RealmList
import io.realm.RealmObject
@ -13,7 +14,7 @@ open class TaskBean @JvmOverloads constructor(
/**
* 测评任务id
*/
// @PrimaryKey
@PrimaryKey
var id: Int = 0,
/**
* 测评任务名称
@ -56,6 +57,12 @@ open class TaskBean @JvmOverloads constructor(
* 当前下载状态
*/
var status: Int = FileDownloadStatus.NONE,
/**
* 上传状态
*/
var syncStatus: Int = FileManager.Companion.FileUploadStatus.NONE,
@Ignore
var message: String = ""
) : RealmObject() {

View File

@ -7,6 +7,7 @@ import com.navinfo.omqs.db.RoomAppDatabase
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager
import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
import com.navinfo.omqs.http.taskupload.TaskUploadManager
import dagger.Module
import dagger.Provides
import dagger.hilt.InstallIn
@ -48,6 +49,16 @@ class ActivityModule {
): TaskDownloadManager =
TaskDownloadManager(importFactory, networkServiceAPI)
/**
* 注入任务下载
*/
@ActivityRetainedScoped
@Provides
fun providesTaskListUploadManager(
networkServiceAPI: RetrofitNetworkServiceAPI,
): TaskUploadManager =
TaskUploadManager(networkServiceAPI)
/**
* 实验失败这样创建viewmodel不会在activity销毁的时候同时销毁
* 4-14:因为没有传入activity的 owner,无法检测生命周期

View File

@ -1,14 +1,12 @@
package com.navinfo.omqs.http
import com.navinfo.omqs.bean.EvaluationInfo
import com.navinfo.omqs.bean.OfflineMapCityBean
import com.navinfo.omqs.bean.TaskBean
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Response
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
import retrofit2.http.Streaming
import retrofit2.http.Url
import retrofit2.http.*
/**
* retrofit2 网络请求接口
@ -58,6 +56,10 @@ interface RetrofitNetworkServiceAPI {
@Query("evaluatorNo") evaluatorNo: String,
): Response<DefaultTaskResponse<List<TaskBean>>>
@Headers("Content-Type: application/json")
@POST("/devcp/upload")
fun postRequest(@Body listEvaluationInfo: List<EvaluationInfo>?): Call<ResponseBody>
/**
* @FormUrlEncoded 请求格式注解请求实体是一个From表单每个键值对需要使用@Field注解
@Field 请求参数注解提交请求的表单字段必须要添加而且需要配合@FormUrlEncoded使用

View File

@ -0,0 +1,105 @@
package com.navinfo.omqs.http.taskupload
import android.content.Context
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.Observer
import com.navinfo.omqs.bean.TaskBean
import com.navinfo.omqs.http.RetrofitNetworkServiceAPI
import java.util.concurrent.ConcurrentHashMap
/**
* 管理任务数据上传
*/
class TaskUploadManager constructor(
val netApi: RetrofitNetworkServiceAPI,
) {
lateinit var context: Context
/**
* 最多同时下载数量
*/
private val MAX_SCOPE = 1
/**
* 存储有哪些城市需要下载的队列
*/
private val scopeMap: ConcurrentHashMap<Int, TaskUploadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
ConcurrentHashMap<Int, TaskUploadScope>()
}
/**
* 存储正在下载的城市队列
*/
private val taskScopeMap: ConcurrentHashMap<Int, TaskUploadScope> by lazy(mode = LazyThreadSafetyMode.SYNCHRONIZED) {
ConcurrentHashMap<Int, TaskUploadScope>()
}
fun init(context: Context) {
this.context = context
}
/**
* 启动下载任务
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
*/
fun launchScope(scope: TaskUploadScope) {
if (taskScopeMap.size >= MAX_SCOPE) {
return
}
if (taskScopeMap.contains(scope.taskBean.id)) {
return
}
taskScopeMap[scope.taskBean.id] = scope
scope.launch()
}
/**
* 启动下一个任务,如果有正在等待中的任务的话
* 请不要直接使用此方法启动下载任务,它是交由[OfflineMapDownloadScope]进行调用
* @param previousUrl 上一个下载任务的下载连接
*/
fun launchNext(id: Int) {
taskScopeMap.remove(id)
for (entrySet in scopeMap) {
val uploadScope = entrySet.value
if (uploadScope.isWaiting()) {
launchScope(uploadScope)
break
}
}
}
/**
* 将下载任务加入到协程作用域的下载队列里
* 请求一个下载任务[OfflineMapDownloadScope]
* 这是创建[OfflineMapDownloadScope]的唯一途径,请不要通过其他方式创建[OfflineMapDownloadScope]
*/
fun start(id: Int) {
scopeMap[id]?.start()
}
fun addTask(taskBean: TaskBean) {
if (!scopeMap.containsKey(taskBean.id)) {
scopeMap[taskBean.id] = TaskUploadScope( this, taskBean)
}
}
fun observer(
id: Int, lifecycleOwner: LifecycleOwner, observer: Observer<TaskBean>
) {
if (scopeMap.containsKey(id)) {
scopeMap[id]!!.observer(lifecycleOwner, observer)
}
}
fun removeObserver(id: Int) {
if (scopeMap.containsKey(id)) {
scopeMap[id]!!.removeObserver()
}
}
}

View File

@ -0,0 +1,181 @@
package com.navinfo.omqs.http.taskupload
import android.os.Build
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.Observer
import com.navinfo.collect.library.data.entity.QsRecordBean
import com.navinfo.omqs.bean.EvaluationInfo
import com.navinfo.omqs.bean.TaskBean
import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import io.realm.Realm
import kotlinx.coroutines.*
import okhttp3.ResponseBody
import retrofit2.Call
import retrofit2.Callback
import retrofit2.Response
import java.util.*
class TaskUploadScope(
private val uploadManager: TaskUploadManager,
val taskBean: TaskBean,
) :
CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapUpLoad")) {
/**
* 用来取消的
*/
private var uploadJob: Job? = null
/**
* 管理观察者同时只有一个就行了
*/
private val observer = Observer<Any> {}
// private var lifecycleOwner: LifecycleOwner? = null
/**
*通知UI更新
*/
private val uploadData = MutableLiveData<TaskBean>()
init {
uploadData.value = taskBean
}
//改进的代码
fun start() {
change(FileDownloadStatus.WAITING)
uploadManager.launchScope(this@TaskUploadScope)
}
/**
* 启动协程进行下载
* 请不要尝试在外部调用此方法,那样会脱离[OfflineMapuploadManager]的管理
*/
fun launch() {
uploadJob = launch() {
upload()
uploadManager.launchNext(taskBean.id)
}
}
/**
* 更新状态
* @param status [OfflineMapCityBean.Status]
*/
private fun change(status: Int, message: String = "") {
if (taskBean.syncStatus != status) {
taskBean.syncStatus = status
uploadData.postValue(taskBean)
launch {
val realm = Realm.getDefaultInstance()
realm.executeTransaction {
it.copyToRealmOrUpdate(taskBean)
}
}
}
}
/**
* 是否未上传
*/
fun isWaiting(): Boolean {
return taskBean.syncStatus == FileManager.Companion.FileUploadStatus.WAITING
}
/**
* 添加下载任务观察者
*/
fun observer(owner: LifecycleOwner, ob: Observer<TaskBean>) {
removeObserver()
uploadData.observe(owner, ob)
}
/**
* 上传文件
*/
@RequiresApi(Build.VERSION_CODES.N)
private suspend fun upload() {
try {
//如果已上传则返回
if (taskBean.syncStatus == FileManager.Companion.FileUploadStatus.DONE) {
return
}
val realm = Realm.getDefaultInstance()
taskBean.hadLinkDvoList.forEach {
val hadLinkDvoBean = it
val liveDataQSList = MutableLiveData<List<QsRecordBean>>()
val objects = realm.where(QsRecordBean::class.java)
.equalTo("linkId", /*"84207223282277331"*/hadLinkDvoBean.linkPid).findAll()
val bodyList: MutableList<EvaluationInfo> = ArrayList()
if (objects != null) {
liveDataQSList.postValue(realm.copyFromRealm(objects))
if (liveDataQSList.value!!.isNotEmpty()) {
liveDataQSList.value?.forEach {
val evaluationInfo = EvaluationInfo(
taskBean.id.toString(),
hadLinkDvoBean.linkPid,//"84207223282277331"
"已测评",
hadLinkDvoBean.mesh,//"20065597"
"",
it.geometry,
it.classType,
it.problemType,
it.phenomenon,
it.description,
it.problemLink,
it.cause,
it.checkUserId,
it.checkTime
)
bodyList.add(evaluationInfo)
}
uploadManager.netApi.postRequest(bodyList).enqueue(object :
Callback<ResponseBody> {
override fun onResponse(
call: Call<ResponseBody>,
response: Response<ResponseBody>
) {
if(response.code()==200){
taskBean.syncStatus = FileManager.Companion.FileUploadStatus.DONE
// handle the response
Log.e("qj", "")
change(FileManager.Companion.FileUploadStatus.DONE)
}
}
override fun onFailure(call: Call<ResponseBody>, t: Throwable) {
// handle the failure
Log.e("qj", "")
change(FileManager.Companion.FileUploadStatus.ERROR)
}
})
}
}
}
} catch (e: Throwable) {
change(FileManager.Companion.FileUploadStatus.ERROR)
Log.e("jingo", "数据上传出错 ${e.message}")
} finally {
}
}
fun removeObserver() {
uploadData.observeForever(observer)
uploadData.removeObserver(observer)
}
}

View File

@ -22,6 +22,13 @@ class FileManager {
const val DONE = 8 //完成
}
object FileUploadStatus {
const val NONE = 0 //无状态
const val DONE = 1 //完成
const val ERROR = 2 //错误
const val WAITING = 3 //等待中
}
//初始化数据文件夹
fun initRootDir(context: Context) {
// 在SD卡创建项目目录

View File

@ -66,9 +66,9 @@ class MainActivity : BaseActivity() {
mapController.locationLayerHandler.startLocation()
//启动轨迹存储
mapController.locationLayerHandler.setNiLocationListener(NiLocationListener {
ToastUtils.showLong("定位${it.longitude}")
binding.viewModel!!.addSaveTrace(it)
binding.viewModel!!.startSaveTraceThread(this)
//ToastUtils.showLong("定位${it.longitude}")
binding!!.viewModel!!.addSaveTrace(it)
binding!!.viewModel!!.startSaveTraceThread(this)
})
//显示轨迹图层
// mapController.layerManagerHandler.showNiLocationLayer(Constant.DATA_PATH+ SystemConstant.USER_ID+"/trace.sqlite")

View File

@ -7,7 +7,6 @@ import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.navigation.findNavController
import com.blankj.utilcode.util.ToastUtils
import com.navinfo.collect.library.data.dao.impl.TraceDataBase
import com.navinfo.collect.library.data.entity.NiLocation
import com.navinfo.collect.library.map.NIMapController
@ -62,7 +61,7 @@ class MainViewModel @Inject constructor(
Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString())
ToastUtils.showShort("点击了相机")
//ToastUtils.showShort("点击了相机")
if (mCameraDialog == null) {
mCameraDialog = CommonDialog(

View File

@ -10,6 +10,8 @@ import com.navinfo.omqs.R
import com.navinfo.omqs.bean.TaskBean
import com.navinfo.omqs.databinding.AdapterTaskListBinding
import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
import com.navinfo.omqs.http.taskupload.TaskUploadManager
import com.navinfo.omqs.tools.FileManager
import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus
import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
import com.navinfo.omqs.ui.other.BaseViewHolder
@ -25,7 +27,8 @@ import javax.inject.Inject
*使用 LifecycleRegistry ViewHolder 分发生命周期(这里使用了这个)
*/
class TaskListAdapter(
private val downloadManager: TaskDownloadManager
private val downloadManager: TaskDownloadManager,
private val uploadManager: TaskUploadManager
) : BaseRecyclerViewAdapter<TaskBean>() {
@ -48,6 +51,18 @@ class TaskListAdapter(
}
}
private val uploadBtnClick = View.OnClickListener() {
if (it.tag != null) {
val taskBean = data[it.tag as Int]
Log.e("jingo", "开始上传 ${taskBean.syncStatus}")
when (taskBean.syncStatus) {
FileManager.Companion.FileUploadStatus.NONE->{
uploadManager.start(taskBean.id)
}
}
}
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
val viewBinding =
AdapterTaskListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
@ -69,8 +84,12 @@ class TaskListAdapter(
changeViews(binding, taskBean)
downloadManager.addTask(taskBean)
downloadManager.observer(taskBean.id, holder, DownloadObserver(taskBean.id, binding))
uploadManager.addTask(taskBean)
uploadManager.observer(taskBean.id, holder, UploadObserver(taskBean.id, binding))
binding.taskDownloadBtn.tag = position
binding.taskDownloadBtn.setOnClickListener(downloadBtnClick)
binding.taskUploadBtn.tag = position
binding.taskUploadBtn.setOnClickListener(uploadBtnClick)
binding.taskName.text = taskBean.evaluationTaskName
binding.taskCityName.text = taskBean.cityName
binding.taskDataVersion.text = "版本号:${taskBean.dataVersion}"
@ -85,6 +104,31 @@ class TaskListAdapter(
}
}
inner class UploadObserver(val id: Int, val binding: AdapterTaskListBinding) :
Observer<TaskBean> {
override fun onChanged(t: TaskBean?) {
if (id == t?.id)
changeUploadTxtViews(binding, t)
}
}
private fun changeUploadTxtViews(binding: AdapterTaskListBinding, taskBean: TaskBean) {
when (taskBean.syncStatus) {
FileManager.Companion.FileUploadStatus.DONE -> {
binding.taskUploadBtn.text = "已上传"
}
FileManager.Companion.FileUploadStatus.ERROR -> {
binding.taskUploadBtn.text = "重新同步"
}
FileManager.Companion.FileUploadStatus.NONE -> {
binding.taskUploadBtn.text = "同步"
}
FileManager.Companion.FileUploadStatus.WAITING -> {
binding.taskUploadBtn.text = "等待同步"
}
}
}
private fun changeViews(binding: AdapterTaskListBinding, taskBean: TaskBean) {
if (taskBean.fileSize > 0L) {

View File

@ -9,6 +9,7 @@ import androidx.navigation.fragment.findNavController
import androidx.recyclerview.widget.LinearLayoutManager
import com.navinfo.omqs.databinding.FragmentTaskListBinding
import com.navinfo.omqs.http.taskdownload.TaskDownloadManager
import com.navinfo.omqs.http.taskupload.TaskUploadManager
import com.navinfo.omqs.ui.fragment.BaseFragment
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@ -17,12 +18,14 @@ import javax.inject.Inject
class TaskListFragment : BaseFragment(){
@Inject
lateinit var downloadManager: TaskDownloadManager
@Inject
lateinit var uploadManager: TaskUploadManager
private var _binding: FragmentTaskListBinding? = null
private val viewModel by viewModels<TaskListViewModel>()
private val binding get() = _binding!!
private val adapter: TaskListAdapter by lazy {
TaskListAdapter(
downloadManager,
downloadManager,uploadManager
)
}
@ -31,6 +34,7 @@ class TaskListFragment : BaseFragment(){
savedInstanceState: Bundle?
): View {
downloadManager.init(requireContext())
uploadManager.init(requireContext())
_binding = FragmentTaskListBinding.inflate(inflater, container, false)
return binding.root

View File

@ -65,6 +65,19 @@
android:textColor="@color/btn_blue_solid"
android:textSize="@dimen/card_title_font_2size" />
<TextView
android:id="@+id/task_upload_btn"
style="@style/map_download_style_btn"
android:layout_width="60dp"
android:layout_centerVertical="true"
android:layout_marginRight="5dp"
android:layout_toLeftOf="@id/task_download_btn"
android:gravity="center"
android:shadowColor="@android:color/transparent"
android:text="同步"
android:textColor="@color/btn_blue_solid"
android:textSize="@dimen/card_title_font_2size" />
<TextView
android:id="@+id/task_status"
android:layout_width="wrap_content"

View File

@ -28,7 +28,7 @@ open class QsRecordBean @JvmOverloads constructor(
/**
* linkPid 绑定的道路ID
*/
var linkId: String = "",
var linkId: String = "84207223282277331",
/**
*问题分类
*/