diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index f3be4c8..ff02bca 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -115,9 +115,13 @@ const val CONNECT_TCP_CODE = 20251001 const val CONNECT_CAMERA_TCP_CODE = 20251002 - const val ADD_POINT_CODE = 20251003 - const val DELETE_POINT_CODE = 20251004 - const val QUERY_POINT_CODE = 20251005 + const val START_ADD_POINT_CODE = 20251003 + const val ADD_LINE_CODE = 20251004 + const val ADD_POINT_CODE = 20251005 + const val SAVE_POINT_CODE = 20251006 + const val DELETE_POINT_CODE = 20251007 + const val START_INSPECTION_CODE = 20251008 + const val STOP_INSPECTION_CODE = 20251009 /*** * SP Key diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index f3be4c8..ff02bca 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -115,9 +115,13 @@ const val CONNECT_TCP_CODE = 20251001 const val CONNECT_CAMERA_TCP_CODE = 20251002 - const val ADD_POINT_CODE = 20251003 - const val DELETE_POINT_CODE = 20251004 - const val QUERY_POINT_CODE = 20251005 + const val START_ADD_POINT_CODE = 20251003 + const val ADD_LINE_CODE = 20251004 + const val ADD_POINT_CODE = 20251005 + const val SAVE_POINT_CODE = 20251006 + const val DELETE_POINT_CODE = 20251007 + const val START_INSPECTION_CODE = 20251008 + const val STOP_INSPECTION_CODE = 20251009 /*** * SP Key diff --git a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt index 4beeb21..65e545f 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt @@ -77,9 +77,6 @@ override fun channelActive(ctx: ChannelHandlerContext) { val address = ctx.channel().remoteAddress() as InetSocketAddress Log.d(kTag, "${address.address.hostAddress} 已连接") - scope.launch(Dispatchers.Main) { - "通讯服务已连接".show(BaseApplication.get()) - } listener.onConnected() } diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index f3be4c8..ff02bca 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -115,9 +115,13 @@ const val CONNECT_TCP_CODE = 20251001 const val CONNECT_CAMERA_TCP_CODE = 20251002 - const val ADD_POINT_CODE = 20251003 - const val DELETE_POINT_CODE = 20251004 - const val QUERY_POINT_CODE = 20251005 + const val START_ADD_POINT_CODE = 20251003 + const val ADD_LINE_CODE = 20251004 + const val ADD_POINT_CODE = 20251005 + const val SAVE_POINT_CODE = 20251006 + const val DELETE_POINT_CODE = 20251007 + const val START_INSPECTION_CODE = 20251008 + const val STOP_INSPECTION_CODE = 20251009 /*** * SP Key diff --git a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt index 4beeb21..65e545f 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt @@ -77,9 +77,6 @@ override fun channelActive(ctx: ChannelHandlerContext) { val address = ctx.channel().remoteAddress() as InetSocketAddress Log.d(kTag, "${address.address.hostAddress} 已连接") - scope.launch(Dispatchers.Main) { - "通讯服务已连接".show(BaseApplication.get()) - } listener.onConnected() } diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt new file mode 100644 index 0000000..4fef23f --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.widgets + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.View +import com.casic.br.operationsite.adapter.ManagePointAdapter +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.databinding.DialogManagePointBinding +import com.casic.br.operationsite.extensions.initDialogLayoutParams +import com.pengxh.kt.lite.divider.RecyclerViewItemDivider +import com.pengxh.kt.lite.extensions.binding + +class ManagePointDialog(builder: Builder) : Dialog(builder.context) { + + private val kTag = "ManagePointDialog" + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + private val context = builder.context + private val line = builder.line + private val listener = builder.listener + + class Builder { + lateinit var context: Context + var line = 0 + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setLine(line: Int): Builder { + this.line = line + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): ManagePointDialog { + return ManagePointDialog(this) + } + } + + private val binding: DialogManagePointBinding by binding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initDialogLayoutParams(0.8f, 0.5f) + setCanceledOnTouchOutside(false) + setCancelable(false) + + val beans = pointBeanDao.queryPointByLine(line) + if (beans.isEmpty()) { + binding.emptyView.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } else { + binding.emptyView.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + val pointAdapter = ManagePointAdapter(context, line, beans, 8) + binding.recyclerView.adapter = pointAdapter + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + } + + binding.dialogCancelButton.setOnClickListener { + listener.onCancelClick() + dismiss() + } + + binding.dialogConfirmButton.setOnClickListener { + listener.onConfirmClick() + dismiss() + } + } + + interface OnDialogButtonClickListener { + fun onConfirmClick() + + fun onCancelClick() + } +} \ No newline at end of file diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index f3be4c8..ff02bca 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -115,9 +115,13 @@ const val CONNECT_TCP_CODE = 20251001 const val CONNECT_CAMERA_TCP_CODE = 20251002 - const val ADD_POINT_CODE = 20251003 - const val DELETE_POINT_CODE = 20251004 - const val QUERY_POINT_CODE = 20251005 + const val START_ADD_POINT_CODE = 20251003 + const val ADD_LINE_CODE = 20251004 + const val ADD_POINT_CODE = 20251005 + const val SAVE_POINT_CODE = 20251006 + const val DELETE_POINT_CODE = 20251007 + const val START_INSPECTION_CODE = 20251008 + const val STOP_INSPECTION_CODE = 20251009 /*** * SP Key diff --git a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt index 4beeb21..65e545f 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt @@ -77,9 +77,6 @@ override fun channelActive(ctx: ChannelHandlerContext) { val address = ctx.channel().remoteAddress() as InetSocketAddress Log.d(kTag, "${address.address.hostAddress} 已连接") - scope.launch(Dispatchers.Main) { - "通讯服务已连接".show(BaseApplication.get()) - } listener.onConnected() } diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt new file mode 100644 index 0000000..4fef23f --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.widgets + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.View +import com.casic.br.operationsite.adapter.ManagePointAdapter +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.databinding.DialogManagePointBinding +import com.casic.br.operationsite.extensions.initDialogLayoutParams +import com.pengxh.kt.lite.divider.RecyclerViewItemDivider +import com.pengxh.kt.lite.extensions.binding + +class ManagePointDialog(builder: Builder) : Dialog(builder.context) { + + private val kTag = "ManagePointDialog" + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + private val context = builder.context + private val line = builder.line + private val listener = builder.listener + + class Builder { + lateinit var context: Context + var line = 0 + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setLine(line: Int): Builder { + this.line = line + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): ManagePointDialog { + return ManagePointDialog(this) + } + } + + private val binding: DialogManagePointBinding by binding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initDialogLayoutParams(0.8f, 0.5f) + setCanceledOnTouchOutside(false) + setCancelable(false) + + val beans = pointBeanDao.queryPointByLine(line) + if (beans.isEmpty()) { + binding.emptyView.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } else { + binding.emptyView.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + val pointAdapter = ManagePointAdapter(context, line, beans, 8) + binding.recyclerView.adapter = pointAdapter + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + } + + binding.dialogCancelButton.setOnClickListener { + listener.onCancelClick() + dismiss() + } + + binding.dialogConfirmButton.setOnClickListener { + listener.onConfirmClick() + dismiss() + } + } + + interface OnDialogButtonClickListener { + fun onConfirmClick() + + fun onCancelClick() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt b/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt new file mode 100644 index 0000000..8f25c08 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt @@ -0,0 +1,114 @@ +package com.casic.br.operationsite.widgets + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.databinding.DialogShowPointBinding +import com.casic.br.operationsite.extensions.initDialogLayoutParams +import com.pengxh.kt.lite.adapter.NormalRecyclerAdapter +import com.pengxh.kt.lite.adapter.SingleChoiceAdapter +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.divider.RecyclerViewItemDivider +import com.pengxh.kt.lite.extensions.binding + +class ShowPointDialog(builder: Builder) : Dialog(builder.context) { + + private val kTag = "ShowPointDialog" + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + private lateinit var pointAdapter: NormalRecyclerAdapter + private var pointBeans: MutableList = ArrayList() + private var selectedLine = 1 + private val listener = builder.listener + + class Builder { + lateinit var context: Context + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): ShowPointDialog { + return ShowPointDialog(this) + } + } + + private val binding: DialogShowPointBinding by binding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initDialogLayoutParams(0.8f, 0.5f) + setCanceledOnTouchOutside(false) + setCancelable(false) + + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return + + val lines = beans.map { it.secondType.toString() }.toSet().toList() + pointBeans = beans.filter { it.secondType.toString() == lines[0] }.toMutableList() + + pointAdapter = object : NormalRecyclerAdapter( + R.layout.item_inspection_point_rv_l, pointBeans + ) { + override fun convertView( + viewHolder: ViewHolder, position: Int, item: PointBean + ) { + viewHolder.setText(R.id.textView, "预置点位 ${item.thirdType}") + } + } + binding.pointRecyclerView.adapter = pointAdapter + binding.pointRecyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + + val lineAdapter = object : SingleChoiceAdapter( + R.layout.item_inspection_line_rv_l, lines.toMutableList() + ) { + override fun convertView(viewHolder: ViewHolder, position: Int, item: String) { + //{"firstType":92,"id":1,"secondType":1,"thirdType":1,"time":"2025-05-30 14:05:36"} + viewHolder.setText(R.id.textView, "巡航线路 $item") + } + } + binding.lineRecyclerView.adapter = lineAdapter + binding.lineRecyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + lineAdapter.setSelectedPosition(0) + lineAdapter.setOnItemCheckedListener(object : + SingleChoiceAdapter.OnItemCheckedListener { + override fun onItemChecked(position: Int, item: String) { + selectedLine = lines[position].toInt() + var points = arrayListOf() + beans.forEach { + if (lines[position] == it.secondType.toString()) { + points.add(it) + } + } + pointBeans = points + pointAdapter.refresh(points) + } + }) + + binding.dialogCancelButton.setOnClickListener { + listener.onCancelClick() + dismiss() + } + + binding.dialogConfirmButton.setOnClickListener { + listener.onConfirmClick(selectedLine) + dismiss() + } + } + + interface OnDialogButtonClickListener { + fun onConfirmClick(line: Int) + + fun onCancelClick() + } +} diff --git a/app/build.gradle b/app/build.gradle index 4b5778e..0cd1f4d 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -120,4 +120,7 @@ implementation 'cn.bertsir.zbarLibary:zbarlibary:1.4.2' //图表 implementation 'com.github.PhilJay:MPAndroidChart:v3.1.0' + //数据库框架 + implementation 'androidx.room:room-runtime:2.6.1' + annotationProcessor 'androidx.room:room-compiler:2.5.2' } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt new file mode 100644 index 0000000..1411b99 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/adapter/ManagePointAdapter.kt @@ -0,0 +1,75 @@ +package com.casic.br.operationsite.adapter + +import android.content.Context +import android.view.LayoutInflater +import android.view.ViewGroup +import android.widget.TextView +import androidx.recyclerview.widget.RecyclerView +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.service.CameraInspectionService +import com.casic.br.operationsite.utils.LocaleConstant +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.extensions.convertColor + +class ManagePointAdapter( + private val context: Context, + private val line: Int, + private val points: MutableList, + private val countLimit: Int +) : RecyclerView.Adapter() { + + private val layoutInflater by lazy { LayoutInflater.from(context) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder { + return ViewHolder( + layoutInflater.inflate(R.layout.item_inspection_point_rv_l, parent, false) + ) + } + + override fun onBindViewHolder(holder: ViewHolder, position: Int) { + val textView = holder.getView(R.id.textView) + if (position == itemCount - 1 && points.size < countLimit) { + textView.text = "添加" + textView.setTextColor(R.color.greenColor.convertColor(context)) + textView.setOnClickListener { + //添加巡航点 + val find = points.find { it.thirdType == 0 } + if (find == null) { + val pointBean = PointBean().apply { + firstType = 92 + secondType = line + thirdType = pointBeanDao.queryPointByLine(line).size + 1 + } + pointBeanDao.insert(pointBean) + points.add(pointBean) + } else { + find.thirdType = 1 + pointBeanDao.updatePoint(find) + } + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.ADD_POINT_CODE + ) + } + } else { + textView.text = "预置点位 ${points[position].thirdType}" + textView.setTextColor(R.color.mainTextColor.convertColor(context)) + // 长按监听 + textView.setOnLongClickListener { v -> + //长按删除巡航点 + pointBeanDao.deletePointById(points[position].id) + points.remove(points[position]) + notifyDataSetChanged() + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.DELETE_POINT_CODE + ) + true + } + } + } + + override fun getItemCount(): Int = minOf(countLimit, points.size + 1) +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt index f6b307f..0429548 100644 --- a/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt +++ b/app/src/main/java/com/casic/br/operationsite/base/BaseApplication.kt @@ -1,6 +1,8 @@ package com.casic.br.operationsite.base import android.app.Application +import androidx.room.Room.databaseBuilder +import com.casic.br.operationsite.utils.ApplicationDataBase import com.pengxh.kt.lite.utils.SaveKeyValues import kotlin.properties.Delegates @@ -12,9 +14,14 @@ fun get() = application } + lateinit var dataBase: ApplicationDataBase + override fun onCreate() { super.onCreate() application = this SaveKeyValues.initSharedPreferences(this) + dataBase = databaseBuilder(this, ApplicationDataBase::class.java, "OperationSite.db") + .allowMainThreadQueries() + .build() } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java new file mode 100644 index 0000000..ab6ada6 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/bean/PointBean.java @@ -0,0 +1,54 @@ +package com.casic.br.operationsite.bean; + +import androidx.room.Entity; +import androidx.room.PrimaryKey; + +@Entity(tableName = "camera_inspection_point") +public class PointBean { + @PrimaryKey(autoGenerate = true) + private long id; //主键ID + private int firstType; //一级类型(92:巡航) + private int secondType; //二级类型(1~8,巡航线路) + private int thirdType; //三级类型(1~255,巡航线路包含的巡航点) + private String time; + + public long getId() { + return id; + } + + public void setId(long id) { + this.id = id; + } + + public int getFirstType() { + return firstType; + } + + public void setFirstType(int firstType) { + this.firstType = firstType; + } + + public int getSecondType() { + return secondType; + } + + public void setSecondType(int secondType) { + this.secondType = secondType; + } + + public int getThirdType() { + return thirdType; + } + + public void setThirdType(int thirdType) { + this.thirdType = thirdType; + } + + public String getTime() { + return time; + } + + public void setTime(String time) { + this.time = time; + } +} diff --git a/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java new file mode 100644 index 0000000..af8dace --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/dao/PointBeanDao.java @@ -0,0 +1,28 @@ +package com.casic.br.operationsite.dao; + +import androidx.room.Dao; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + +import com.casic.br.operationsite.bean.PointBean; + +import java.util.List; + +@Dao +public interface PointBeanDao { + @Insert + void insert(PointBean point); + + @Query("SELECT * FROM camera_inspection_point") + List loadAll(); + + @Query("SELECT * FROM camera_inspection_point WHERE secondType = :line") + List queryPointByLine(int line); + + @Update + void updatePoint(PointBean point); + + @Query("DELETE FROM camera_inspection_point WHERE id = :id") + void deletePointById(long id); +} diff --git a/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt new file mode 100644 index 0000000..87fcb88 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/extensions/Dialog.kt @@ -0,0 +1,18 @@ +package com.casic.br.operationsite.extensions + +import android.app.Dialog +import android.graphics.Color +import androidx.core.graphics.drawable.toDrawable +import com.pengxh.kt.lite.extensions.getScreenHeight +import com.pengxh.kt.lite.extensions.getScreenWidth +import kotlin.math.roundToInt + +fun Dialog.initDialogLayoutParams(widthRatio: Float, heightRatio: Float) { + val window = this.window ?: return + window.setBackgroundDrawable(Color.TRANSPARENT.toDrawable()) + window.decorView.setBackgroundColor(Color.TRANSPARENT) + val params = window.attributes + params.width = ((context.getScreenWidth() * widthRatio).roundToInt()) + params.height = ((context.getScreenHeight() * heightRatio).roundToInt()) + window.attributes = params +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt index 4c4455a..74f5f0e 100644 --- a/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt +++ b/app/src/main/java/com/casic/br/operationsite/fragments/DeviceControllerFragment.kt @@ -1,23 +1,33 @@ package com.casic.br.operationsite.fragments import android.annotation.SuppressLint +import android.graphics.Color import android.os.Bundle import android.view.LayoutInflater import android.view.MotionEvent import android.view.ViewGroup +import androidx.core.text.isDigitsOnly import androidx.lifecycle.ViewModelProvider +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean import com.casic.br.operationsite.databinding.FragmentDeviceControllerBinding import com.casic.br.operationsite.service.CameraInspectionService import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.view.DeviceControlActivity import com.casic.br.operationsite.vm.DeviceViewModel +import com.casic.br.operationsite.widgets.ManagePointDialog +import com.casic.br.operationsite.widgets.ShowPointDialog import com.pengxh.kt.lite.base.KotlinBaseFragment +import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.widget.SteeringWheelView +import com.pengxh.kt.lite.widget.dialog.AlertInputDialog +import com.pengxh.kt.lite.widget.dialog.BottomActionSheet class DeviceControllerFragment : KotlinBaseFragment() { private val kTag = "DeviceFragment" private val deviceViewModel by lazy { ViewModelProvider(this)[DeviceViewModel::class.java] } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private var speed = 5 override fun initOnCreate(savedInstanceState: Bundle?) { @@ -91,15 +101,111 @@ binding.currentSpeedView.text = "速度:${speed}" } - binding.addPointButton.setOnClickListener { - CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.ADD_POINT_CODE) + binding.managePointButton.setOnClickListener { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage(LocaleConstant.START_ADD_POINT_CODE) + //再选择巡航线 + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return@setOnClickListener + + val lines = ArrayList() + beans.map { it.secondType.toString() }.toSet().forEach { + lines.add("巡航线路 $it") + } + if (lines.size < 8) { + lines.add("新增巡航线路") + } + BottomActionSheet.Builder() + .setContext(requireContext()) + .setActionItemTitle(lines) + .setItemTextColor(Color.BLUE) + .setOnActionSheetListener(object : BottomActionSheet.OnActionSheetListener { + override fun onActionItemClick(position: Int) { + if (lines[position] == "新增巡航线路") { + addLine() + } else { + var line = position + 1 + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.ADD_LINE_CODE + message.obj = line + } + //弹框添加预置点 + managePoint(line) + } + } + }).build().show() } - binding.getPointsButton.setOnClickListener { + binding.queryPointButton.setOnClickListener { + ShowPointDialog.Builder() + .setContext(requireContext()) + .setOnDialogButtonClickListener(object : + ShowPointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + } + + override fun onConfirmClick(line: Int) { + CameraInspectionService.weakReferenceHandler?.let { + val message = it.obtainMessage() + message.what = LocaleConstant.START_INSPECTION_CODE + message.obj = line + it.sendMessage(message) + } + } + }).build().show() } } + private fun addLine() { + AlertInputDialog.Builder() + .setContext(requireContext()) + .setTitle("新增巡航线路") + .setHintMessage("请输入巡航线路编号,最大为8") + .setNegativeButton("取消") + .setPositiveButton("确定") + .setOnDialogButtonClickListener(object : AlertInputDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick(value: String) { + if (!value.isDigitsOnly()) { + "巡航线路编号只能输入数字".show(requireContext()) + return + } + + if (value.toInt() > 8) { + "巡航线路编号最大为8".show(requireContext()) + return + } + + val pointBean = PointBean().apply { + firstType = 92 + secondType = value.toInt() + } + pointBeanDao.insert(pointBean) + } + }).build().show() + } + + private fun managePoint(line: Int) { + ManagePointDialog.Builder() + .setContext(requireContext()) + .setLine(line) + .setOnDialogButtonClickListener(object : ManagePointDialog.OnDialogButtonClickListener { + override fun onCancelClick() { + + } + + override fun onConfirmClick() { + CameraInspectionService.weakReferenceHandler?.sendEmptyMessage( + LocaleConstant.SAVE_POINT_CODE + ) + } + }).build().show() + } + private fun executeCommand(action: String) { deviceViewModel.executeDeviceCommand(action, speed) } diff --git a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt index 239d538..299e0c4 100644 --- a/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt +++ b/app/src/main/java/com/casic/br/operationsite/service/CameraInspectionService.kt @@ -10,13 +10,18 @@ import android.util.Log import androidx.core.app.NotificationCompat import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication import com.casic.br.operationsite.utils.CommandCreator import com.casic.br.operationsite.utils.LocaleConstant import com.casic.br.operationsite.utils.OnTcpConnectStateListener import com.casic.br.operationsite.utils.TcpClient -import com.pengxh.kt.lite.extensions.show import com.pengxh.kt.lite.utils.SaveKeyValues import com.pengxh.kt.lite.utils.WeakReferenceHandler +import kotlinx.coroutines.CoroutineScope +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.SupervisorJob +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch class CameraInspectionService : Service(), OnTcpConnectStateListener, Handler.Callback { @@ -27,7 +32,9 @@ private val kTag = "InspectionService" private val notificationId = 2 private val tcpClient by lazy { TcpClient(this) } + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } private val notificationManager by lazy { getSystemService(NOTIFICATION_SERVICE) as NotificationManager } + private val scope = CoroutineScope(Dispatchers.IO + SupervisorJob()) private var notificationBuilder: NotificationCompat.Builder? = null override fun handleMessage(msg: Message): Boolean { @@ -36,53 +43,45 @@ tcpClient.start() } + LocaleConstant.START_ADD_POINT_CODE -> { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + } + + LocaleConstant.ADD_LINE_CODE -> { + val line = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(line)) + } + LocaleConstant.ADD_POINT_CODE -> { - if (lineIndex < 8) { - if (pointIndex >= 8) { - //如果一条线添加满了,则换一条线 - lineIndex++ - pointIndex = 1 - } - //先设置一级类(92,表示巡航) - weakReferenceHandler?.post(firstTypeRunnable) - } else { - "最多能添加8条巡航线".show(this) + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.addPoint(point)) + } + + LocaleConstant.SAVE_POINT_CODE -> { + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.setConfig(9)) + } + } + + LocaleConstant.DELETE_POINT_CODE -> { + val point = msg.obj as Int + tcpClient.sendMessage(CommandCreator.deletePoint(point)) + } + + LocaleConstant.START_INSPECTION_CODE -> { + val line = msg.obj as Int + scope.launch(Dispatchers.IO) { + tcpClient.sendMessage(CommandCreator.setConfig(92)) + delay(500) + tcpClient.sendMessage(CommandCreator.addPoint(line)) } } } return true } - private val firstTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(92)) - Log.d(kTag, "run: 设置一级类") - //再设置二级类(01~08,表示巡航线) - weakReferenceHandler?.postDelayed(secondTypeRunnable, 500) - } - } - - private var lineIndex = 1 - - private val secondTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(lineIndex)) - Log.d(kTag, "run: 设置二级类 ===> $lineIndex") - //最后设置三级类(01~08,表示巡航点) - weakReferenceHandler?.postDelayed(thirdTypeRunnable, 500) - } - } - - private var pointIndex = 1 - - private val thirdTypeRunnable = object : Runnable { - override fun run() { - tcpClient.sendMessage(CommandCreator.addPoint(pointIndex)) - Log.d(kTag, "run: 设置三级类 ===> $pointIndex") - pointIndex++ - } - } - override fun onCreate() { super.onCreate() val name = "${resources.getString(R.string.app_name)}前台服务" diff --git a/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java new file mode 100644 index 0000000..d6740d9 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/utils/ApplicationDataBase.java @@ -0,0 +1,12 @@ +package com.casic.br.operationsite.utils; + +import androidx.room.Database; +import androidx.room.RoomDatabase; + +import com.casic.br.operationsite.bean.PointBean; +import com.casic.br.operationsite.dao.PointBeanDao; + +@Database(entities = {PointBean.class}, version = 1) +public abstract class ApplicationDataBase extends RoomDatabase { + public abstract PointBeanDao pointBeanDao(); +} diff --git a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt index ee75550..f72a1ab 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/CommandCreator.kt @@ -219,10 +219,36 @@ * 添加预置点,范围:1~255 * * - * 只要是设置巡航点,就都要先调用一个92号预置位,再调用1号预置位,开始设置巡航点, - * 然后添加点位的才是实际的巡航点, - * 最后再调用一次92和9预置位保存巡航点信息 + * 整个流程就是: + * 1.设置92,调用1(1~8巡航线路) + * 2.调用1,调用2.......(添加巡航的预置位) + * 3.设置92,再调用9 (保存巡航线路) + * 4.调用92,调用1(开始跑巡航线路) * */ + fun setConfig(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x03.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + fun addPoint(index: Int): ByteArray { val bytes = byteArrayOf( 0xFF.toByte(), @@ -246,4 +272,52 @@ bytes[6] = sum.toByte() return bytes } + + fun deletePoint(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } + + fun startLine(index: Int): ByteArray { + val bytes = byteArrayOf( + 0xFF.toByte(), + 0x01, + 0x00, + 0x05.toByte(), + 0x00.toByte(), + 0x00.toByte(), + 0x00.toByte() + ) + + // 将 index 转换为 2 字节的高位和低位 + bytes[4] = (index ushr 8).toByte() // 高位字节 + bytes[5] = (index and 0xFF).toByte() // 低位字节 + + //计算校验位。 + var sum = 0 + for (l in 1 until bytes.size - 1) { + sum += bytes[l] + } + bytes[6] = sum.toByte() + return bytes + } } \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt index f3be4c8..ff02bca 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/LocaleConstant.kt @@ -115,9 +115,13 @@ const val CONNECT_TCP_CODE = 20251001 const val CONNECT_CAMERA_TCP_CODE = 20251002 - const val ADD_POINT_CODE = 20251003 - const val DELETE_POINT_CODE = 20251004 - const val QUERY_POINT_CODE = 20251005 + const val START_ADD_POINT_CODE = 20251003 + const val ADD_LINE_CODE = 20251004 + const val ADD_POINT_CODE = 20251005 + const val SAVE_POINT_CODE = 20251006 + const val DELETE_POINT_CODE = 20251007 + const val START_INSPECTION_CODE = 20251008 + const val STOP_INSPECTION_CODE = 20251009 /*** * SP Key diff --git a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt index 4beeb21..65e545f 100644 --- a/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt +++ b/app/src/main/java/com/casic/br/operationsite/utils/TcpClient.kt @@ -77,9 +77,6 @@ override fun channelActive(ctx: ChannelHandlerContext) { val address = ctx.channel().remoteAddress() as InetSocketAddress Log.d(kTag, "${address.address.hostAddress} 已连接") - scope.launch(Dispatchers.Main) { - "通讯服务已连接".show(BaseApplication.get()) - } listener.onConnected() } diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt new file mode 100644 index 0000000..4fef23f --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/ManagePointDialog.kt @@ -0,0 +1,84 @@ +package com.casic.br.operationsite.widgets + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import android.view.View +import com.casic.br.operationsite.adapter.ManagePointAdapter +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.databinding.DialogManagePointBinding +import com.casic.br.operationsite.extensions.initDialogLayoutParams +import com.pengxh.kt.lite.divider.RecyclerViewItemDivider +import com.pengxh.kt.lite.extensions.binding + +class ManagePointDialog(builder: Builder) : Dialog(builder.context) { + + private val kTag = "ManagePointDialog" + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + private val context = builder.context + private val line = builder.line + private val listener = builder.listener + + class Builder { + lateinit var context: Context + var line = 0 + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setLine(line: Int): Builder { + this.line = line + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): ManagePointDialog { + return ManagePointDialog(this) + } + } + + private val binding: DialogManagePointBinding by binding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initDialogLayoutParams(0.8f, 0.5f) + setCanceledOnTouchOutside(false) + setCancelable(false) + + val beans = pointBeanDao.queryPointByLine(line) + if (beans.isEmpty()) { + binding.emptyView.visibility = View.VISIBLE + binding.recyclerView.visibility = View.GONE + } else { + binding.emptyView.visibility = View.GONE + binding.recyclerView.visibility = View.VISIBLE + val pointAdapter = ManagePointAdapter(context, line, beans, 8) + binding.recyclerView.adapter = pointAdapter + binding.recyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + } + + binding.dialogCancelButton.setOnClickListener { + listener.onCancelClick() + dismiss() + } + + binding.dialogConfirmButton.setOnClickListener { + listener.onConfirmClick() + dismiss() + } + } + + interface OnDialogButtonClickListener { + fun onConfirmClick() + + fun onCancelClick() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt b/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt new file mode 100644 index 0000000..8f25c08 --- /dev/null +++ b/app/src/main/java/com/casic/br/operationsite/widgets/ShowPointDialog.kt @@ -0,0 +1,114 @@ +package com.casic.br.operationsite.widgets + +import android.app.Dialog +import android.content.Context +import android.graphics.Color +import android.os.Bundle +import com.casic.br.operationsite.R +import com.casic.br.operationsite.base.BaseApplication +import com.casic.br.operationsite.bean.PointBean +import com.casic.br.operationsite.databinding.DialogShowPointBinding +import com.casic.br.operationsite.extensions.initDialogLayoutParams +import com.pengxh.kt.lite.adapter.NormalRecyclerAdapter +import com.pengxh.kt.lite.adapter.SingleChoiceAdapter +import com.pengxh.kt.lite.adapter.ViewHolder +import com.pengxh.kt.lite.divider.RecyclerViewItemDivider +import com.pengxh.kt.lite.extensions.binding + +class ShowPointDialog(builder: Builder) : Dialog(builder.context) { + + private val kTag = "ShowPointDialog" + private val pointBeanDao by lazy { BaseApplication.get().dataBase.pointBeanDao() } + private lateinit var pointAdapter: NormalRecyclerAdapter + private var pointBeans: MutableList = ArrayList() + private var selectedLine = 1 + private val listener = builder.listener + + class Builder { + lateinit var context: Context + lateinit var listener: OnDialogButtonClickListener + + fun setContext(context: Context): Builder { + this.context = context + return this + } + + fun setOnDialogButtonClickListener(listener: OnDialogButtonClickListener): Builder { + this.listener = listener + return this + } + + fun build(): ShowPointDialog { + return ShowPointDialog(this) + } + } + + private val binding: DialogShowPointBinding by binding() + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + initDialogLayoutParams(0.8f, 0.5f) + setCanceledOnTouchOutside(false) + setCancelable(false) + + val beans = pointBeanDao.loadAll() + if (beans.isEmpty()) return + + val lines = beans.map { it.secondType.toString() }.toSet().toList() + pointBeans = beans.filter { it.secondType.toString() == lines[0] }.toMutableList() + + pointAdapter = object : NormalRecyclerAdapter( + R.layout.item_inspection_point_rv_l, pointBeans + ) { + override fun convertView( + viewHolder: ViewHolder, position: Int, item: PointBean + ) { + viewHolder.setText(R.id.textView, "预置点位 ${item.thirdType}") + } + } + binding.pointRecyclerView.adapter = pointAdapter + binding.pointRecyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + + val lineAdapter = object : SingleChoiceAdapter( + R.layout.item_inspection_line_rv_l, lines.toMutableList() + ) { + override fun convertView(viewHolder: ViewHolder, position: Int, item: String) { + //{"firstType":92,"id":1,"secondType":1,"thirdType":1,"time":"2025-05-30 14:05:36"} + viewHolder.setText(R.id.textView, "巡航线路 $item") + } + } + binding.lineRecyclerView.adapter = lineAdapter + binding.lineRecyclerView.addItemDecoration(RecyclerViewItemDivider(0f, 0f, Color.LTGRAY)) + lineAdapter.setSelectedPosition(0) + lineAdapter.setOnItemCheckedListener(object : + SingleChoiceAdapter.OnItemCheckedListener { + override fun onItemChecked(position: Int, item: String) { + selectedLine = lines[position].toInt() + var points = arrayListOf() + beans.forEach { + if (lines[position] == it.secondType.toString()) { + points.add(it) + } + } + pointBeans = points + pointAdapter.refresh(points) + } + }) + + binding.dialogCancelButton.setOnClickListener { + listener.onCancelClick() + dismiss() + } + + binding.dialogConfirmButton.setOnClickListener { + listener.onConfirmClick(selectedLine) + dismiss() + } + } + + interface OnDialogButtonClickListener { + fun onConfirmClick(line: Int) + + fun onCancelClick() + } +} diff --git a/app/src/main/res/layout/dialog_manage_point.xml b/app/src/main/res/layout/dialog_manage_point.xml new file mode 100644 index 0000000..5665902 --- /dev/null +++ b/app/src/main/res/layout/dialog_manage_point.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + + +