diff --git a/app/build.gradle b/app/build.gradle index ab7fa750..f6479027 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -127,6 +127,14 @@ dependencies { implementation "androidx.camera:camera-view:1.0.0-alpha24" implementation 'com.google.mlkit:barcode-scanning:16.1.1' + + //图片加载 + implementation 'com.github.bumptech.glide:glide:4.15.1' + annotationProcessor 'com.github.bumptech.glide:compiler:4.15.1' +// implementation "io.realm:realm-kotlin-extensions:6.1.0" + + //带侧滑的自定义列表 + implementation 'com.yanzhenjie.recyclerview:x:1.3.2' } //允许引用生成的代码 kapt { diff --git a/app/src/main/assets/omdb_config.json b/app/src/main/assets/omdb_config.json index 7788238c..0bac6a8c 100644 --- a/app/src/main/assets/omdb_config.json +++ b/app/src/main/assets/omdb_config.json @@ -1,29 +1,46 @@ { "tableMap" : { + "1012": { + "table": "OMDB_CHECKPOINT", + "code": 1012, + "name": "检查点", + "zoomMin": 18, + "zoomMax": 23 + }, "2001": { "table": "OMDB_RD_LINK", "code": 2001, - "name": "道路线" + "name": "道路线", + "zoomMin": 18, + "zoomMax": 19 }, "2002": { "table": "OMDB_RD_LINK_FUNCTION_CLASS", "code": 2002, - "name": "道路功能等级" + "name": "道路功能等级", + "zoomMin": 18, + "zoomMax": 19 }, "2008": { "table": "OMDB_RD_LINK_KIND", "code": 2008, - "name": "道路种别" + "name": "道路种别", + "zoomMin": 18, + "zoomMax": 19 }, "2010": { "table": "OMDB_LINK_DIRECT", "code": 2010, - "name": "道路方向" + "name": "道路方向", + "zoomMin": 18, + "zoomMax": 19 }, "2011": { "table": "OMDB_LINK_NAME", "code": 2011, "name": "道路名", + "zoomMin": 18, + "zoomMax": 19, "transformer": [ { "k": "geometry", @@ -37,6 +54,8 @@ "table": "OMDB_LANE_MARK_BOUNDARYTYPE", "code": 2013, "name": "车道边界类型", + "zoomMin": 20, + "zoomMax": 23, "transformer": [ { "k": "geometry", @@ -49,17 +68,23 @@ "2019": { "table": "OMDB_LINK_SPEEDLIMIT", "code": 2019, - "name": "常规线限速" + "name": "常规线限速", + "zoomMin": 18, + "zoomMax": 19 }, "2020": { "table": "OMDB_LINK_SPEEDLIMIT_COND", "code": 2020, - "name": "条件线限速" + "name": "条件线限速", + "zoomMin": 18, + "zoomMax": 19 }, "2021": { "table": "OMDB_LINK_SPEEDLIMIT_VAR", "code": 2021, - "name": "可变线限速" + "name": "可变线限速", + "zoomMin": 18, + "zoomMax": 19 }, "2022": { "table": "OMDB_CON_ACCESS", @@ -84,7 +109,9 @@ "2083":{ "table": "OMDB_RDBOUND_BOUNDARYTYPE", "code": 2083, - "name": "道路边界类型" + "name": "道路边界类型", + "zoomMin": 20, + "zoomMax": 23 }, "2201":{ "table": "OMDB_BRIDGE", @@ -94,12 +121,24 @@ "2202":{ "table": "OMDB_TUNNEL", "code": 2202, - "name": "隧道" + "name": "隧道", + "zoomMin": 18, + "zoomMax": 23, + "transformer": [ + { + "k": "geometry", + "v": "~", + "klib": "geometry", + "vlib": "generateS2EReferencePoint()" + } + ] }, "4001": { "table": "OMDB_INTERSECTION", "code": 4001, "name": "路口", + "zoomMin": 18, + "zoomMax": 19, "transformer": [ { "k": "geometry", @@ -113,6 +152,8 @@ "table": "OMDB_SPEEDLIMIT", "code": 4002, "name": "常规点限速", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ { "k": "maxSpeed", @@ -132,6 +173,8 @@ "table": "OMDB_SPEEDLIMIT_COND", "code": 4003, "name": "条件点限速", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ { "k": "maxSpeed", @@ -145,6 +188,8 @@ "table": "OMDB_SPEEDLIMIT_VAR", "code": 4004, "name": "可变点限速", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ { "k": "location", @@ -170,6 +215,8 @@ "table": "OMDB_RESTRICTION", "code": 4006, "name": "普通交限", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ { "k": "geometry", @@ -207,6 +254,8 @@ "table": "OMDB_ELECTRONICEYE", "code": 4010, "name": "电子眼", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ { "k": "geometry", @@ -238,6 +287,8 @@ "table": "OMDB_TRAFFICLIGHT", "code": 4022, "name": "交通灯", + "zoomMin": 18, + "zoomMax": 23, "transformer": [ ] }, @@ -245,6 +296,8 @@ "table": "OMDB_LANEINFO", "code": 4601, "name": "车信", + "zoomMin": 18, + "zoomMax": 19, "transformer": [ { "k": "geometry", @@ -269,7 +322,17 @@ "5001":{ "table": "OMDB_LANE_LINK_LG", "code": 5001, - "name": "车道中心线" + "name": "车道中心线", + "zoomMin": 20, + "zoomMax": 23, + "transformer": [ + { + "k": "geometry", + "v": "~", + "klib": "geometry", + "vlib": "generateAddWidthLine()" + } + ] }, "5002":{ "table": "OMDB_AREA", diff --git a/app/src/main/java/com/navinfo/omqs/Constant.kt b/app/src/main/java/com/navinfo/omqs/Constant.kt index a885a6ea..30e53f7a 100644 --- a/app/src/main/java/com/navinfo/omqs/Constant.kt +++ b/app/src/main/java/com/navinfo/omqs/Constant.kt @@ -24,6 +24,11 @@ class Constant { */ lateinit var USER_ID: String + /** + * 当前用户名称 + */ + lateinit var USER_REAL_NAME: String + //数据版本 lateinit var VERSION_ID: String @@ -55,10 +60,20 @@ class Constant { /** * 室内整理工具IP */ - lateinit var INDOOR_IP: String + var INDOOR_IP: String = "" const val DEBUG = true + /** + * 地图最多缩放级别23 + */ + const val MAX_ZOOM = 23 + + /** + * 是否自动定位 + */ + var AUTO_LOCATION = false + var IS_VIDEO_SPEED by kotlin.properties.Delegates.notNull() const val message_status_late = "预约,待发送" @@ -92,6 +107,10 @@ class Constant { val OMDB_LAYER_VISIBLE_LIST: MutableList = mutableListOf() // 记录OMDB数据显示的图层名称列表 const val EVENT_LAYER_MANAGER_CHANGE = "EVENT_LAYER_MANAGER_CHANGE" // 图层管理中的配置修改 + + const val SELECT_TASK_ID = "select_task_id" //选中的任务ID + + const val SHARED_SYNC_TASK_LINK_ID = "shared_sync_task_link_id"//利用shared通知任务页面更新 } diff --git a/app/src/main/java/com/navinfo/omqs/bean/EvaluationInfo.kt b/app/src/main/java/com/navinfo/omqs/bean/EvaluationInfo.kt index b1418d1d..c0c49de6 100644 --- a/app/src/main/java/com/navinfo/omqs/bean/EvaluationInfo.kt +++ b/app/src/main/java/com/navinfo/omqs/bean/EvaluationInfo.kt @@ -51,10 +51,10 @@ data class EvaluationInfo( val evaluationWay: Int = 2,//测评方式 1生产测评 2现场测评 服务字段定义为Integer,使用包装类,对应无值情况为空 @SerializedName("roadClassfcation") - val roadClassfcation: String = "",//道路种别 + val roadClassfcation: Int = 1,//道路种别 @SerializedName("roadFunctionGrade") - val roadFunctionGrade: String = "",//道路功能等级 + val roadFunctionGrade: Int = 1,//道路功能等级 @SerializedName("noEvaluationreason") val noEvaluationreason: String = "",//未测评原因 @@ -63,7 +63,7 @@ data class EvaluationInfo( val linkLength: Double = 0.0,//link长度(m 保留3位小数) @SerializedName("dataLevel") - val dataLevel: String = "",//数据级别 + val dataLevel: Int = 1,//数据级别 @SerializedName("linstringLength") val linstringLength: Double = 0.0,//错误要素长度(m) diff --git a/app/src/main/java/com/navinfo/omqs/bean/ImportConfig.kt b/app/src/main/java/com/navinfo/omqs/bean/ImportConfig.kt index 5c6683b2..4cbbb603 100644 --- a/app/src/main/java/com/navinfo/omqs/bean/ImportConfig.kt +++ b/app/src/main/java/com/navinfo/omqs/bean/ImportConfig.kt @@ -115,6 +115,8 @@ class ImportConfig { class TableInfo { val table: String = "" val code: Int = 0 + val zoomMin: Int = 18 + val zoomMax: Int = 23 val name: String = "" var checked : Boolean = true var transformer: MutableList = mutableListOf() diff --git a/app/src/main/java/com/navinfo/omqs/bean/QRCodeBean.kt b/app/src/main/java/com/navinfo/omqs/bean/QRCodeBean.kt index 4790dd17..2438f705 100644 --- a/app/src/main/java/com/navinfo/omqs/bean/QRCodeBean.kt +++ b/app/src/main/java/com/navinfo/omqs/bean/QRCodeBean.kt @@ -2,5 +2,5 @@ package com.navinfo.omqs.bean data class QRCodeBean( var errcode: Int = -1, - var msg: String = "" + var errmsg: String = "" ) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/bean/TraceVideoBean.kt b/app/src/main/java/com/navinfo/omqs/bean/TraceVideoBean.kt new file mode 100644 index 00000000..f8d0da42 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/bean/TraceVideoBean.kt @@ -0,0 +1,8 @@ +package com.navinfo.omqs.bean + +data class TraceVideoBean( + var userid: String = "", + var playMode: String = "", + var time: String = "", + var command: String = "", +) \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt b/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt index cbc2aca8..a22adad6 100644 --- a/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt +++ b/app/src/main/java/com/navinfo/omqs/db/ImportOMDBHelper.kt @@ -5,23 +5,19 @@ import android.database.Cursor.* import android.os.Build import android.util.Log import androidx.annotation.RequiresApi -import androidx.core.database.getBlobOrNull -import androidx.core.database.getFloatOrNull -import androidx.core.database.getIntOrNull -import androidx.core.database.getStringOrNull import com.blankj.utilcode.util.FileIOUtils import com.blankj.utilcode.util.ZipUtils import com.google.gson.Gson import com.google.gson.reflect.TypeToken +import com.navinfo.collect.library.data.entity.ReferenceEntity import com.navinfo.collect.library.data.entity.RenderEntity import com.navinfo.omqs.Constant import com.navinfo.omqs.bean.ImportConfig -import com.navinfo.omqs.bean.Transform -import com.navinfo.omqs.hilt.ImportOMDBHiltFactory import com.navinfo.omqs.hilt.OMDBDataBaseHiltFactory import dagger.assisted.Assisted import dagger.assisted.AssistedInject import io.realm.Realm +import io.realm.RealmQuery import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.flow @@ -127,7 +123,7 @@ class ImportOMDBHelper @AssistedInject constructor( * @param omdbZipFile omdb数据抽取生成的Zip文件 * @param configFile 对应的配置文件 * */ - suspend fun importOmdbZipFile(omdbZipFile: File): Flow = withContext(Dispatchers.IO) { + suspend fun importOmdbZipFile(omdbZipFile: File, taskId: Int): Flow = withContext(Dispatchers.IO) { val unZipFolder = File(omdbZipFile.parentFile, "result") flow { if (unZipFolder.exists()) { @@ -137,8 +133,9 @@ class ImportOMDBHelper @AssistedInject constructor( // 开始解压zip文件 val unZipFiles = ZipUtils.unzipFile(omdbZipFile, unZipFolder) // 将listResult数据插入到Realm数据库中 + val realm = Realm.getDefaultInstance() + realm.beginTransaction() try { - Realm.getDefaultInstance().beginTransaction() // 遍历解压后的文件,读取该数据返回 for ((index, currentEntry) in importConfig.tableMap.entries.withIndex()) { val currentConfig = currentEntry.value @@ -161,7 +158,11 @@ class ImportOMDBHelper @AssistedInject constructor( .toMutableMap() map["qi_table"] = currentConfig.table map["qi_name"] = currentConfig.name + map["qi_code"] = + if (currentConfig.code == 0) currentConfig.code else currentEntry.key map["qi_code"] = if (currentConfig.code == 0) currentConfig.code else currentEntry.key + map["qi_zoomMin"] = currentConfig.zoomMin + map["qi_zoomMax"] = currentConfig.zoomMax // 先查询这个mesh下有没有数据,如果有则跳过即可 // val meshEntity = Realm.getDefaultInstance().where(RenderEntity::class.java).equalTo("properties['mesh']", map["mesh"].toString()).findFirst() @@ -169,21 +170,31 @@ class ImportOMDBHelper @AssistedInject constructor( renderEntity.code = map["qi_code"].toString().toInt() renderEntity.name = map["qi_name"].toString() renderEntity.table = map["qi_table"].toString() + renderEntity.taskId = taskId + renderEntity.zoomMin = map["qi_zoomMin"].toString().toInt() + renderEntity.zoomMax = map["qi_zoomMax"].toString().toInt() + // 其他数据插入到Properties中 renderEntity.geometry = map["geometry"].toString() for ((key, value) in map) { when (value) { is String -> renderEntity.properties.put(key, value) - is Int -> renderEntity.properties.put(key, value.toInt().toString()) - is Double -> renderEntity.properties.put(key, value.toDouble().toString()) + is Int -> renderEntity.properties.put( + key, + value.toInt().toString() + ) + is Double -> renderEntity.properties.put( + key, + value.toDouble().toString() + ) else -> renderEntity.properties.put(key, value.toString()) } } listResult.add(renderEntity) // 对renderEntity做预处理后再保存 val resultEntity = importConfig.transformProperties(renderEntity) - if (resultEntity!=null) { - Realm.getDefaultInstance().insert(renderEntity) + if (resultEntity != null) { + realm.insert(renderEntity) } } } @@ -192,12 +203,14 @@ class ImportOMDBHelper @AssistedInject constructor( emit("${index + 1}/${importConfig.tableMap.size}") // 如果当前解析的是OMDB_RD_LINK数据,将其缓存在预处理类中,以便后续处理其他要素时使用 if (currentConfig.table == "OMDB_RD_LINK") { - importConfig.preProcess.cacheRdLink = listResult.associateBy { it.properties["linkPid"] } + importConfig.preProcess.cacheRdLink = + listResult.associateBy { it.properties["linkPid"] } } } - Realm.getDefaultInstance().commitTransaction() + realm.commitTransaction() + realm.close() } catch (e: Exception) { - Realm.getDefaultInstance().cancelTransaction() + realm.cancelTransaction() throw e } emit("finish") diff --git a/app/src/main/java/com/navinfo/omqs/db/ImportPreProcess.kt b/app/src/main/java/com/navinfo/omqs/db/ImportPreProcess.kt index a5f70604..92c807f1 100644 --- a/app/src/main/java/com/navinfo/omqs/db/ImportPreProcess.kt +++ b/app/src/main/java/com/navinfo/omqs/db/ImportPreProcess.kt @@ -158,6 +158,10 @@ class ImportPreProcess { startEndReference.renderEntityId = renderEntity.id startEndReference.name = "${renderEntity.name}参考线" startEndReference.table = renderEntity.table + startEndReference.zoomMin = renderEntity.zoomMin + startEndReference.zoomMax = renderEntity.zoomMax + startEndReference.taskId = renderEntity.taskId + // 起终点坐标组成的线 startEndReference.geometry = GeometryTools.createLineString(arrayOf(pointStart, pointEnd)).toString() startEndReference.properties["qi_table"] = renderEntity.table @@ -165,6 +169,42 @@ class ImportPreProcess { Realm.getDefaultInstance().insert(startEndReference) } + fun generateS2EReferencePoint(renderEntity: RenderEntity) { + val geometry = GeometryTools.createGeometry(renderEntity.properties["geometry"]) + + val pointEnd = geometry!!.coordinates[geometry.numPoints-1] // 获取这个geometry对应的结束点坐标 + val pointStart = geometry!!.coordinates[0] // 获取这个geometry对应的起点 + + // 将这个起终点的线记录在数据中 + val startReference = ReferenceEntity() + startReference.renderEntityId = renderEntity.id + startReference.name = "${renderEntity.name}参考线" + startReference.table = renderEntity.table + startReference.zoomMin = renderEntity.zoomMin + startReference.zoomMax = renderEntity.zoomMax + startReference.taskId = renderEntity.taskId + + // 起点坐标 + startReference.geometry = GeometryTools.createGeometry(GeoPoint(pointStart.y,pointStart.x)).toString() + startReference.properties["qi_table"] = renderEntity.table + startReference.properties["type"] = "s_2_p" + Realm.getDefaultInstance().insert(startReference) + + val endReference = ReferenceEntity() + endReference.renderEntityId = renderEntity.id + endReference.name = "${renderEntity.name}参考线" + endReference.table = renderEntity.table + endReference.zoomMin = renderEntity.zoomMin + endReference.zoomMax = renderEntity.zoomMax + endReference.taskId = renderEntity.taskId + + // 终点坐标 + endReference.geometry = GeometryTools.createGeometry(GeoPoint(pointEnd.y,pointEnd.x)).toString() + endReference.properties["qi_table"] = renderEntity.table + endReference.properties["type"] = "e_2_p" + Realm.getDefaultInstance().insert(endReference) + } + /** * 生成与对应方向相同的方向线,用以绘制方向箭头 * */ @@ -211,6 +251,9 @@ class ImportPreProcess { angleReference.renderEntityId = renderEntity.id angleReference.name = "${renderEntity.name}参考方向" angleReference.table = renderEntity.table + angleReference.zoomMin = renderEntity.zoomMin + angleReference.zoomMax = renderEntity.zoomMax + angleReference.taskId = renderEntity.taskId // 与原有方向指向平行的线 angleReference.geometry = GeometryTools.createLineString(arrayOf(point, coorEnd)).toString() angleReference.properties["qi_table"] = renderEntity.table @@ -299,7 +342,6 @@ class ImportPreProcess { } - /** * 生成默认道路名数据 * */ @@ -343,6 +385,25 @@ class ImportPreProcess { } } + /** + * 生成车道中心线面宽度 + * */ + fun generateAddWidthLine(renderEntity: RenderEntity) { + // 添加车道中心面渲染原则,根据车道宽度进行渲染 + val angleReference = ReferenceEntity() + angleReference.renderEntityId = renderEntity.id + angleReference.name = "${renderEntity.name}车道中线面" + angleReference.table = renderEntity.table + angleReference.geometry = renderEntity.geometry + angleReference.properties["qi_table"] = renderEntity.table + angleReference.properties["width"] = "3" + angleReference.zoomMin = renderEntity.zoomMin + angleReference.zoomMax = renderEntity.zoomMax + angleReference.taskId = renderEntity.taskId + Realm.getDefaultInstance().insert(angleReference) + } + + /** * 生成默认路口数据的参考数据 * */ @@ -356,6 +417,9 @@ class ImportPreProcess { intersectionReference.renderEntityId = renderEntity.id intersectionReference.name = "${renderEntity.name}参考点" intersectionReference.table = renderEntity.table + intersectionReference.zoomMin = renderEntity.zoomMin + intersectionReference.zoomMax = renderEntity.zoomMax + intersectionReference.taskId = renderEntity.taskId // 与原有方向指向平行的线 intersectionReference.geometry = GeometryTools.createGeometry(nodeJSONObject["geometry"].toString()).toString() intersectionReference.properties["qi_table"] = renderEntity.table diff --git a/app/src/main/java/com/navinfo/omqs/db/RealmOperateHelper.kt b/app/src/main/java/com/navinfo/omqs/db/RealmOperateHelper.kt index 8edb79c4..a1a02fd4 100644 --- a/app/src/main/java/com/navinfo/omqs/db/RealmOperateHelper.kt +++ b/app/src/main/java/com/navinfo/omqs/db/RealmOperateHelper.kt @@ -3,6 +3,7 @@ package com.navinfo.omqs.db import android.os.Build import android.util.Log import androidx.annotation.RequiresApi +import com.navinfo.collect.library.data.entity.HadLinkDvoBean import com.navinfo.collect.library.data.entity.RenderEntity import com.navinfo.collect.library.data.entity.RenderEntity.Companion.LinkTable import com.navinfo.collect.library.map.NIMapController @@ -85,6 +86,39 @@ class RealmOperateHelper() { } + suspend fun captureTaskLink( + taskId: Int, + point: GeoPoint, + buffer: Double = DEFAULT_BUFFER, + bufferType: BUFFER_TYPE = DEFAULT_BUFFER_TYPE, + ): HadLinkDvoBean? { + + val polygon = getPolygonFromPoint( + GeometryTools.createPoint(point.longitude, point.latitude), + buffer, + bufferType + ) + + val realm = Realm.getDefaultInstance() + val realmList = realm.where(HadLinkDvoBean::class.java) + .equalTo("taskId", taskId) + .findAll() + var linkBean: HadLinkDvoBean? = null + var nearLast: Double = 99999.99 + for (link in realmList) { + if (polygon.intersects(GeometryTools.createGeometry(link.geometry))) { + val near = point.distance(GeometryTools.createGeoPoint(link.geometry)) + if (near < nearLast) { + nearLast = near + linkBean = link + } + } + } + if (linkBean != null) + return realm.copyFromRealm(linkBean) + return null + } + suspend fun queryLink(linkPid: String): RenderEntity? { var link: RenderEntity? = null withContext(Dispatchers.IO) { @@ -237,6 +271,8 @@ class RealmOperateHelper() { Log.d("queryLink", wkt.toString()) return wkt } + + } enum class BUFFER_TYPE(val index: Int) { diff --git a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt index c72b6022..f1c356d5 100644 --- a/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt +++ b/app/src/main/java/com/navinfo/omqs/hilt/GlobalModule.kt @@ -1,6 +1,8 @@ package com.navinfo.omqs.hilt import android.app.Application +import android.content.Context +import android.content.SharedPreferences import android.util.Log import androidx.room.Room import com.google.gson.Gson @@ -25,7 +27,6 @@ import io.realm.Realm import kotlinx.coroutines.* import okhttp3.OkHttpClient import okhttp3.logging.HttpLoggingInterceptor -import retrofit2.Call import retrofit2.Retrofit import retrofit2.converter.gson.GsonConverterFactory import java.util.concurrent.TimeUnit @@ -38,6 +39,7 @@ import javax.inject.Singleton @InstallIn(SingletonComponent::class) class GlobalModule { + @Singleton @Provides fun provideApplication(@ApplicationContext application: Application): OMQSApplication { @@ -166,4 +168,10 @@ class GlobalModule { fun provideRealmDefaultInstance(): Realm { return Realm.getDefaultInstance() } + + @Singleton + @Provides + fun provideSharedPreferences(context: Application): SharedPreferences { + return context.getSharedPreferences("Shared" + Constant.USER_ID, Context.MODE_PRIVATE) + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt b/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt index beca12b6..2570f031 100644 --- a/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt +++ b/app/src/main/java/com/navinfo/omqs/http/NetworkService.kt @@ -6,8 +6,7 @@ import com.navinfo.omqs.bean.IndoorConnectionInfoBean import com.navinfo.omqs.bean.LoginUserBean import com.navinfo.omqs.bean.QRCodeBean import com.navinfo.omqs.bean.SysUserBean -import okhttp3.ResponseBody -import retrofit2.Response +import com.navinfo.omqs.bean.TraceVideoBean /** @@ -38,4 +37,9 @@ interface NetworkService { * 更新用户信息 */ suspend fun updateServerInfo(url: String,indoorConnectionInfoBean: IndoorConnectionInfoBean): NetResult + + /** + * 设置轨迹对应的视频 + */ + suspend fun sendServerCommand(url: String,traceVideoBean: TraceVideoBean): NetResult } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt b/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt index 5bcd2782..059229b8 100644 --- a/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt +++ b/app/src/main/java/com/navinfo/omqs/http/NetworkServiceImpl.kt @@ -6,6 +6,7 @@ import com.navinfo.omqs.bean.IndoorConnectionInfoBean import com.navinfo.omqs.bean.LoginUserBean import com.navinfo.omqs.bean.QRCodeBean import com.navinfo.omqs.bean.SysUserBean +import com.navinfo.omqs.bean.TraceVideoBean import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.withContext import okhttp3.ResponseBody @@ -101,7 +102,43 @@ class NetworkServiceImpl @Inject constructor( //在IO线程中运行 withContext(Dispatchers.IO) { return@withContext try { - val result = netApi.retrofitUpdateServerInfo(url,indoorConnectionInfoBean) + val map: MutableMap = HashMap() + map["username"] = indoorConnectionInfoBean.username + map["uname"] = indoorConnectionInfoBean.uname + map["userid"] = indoorConnectionInfoBean.userid + map["plate"] = indoorConnectionInfoBean.plate + map["token"] = indoorConnectionInfoBean.token + map["baseurl"] = indoorConnectionInfoBean.baseurl + map["platform"] = indoorConnectionInfoBean.platform + + val result = netApi.retrofitUpdateServerInfo(url,map) + if (result.isSuccessful) { + if (result.code() == 200) { + NetResult.Success(result.body()) + } else { + NetResult.Failure(result.code(), result.message()) + } + } else { + NetResult.Failure(result.code(), result.message()) + } + } catch (e: Exception) { + NetResult.Error(e) + } + } + + override suspend fun sendServerCommand( + url: String, + traceVideoBean: TraceVideoBean + ): NetResult = + //在IO线程中运行 + withContext(Dispatchers.IO) { + return@withContext try { + val map: MutableMap = HashMap() + map["userid"] = traceVideoBean.userid + map["playMode"] = traceVideoBean.playMode + map["time"] = traceVideoBean.time + + val result = netApi.retrofitUpdateServerInfo(url,map) if (result.isSuccessful) { if (result.code() == 200) { NetResult.Success(result.body()) diff --git a/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt b/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt index 2f56bbab..640c0e21 100644 --- a/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt +++ b/app/src/main/java/com/navinfo/omqs/http/RetrofitNetworkServiceAPI.kt @@ -78,9 +78,9 @@ interface RetrofitNetworkServiceAPI { /** * 登录接口 */ - @Headers("Content-Type: application/json") + @FormUrlEncoded @POST - suspend fun retrofitUpdateServerInfo(@Url url: String,@Body indoorConnectionInfoBean: IndoorConnectionInfoBean): Response + suspend fun retrofitUpdateServerInfo(@Url url: String,@FieldMap map: Map): Response @Headers("Content-Type: application/json") @POST("/devcp/uploadSceneProblem") diff --git a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt index bfd74d35..4a0a95e8 100644 --- a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt +++ b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadManager.kt @@ -1,6 +1,7 @@ package com.navinfo.omqs.http.taskdownload import android.content.Context +import android.util.Log import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import com.navinfo.collect.library.map.NIMapController @@ -17,7 +18,7 @@ import java.util.concurrent.ConcurrentHashMap class TaskDownloadManager constructor( val importFactory: ImportOMDBHiltFactory, val netApi: RetrofitNetworkServiceAPI, - val mapController:NIMapController + val mapController: NIMapController ) { lateinit var context: Context @@ -41,6 +42,7 @@ class TaskDownloadManager constructor( ConcurrentHashMap() } + fun init(context: Context) { this.context = context } @@ -102,8 +104,9 @@ class TaskDownloadManager constructor( fun addTask(taskBean: TaskBean) { + Log.e("jingo", "下载线程 ${taskBean.id}") if (!scopeMap.containsKey(taskBean.id)) { - scopeMap[taskBean.id] = TaskDownloadScope( this, taskBean) + scopeMap[taskBean.id] = TaskDownloadScope(this, taskBean) } } @@ -111,6 +114,7 @@ class TaskDownloadManager constructor( fun observer( id: Int, lifecycleOwner: LifecycleOwner, observer: Observer ) { + Log.e("jingo", "监听线程 ${id}") if (scopeMap.containsKey(id)) { scopeMap[id]!!.observer(lifecycleOwner, observer) } diff --git a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt index 53e1d6da..f628ed7d 100644 --- a/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt +++ b/app/src/main/java/com/navinfo/omqs/http/taskdownload/TaskDownloadScope.kt @@ -1,14 +1,16 @@ package com.navinfo.omqs.http.taskdownload import android.util.Log +import androidx.lifecycle.Lifecycle import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Observer -import com.navinfo.omqs.Constant import com.navinfo.collect.library.data.entity.TaskBean +import com.navinfo.omqs.Constant import com.navinfo.omqs.db.ImportOMDBHelper import com.navinfo.omqs.tools.FileManager import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus +import com.navinfo.omqs.ui.other.BaseViewHolder import com.navinfo.omqs.util.DateTimeUtil import io.realm.Realm import kotlinx.coroutines.* @@ -21,7 +23,7 @@ class TaskDownloadScope( private val downloadManager: TaskDownloadManager, val taskBean: TaskBean, ) : - CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("OfflineMapDownLoad")) { + CoroutineScope by CoroutineScope(Dispatchers.IO + CoroutineName("TaskMapDownLoad")) { /** *下载任务,用来取消的 @@ -31,8 +33,8 @@ class TaskDownloadScope( /** * 管理观察者,同时只有一个就行了 */ - private val observer = Observer {} -// private var lifecycleOwner: LifecycleOwner? = null +// private val observer = Observer {} + private var lifecycleOwner: LifecycleOwner? = null /** *通知UI更新 @@ -71,7 +73,7 @@ class TaskDownloadScope( downloadJob = launch() { FileManager.checkOMDBFileInfo(taskBean) if (taskBean.status == FileDownloadStatus.IMPORT) { - importData() + importData(taskId = taskBean.id) } else { download() } @@ -92,6 +94,7 @@ class TaskDownloadScope( * @param status [OfflineMapCityBean.Status] */ private suspend fun change(status: Int, message: String = "") { + if (taskBean.status != status || status == FileDownloadStatus.LOADING || status == FileDownloadStatus.IMPORTING) { taskBean.status = status taskBean.message = message @@ -111,15 +114,16 @@ class TaskDownloadScope( * 添加下载任务观察者 */ fun observer(owner: LifecycleOwner, ob: Observer) { + removeObserver() -// this.lifecycleOwner = owner + this.lifecycleOwner = owner downloadData.observe(owner, ob) } /** * 导入数据 */ - private suspend fun importData(file: File? = null) { + private suspend fun importData(file: File? = null, taskId: Int?=0) { try { Log.e("jingo", "importData SSS") change(FileDownloadStatus.IMPORTING) @@ -130,15 +134,17 @@ class TaskDownloadScope( downloadManager.context, fileNew ) - importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { - Log.e("jingo", "数据安装 $it") - if (it == "finish") { - change(FileDownloadStatus.DONE) - withContext(Dispatchers.Main) { - downloadManager.mapController.mMapView.updateMap(true) + if (taskId != null) { + importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile,taskId).collect { + Log.e("jingo", "数据安装 $it") + if (it == "finish") { + change(FileDownloadStatus.DONE) + withContext(Dispatchers.Main) { + downloadManager.mapController.mMapView.updateMap(true) + } + } else { + change(FileDownloadStatus.IMPORTING, it) } - } else { - change(FileDownloadStatus.IMPORTING, it) } } } catch (e: Exception) { @@ -177,7 +183,7 @@ class TaskDownloadScope( startPosition = 0 } if (fileTemp.length() > 0 && taskBean.fileSize > 0 && fileTemp.length() == taskBean.fileSize) { - importData(fileTemp) + importData(fileTemp,taskBean.id) return } @@ -218,7 +224,7 @@ class TaskDownloadScope( randomAccessFile?.close() inputStream = null randomAccessFile = null - importData() + importData(taskId = taskBean.id) } else { change(FileDownloadStatus.PAUSE) } @@ -232,10 +238,17 @@ class TaskDownloadScope( } fun removeObserver() { - downloadData.observeForever(observer) -// lifecycleOwner?.let { - downloadData.removeObserver(observer) -// null -// } +// downloadData.observeForever(observer) +//// lifecycleOwner?.let { +// downloadData.removeObserver(observer) +//// null +//// } + if (lifecycleOwner != null) { + Log.e( + "jingo", + "移除的上一个监听者 ${lifecycleOwner.hashCode()} ${(lifecycleOwner as BaseViewHolder).tag}" + ) + downloadData.removeObservers(lifecycleOwner!!) + } } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/http/taskupload/TaskUploadScope.kt b/app/src/main/java/com/navinfo/omqs/http/taskupload/TaskUploadScope.kt index dd1189c2..41b948ef 100644 --- a/app/src/main/java/com/navinfo/omqs/http/taskupload/TaskUploadScope.kt +++ b/app/src/main/java/com/navinfo/omqs/http/taskupload/TaskUploadScope.kt @@ -77,7 +77,7 @@ class TaskUploadScope( taskBean.operationTime = DateTimeUtil.getNowDate().time uploadData.postValue(taskBean) //同步中不进行状态记录,只做界面变更显示 - if(status!=FileUploadStatus.UPLOADING){ + if (status != FileUploadStatus.UPLOADING) { launch { val realm = Realm.getDefaultInstance() realm.executeTransaction { @@ -118,59 +118,18 @@ class TaskUploadScope( val bodyList: MutableList = ArrayList() - if (taskBean.syncStatus == FileUploadStatus.WAITING){ + if (taskBean.syncStatus == FileUploadStatus.WAITING) { change(FileUploadStatus.UPLOADING) } taskBean.hadLinkDvoList.forEach { hadLinkDvoBean -> - val objects = realm.where(QsRecordBean::class.java) - .equalTo("linkId", /*"84207223282277331"*/hadLinkDvoBean.linkPid).findAll() - if (objects != null&&objects.size>0) { - val copyList = realm.copyFromRealm(objects) - copyList.forEach { - var problemType = 0 - if(it.problemType=="错误"){ - problemType = 0 - }else if(it.problemType=="多余"){ - problemType = 1 - }else if(it.problemType=="遗漏"){ - problemType = 2 - } - var evaluationWay = 2 - val evaluationInfo = EvaluationInfo( - evaluationTaskId = taskBean.id.toString(), - linkPid = hadLinkDvoBean.linkPid,//"84207223282277331" - linkStatus = 1, - markId = hadLinkDvoBean.mesh,//"20065597" - trackPhotoNumber = "", - markGeometry = it.geometry, - featureName = it.classCode, - problemType = problemType, - problemPhenomenon = it.phenomenon, - problemDesc = it.description, - problemLink = it.problemLink, - preliminaryAnalysis = it.cause, - evaluatorName = it.checkUserId, - evaluationDate = it.checkTime, - evaluationWay = evaluationWay, - roadClassfcation = "", - roadFunctionGrade = "", - noEvaluationreason = "", - linkLength = 0.0, - dataLevel = "", - linstringLength = 0.0, - ) - bodyList.add(evaluationInfo) - } - }else{ - val linkStatus = 1 - //存在原因标记未测评 - if(hadLinkDvoBean.reason.isNotEmpty()){ - val linkStatus = 0 - }else{ - val linkStatus = 1 - } + val linkStatus = 1 + //存在原因标记未测评 + if (hadLinkDvoBean.reason.isNotEmpty()) { + //未测评 + val linkStatus = 0 + val evaluationInfo = EvaluationInfo( evaluationTaskId = taskBean.id.toString(), linkPid = hadLinkDvoBean.linkPid,//"84207223282277331" @@ -187,27 +146,92 @@ class TaskUploadScope( evaluatorName = "", evaluationDate = "", evaluationWay = 2, - roadClassfcation = "", - roadFunctionGrade = "", + roadClassfcation = 1, + roadFunctionGrade = 0, noEvaluationreason = hadLinkDvoBean.reason, linkLength = 0.0, - dataLevel = "", + dataLevel = 0, linstringLength = 0.0, ) + bodyList.add(evaluationInfo) + + } else { + + val linkStatus = hadLinkDvoBean.linkStatus + + var s: String = "%.3f".format(hadLinkDvoBean.length)//保留一位小数(且支持四舍五入) + + val objects = realm.where(QsRecordBean::class.java) + .equalTo("linkId", /*"84207223282277331"*/hadLinkDvoBean.linkPid).and() + .equalTo("taskId", hadLinkDvoBean.taskId).findAll() + + if (objects != null && objects.size > 0) { + val copyList = realm.copyFromRealm(objects) + copyList.forEach { + var problemType = 0 + if (it.problemType == "错误") { + problemType = 0 + } else if (it.problemType == "多余") { + problemType = 1 + } else if (it.problemType == "遗漏") { + problemType = 2 + } + + var roadClassfcation = 0 + + var roadFunctionGrade = 0 + + var dataLevel = 0 + + if (hadLinkDvoBean.linkInfo != null) { + roadClassfcation = hadLinkDvoBean.linkInfo!!.kind + roadFunctionGrade = hadLinkDvoBean.linkInfo!!.functionLevel + dataLevel = hadLinkDvoBean.linkInfo!!.dataLevel + } + + var evaluationWay = 2 + val evaluationInfo = EvaluationInfo( + evaluationTaskId = taskBean.id.toString(), + linkPid = hadLinkDvoBean.linkPid,//"84207223282277331" + linkStatus = linkStatus, + markId = hadLinkDvoBean.mesh,//"20065597" + trackPhotoNumber = "", + markGeometry = it.geometry, + featureName = it.classCode, + problemType = problemType, + problemPhenomenon = it.phenomenon, + problemDesc = it.description, + problemLink = it.problemLink, + preliminaryAnalysis = it.cause, + evaluatorName = it.checkUserId, + evaluationDate = it.checkTime, + evaluationWay = evaluationWay, + roadClassfcation = roadClassfcation, + roadFunctionGrade = roadFunctionGrade, + noEvaluationreason = "", + linkLength = s.toDouble(), + dataLevel = dataLevel, + linstringLength = 0.0, + ) + + bodyList.add(evaluationInfo) + } + } } + } - if(bodyList.size>0){ + if (bodyList.size > 0) { val result = uploadManager.netApi.postRequest(bodyList)// .enqueue(object : // Callback { if (result.isSuccessful) { - if (result.code() == 200&&result.body()!=null) { + if (result.code() == 200 && result.body() != null) { val defaultUserResponse = result.body() as DefaultResponse<*> - if(defaultUserResponse.success){ - change(FileUploadStatus.DONE,"上传成功") - }else{ - change(FileUploadStatus.ERROR,"${defaultUserResponse.msg}") + if (defaultUserResponse.success) { + change(FileUploadStatus.DONE, "上传成功") + } else { + change(FileUploadStatus.ERROR, "${defaultUserResponse.msg}") } } else { // handle the failure @@ -216,7 +240,7 @@ class TaskUploadScope( } else { change(FileUploadStatus.ERROR) } - }else{ + } else { change(FileUploadStatus.NONE) } } catch (e: Throwable) { diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt index 095664d1..5b2a9b32 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/login/LoginViewModel.kt @@ -1,6 +1,7 @@ package com.navinfo.omqs.ui.activity.login import android.content.Context +import android.content.SharedPreferences import android.util.Log import android.view.View import android.widget.Toast @@ -16,11 +17,11 @@ import com.navinfo.omqs.http.DefaultResponse import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.tools.FileManager +import com.navinfo.omqs.util.NetUtils import dagger.hilt.android.lifecycle.HiltViewModel import io.realm.Realm import io.realm.RealmConfiguration import kotlinx.coroutines.* -import retrofit2.Response import java.io.File import java.io.IOException import javax.inject.Inject @@ -73,7 +74,9 @@ class LoginViewModel @Inject constructor( //是不是登录成功 val loginStatus: MutableLiveData = MutableLiveData() - var jobLogin: Job? = null; + var jobLogin: Job? = null + + var sharedPreferences: SharedPreferences? = null init { loginUser.value = LoginUserBean(userCode = "haofuyue00213", passWord = "123456") @@ -98,10 +101,27 @@ class LoginViewModel @Inject constructor( if (password.isEmpty()) { Toast.makeText(context, "请输入密码", Toast.LENGTH_SHORT).show() } + sharedPreferences = + context.getSharedPreferences("USER_SHAREDPREFERENCES", Context.MODE_PRIVATE) + val userNameCache = sharedPreferences?.getString("userName", null) + val passwordCache = sharedPreferences?.getString("passWord", null) + val userCodeCache = sharedPreferences?.getString("userCode", null) + val userRealName = sharedPreferences?.getString("userRealName", null) + //增加缓存记录,不用每次连接网络登录 + if (userNameCache != null && passwordCache != null && userCodeCache != null&&userRealName!=null) { + if (userNameCache == userName && passwordCache == password) { + viewModelScope.launch(Dispatchers.IO) { + createUserFolder(context, userCodeCache,userRealName) + loginStatus.postValue(LoginStatus.LOGIN_STATUS_SUCCESS) + } + return + } + } //不指定IO,会在主线程里运行 jobLogin = viewModelScope.launch(Dispatchers.IO) { loginCheck(context, userName, password) } + } /** @@ -114,26 +134,36 @@ class LoginViewModel @Inject constructor( //网络访问 loginStatus.postValue(LoginStatus.LOGIN_STATUS_NET_LOADING) var userCode = "99999"; + var userRealName = ""; //登录访问 - when (val result = networkService.loginUser(LoginUserBean(userName,password))) { - is NetResult.Success<*> ->{ - if (result.data!=null) { + when (val result = networkService.loginUser(LoginUserBean(userName, password))) { + is NetResult.Success<*> -> { + if (result.data != null) { try { val defaultUserResponse = result.data as DefaultResponse - if(defaultUserResponse.success){ - if(defaultUserResponse.obj==null|| defaultUserResponse.obj!!.userCode==null){ + if (defaultUserResponse.success) { + if (defaultUserResponse.obj == null || defaultUserResponse.obj!!.userCode == null) { withContext(Dispatchers.Main) { - Toast.makeText(context, "服务返回用户Code信息错误", Toast.LENGTH_SHORT) + Toast.makeText( + context, + "服务返回用户Code信息错误", + Toast.LENGTH_SHORT + ) .show() } loginStatus.postValue(LoginStatus.LOGIN_STATUS_CANCEL) return - }else{ + } else { userCode = defaultUserResponse.obj?.userCode.toString() + userRealName = defaultUserResponse.obj?.userName.toString() } - }else{ + } else { withContext(Dispatchers.Main) { - Toast.makeText(context, "${defaultUserResponse.msg}", Toast.LENGTH_SHORT) + Toast.makeText( + context, + "${defaultUserResponse.msg}", + Toast.LENGTH_SHORT + ) .show() } loginStatus.postValue(LoginStatus.LOGIN_STATUS_CANCEL) @@ -145,7 +175,8 @@ class LoginViewModel @Inject constructor( } } } - is NetResult.Error<*> ->{ + + is NetResult.Error<*> -> { withContext(Dispatchers.Main) { Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT) .show() @@ -153,7 +184,8 @@ class LoginViewModel @Inject constructor( loginStatus.postValue(LoginStatus.LOGIN_STATUS_CANCEL) return } - is NetResult.Failure<*> ->{ + + is NetResult.Failure<*> -> { withContext(Dispatchers.Main) { Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT) .show() @@ -161,13 +193,19 @@ class LoginViewModel @Inject constructor( loginStatus.postValue(LoginStatus.LOGIN_STATUS_CANCEL) return } + else -> {} } //文件夹初始化 try { loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_INIT) - createUserFolder(context, userCode) + sharedPreferences?.edit()?.putString("userName", userName)?.commit() + sharedPreferences?.edit()?.putString("passWord", password)?.commit() + sharedPreferences?.edit()?.putString("userCode", userCode)?.commit() + sharedPreferences?.edit()?.putString("userRealName", userRealName)?.commit() + + createUserFolder(context, userCode,userRealName) } catch (e: IOException) { loginStatus.postValue(LoginStatus.LOGIN_STATUS_FOLDER_FAILURE) } @@ -185,18 +223,21 @@ class LoginViewModel @Inject constructor( roomAppDatabase.getOfflineMapDao().insertOrUpdate(result.data) } } + is NetResult.Error<*> -> { withContext(Dispatchers.Main) { Toast.makeText(context, "${result.exception.message}", Toast.LENGTH_SHORT) .show() } } + is NetResult.Failure<*> -> { withContext(Dispatchers.Main) { Toast.makeText(context, "${result.code}:${result.msg}", Toast.LENGTH_SHORT) .show() } } + is NetResult.Loading -> {} else -> {} } @@ -206,9 +247,10 @@ class LoginViewModel @Inject constructor( /** * 创建用户目录 */ - private fun createUserFolder(context: Context, userId: String) { + private fun createUserFolder(context: Context, userId: String,userRealName:String) { Constant.IS_VIDEO_SPEED = false Constant.USER_ID = userId + Constant.USER_REAL_NAME = userRealName Constant.VERSION_ID = userId Constant.USER_DATA_PATH = Constant.DATA_PATH + Constant.USER_ID + "/" + Constant.VERSION_ID Constant.USER_DATA_ATTACHEMNT_PATH = Constant.USER_DATA_PATH + "/attachment/" @@ -234,7 +276,7 @@ class LoginViewModel @Inject constructor( // 拷贝配置文件到用户目录下 val omdbConfigFile = File(userFolder.absolutePath, Constant.OMDB_CONFIG); // if (!omdbConfigFile.exists()) { - ResourceUtils.copyFileFromAssets(Constant.OMDB_CONFIG, omdbConfigFile.absolutePath) + ResourceUtils.copyFileFromAssets(Constant.OMDB_CONFIG, omdbConfigFile.absolutePath) // } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt index 8c7ec3f1..5d08ab26 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt @@ -20,10 +20,12 @@ import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.map.NIMapOptions import com.navinfo.omqs.Constant import com.navinfo.omqs.R import com.navinfo.omqs.bean.ImportConfig import com.navinfo.omqs.bean.SignBean +import com.navinfo.omqs.bean.TraceVideoBean import com.navinfo.omqs.databinding.ActivityMainBinding import com.navinfo.omqs.http.offlinemapdownload.OfflineMapDownloadManager import com.navinfo.omqs.tools.LayerConfigUtils @@ -33,11 +35,14 @@ import com.navinfo.omqs.ui.fragment.offlinemap.OfflineMapFragment import com.navinfo.omqs.ui.fragment.qsrecordlist.QsRecordListFragment import com.navinfo.omqs.ui.fragment.signMoreInfo.SignMoreInfoFragment import com.navinfo.omqs.ui.fragment.tasklist.TaskManagerFragment +import com.navinfo.omqs.ui.other.BaseToast import com.navinfo.omqs.ui.widget.RecyclerViewSpacesItemDecoration import com.navinfo.omqs.util.FlowEventBus import com.navinfo.omqs.util.SpeakMode import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch +import org.oscim.core.GeoPoint +import org.oscim.renderer.GLViewport import org.videolan.vlc.Util import java.math.BigDecimal import java.math.RoundingMode @@ -163,8 +168,7 @@ class MainActivity : BaseActivity() { viewModel.speakMode = SpeakMode(this) // 在mapController初始化前获取当前OMDB图层显隐 viewModel.refreshOMDBLayer(LayerConfigUtils.getLayerConfigList()) - mapController.mMapView.vtmMap.viewport().maxZoomLevel = 25 - mapController.mMapView.vtmMap.viewport().maxTilt = 85f + mapController.mMapView.vtmMap.viewport().maxZoomLevel = Constant.MAX_ZOOM //关联生命周期 binding.lifecycleOwner = this //给xml转递对象 @@ -177,6 +181,7 @@ class MainActivity : BaseActivity() { MotionEvent.ACTION_DOWN -> { voiceOnTouchStart()//Do Something } + MotionEvent.ACTION_UP -> { voiceOnTouchStop()//Do Something } @@ -185,9 +190,62 @@ class MainActivity : BaseActivity() { } //捕捉列表变化回调 viewModel.liveDataQsRecordIdList.observe(this) { - //处理页面跳转 - viewModel.navigationRightFragment(this, it) + //跳转到质检数据页面 + //获取右侧fragment容器 + val naviController = findNavController(R.id.main_activity_right_fragment) + + naviController.currentDestination?.let { navDestination -> + when (navDestination.id) { + R.id.RightEmptyFragment -> { + if (it.size == 1) { + val bundle = Bundle() + bundle.putString("QsId", it[0]) + naviController.navigate(R.id.EvaluationResultFragment, bundle) + } + } + } + } } + //捕捉列表变化回调 + viewModel.liveDataNoteId.observe(this) { + //跳转到质检数据页面 + //获取右侧fragment容器 + val naviController = findNavController(R.id.main_activity_right_fragment) + + naviController.currentDestination?.let { navDestination -> + when (navDestination.id) { + R.id.RightEmptyFragment -> { + val bundle = Bundle() + bundle.putString("NoteId", it) + naviController.navigate(R.id.NoteFragment, bundle) + } + } + } + } + + viewModel.liveDataTaskLink.observe(this) { + val bundle = Bundle() + bundle.putString("TaskLinkId", it) + findNavController(R.id.main_activity_right_fragment).navigate( + R.id.TaskLinkFragment, bundle + ) + } + + //捕捉轨迹点 + viewModel.liveDataNILocationList.observe(this) { + if (viewModel.isSelectTrace()) { + //Toast.makeText(this,"轨迹被点击了",Toast.LENGTH_LONG).show() + viewModel.showMarker(this, it) + viewModel.setCurrentIndexNiLocation(it) + val traceVideoBean = TraceVideoBean( + command = "videotime?", + userid = Constant.USER_ID, + time = "${it.time}:000" + ) + viewModel.sendServerCommand(this, traceVideoBean, IndoorToolsCommand.SELECT_POINT) + } + } + //右上角菜单是否被点击 viewModel.liveDataMenuState.observe(this) { binding.mainActivityMenu.isSelected = it @@ -254,7 +312,7 @@ class MainActivity : BaseActivity() { } } - viewModel.liveDataSignMoreInfo.observe(this){ + viewModel.liveDataSignMoreInfo.observe(this) { val fragment = supportFragmentManager.findFragmentById(R.id.main_activity_sign_more_info_fragment) if (fragment == null) { @@ -264,6 +322,55 @@ class MainActivity : BaseActivity() { } } + viewModel.liveIndoorToolsResp.observe(this) { + when (it) { + IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_SUCCESS -> { + + if (viewModel.indoorToolsCommand == IndoorToolsCommand.SELECT_POINT) { + selectPointFinish(true) + } + //启动自动播放 + if (viewModel.indoorToolsCommand == IndoorToolsCommand.PLAY) { + viewModel.startTimer() + } + } + + IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_FAILURE -> { + if (viewModel.indoorToolsCommand == IndoorToolsCommand.SELECT_POINT) { + selectPointFinish(false) + } + } + } + } + + //室内整理工具反向控制 + viewModel.liveIndoorToolsCommand.observe(this) { + when (it) { + IndoorToolsCommand.PLAY -> { + setPlayStatus() + } + + IndoorToolsCommand.INDEXING -> { + pausePlayTrace() + } + + IndoorToolsCommand.SELECT_POINT -> { + + } + + IndoorToolsCommand.NEXT -> { + } + + IndoorToolsCommand.REWIND -> { + } + + IndoorToolsCommand.STOP -> { + //切换为暂停状态 + pausePlayTrace() + } + } + } + lifecycleScope.launch { // 初始化地图图层控制接收器 FlowEventBus.subscribe>( @@ -283,8 +390,13 @@ class MainActivity : BaseActivity() { } } - supportFragmentManager.beginTransaction() - .add(R.id.console_fragment_layout, ConsoleFragment()).commit() + //自动连接相机 + if (viewModel.isAutoCamera()) { + viewModel.autoCamera() + } else { + supportFragmentManager.beginTransaction() + .add(R.id.console_fragment_layout, ConsoleFragment()).commit() + } } //根据输入的经纬度跳转坐标 @@ -293,7 +405,7 @@ class MainActivity : BaseActivity() { val inputDialog = MaterialAlertDialogBuilder( this ).setTitle("坐标定位").setView(view) - var editText = view.findViewById(R.id.dialog_edittext) + val editText = view.findViewById(R.id.dialog_edittext) editText.hint = "请输入经纬度例如:\n116.1234567,39.1234567\n116.1234567 39.1234567" inputDialog.setNegativeButton("取消") { dialog, _ -> dialog.dismiss() @@ -430,13 +542,42 @@ class MainActivity : BaseActivity() { } } + /** + * 准星的显隐控制 + */ + fun setHomeCenterVisibility(visible: Int) { + binding.mainActivityHomeCenter.visibility = visible + binding.mainActivityHomeCenterText.visibility = visible + if (visible != View.VISIBLE) { + binding.mainActivityHomeCenterText.text = "" + } + } + + /** + * 设置屏幕中心文字内容 + */ + fun setHomeCenterText(str: String) { + binding.mainActivityHomeCenterText.text = str + } + /** * 隐藏或显示右侧展开按钮 */ - fun setRightSwitchButton(visibility: Int) { + fun setRightSwitchButtonVisibility(visibility: Int) { binding.mainActivityFragmentSwitch.visibility = visibility } + /** + * 顶部菜单按钮 + */ + fun setTopMenuButtonVisibility(visibility: Int) { + binding.mainActivityMenu.visibility = visibility + if (visibility != View.VISIBLE) { + binding.mainActivityMenuGroup.visibility = View.INVISIBLE + binding.mainActivityMenu.isSelected = false + } + } + /** * 点击录音按钮 */ @@ -453,6 +594,167 @@ class MainActivity : BaseActivity() { binding.mainActivitySelectLine.isSelected = viewModel.isSelectRoad() } + /** + * 点击线选择 + */ + fun tracePointsOnclick() { + viewModel.setSelectTrace(!viewModel.isSelectTrace()) + binding.mainActivityTraceSnapshotPoints.isSelected = viewModel.isSelectTrace() + + if (viewModel.isSelectTrace()) { + Toast.makeText(this, "请选择轨迹点!", Toast.LENGTH_LONG).show() + //调用撤销自动播放 + setViewEnable(false) + viewModel.cancelTrace() + } + } + + /** + * 点击结束轨迹操作 + */ + fun finishTraceOnclick() { + setIndoorGroupEnable(false) + viewModel.setSelectTrace(false) + viewModel.setMediaFlag(false) + viewModel.setSelectPauseTrace(false) + binding.mainActivityMenuIndoorGroup.visibility = View.GONE + binding.mainActivityTraceSnapshotPoints.isSelected = viewModel.isSelectTrace() + //binding.mainActivitySnapshotMediaFlag.isSelected = viewModel.isMediaFlag() + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + } + + /** + * 点击结束轨迹操作 + */ + fun mediaFlagOnclick() { +/* viewModel.setMediaFlag(!viewModel.isMediaFlag()) + binding.mainActivitySnapshotMediaFlag.isSelected = viewModel.isMediaFlag()*/ + } + + /** + * 点击上一个轨迹点播放操作 + */ + fun rewindTraceOnclick() { + pausePlayTrace() + val item = + mapController.markerHandle.getNILocation(viewModel.getCurrentNiLocationIndex() - 1) + if (item != null) { + viewModel.setCurrentIndexLoction(viewModel.getCurrentNiLocationIndex() - 1) + viewModel.showMarker(this, item) + val traceVideoBean = TraceVideoBean( + command = "videotime?", + userid = Constant.USER_ID, + time = "${item.time}:000" + ) + viewModel.sendServerCommand(this, traceVideoBean, IndoorToolsCommand.REWIND) + } else { + dealNoData() + } + } + + /** + * 点击暂停播放轨迹操作 + */ + fun pauseTraceOnclick() { + viewModel.setSelectPauseTrace(!viewModel.isSelectPauseTrace()) + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + viewModel.setSelectTrace(false) + binding.mainActivityTraceSnapshotPoints.isSelected = viewModel.isSelectTrace() + if (viewModel.isSelectPauseTrace()) { + playVideo() + } else { + pauseVideo() + viewModel.cancelTrace() + } + } + + private fun playVideo() { + if (mapController.markerHandle.getCurrentMark() == null) { + BaseToast.makeText(this, "请先选择轨迹点!", BaseToast.LENGTH_SHORT).show() + return + } + viewModel.setSelectTrace(false) + binding.mainActivityTraceSnapshotPoints.isSelected = viewModel.isSelectTrace() + val traceVideoBean = TraceVideoBean(command = "playVideo?", userid = Constant.USER_ID) + viewModel.sendServerCommand(this, traceVideoBean, IndoorToolsCommand.PLAY) + } + + /** + * 设置为播放状态 + */ + private fun setPlayStatus() { + //切换为播放 + viewModel.setSelectPauseTrace(true) + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + playVideo() + } + + private fun pauseVideo() { + val traceVideoBean = TraceVideoBean(command = "pauseVideo?", userid = Constant.USER_ID) + viewModel.sendServerCommand(this, traceVideoBean, IndoorToolsCommand.STOP) + } + + /** + * 点击下一个轨迹点 + */ + fun nextTraceOnclick() { + pausePlayTrace() + val item = + mapController.markerHandle.getNILocation(viewModel.getCurrentNiLocationIndex() + 1) + if (item != null) { + viewModel.setCurrentIndexLoction(viewModel.getCurrentNiLocationIndex() + 1) + viewModel.showMarker(this, item) + val traceVideoBean = TraceVideoBean( + command = "videotime?", + userid = Constant.USER_ID, + time = "${item.time}:000" + ) + viewModel.sendServerCommand(this, traceVideoBean, IndoorToolsCommand.NEXT) + } else { + dealNoData() + } + } + + private fun dealNoData() { + BaseToast.makeText(this, "无数据了!", Toast.LENGTH_SHORT).show() + + //无数据时自动暂停播放,并停止轨迹 + if (viewModel.isSelectPauseTrace()) { + pauseVideo() + viewModel.cancelTrace() + viewModel.setSelectPauseTrace(false) + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + } + } + + fun pausePlayTrace() { + viewModel.setSelectTrace(false) + binding.mainActivityTraceSnapshotPoints.isSelected = viewModel.isSelectTrace() + viewModel.setSelectPauseTrace(false) + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + viewModel.cancelTrace() + } + + /** + * 选点结束 + * @param value true 选点成功 false 选点失败 + */ + private fun selectPointFinish(value: Boolean) { + if (value) { + setViewEnable(true) + viewModel.setSelectPauseTrace(false) + binding.mainActivitySnapshotPause.isSelected = viewModel.isSelectPauseTrace() + } + } + + private fun setViewEnable(value: Boolean) { + binding.mainActivitySnapshotRewind.isEnabled = value + binding.mainActivitySnapshotNext.isEnabled = value + binding.mainActivitySnapshotPause.isEnabled = value + binding.mainActivitySnapshotFinish.isEnabled = value + viewModel.cancelTrace() + } + /** * 打开或关闭底部导航栏 @@ -466,9 +768,18 @@ class MainActivity : BaseActivity() { } binding.mainActivityBottomSheetGroup.visibility = View.GONE + + mapController.mMapView.setScaleBarLayer(GLViewport.Position.BOTTOM_CENTER, 128, 5) } else { binding.mainActivityBottomSheetGroup.visibility = View.VISIBLE + mapController.mMapView.setScaleBarLayer(GLViewport.Position.BOTTOM_CENTER, 128, 65) } + mapController.mMapView.vtmMap.animator().animateTo( + GeoPoint( + mapController.mMapView.vtmMap.mapPosition.geoPoint.latitude, + mapController.mMapView.vtmMap.mapPosition.geoPoint.longitude + ) + ) } private fun voiceOnTouchStart() { @@ -523,6 +834,27 @@ class MainActivity : BaseActivity() { } } + /** + * 显示轨迹回放布局 + */ + fun showIndoorDataLayout() { + binding.mainActivityMenuIndoorGroup.visibility = View.VISIBLE + if (Constant.INDOOR_IP.isNotEmpty()) { + setIndoorGroupEnable(true) + } else { + setIndoorGroupEnable(false) + } + } + + private fun setIndoorGroupEnable(enable: Boolean) { + binding.mainActivitySnapshotFinish.isEnabled = enable + binding.mainActivityTraceSnapshotPoints.isEnabled = enable + //binding.mainActivitySnapshotMediaFlag.isEnabled = enable + binding.mainActivitySnapshotRewind.isEnabled = enable + binding.mainActivitySnapshotPause.isEnabled = enable + binding.mainActivitySnapshotNext.isEnabled = enable + } + /** * 路径规划 */ @@ -558,4 +890,25 @@ class MainActivity : BaseActivity() { viewModel.showSignMoreInfo(viewModel.liveDataRoadName.value!!) } } + + /** + * 新增便签,打开便签fragment + */ + fun onClickNewNote() { + rightController.navigate(R.id.NoteFragment) + } + + /** + * 新增评测link + */ + fun onClickTaskLink() { + rightController.navigate(R.id.TaskLinkFragment) + } + + /** + * 右侧按钮+经纬度按钮 + */ + fun setRightButtonsVisible(visible: Int) { + binding.mainActivityRightVisibilityButtonsGroup2.visibility = visible + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt index b456f1b1..0120d0c1 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt @@ -3,6 +3,7 @@ package com.navinfo.omqs.ui.activity.map import android.app.Activity import android.content.Context import android.content.DialogInterface +import android.content.SharedPreferences import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.BitmapDrawable import android.os.Build @@ -14,46 +15,60 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import android.widget.PopupWindow +import android.widget.Toast import androidx.annotation.RequiresApi -import androidx.constraintlayout.widget.Group import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import androidx.navigation.findNavController import com.blankj.utilcode.util.ToastUtils +import com.blankj.utilcode.util.ViewUtils.runOnUiThread import com.navinfo.collect.library.data.dao.impl.TraceDataBase -import com.navinfo.collect.library.data.entity.RenderEntity -import com.navinfo.collect.library.data.entity.TaskBean +import com.navinfo.collect.library.data.entity.* +import com.navinfo.collect.library.garminvirbxe.HostBean import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.map.OnGeoPointClickListener +import com.navinfo.collect.library.map.handler.ONNoteItemClickListener +import com.navinfo.collect.library.map.handler.OnNiLocationItemListener import com.navinfo.collect.library.map.handler.OnQsRecordItemClickListener +import com.navinfo.collect.library.map.handler.OnTaskLinkItemClickListener import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.collect.library.utils.GeometryToolsKt import com.navinfo.omqs.Constant import com.navinfo.omqs.R import com.navinfo.omqs.bean.ImportConfig -import com.navinfo.omqs.bean.RoadNameBean +import com.navinfo.omqs.bean.QRCodeBean import com.navinfo.omqs.bean.SignBean +import com.navinfo.omqs.bean.TraceVideoBean import com.navinfo.omqs.db.RealmOperateHelper +import com.navinfo.omqs.http.NetResult +import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.ui.dialog.CommonDialog import com.navinfo.omqs.ui.manager.TakePhotoManager +import com.navinfo.omqs.ui.other.BaseToast import com.navinfo.omqs.ui.widget.SignUtil import com.navinfo.omqs.util.DateTimeUtil +import com.navinfo.omqs.util.ShareUtil import com.navinfo.omqs.util.SoundMeter import com.navinfo.omqs.util.SpeakMode import dagger.hilt.android.lifecycle.HiltViewModel import io.realm.Realm import io.realm.RealmSet +import io.realm.kotlin.where import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import org.oscim.core.GeoPoint import org.oscim.core.MapPosition +import org.oscim.layers.marker.MarkerItem import org.oscim.map.Map import org.videolan.libvlc.LibVlcUtil import java.io.File +import java.io.IOException import java.util.* import javax.inject.Inject +import kotlin.concurrent.fixedRateTimer /** * 创建Activity全局viewmode @@ -64,13 +79,24 @@ class MainViewModel @Inject constructor( private val mapController: NIMapController, private val traceDataBase: TraceDataBase, private val realmOperateHelper: RealmOperateHelper, -) : ViewModel() { + private val networkService: NetworkService, + private val sharedPreferences: SharedPreferences +) : ViewModel(), SocketServer.OnConnectSinsListener, + SharedPreferences.OnSharedPreferenceChangeListener { + + private val TAG = "MainViewModel" private var mCameraDialog: CommonDialog? = null //地图点击捕捉到的质检数据ID列表 val liveDataQsRecordIdList = MutableLiveData>() + //地图点击捕捉到的标签ID列表 + val liveDataNoteId = MutableLiveData() + + //地图点击捕捉到的轨迹列表 + val liveDataNILocationList = MutableLiveData() + //左侧看板数据 val liveDataSignList = MutableLiveData>() @@ -80,11 +106,26 @@ class MainViewModel @Inject constructor( //道路名 val liveDataRoadName = MutableLiveData() + //捕捉到新增的link + val liveDataTaskLink = MutableLiveData() + /** * 当前选中的要展示的详细信息的要素 */ val liveDataSignMoreInfo = MutableLiveData() + private var traceTag: String = "TRACE_TAG" + + /** + * 右上角菜单状态 + */ + val liveDataMenuState = MutableLiveData() + + /** + * 地图中心坐标 + */ + val liveDataCenterPoint = MutableLiveData() + // var testPoint = GeoPoint(0, 0) //uuid标识,用于记录轨迹组 @@ -102,19 +143,54 @@ class MainViewModel @Inject constructor( var menuState: Boolean = false + var captureLinkState: Boolean = false - val liveDataMenuState = MutableLiveData() + var currentTaskBean: TaskBean? = null - val liveDataCenterPoint = MutableLiveData() + //状态 + val liveIndoorToolsResp: MutableLiveData = MutableLiveData() + + //状态 + val liveIndoorToolsCommand: MutableLiveData = MutableLiveData() /** * 是不是线选择模式 */ private var bSelectRoad = false + /** + * 是不是选择轨迹点 + */ + private var bSelectTrace = false + + /** + * 是不是选择标题标识 + */ + private var isMediaFlag = false + + /** + * 是不是暂停 + */ + private var bSelectPauseTrace = false + private var linkIdCache = "" + private var lastNiLocaion: NiLocation? = null + + private var currentIndexNiLocation: Int = 0 + + private var socketServer: SocketServer? = null + + var indoorToolsCommand: IndoorToolsCommand? = null + + private var shareUtil: ShareUtil? = null + + private var timer: Timer? = null + + private var disTime: Long = 1000 + init { + mapController.mMapView.vtmMap.events.bind(Map.UpdateListener { e, mapPosition -> when (e) { Map.SCALE_EVENT, Map.MOVE_EVENT, Map.ROTATE_EVENT -> liveDataCenterPoint.value = @@ -122,197 +198,355 @@ class MainViewModel @Inject constructor( } }) - //处理质检数据点击事件 - mapController.markerHandle.setOnQsRecordItemClickListener(object : - OnQsRecordItemClickListener { - override fun onQsRecordList(list: MutableList) { - liveDataQsRecordIdList.value = list - } - }) + + shareUtil = ShareUtil(mapController.mMapView.context, 1) + initLocation() - //处理地图点击操作 - viewModelScope.launch(Dispatchers.Default) { - mapController.onMapClickFlow.collectLatest { -// testPoint = it - //线选择状态 - if (bSelectRoad) { - captureLink(it) + /** + * 处理点击道路捕捉回调功能 + */ + mapController.mMapView.addOnNIMapClickListener( + TAG, + //处理地图点击操作 + object : OnGeoPointClickListener { + override fun onMapClick(tag: String, point: GeoPoint) { + if (tag == TAG) { + viewModelScope.launch(Dispatchers.Default) { + //线选择状态 + if (bSelectRoad) { + captureLink(point) + } + } + } + } + }, + /** + * 处理之间数据的点击 + */ + object : OnQsRecordItemClickListener { + override fun onQsRecordList(tag: String, list: MutableList) { + if (tag == TAG) + liveDataQsRecordIdList.value = list + } + }, + /** + * 处理新增link线点击编辑 + */ + object : OnTaskLinkItemClickListener { + override fun onTaskLink(tag: String, taskLinkId: String) { + if (tag == TAG) + liveDataTaskLink.value = taskLinkId + } + }, + /** + * 处理便签点击 + */ + object : ONNoteItemClickListener { + override fun onNote(tag: String, noteId: String) { + if (tag == TAG) + liveDataNoteId.value = noteId + } + + }, + /** + * 处理定位点的点击 + */ + object : OnNiLocationItemListener { + override fun onNiLocation(tag: String, index: Int, it: NiLocation) { + if (tag == TAG) + liveDataNILocationList.value = it } } - } + ) - initTaskData() + viewModelScope.launch(Dispatchers.IO) { + getTaskBean() + //初始化选中的任务高亮高亮 + if (currentTaskBean != null) { + mapController.lineHandler.showTaskLines(currentTaskBean!!.hadLinkDvoList) + } + initQsRecordData() + initNoteData() + initNILocationData() + } + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + socketServer = SocketServer(mapController, traceDataBase, sharedPreferences) + } + + + /** + * 获取当前任务 + */ + private suspend fun getTaskBean() { + val id = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + val realm = Realm.getDefaultInstance() + val res = realm.where(TaskBean::class.java).equalTo("id", id).findFirst() + if (res != null) { + currentTaskBean = realm.copyFromRealm(res) + } + } + + + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == Constant.SELECT_TASK_ID) { + viewModelScope.launch(Dispatchers.IO) { + getTaskBean() + initQsRecordData() + } + } } /** - * 初始话任务高亮高亮 + * 初始化渲染质检数据 */ - private fun initTaskData() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - viewModelScope.launch { - val realm = Realm.getDefaultInstance() - val results = realm.where(TaskBean::class.java).findAll() - val list = realm.copyFromRealm(results) - results.addChangeListener { changes -> - val list2 = realm.copyFromRealm(changes) - mapController.lineHandler.omdbTaskLinkLayer.removeAll() - for (item in list2) { - mapController.lineHandler.omdbTaskLinkLayer.addLineList(item.hadLinkDvoList) - } - } - mapController.lineHandler.omdbTaskLinkLayer.removeAll() - for (item in list) { -// if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { -// mapController.lineHandler.omdbTaskLinkLayer.setLineColor(Color.valueOf(item.color)) -// } - mapController.lineHandler.omdbTaskLinkLayer.addLineList(item.hadLinkDvoList) - } + private suspend fun initQsRecordData() { + if (currentTaskBean != null) { + var list = mutableListOf() + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val objects = + realm.where().equalTo("taskId", currentTaskBean!!.id).findAll() + list = realm.copyFromRealm(objects) + } + mapController.markerHandle.removeAllQsMarker() + for (item in list) { + mapController.markerHandle.addOrUpdateQsRecordMark(item) } -// realm.close() } } + /** + * 初始化渲染便签数据 + */ + private suspend fun initNoteData() { + var list = mutableListOf() + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val objects = realm.where().findAll() + list = realm.copyFromRealm(objects) + } + + + + for (item in list) { + mapController.markerHandle.addOrUpdateNoteMark(item) + } + } + + private suspend fun initNILocationData() { + //加载轨迹数据 + val id = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + val list: List? = TraceDataBase.getDatabase( + mapController.mMapView.context, Constant.USER_DATA_PATH + ).niLocationDao.findToTaskIdAll(id.toString()) + if (list != null) { + for (location in list) { + mapController.markerHandle.addNiLocationMarkerItem(location) + } + } + } + + /** + * 初始化定位信息 + */ private fun initLocation() { + //用于定位点存储到数据库 viewModelScope.launch(Dispatchers.Default) { + //用于定位点捕捉道路 mapController.locationLayerHandler.niLocationFlow.collect { location -> - val geometry = GeometryTools.createGeometry( - GeoPoint( - location.latitude, location.longitude - ) - ) - val tileX = RealmSet() - GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX) - val tileY = RealmSet() - GeometryToolsKt.getTileYByGeometry(geometry.toString(), tileY) - //遍历存储tile对应的x与y的值 - tileX.forEach { x -> - tileY.forEach { y -> - location.tilex = x - location.tiley = y + //过滤掉无效点 + if (!GeometryTools.isCheckError(location.longitude, location.latitude)) { + val geometry = GeometryTools.createGeometry( + GeoPoint( + location.latitude, location.longitude + ) + ) + val tileX = RealmSet() + GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX) + val tileY = RealmSet() + GeometryToolsKt.getTileYByGeometry(geometry.toString(), tileY) + + //遍历存储tile对应的x与y的值 + tileX.forEach { x -> + tileY.forEach { y -> + location.tilex = x + location.tiley = y + } + } + location.groupId = uuid + try { + location.timeStamp = DateTimeUtil.getTime(location.time).toString() + } catch (e: Exception) { + + } + + location.taskId = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1).toString() + + //判断如果是连接状态并处于录像模式,标记为有效点 + if (shareUtil?.connectstate == true&&shareUtil?.takeCameraMode==0) { + location.media = 1 + } + var disance = 0.0 + //增加间距判断 + if (lastNiLocaion != null) { + disance = GeometryTools.getDistance( + location.latitude, location.longitude, + lastNiLocaion!!.latitude, lastNiLocaion!!.longitude + ) + } + //室内整理工具时不能进行轨迹存储,判断轨迹间隔要超过2.5并小于60米 + if (Constant.INDOOR_IP.isEmpty() && (disance == 0.0 || (disance > 2.5 && disance < 60))) { + traceDataBase.niLocationDao.insert(location) + mapController.markerHandle.addNiLocationMarkerItem(location) + mapController.mMapView.vtmMap.updateMap(true) + lastNiLocaion = location } } - location.groupId = uuid - try { - location.timeStamp = DateTimeUtil.getTime(location.time).toString() - } catch (e: Exception) { - - } - traceDataBase.niLocationDao.insert(location) - mapController.mMapView.vtmMap.updateMap(true) } + } - //用于定位点捕捉道路 viewModelScope.launch(Dispatchers.Default) { + //用于定位点捕捉道路 mapController.locationLayerHandler.niLocationFlow.collectLatest { location -> - if (!isSelectRoad()) captureLink(GeoPoint(location.latitude, location.longitude)) + if (!isSelectRoad() && !GeometryTools.isCheckError( + location.longitude, location.latitude + ) + ) { + captureLink( + GeoPoint( + location.latitude, location.longitude + ) + ) + } + withContext(Dispatchers.Main) { + if (Constant.AUTO_LOCATION) { + mapController.mMapView.vtmMap.animator() + .animateTo(GeoPoint(location.longitude, location.latitude)) + } + } } } - //显示轨迹图层 mapController.layerManagerHandler.showNiLocationLayer() - } + /** * 捕获道路和面板 */ private suspend fun captureLink(point: GeoPoint) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val linkList = realmOperateHelper.queryLink( - point = point, - ) - var hisRoadName = false - if (linkList.isNotEmpty()) { - //看板数据 - val signList = mutableListOf() - val topSignList = mutableListOf() - mapController.lineHandler.linksLayer.clear() - - val link = linkList[0] - - val linkId = link.properties[RenderEntity.Companion.LinkTable.linkPid] - - if (linkIdCache != linkId) { - - mapController.lineHandler.showLine(link.geometry) - linkId?.let { - var elementList = realmOperateHelper.queryLinkByLinkPid(it) - for (element in elementList) { - - if (element.code == 2011) { - hisRoadName = true - liveDataRoadName.postValue(element) - continue - } - - val distance = GeometryTools.distanceToDouble( - point, GeometryTools.createGeoPoint(element.geometry) - ) - - val signBean = SignBean( - iconId = SignUtil.getSignIcon(element), - iconText = SignUtil.getSignIconText(element), - distance = distance.toInt(), - linkId = linkId, - name = SignUtil.getSignNameText(element), - bottomRightText = SignUtil.getSignBottomRightText(element), - renderEntity = element, - isMoreInfo = SignUtil.isMoreInfo(element), - index = SignUtil.getRoadInfoIndex(element) - ) - Log.e("jingo", "捕捉到的数据code ${element.code}") - when (element.code) { - //车道数,种别,功能等级,线限速,道路方向 - 2041, 2008, 2002, 2019, 2010 -> topSignList.add( - signBean - ) - 4002, 4003, 4004, 4010, 4022, 4601 -> signList.add( - signBean - ) - } - - } - - val realm = Realm.getDefaultInstance() - val entity = realm.where(RenderEntity::class.java) - .equalTo("table", "OMDB_RESTRICTION").and().equalTo( - "properties['linkIn']", it - ).findFirst() - if (entity != null) { - val outLink = entity.properties["linkOut"] - val linkOutEntity = realm.where(RenderEntity::class.java) - .equalTo("table", "OMDB_RD_LINK").and().equalTo( - "properties['${RenderEntity.Companion.LinkTable.linkPid}']", - outLink - ).findFirst() - if (linkOutEntity != null) { - mapController.lineHandler.linksLayer.addLine( - linkOutEntity.geometry, 0x7DFF0000 - ) - } - } - } - - liveDataTopSignList.postValue(topSignList.distinctBy { it.name }.sortedBy { it.index }) - - liveDataSignList.postValue(signList.sortedBy { it.distance }) - val speechText = SignUtil.getRoadSpeechText(topSignList) - withContext(Dispatchers.Main) { - speakMode?.speakText(speechText) - } - linkIdCache = linkId ?: "" - } - } else { - mapController.lineHandler.removeLine() - linkIdCache = "" - } - //如果没有捕捉到道路名 - if (!hisRoadName) { - liveDataRoadName.postValue(null) - } + if (captureLinkState) { + return } + + try { + captureLinkState = true + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + val linkList = realmOperateHelper.queryLink( + point = point, + ) + + var hisRoadName = false + if (linkList.isNotEmpty()) { + //看板数据 + val signList = mutableListOf() + val topSignList = mutableListOf() + mapController.lineHandler.linksLayer.clear() + + val link = linkList[0] + + val linkId = link.properties[RenderEntity.Companion.LinkTable.linkPid] + + if (linkIdCache != linkId) { + + mapController.lineHandler.showLine(link.geometry) + linkId?.let { + var elementList = realmOperateHelper.queryLinkByLinkPid(it) + for (element in elementList) { + + if (element.code == 2011) { + hisRoadName = true + liveDataRoadName.postValue(element) + continue + } + + val distance = GeometryTools.distanceToDouble( + point, GeometryTools.createGeoPoint(element.geometry) + ) + + val signBean = SignBean( + iconId = SignUtil.getSignIcon(element), + iconText = SignUtil.getSignIconText(element), + distance = distance.toInt(), + linkId = linkId, + name = SignUtil.getSignNameText(element), + bottomRightText = SignUtil.getSignBottomRightText(element), + renderEntity = element, + isMoreInfo = SignUtil.isMoreInfo(element), + index = SignUtil.getRoadInfoIndex(element) + ) + Log.e("jingo", "捕捉到的数据code ${element.code}") + when (element.code) { + //车道数,种别,功能等级,线限速,道路方向 + 2041, 2008, 2002, 2019, 2010 -> topSignList.add( + signBean + ) + + 4002, 4003, 4004, 4010, 4022, 4601 -> signList.add( + signBean + ) + } + + } + + val realm = Realm.getDefaultInstance() + val entity = realm.where(RenderEntity::class.java) + .equalTo("table", "OMDB_RESTRICTION").and().equalTo( + "properties['linkIn']", it + ).findFirst() + if (entity != null) { + val outLink = entity.properties["linkOut"] + val linkOutEntity = realm.where(RenderEntity::class.java) + .equalTo("table", "OMDB_RD_LINK").and().equalTo( + "properties['${RenderEntity.Companion.LinkTable.linkPid}']", + outLink + ).findFirst() + if (linkOutEntity != null) { + mapController.lineHandler.linksLayer.addLine( + linkOutEntity.geometry, 0x7DFF0000 + ) + } + } + } + + liveDataTopSignList.postValue(topSignList.distinctBy { it.name } + .sortedBy { it.index }) + + liveDataSignList.postValue(signList.sortedBy { it.distance }) + val speechText = SignUtil.getRoadSpeechText(topSignList) + withContext(Dispatchers.Main) { + speakMode?.speakText(speechText) + } + linkIdCache = linkId ?: "" + } + } else { + mapController.lineHandler.removeLine() + linkIdCache = "" + } + //如果没有捕捉到道路名 + if (!hisRoadName) { + liveDataRoadName.postValue(null) + } + } + } catch (e: Exception) { + + } finally { + captureLinkState = false + } + } /** @@ -332,9 +566,9 @@ class MainViewModel @Inject constructor( override fun onCleared() { super.onCleared() - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - mapController.lineHandler.removeLine() - } + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) + mapController.mMapView.removeOnNIMapClickListener(TAG) + mapController.lineHandler.removeLine() } //点击相机按钮 @@ -342,17 +576,8 @@ class MainViewModel @Inject constructor( Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString()) - if (mCameraDialog == null) { - mCameraDialog = CommonDialog( - context, - context.resources.getDimension(R.dimen.head_img_width) - .toInt() * 3 + context.resources.getDimension(R.dimen.ten) - .toInt() + context.resources.getDimension(R.dimen.twenty_four).toInt(), - context.resources.getDimension(R.dimen.head_img_width).toInt() + 10, - 1 - ) - mCameraDialog!!.setCancelable(true) - } + initCameraDialog(context) + mCameraDialog!!.openCamear(mCameraDialog!!.getmShareUtil().continusTakePhotoState) mCameraDialog!!.show() mCameraDialog!!.setOnDismissListener(DialogInterface.OnDismissListener { @@ -360,7 +585,9 @@ class MainViewModel @Inject constructor( mCameraDialog!!.stopVideo() try { if (!mCameraDialog!!.getmShareUtil().connectstate) { - mCameraDialog!!.updateCameraResources(1, mCameraDialog!!.getmDeviceNum()) + mCameraDialog!!.updateCameraResources( + 1, mCameraDialog!!.getmDeviceNum() + ) } TakePhotoManager.getInstance().getCameraVedioClent(mCameraDialog!!.getmDeviceNum()) .StopSearch() @@ -375,6 +602,20 @@ class MainViewModel @Inject constructor( }) } + private fun initCameraDialog(context: Context) { + if (mCameraDialog == null) { + mCameraDialog = CommonDialog( + context, + context.resources.getDimension(R.dimen.head_img_width) + .toInt() * 3 + context.resources.getDimension(R.dimen.ten) + .toInt() + context.resources.getDimension(R.dimen.twenty_four).toInt(), + context.resources.getDimension(R.dimen.head_img_width).toInt() + 10, + 1 + ) + mCameraDialog!!.setCancelable(true) + } + } + fun startSoundMetter(context: Context, v: View) { //语音识别动画 @@ -470,25 +711,6 @@ class MainViewModel @Inject constructor( } } - /** - * 处理页面调转 - */ - fun navigationRightFragment(activity: MainActivity, list: List) { - //获取右侧fragment容器 - val naviController = activity.findNavController(R.id.main_activity_right_fragment) - - naviController.currentDestination?.let { navDestination -> - when (navDestination.id) { - R.id.RightEmptyFragment -> { - if (list.size == 1) { - val bundle = Bundle() - bundle.putString("QsId", list[0]) - naviController.navigate(R.id.EvaluationResultFragment, bundle) - } - } - } - } - } /** * 开启线选择 @@ -497,10 +719,8 @@ class MainViewModel @Inject constructor( bSelectRoad = select //去掉缓存 linkIdCache = "" - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mapController.lineHandler.removeLine() - liveDataSignList.value = mutableListOf() - } + mapController.lineHandler.removeLine() + liveDataSignList.value = mutableListOf() } /** @@ -510,6 +730,48 @@ class MainViewModel @Inject constructor( return bSelectRoad } + /** + * 开启轨迹选择 + */ + fun setSelectTrace(select: Boolean) { + bSelectTrace = select + } + + /** + * 是否开启了轨迹选择 + */ + fun isSelectTrace(): Boolean { + return bSelectTrace + } + + /** + * 开启媒体标识 + */ + fun setMediaFlag(select: Boolean) { + isMediaFlag = select + } + + /** + * 是否开启了媒体标识 + */ + fun isMediaFlag(): Boolean { + return isMediaFlag + } + + /** + * 开启轨迹选择 + */ + fun setSelectPauseTrace(select: Boolean) { + bSelectPauseTrace = select + } + + /** + * 是否开启了轨迹选择 + */ + fun isSelectPauseTrace(): Boolean { + return bSelectPauseTrace + } + /** * 要展示的要素详细信息 */ @@ -518,4 +780,248 @@ class MainViewModel @Inject constructor( liveDataSignMoreInfo.value = data } -} \ No newline at end of file + fun sendServerCommand( + context: Context, + traceVideoBean: TraceVideoBean, + indoorToolsCommand: IndoorToolsCommand + ) { + + if (TextUtils.isEmpty(Constant.INDOOR_IP)) { + Toast.makeText(context, "获取ip失败!", Toast.LENGTH_LONG).show() + return + } + + this.indoorToolsCommand = indoorToolsCommand + + viewModelScope.launch(Dispatchers.Default) { + val url = "http://${Constant.INDOOR_IP}:8080/sensor/service/${traceVideoBean.command}?" + + when (val result = networkService.sendServerCommand( + url = url, + traceVideoBean = traceVideoBean + )) { + is NetResult.Success<*> -> { + + if (result.data != null) { + try { + + val defaultUserResponse = result.data as QRCodeBean + + if (defaultUserResponse.errcode == 0) { + + withContext(Dispatchers.Main) { + Toast.makeText( + context, + "命令成功。", + Toast.LENGTH_LONG + ).show() + + liveIndoorToolsResp.postValue(IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_SUCCESS) + + //启动双向控制服务 + + //启动双向控制服务 + if (socketServer != null && socketServer!!.isServerClose) { + socketServer!!.connect( + Constant.INDOOR_IP, + this@MainViewModel + ) + } + + } + } else { + withContext(Dispatchers.Main) { + Toast.makeText( + context, + "命令无效${defaultUserResponse.errmsg}", + Toast.LENGTH_SHORT + ) + .show() + } + liveIndoorToolsResp.postValue(IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_FAILURE) + } + + } catch (e: IOException) { + withContext(Dispatchers.Main) { + Toast.makeText( + context, + "${e.message}", + Toast.LENGTH_SHORT + ).show() + } + } + } + } + + is NetResult.Error<*> -> { + withContext(Dispatchers.Main) { + Toast.makeText( + context, + "${result.exception.message}", + Toast.LENGTH_SHORT + ) + .show() + } + liveIndoorToolsResp.postValue(IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_FAILURE) + } + + is NetResult.Failure<*> -> { + withContext(Dispatchers.Main) { + Toast.makeText( + context, + "${result.code}:${result.msg}", + Toast.LENGTH_SHORT + ) + .show() + } + liveIndoorToolsResp.postValue(IndoorToolsResp.QR_CODE_STATUS_UPDATE_VIDEO_INFO_FAILURE) + } + + else -> {} + } + + } + + } + + /** + * 显示marker + * @param trackCollection 轨迹点 + * @param type 1 提示最后一个轨迹点 非1提示第一个轨迹点 + */ + fun showMarker(context: Context, niLocation: NiLocation) { + if (mapController.markerHandle != null) { + mapController.markerHandle.removeMarker(traceTag) + if (niLocation != null) { + mapController.markerHandle.addMarker( + GeoPoint( + niLocation.latitude, + niLocation.longitude + ), traceTag, "", niLocation as java.lang.Object + ) + } + } + } + + /** + * 显示索引位置 + * @param niLocation 轨迹点 + */ + fun setCurrentIndexNiLocation(niLocation: NiLocation) { + viewModelScope.launch(Dispatchers.IO) { + Log.e("qj", "开始$currentIndexNiLocation") + currentIndexNiLocation = mapController.markerHandle.getNILocationIndex(niLocation)!! + Log.e("qj", "结束$currentIndexNiLocation") + } + } + + /** + * 设置索引位置 + * @param index 索引 + */ + fun setCurrentIndexLoction(index: Int) { + currentIndexNiLocation = index + } + + /** + * + * @return index 索引 + */ + fun getCurrentNiLocationIndex(): Int { + return currentIndexNiLocation + } + + override fun onConnect(success: Boolean) { + if (!success && socketServer != null) { + BaseToast.makeText( + mapController.mMapView.context, + "轨迹反向控制服务失败,请确认连接是否正常!", + Toast.LENGTH_SHORT + ).show() + } + } + + override fun onIndexing() { + //切换为暂停状态 + liveIndoorToolsCommand.postValue(IndoorToolsCommand.INDEXING) + } + + override fun onStop() { + liveIndoorToolsCommand.postValue(IndoorToolsCommand.STOP) + } + + override fun onPlay() { + liveIndoorToolsCommand.postValue(IndoorToolsCommand.PLAY) + } + + override fun onParseEnd() { + + } + + override fun onReceiveLocation(mNiLocation: NiLocation?) { + if (mNiLocation != null) { + setCurrentIndexNiLocation(mNiLocation) + showMarker(mapController.mMapView.context, mNiLocation) + Log.e("qj", "反向控制$currentIndexNiLocation") + } else { + BaseToast.makeText( + mapController.mMapView.context, + "没有找到对应轨迹点!", + Toast.LENGTH_SHORT + ).show() + } + } + + fun isAutoCamera(): Boolean { + + return shareUtil?.connectstate == true + } + + fun autoCamera() { + if (shareUtil?.connectstate == true) { + val hostBean1 = HostBean() + hostBean1.ipAddress = shareUtil!!.takeCameraIP + hostBean1.hardwareAddress = shareUtil!!.takeCameraMac + onClickCameraButton(mapController.mMapView.context) + mCameraDialog?.connection(hostBean1) + } + } + + fun startTimer() { + if (timer != null) { + cancelTrace() + } + timer = fixedRateTimer("", false, disTime, disTime) { + if (currentIndexNiLocation < mapController.markerHandle.getNILocationItemizedLayerSize()) { + Log.e("qj", "定时器") + val niLocation = mapController.markerHandle.getNILocation(currentIndexNiLocation) + val nextNiLocation = + mapController.markerHandle.getNILocation(currentIndexNiLocation + 1) + if (nextNiLocation != null && niLocation != null) { + var nilocationDisTime = + nextNiLocation.timeStamp.toLong() - niLocation.timeStamp.toLong() + disTime = if (nilocationDisTime < 1000) { + 1000 + } else { + nilocationDisTime + } + showMarker(mapController.mMapView.context, nextNiLocation) + currentIndexNiLocation += 1 + //再次启动 + startTimer() + } + } else { + Toast.makeText(mapController.mMapView.context, "无数据了!", Toast.LENGTH_LONG).show() + cancelTrace() + } + } + } + + /** + * 结束自动播放 + */ + fun cancelTrace() { + timer?.cancel() + } +} + diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/map/SocketServer.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/map/SocketServer.kt new file mode 100644 index 00000000..b2d0a9dd --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/map/SocketServer.kt @@ -0,0 +1,606 @@ +package com.navinfo.omqs.ui.activity.map + +import android.app.Service +import android.content.Intent +import android.content.SharedPreferences +import android.os.Binder +import android.os.Handler +import android.os.IBinder +import android.os.Message +import android.text.TextUtils +import android.util.Log +import com.navinfo.collect.library.data.dao.impl.TraceDataBase +import com.navinfo.collect.library.data.entity.NiLocation +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.Constant +import com.navinfo.omqs.util.DateTimeUtil +import org.json.JSONObject +import java.io.IOException +import java.io.InputStream +import java.io.OutputStream +import java.io.Serializable +import java.net.Socket +import java.util.Collections +import kotlin.math.abs + + +enum class IndoorToolsCommand { + PLAY, + SELECT_POINT, + INDEXING, + NEXT, + REWIND, + STOP +} + +enum class IndoorToolsResp{ + /** + * 信息更新轨迹成功 + */ + QR_CODE_STATUS_UPDATE_VIDEO_INFO_SUCCESS, + + /** + * 信息更新轨迹失败 + */ + QR_CODE_STATUS_UPDATE_VIDEO_INFO_FAILURE, +} + +/** + * @author qj + * @version V1.0 + * @Date 2018/4/18. + * @Description: 轨迹反向控制服务 + */ +class SocketServer( + private val mapController: NIMapController, + private val traceDataBase: TraceDataBase, + private val sharedPreferences: SharedPreferences +) : Service() { + //类标识 + private val TAG = "SocketServer" + + //线程池 + private val threadConnect = ThreadLocal() + + //读的线程 + private var tRecv: RecvThread? = null + + //解析线程 + private var tParse: ParseThread? = null + + //输出流 + private var outStr: OutputStream? = null + + //输入流 + private var inStr: InputStream? = null + + //状态 + var connectstatus = false + + //socket + private var client: Socket? = null + + //接收缓存 + private val sData = ByteArray(512) + + //反馈接口 + private var mListener: OnConnectSinsListener? = null + + //服务 + private val mBinder: MyBinder = MyBinder() + + //接收集合 + private val mTaskList = Collections.synchronizedList(ArrayList()) + + //连接线程 + private var connectThread: Thread? = null + + //缓存ip + private var lastIp = "" + private val mHandler: Handler = object : Handler() { + override fun handleMessage(msg: Message) { + when (msg.what) { + 0x11 -> if (mListener != null) { + if (msg.obj != null && msg.obj is NiLocation) { + mListener!!.onReceiveLocation(msg.obj as NiLocation) + } else { + mListener!!.onReceiveLocation(null) + } + } + + 0x22 -> //索引定位中 + if (mListener != null) { + mListener!!.onIndexing() + } + + 0x33 -> if (mListener != null) { + mListener!!.onConnect(true) + } + + 0x44 -> if (mListener != null) { + mListener!!.onConnect(false) + } + + 0x55 -> if (mListener != null) { + mListener!!.onPlay() + } + + 0x66 -> if (mListener != null) { + mListener!!.onStop() + } + + 0x99 -> if (mListener != null) { + mListener!!.onParseEnd() + } + + 0x999 -> if (mListener != null) { + mListener!!.onConnect(false) + disconnect() + } + } + } + } + + override fun onCreate() { + super.onCreate() + } + + override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int { + return super.onStartCommand(intent, flags, startId) + } + + override fun onDestroy() { + super.onDestroy() + } + + override fun onBind(intent: Intent): IBinder? { + return mBinder + } + + inner class MyBinder : Binder() { + // 返回Activity所关联的Service对象,这样在Activity里,就可调用Service里的一些公用方法 和公用属性 + val service: SocketServer + get() =// 返回Activity所关联的Service对象,这样在Activity里,就可调用Service里的一些公用方法 和公用属性 + this@SocketServer + } + + /** + * 启动sock连接 + * + * @param ip + * @param listener 结果回调 + */ + fun connect(ip: String, listener: OnConnectSinsListener?) { + if (connectThread != null && connectThread!!.isAlive && TextUtils.equals(lastIp, ip)) { + return + } + mListener = listener + lastIp = ip + connectThread = object : Thread() { + override fun run() { + try { + client = threadConnect.get() + if (client == null) { + client = Socket(ip, 8010) + client!!.soTimeout = 3000000 + client!!.keepAlive = true + threadConnect.set(client) + } + outStr = client!!.getOutputStream() + inStr = client!!.getInputStream() + if (tRecv != null) { + tRecv!!.cancel() + } + tRecv = RecvThread() + val thread = Thread(tRecv) + thread.start() + + //解析线程 + if (tParse != null) { + tParse!!.cancel() + } + tParse = ParseThread() + val parsethread = Thread(tParse) + parsethread.start() + + //socket启动成功 + val msg = Message() + msg.what = 0x33 + mHandler.sendMessage(msg) + if (!connectstatus) { + connectstatus = true // 更改连接状态 + } + } catch (e: Exception) { + e.printStackTrace() + //启动失败 + val msg = Message() + msg.what = 0x44 + mHandler.sendMessage(msg) + } + } + } + (connectThread as Thread).start() + } + + /** + * sock是否启动 + * + * @return true 启动 false停止 + */ + val isStart: Boolean + get() = if (connectThread != null && connectThread!!.isAlive) { + true + } else false + + /** + * 销毁连接 + */ + fun disconnect() { + try { + + //销毁线程 + if (tRecv != null) { + tRecv!!.cancel() + } + + //销毁线程 + if (tParse != null) { + tParse!!.cancel() + } + } catch (e: Exception) { + } + try { + if (outStr != null) outStr!!.close() + if (inStr != null) inStr!!.close() + if (client != null) client!!.close() + } catch (e: IOException) { + e.printStackTrace() + } + } + + /** + * 解析接收到得线程 + */ + private inner class ParseThread : Runnable { + private var runFlag = true + + //轨迹时间buffer + private val traceTimeBuffer = 1500 + private var timeIndex = 0 + fun cancel() { + runFlag = false + } + + override fun run() { + try { + while (runFlag) { + if (mTaskList.size > 0) { + timeIndex = mTaskList.size - 1 + val result = parseResult(mTaskList[timeIndex]) + var resultNiLocation: NiLocation? = null + var index: Int = -1 + if (result != null) { + when (result.type) { + 1 -> { + //先暂停播放 + val msg = Message() + msg.what = 0x22 + mHandler.sendMessage(msg) + val currentTime: Long = DateTimeUtil.getTimePointSSS( + result.data + ) + val currentTimeStr: String = DateTimeUtil.TimePointSSSToTime( + result.data + ) + + Log.e(TAG, "反向"+result.data) + + val startTime = currentTime - traceTimeBuffer + val endTme = currentTime + traceTimeBuffer + + //转换为数据库时间 + val startTimeStr: String = + DateTimeUtil.getDateSimpleTime(startTime) + + //转换为数据库时间 + val endTimeStr: String = + DateTimeUtil.getDateSimpleTime(endTme) + if (!TextUtils.isEmpty(startTimeStr) && !TextUtils.isEmpty( + endTimeStr + ) + ) { + + Log.e(TAG, "getTraceData开始") + + val list: List? = getTrackList(startTimeStr, endTimeStr, currentTimeStr) + + Log.e(TAG, "getTraceData结束") + + if (list != null && list.size > 0) { + + var disTime: Long = 0 + //只有一个点不进行判断直接返回结果 + if (list.size == 1) { + resultNiLocation = list[0] + } else { + //遍历集合取最近时间的轨迹点 + b@ for (nilocation in list) { + + if (!TextUtils.isEmpty(nilocation.time)) { + + //只获取到秒的常量 + val time: Long = + nilocation.timeStamp.toLong() + + val disTimeTemp = abs(time - currentTime) + + //如果时间相同直接返回该点 + if (disTimeTemp == 0L) { + resultNiLocation = nilocation + break@b + } else { + + //第一次不对比,取当前值 + if (disTime == 0L) { + disTime = disTimeTemp + resultNiLocation = nilocation + } else { + + //前一个差值大于当前差值则取当前相对小的值 + if (disTime - disTimeTemp > 0) { + disTime = disTimeTemp + resultNiLocation = nilocation + } + } + } + } + } + } + } + } + val msg1 = Message() + msg1.what = 0x11 + msg1.obj = resultNiLocation + if (resultNiLocation != null) { + Log.e(TAG, "反向app"+resultNiLocation.time) + } + mHandler.sendMessage(msg1) + } + + 2 -> { + val msg4 = Message() + msg4.what = 0x55 + mHandler.sendMessage(msg4) + } + + 3 -> { + val msg5 = Message() + msg5.what = 0x66 + mHandler.sendMessage(msg5) + } + } + } + + + //解析时索引与集合索引对比,如果不相同代表有新命令,需要继续解析最后一条,否则清空集合不在解析 + try { + if (timeIndex == mTaskList.size - 1) { + mTaskList.clear() + } + } catch (e: Exception) { + } + val msg2 = Message() + msg2.what = 0x99 + mHandler.sendMessage(msg2) + } + } + Thread.sleep(10) + } catch (e: Exception) { + e.printStackTrace() + val msg = Message() + msg.what = 0x99 + mHandler.sendMessage(msg) + } + } + } + + /** + * 获取轨迹数据 + * + * @param startTimeStr 起始时间 + * @param endTimeStr 结束时间 + * @param currentTimeStr 当前点时间,如果存在便直接获取一个点 + * @return list 数据集合 + */ + private fun getTrackList( + startTimeStr: String, + endTimeStr: String, + currentTimeStr: String + ): List? { + if (!TextUtils.isEmpty(startTimeStr) && !TextUtils.isEmpty(endTimeStr)) { + var startTime: Long = 0 + var endTime: Long = 0 + try { + startTime = startTimeStr.toLong() + endTime = endTimeStr.toLong() + } catch (e: java.lang.Exception) { + } + if (startTime != 0L && endTime != 0L) { + + val id = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + + val list: MutableList = traceDataBase.niLocationDao.findToTaskIdAll(id.toString()) + + if (list.size > 0) return list + } + } + return null + } + + /** + * 接收管道数据 + */ + private inner class RecvThread : Runnable { + private var runFlag = true + fun cancel() { + runFlag = false + } + + override fun run() { + var rlRead: Int + try { + while (runFlag) { + var line: String = "" + if (!isServerClose) { + rlRead = inStr!!.read(sData) //对方断开返回-1 + if (rlRead > 0) { + Log.e(TAG, sData.toString() + "") + line = String(sData, 0, rlRead) + mTaskList.add(line) + } else { + connectFaild("连接断开") + } + } else { + connectFaild("连接断开") + } + } + Thread.sleep(10) + } catch (e: IOException) { + connectFaild(e.toString()) + e.printStackTrace() + } + } + } + + /** + * 连接失败 + * @param e 原因 + */ + private fun connectFaild(e: String) { + val msg2 = Message() + msg2.what = 0x999 + mHandler.sendMessage(msg2) + } + + /** + * 判断是否断开连接,断开返回true,没有返回false + * @return + */ + val isServerClose: Boolean + get() { + return try { + client!!.sendUrgentData(0) //发送1个字节的紧急数据,默认情况下,服务器端没有开启紧急数据处理,不影响正常通信 + false + } catch (se: Exception) { + true + } + } + + /** + * 停止接收管道数据 + */ + fun stop() { + Log.e(TAG, "stop!") + connectstatus = false + if (tRecv != null) { + tRecv!!.cancel() + } + if (tParse != null) { + tParse!!.cancel() + } + } + + /** + * 开始接收管道数据 + */ + fun start() { + Log.e(TAG, "start!") + if (tRecv != null) { + tRecv!!.cancel() + } + tRecv = RecvThread() + val thread = Thread(tRecv) + thread.start() + + //解析线程 + if (tParse != null) { + tParse!!.cancel() + } + tParse = ParseThread() + val parsethread = Thread(tParse) + parsethread.start() + } + + fun setTraceMap() { + + } + + /** + * 轨迹反向控制回调接口 + */ + interface OnConnectSinsListener { + /** + * 连接状态 + * + * @param success true 连接成功 false 连接失败 + */ + fun onConnect(success: Boolean) + + /** + * 索引中 + */ + fun onIndexing() + + /** + * 暂停 + */ + fun onStop() + + /** + * 播放 + */ + fun onPlay() + + /** + * 结束完成 + */ + fun onParseEnd() + + /** + * 轨迹点 + * + * @param mNiLocation + */ + fun onReceiveLocation(mNiLocation: NiLocation?) + } + + /** + * 解析返回值 + * + * @return 时间信息 + */ + private fun parseResult(data: String): Result? { + var data = data + if (!TextUtils.isEmpty(data)) { + try { + data = data.replace("\n".toRegex(), "") + val json = JSONObject(data) + val type = json.optInt("type") + val mResult: Result = Result() + mResult.type = type + if (type == 1) { + mResult.data = json.optString("data", "") + } + return mResult + } catch (e: Exception) { + } + } + return null + } + + //结果类对象 + internal inner class Result : Serializable { + var type = 0 + var data: String? = null + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/scan/QRCodeViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/activity/scan/QRCodeViewModel.kt index 36921971..f6122749 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/activity/scan/QRCodeViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/activity/scan/QRCodeViewModel.kt @@ -9,14 +9,10 @@ import androidx.lifecycle.viewModelScope import com.navinfo.omqs.Constant import com.navinfo.omqs.bean.IndoorConnectionInfoBean import com.navinfo.omqs.bean.QRCodeBean -import com.navinfo.omqs.bean.SysUserBean -import com.navinfo.omqs.http.DefaultResponse import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetworkService -import com.navinfo.omqs.ui.activity.login.LoginStatus import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.Job import kotlinx.coroutines.launch import kotlinx.coroutines.withContext import java.io.IOException @@ -37,6 +33,7 @@ enum class QrCodeStatus { * 信息更新成功 */ QR_CODE_STATUS_SERVER_INFO_SUCCESS, + } @HiltViewModel @@ -99,11 +96,14 @@ class QrCodeViewModel @Inject constructor( Toast.LENGTH_LONG ).show() } + + updateServerInfo(context) + } else { withContext(Dispatchers.Main) { Toast.makeText( context, - "${defaultUserResponse.msg}", + "${defaultUserResponse.errmsg}", Toast.LENGTH_SHORT ) .show() @@ -165,17 +165,20 @@ class QrCodeViewModel @Inject constructor( viewModelScope.launch(Dispatchers.Default) { val url = "http://${Constant.INDOOR_IP}:8080/sensor/service/connection" + + + val indoorConnectionInfoBean = IndoorConnectionInfoBean( + Constant.USER_ID, + Constant.USER_ID, + Constant.USER_ID, + "eyJhbGciOiJIUzI1NiJ9.eyJleHAiOjE2ODk2MjI5MjQsInVzZXJJZCI6IjEwNCIsImlhdCI6MTY4OTU3MjUyNCwidXNlcm5hbWUiOiJ3ZWl3ZWlsaW4wMDEwNCJ9.9WUqOhme8Yi_2xRBKMMe0ihb_yR1uwTqWTdZfZ7dMtE", + "http://fastmap.navinfo.com/onemap", + Constant.USER_ID, + "Android" + ) when (val result = networkService.updateServerInfo( url = url, - indoorConnectionInfoBean = IndoorConnectionInfoBean( - Constant.USER_ID, - Constant.USER_ID, - Constant.USER_ID, - Constant.USER_ID, - com.navinfo.collect.library.system.Constant.SERVER_ADDRESS, - Constant.USER_ID, - "Android" - ) + indoorConnectionInfoBean = indoorConnectionInfoBean )) { is NetResult.Success<*> -> { @@ -198,7 +201,7 @@ class QrCodeViewModel @Inject constructor( withContext(Dispatchers.Main) { Toast.makeText( context, - "${defaultUserResponse.msg}", + "${defaultUserResponse.errmsg}", Toast.LENGTH_SHORT ) .show() diff --git a/app/src/main/java/com/navinfo/omqs/ui/dialog/CommonDialog.java b/app/src/main/java/com/navinfo/omqs/ui/dialog/CommonDialog.java index 4cac5dc0..5168b7e0 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/dialog/CommonDialog.java +++ b/app/src/main/java/com/navinfo/omqs/ui/dialog/CommonDialog.java @@ -676,6 +676,7 @@ public class CommonDialog extends Dialog implements SurfaceHolder.Callback, IVid //当前为连接时启动已有的状态 if (connectstate) { + mOneBtConnect.setPressed(true); mOneBtConnect.setBackgroundResource(R.drawable.shape_btn_red_disconnect_bg); @@ -1525,7 +1526,7 @@ public class CommonDialog extends Dialog implements SurfaceHolder.Callback, IVid } //连接 - private void connection(HostBean hostBean) { + public void connection(HostBean hostBean) { if (hostBean != null) { SensorParams params = new SensorParams(); diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/console/ConsoleFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/console/ConsoleFragment.kt index 3318c4c6..3cb56387 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/console/ConsoleFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/console/ConsoleFragment.kt @@ -193,14 +193,29 @@ class ConsoleFragment : BaseFragment(), OnClickListener { R.id.console_personal_center_bg, R.id.console_personal_center_icon_bg -> { if (sceneFlag) { mFragment = PersonalCenterFragment { - TransitionManager.go(aScene, aTransition) + if(it){ + activity?.let { a -> + a.supportFragmentManager.beginTransaction().remove(this).commit() + (a as MainActivity).showIndoorDataLayout() + } + }else{ + TransitionManager.go(aScene, aTransition) + } + } sceneFlag = false TransitionManager.go(bScene, bTransition) } else { if (mFragment !is PersonalCenterFragment) { mFragment = PersonalCenterFragment { - TransitionManager.go(aScene, aTransition) + if(it){ + activity?.let { a -> + a.supportFragmentManager.beginTransaction().remove(this).commit() + (a as MainActivity).showIndoorDataLayout() + } + }else{ + TransitionManager.go(aScene, aTransition) + } } childFragmentManager.beginTransaction().replace(fragmentId, mFragment!!) .commit() diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt index f449b3fc..0792fa32 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/empty/EmptyFragment.kt @@ -32,18 +32,27 @@ class EmptyFragment : Fragment() { override fun onStart() { super.onStart() val currentDestination = findNavController().currentDestination - //有右侧面板的时候 - if (currentDestination?.label == "右侧空页面") { - currentDestinationLabel = "右侧空页面" - (activity as MainActivity).setRightSwitchButton(View.GONE) + currentDestination?.let { + //有右侧面板的时候 + currentDestinationLabel = it.label.toString() + if (it.label == "右侧空页面") { + (activity as MainActivity).setRightSwitchButtonVisibility(View.GONE) + (activity as MainActivity).setTopMenuButtonVisibility(View.VISIBLE) + } else if (it.label == "中间空页面") { + (activity as MainActivity).setRightButtonsVisible(View.VISIBLE) + } } + } override fun onStop() { super.onStop() //没有有右侧面板的时候 if (currentDestinationLabel == "右侧空页面") { - (activity as MainActivity).setRightSwitchButton(View.VISIBLE) + (activity as MainActivity).setRightSwitchButtonVisibility(View.VISIBLE) + (activity as MainActivity).setTopMenuButtonVisibility(View.GONE) + } else if (currentDestinationLabel == "中间空页面") { + (activity as MainActivity).setRightButtonsVisible(View.GONE) } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt index 54af711c..60a50bea 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt @@ -1,12 +1,17 @@ package com.navinfo.omqs.ui.fragment.evaluationresult +import android.app.Activity +import android.content.Intent +import android.graphics.Bitmap import android.os.Build import android.os.Bundle +import android.provider.MediaStore import android.util.Log -import android.view.LayoutInflater -import android.view.MotionEvent -import android.view.View -import android.view.ViewGroup +import android.view.* +import android.widget.Toast +import androidx.activity.result.ActivityResult +import androidx.activity.result.ActivityResultLauncher +import androidx.activity.result.contract.ActivityResultContracts import androidx.databinding.DataBindingUtil import androidx.navigation.NavOptions import androidx.navigation.findNavController @@ -21,12 +26,12 @@ import com.navinfo.omqs.ui.dialog.FirstDialog import com.navinfo.omqs.ui.fragment.BaseFragment import com.navinfo.omqs.ui.other.shareViewModels import dagger.hilt.android.AndroidEntryPoint -import org.videolan.vlc.Util @AndroidEntryPoint class EvaluationResultFragment : BaseFragment(), View.OnClickListener { private lateinit var binding: FragmentEvaluationResultBinding + private var mCameraLauncher: ActivityResultLauncher? = null /** * 和[PhenomenonFragment],[ProblemLinkFragment],[EvaluationResultFragment]共用同一个viewModel @@ -37,6 +42,23 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { PictureAdapter() } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + mCameraLauncher = registerForActivityResult( + ActivityResultContracts.StartActivityForResult() + ) { result: ActivityResult -> + if (result.resultCode == Activity.RESULT_OK) { + // 处理相机返回的结果 + val extras = result.data!!.extras + val imageBitmap: Bitmap? = extras!!["data"] as Bitmap? + // 在这里处理图片数据 + if (imageBitmap != null) + viewModel.savePhoto(imageBitmap) + } + } + } + // private val args:EmptyFragmentArgs by navArgs() override fun onCreateView( inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? @@ -44,37 +66,28 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { binding = DataBindingUtil.inflate(inflater, R.layout.fragment_evaluation_result, container, false) binding.fragment = this - val layoutManager = LinearLayoutManager(context) binding.viewModel = viewModel binding.lifecycleOwner = this - //// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能 - binding.evaluationVoiceRecyclerview.setHasFixedSize(true) - binding.evaluationVoiceRecyclerview.layoutManager = layoutManager - /** - * 监听左侧栏的点击事件 - */ - val adapter = SoundtListAdapter { _, view -> - - } - - binding.evaluationVoiceRecyclerview.adapter = adapter - viewModel.listDataChatMsgEntityList.observe(viewLifecycleOwner) { - adapter.refreshData(it) - } - - binding.evaluationPictureViewpager - return binding.root } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) - //监听是否退出当前页面 - viewModel.liveDataFinish.observe(viewLifecycleOwner) { - onBackPressed() + //// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能 + binding.evaluationVoiceRecyclerview.setHasFixedSize(true) + val layoutManager = LinearLayoutManager(context) + binding.evaluationVoiceRecyclerview.layoutManager = layoutManager + /** + * 监听左侧栏的点击事件 + */ + val adapter = SoundtListAdapter { _, _ -> + } + + binding.evaluationVoiceRecyclerview.adapter = adapter + //返回按钮点击 - binding.evaluationBar.setOnClickListener() { + binding.evaluationBar.setOnClickListener { val mDialog = FirstDialog(context) mDialog.setTitle("提示?") mDialog.setMessage("是否退出,请确认!") @@ -89,15 +102,13 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { } //保存事件 - binding.evaluationBarSave.setOnClickListener() { + binding.evaluationBarSave.setOnClickListener { viewModel.saveData() } //删除事件 - binding.evaluationBarDelete.setOnClickListener() { - + binding.evaluationBarDelete.setOnClickListener { viewModel.deleteData(requireContext()) - } /** * 照片view @@ -106,15 +117,26 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { val list = mutableListOf("1", "2", "3") pictureAdapter.refreshData(list) + //照片左右选择键点击监听 binding.evaluationPictureLeft.setOnClickListener(this) binding.evaluationPictureRight.setOnClickListener(this) + binding.evaluationCamera.setOnClickListener(this) + //设置照片偏移量 + val viewPager = binding.evaluationPictureViewpager + val vto = viewPager.viewTreeObserver + vto.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { + override fun onGlobalLayout() { + val width = viewPager.width + // 处理View宽度 + // 在回调完成后,需要将监听器从View树中移除,以避免重复调用 + viewPager.viewTreeObserver.removeOnGlobalLayoutListener(this) + val recyclerView = viewPager.getChildAt(0) as RecyclerView - val recyclerView = binding.evaluationPictureViewpager.getChildAt(0) as RecyclerView - - recyclerView.setPadding(0, 0, Util.convertDpToPx(requireContext(), 50), 0) - recyclerView.clipToPadding = false - + recyclerView.setPadding(0, 0, width / 2 - 30, 0) + recyclerView.clipToPadding = false + } + }) binding.evaluationVoice.setOnTouchListener { _, event -> Log.e("qj", event?.action.toString()) @@ -123,11 +145,7 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { voiceOnTouchStart()//Do Something } - MotionEvent.ACTION_UP -> { - voiceOnTouchStop()//Do Something - } - - MotionEvent.ACTION_CANCEL -> { + MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL, MotionEvent.ACTION_OUTSIDE -> { voiceOnTouchStop()//Do Something } } @@ -138,22 +156,21 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { /** * 读取元数据 */ -// val id = args.qsId var id = "" var signBean: SignBean? = null - var autoSave: Boolean = false - var filePath: String = "" + var autoSave = false + var filePath = "" arguments?.let { id = it.getString("QsId", "") filePath = it.getString("filePath", "") try { signBean = it.getParcelable("SignBean") autoSave = it.getBoolean("AutoSave") - } catch (e: java.lang.Exception) { + } catch (_: java.lang.Exception) { } } - if (id == null || id.isEmpty()) { + if (id.isEmpty()) { viewModel.initNewData(signBean, filePath) //增加监听,联动列表自动保存 viewModel.liveDataRightTypeList.observe(viewLifecycleOwner) { @@ -164,102 +181,22 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { } else { viewModel.initData(id) } -// //监听大分类数据变化 -// viewModel.liveDataClassTypeList.observe(viewLifecycleOwner) { -// if (it == null || it.isEmpty()) { -// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() -// } else { -// binding.evaluationClassType.adapter = -// ArrayAdapter(requireContext(), R.layout.text_item_select, it) -// } -// } -// -// viewModel.liveDataProblemTypeList.observe(viewLifecycleOwner){ -// if (it == null || it.isEmpty()) { -// Toast.makeText(requireContext(), "还没有导入元数据!", Toast.LENGTH_SHORT).show() -// }else{ -// binding.evaluationProblemType.adapter = -// ArrayAdapter(requireContext(), R.layout.text_item_select, it) -// } -// } -// //选择问题分类的回调 -// binding.evaluationClassType.onItemSelectedListener = -// object : AdapterView.OnItemSelectedListener { -// override fun onItemSelected( -// parent: AdapterView<*>?, view: View?, position: Int, id: Long -// ) { -// viewModel.getProblemTypeList(position) -// } -// -// override fun onNothingSelected(parent: AdapterView<*>?) {} -// } -// /** -// * 监听联动选择的内容 -// */ -// viewModel.problemTypeListLiveData.observe(viewLifecycleOwner) { -// binding.evaluationClassTabLayout.let { tabLayout -> -// tabLayout.removeAllTabs() -// val fragmentList = mutableListOf() -// for (item in it) { -// val tab = tabLayout.newTab() -// tab.text = item -// tabLayout.addTab(tab) -// fragmentList.add(PhenomenonFragment(viewModel.currentClassType, item)) -// } -// phenomenonFragmentAdapter = -// activity?.let { a -> EvaluationResultAdapter(a, fragmentList) } -// binding.evaluationViewpager.adapter = phenomenonFragmentAdapter -// -// TabLayoutMediator( -// binding.evaluationClassTabLayout, -// binding.evaluationViewpager -// ) { tab, position -> -// tab.text = it[position] -// }.attach() -// updateHeight(0) -// } -// -// } + viewModel.listDataChatMsgEntityList.observe(viewLifecycleOwner) { + adapter.refreshData(it) + } + + //监听是否退出当前页面 + viewModel.liveDataFinish.observe(viewLifecycleOwner) { + onBackPressed() + } + //监听要提示的信息 + viewModel.liveDataToastMessage.observe(viewLifecycleOwner) { + Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() + } -// binding.evaluationViewpager.registerOnPageChangeCallback(object : -// ViewPager2.OnPageChangeCallback() { -// override fun onPageSelected(position: Int) { -// super.onPageSelected(position) -// updateHeight(position) -// } -// }) } - -// private fun updateHeight(position: Int) { -// phenomenonFragmentAdapter?.let { -// if (it.fragmentList.size > position) { -// val fragment: Fragment = it.fragmentList[position] -// if (fragment.view != null) { -// val viewWidth = View.MeasureSpec.makeMeasureSpec( -// fragment.requireView().width, View.MeasureSpec.EXACTLY -// ) -// val viewHeight = -// View.MeasureSpec.makeMeasureSpec(0, View.MeasureSpec.UNSPECIFIED) -// fragment.requireView().measure(viewWidth, viewHeight) -// binding.evaluationViewpager.let { viewpager -> -// if (viewpager.layoutParams.height != fragment.requireView().measuredHeight) { -// //必须要用对象去接收,然后修改该对象再采用该对象,否则无法生效... -// val layoutParams: ViewGroup.LayoutParams = -// viewpager.layoutParams -// layoutParams.height = fragment.requireView().measuredHeight -// viewpager.layoutParams = layoutParams -// } -// } -// -// } -// } -// } -// -// } - - override fun onDestroyView() { activity?.run { findNavController(R.id.main_activity_middle_fragment).navigateUp() @@ -279,6 +216,7 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { if (currentItem > 0) { binding.evaluationPictureViewpager.currentItem = currentItem - 1 } else { + return } } @@ -288,7 +226,7 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { if (currentItem < pictureAdapter.data.size - 1) { binding.evaluationPictureViewpager.currentItem = currentItem + 1 } else { - + return } } //上三项,打开面板 @@ -338,8 +276,12 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { } } - - else -> {} + R.id.evaluation_camera -> { + takePhoto() + } + else -> { + return + } } } } @@ -362,4 +304,14 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener { return true } + private fun takePhoto() { + try { + val takePictureIntent = Intent(MediaStore.ACTION_IMAGE_CAPTURE) + if (takePictureIntent.resolveActivity(requireActivity().packageManager) != null) { + mCameraLauncher!!.launch(takePictureIntent) + } + } catch (e: Exception) { + Log.d("TTTT", e.toString()) + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt index e5f47088..1c57cb41 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt @@ -3,6 +3,8 @@ package com.navinfo.omqs.ui.fragment.evaluationresult import android.app.Activity import android.app.Dialog import android.content.Context +import android.content.SharedPreferences +import android.graphics.Bitmap import android.graphics.drawable.AnimationDrawable import android.graphics.drawable.BitmapDrawable import android.os.Build @@ -19,9 +21,12 @@ import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.blankj.utilcode.util.ToastUtils import com.navinfo.collect.library.data.entity.AttachmentBean +import com.navinfo.collect.library.data.entity.HadLinkDvoBean import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.collect.library.data.entity.RenderEntity.Companion.LinkTable +import com.navinfo.collect.library.data.entity.TaskBean import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.map.OnGeoPointClickListener import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.omqs.Constant import com.navinfo.omqs.R @@ -35,17 +40,14 @@ import com.navinfo.omqs.util.DateTimeUtil import com.navinfo.omqs.util.SoundMeter import com.navinfo.omqs.util.SpeakMode import dagger.hilt.android.lifecycle.HiltViewModel -import io.realm.OrderedCollectionChangeSet import io.realm.Realm import io.realm.RealmList -import io.realm.RealmModel -import io.realm.RealmResults -import io.realm.kotlin.addChangeListener -import io.realm.kotlin.where import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import org.oscim.core.GeoPoint import java.io.File +import java.io.FileOutputStream import java.util.* import javax.inject.Inject @@ -54,9 +56,11 @@ class EvaluationResultViewModel @Inject constructor( private val roomAppDatabase: RoomAppDatabase, private val mapController: NIMapController, private val realmOperateHelper: RealmOperateHelper, -) : ViewModel() { + private val sharedPreferences: SharedPreferences +) : ViewModel(), SharedPreferences.OnSharedPreferenceChangeListener { - private val markerTitle = "点选marker" + + private val TAG = "点选marker" /** * 操作结束,销毁页面 @@ -68,20 +72,40 @@ class EvaluationResultViewModel @Inject constructor( */ val liveDataLeftTypeList = MutableLiveData>() - /** - * 问题类型 liveData 给[MiddleAdapter]展示的数据 - */ -// val liveDataMiddleTypeList = MutableLiveData>() /** * 问题现象 liveData 给[RightGroupHeaderAdapter]展示的数据 */ val liveDataRightTypeList = MutableLiveData>() - var liveDataQsRecordBean = MutableLiveData() + /** + * 要保存的评测数据 + */ + val liveDataQsRecordBean = MutableLiveData(QsRecordBean(id = UUID.randomUUID().toString())) - var listDataChatMsgEntityList = MutableLiveData>() + /** + * 语音列表 + */ + val listDataChatMsgEntityList = MutableLiveData>() + /** + * 照片列表 + */ + val liveDataPictureList = MutableLiveData>() + + /** + * toast信息 + */ + val liveDataToastMessage = MutableLiveData() + + /** + * 当前选择的任务 + */ + val liveDataTaskBean = MutableLiveData() + + /** + * 编辑数据时用来差分数据 + */ var oldBean: QsRecordBean? = null //语音窗体 @@ -99,24 +123,27 @@ class EvaluationResultViewModel @Inject constructor( var classCodeTemp: String = "" init { - liveDataQsRecordBean.value = QsRecordBean(id = UUID.randomUUID().toString()) - viewModelScope.launch { - mapController.onMapClickFlow.collect { - liveDataQsRecordBean.value!!.geometry = GeometryTools.createGeometry(it).toText() - mapController.markerHandle.addMarker(it, markerTitle) - viewModelScope.launch { - captureLink(it.longitude, it.latitude) + mapController.mMapView.addOnNIMapClickListener(TAG, object : OnGeoPointClickListener { + override fun onMapClick(tag: String, point: GeoPoint) { + if (tag == TAG) { + liveDataQsRecordBean.value!!.geometry = + GeometryTools.createGeometry(point).toText() + mapController.markerHandle.addMarker(point, TAG) + viewModelScope.launch { + captureLink(point) + } } + } - } + }) + sharedPreferences.registerOnSharedPreferenceChangeListener(this) } override fun onCleared() { super.onCleared() - mapController.markerHandle.removeMarker(markerTitle) - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - mapController.lineHandler.removeLine() - } + mapController.mMapView.removeOnNIMapClickListener(TAG) + mapController.markerHandle.removeMarker(TAG) + mapController.lineHandler.removeLine() } @@ -124,42 +151,57 @@ class EvaluationResultViewModel @Inject constructor( * 查询数据库,获取问题分类 */ fun initNewData(bean: SignBean?, filePath: String) { - //获取当前定位点 - val geoPoint = mapController.locationLayerHandler.getCurrentGeoPoint() - //如果不是从面板进来的 - if (bean == null) { - geoPoint?.let { - liveDataQsRecordBean.value!!.geometry = GeometryTools.createGeometry(it).toText() - mapController.markerHandle.addMarker(geoPoint, markerTitle) - mapController.animationHandler.animationByLatLon( - geoPoint.latitude, geoPoint.longitude - ) - viewModelScope.launch { - captureLink(geoPoint.longitude, geoPoint.latitude) - } + //查询元数据 + viewModelScope.launch(Dispatchers.IO) { + /** + * 获取当前所选的任务 + */ + val taskId = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + val realm = Realm.getDefaultInstance() + val objects = realm.where(TaskBean::class.java).equalTo("id", taskId).findFirst() + if (objects != null) { + liveDataTaskBean.postValue(realm.copyFromRealm(objects)) } - } else { - liveDataQsRecordBean.value?.run { - elementId = bean.renderEntity.code.toString() - linkId = bean.linkId - if (linkId.isNotEmpty()) { - viewModelScope.launch { - val link = realmOperateHelper.queryLink(linkId) - link?.let { l -> - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + + //获取当前定位点 + val geoPoint = mapController.locationLayerHandler.getCurrentGeoPoint() + //如果不是从面板进来的 + if (bean == null) { + geoPoint?.let { + liveDataQsRecordBean.value!!.geometry = + GeometryTools.createGeometry(it).toText() + withContext(Dispatchers.Main) { + mapController.markerHandle.addMarker(geoPoint, TAG) + mapController.animationHandler.animationByLatLon( + geoPoint.latitude, geoPoint.longitude + ) + } + captureLink(geoPoint) + } + } else { + liveDataQsRecordBean.value?.run { + elementId = bean.renderEntity.code.toString() + linkId = bean.linkId + if (linkId.isNotEmpty()) { + viewModelScope.launch { + val link = realmOperateHelper.queryLink(linkId) + link?.let { l -> mapController.lineHandler.showLine(l.geometry) } } } + + val point = GeometryTools.createGeoPoint(bean.renderEntity.geometry) + this.geometry = GeometryTools.createGeometry(point).toText() + withContext(Dispatchers.Main) { + mapController.animationHandler.animationByLatLon( + point.latitude, point.longitude + ) + mapController.markerHandle.addMarker(point, TAG) + } } - val point = GeometryTools.createGeoPoint(bean.renderEntity.geometry) - this.geometry = GeometryTools.createGeometry(point).toText() - mapController.animationHandler.animationByLatLon(point.latitude, point.longitude) - mapController.markerHandle.addMarker(point, markerTitle) } - } - //查询元数据 - viewModelScope.launch(Dispatchers.IO) { + getClassTypeList(bean) getProblemLinkList() } @@ -167,23 +209,32 @@ class EvaluationResultViewModel @Inject constructor( } /** - * 捕捉道路 + * 捕捉道路或新增评测link */ - private suspend fun captureLink(longitude: Double, latitude: Double) { + private suspend fun captureLink(point: GeoPoint) { + if (liveDataTaskBean.value == null) { + liveDataToastMessage.postValue("请先选择所属任务!") + return + } if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - val linkList = realmOperateHelper.queryLink( - point = GeoPoint(latitude, longitude), - ) - liveDataQsRecordBean.value?.let { - if (linkList.isNotEmpty()) { - it.linkId = linkList[0].properties[LinkTable.linkPid] ?: "" - mapController.lineHandler.showLine(linkList[0].geometry) - Log.e("jingo", "捕捉到的linkId = ${it.linkId}") + + val taskLink = + realmOperateHelper.captureTaskLink(liveDataTaskBean.value!!.id, point) + if (taskLink != null) { + it.linkId = taskLink.linkPid + mapController.lineHandler.showLine(taskLink.geometry) + return } else { - it.linkId = "" - mapController.lineHandler.removeLine() + val linkList = realmOperateHelper.queryLink(point = point) + if (linkList.isNotEmpty()) { + it.linkId = linkList[0].properties[LinkTable.linkPid] ?: "" + mapController.lineHandler.showLine(linkList[0].geometry) + return + } } + it.linkId = "" + mapController.lineHandler.removeLine() } } } @@ -201,10 +252,12 @@ class EvaluationResultViewModel @Inject constructor( var classCode = list[0].elementCode liveDataLeftTypeList.postValue(it) if (bean != null) { - val classType2 = roomAppDatabase.getScProblemTypeDao().findClassTypeByCode(bean.renderEntity.code) + val classType2 = roomAppDatabase.getScProblemTypeDao() + .findClassTypeByCode(bean.renderEntity.code) if (classType2 != null) { classType = classType2 } + classCode = bean.renderEntity.code.toString() } //如果右侧栏没数据,给个默认值 if (liveDataQsRecordBean.value!!.classType.isEmpty()) { @@ -291,11 +344,12 @@ class EvaluationResultViewModel @Inject constructor( /** * 查询问题类型列表 */ - fun getProblemTypeList(classType: String) { + fun getProblemTypeList(scProblemTypeBean: ScProblemTypeBean) { viewModelScope.launch(Dispatchers.IO) { - getProblemList(classType) + getProblemList(scProblemTypeBean.classType) } - classTypeTemp = classType + classTypeTemp = scProblemTypeBean.classType + classCodeTemp = scProblemTypeBean.elementCode } /** @@ -315,19 +369,52 @@ class EvaluationResultViewModel @Inject constructor( liveDataQsRecordBean.postValue(liveDataQsRecordBean.value) } + /** + * 保存数据 + */ + fun saveData() { + viewModelScope.launch(Dispatchers.IO) { + val taskBean = liveDataQsRecordBean.value!! + if (liveDataTaskBean.value == null) { + liveDataToastMessage.postValue("请选择所属任务!") + return@launch + } else if (taskBean.classType.isEmpty()) { + liveDataToastMessage.postValue("请选择要素分类!") + return@launch + } else if (taskBean.problemType.isEmpty()) { + liveDataToastMessage.postValue("请选择问题类型!") + return@launch + } else if (taskBean.phenomenon.isEmpty()) { + liveDataToastMessage.postValue("请选择问题现象!") + return@launch + } else if (taskBean.problemLink.isEmpty()) { + liveDataToastMessage.postValue("请选择问题环节!") + return@launch + } else if (taskBean.classType.isEmpty()) { + liveDataToastMessage.postValue("请选择问题分类!") + return@launch + } else if (taskBean.cause.isEmpty()) { + liveDataToastMessage.postValue("请选择初步分析原因!") + return@launch + } + val realm = Realm.getDefaultInstance() + liveDataQsRecordBean.value!!.taskId = liveDataTaskBean.value!!.id liveDataQsRecordBean.value!!.checkTime = DateTimeUtil.getDataTime() + liveDataQsRecordBean.value!!.checkUserId = Constant.USER_REAL_NAME realm.executeTransaction { it.copyToRealmOrUpdate(liveDataQsRecordBean.value) } -// realm.close() mapController.markerHandle.addOrUpdateQsRecordMark(liveDataQsRecordBean.value!!) liveDataFinish.postValue(true) } } + /** + * 删除数据 + */ fun deleteData(context: Context) { val mDialog = FirstDialog(context) mDialog.setTitle("提示?") @@ -357,160 +444,222 @@ class EvaluationResultViewModel @Inject constructor( */ fun initData(id: String) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { - viewModelScope.launch(Dispatchers.IO) { + viewModelScope.launch(Dispatchers.IO) { - Realm.getDefaultInstance().use { realm -> - realm.executeTransactionAsync { bgRealm -> - // find the item - val objects = bgRealm.where(QsRecordBean::class.java).equalTo("id", id).findFirst() - if (objects != null) { - oldBean = bgRealm.copyFromRealm(objects) - oldBean?.let { - liveDataQsRecordBean.postValue(it.copy()) - val p = GeometryTools.createGeoPoint(it.geometry) - mapController.markerHandle.addMarker(GeoPoint(p.latitude, p.longitude), markerTitle) + val realm = Realm.getDefaultInstance() + val objects = realm.where(QsRecordBean::class.java).equalTo("id", id).findFirst() + Log.e("jingo", "查询数据 id= $id") + if (objects != null) { + oldBean = realm.copyFromRealm(objects) + oldBean?.let { + /** + * 获取当前所选的任务 + */ + val objects = + realm.where(TaskBean::class.java).equalTo("id", it.taskId).findFirst() + if (objects != null) { + liveDataTaskBean.postValue(realm.copyFromRealm(objects)) + } - //获取linkid - if (it.linkId.isNotEmpty()) { - viewModelScope.launch(Dispatchers.IO) { - val link = realmOperateHelper.queryLink(it.linkId) - link?.let { l -> - mapController.lineHandler.showLine(l.geometry) - } - } - } - liveDataQsRecordBean.value?.attachmentBeanList = it.attachmentBeanList - // 显示语音数据到界面 - getChatMsgEntityList() + liveDataQsRecordBean.postValue(it.copy()) + val p = GeometryTools.createGeoPoint(it.geometry) + mapController.markerHandle.addMarker( + GeoPoint( + p.latitude, p.longitude + ), TAG, "", null + ) + + //获取linkid + if (it.linkId.isNotEmpty()) { + val link = realmOperateHelper.queryLink(it.linkId) + if (link != null) { + mapController.lineHandler.showLine(link.geometry) + } else { + val realmR = realm.where(HadLinkDvoBean::class.java) + .equalTo("linkPid", it.linkId).and().equalTo("taskId", it.taskId) + .findFirst() + if (realmR != null) { + mapController.lineHandler.showLine(realmR.geometry) } } } + liveDataQsRecordBean.value?.attachmentBeanList = it.attachmentBeanList + // 显示语音数据到界面 + getChatMsgEntityList() + } + } else { + liveDataToastMessage.postValue("数据读取失败") + } +} +} + +/** + * 查询问题类型列表 + */ + private suspend fun getChatMsgEntityList() { + val chatMsgEntityList: MutableList = ArrayList() + liveDataQsRecordBean.value?.attachmentBeanList?.forEach { + //1 录音 + if (it.type == 1) { + val chatMsgEntity = ChatMsgEntity() + chatMsgEntity.name = it.name + chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH + chatMsgEntityList.add(chatMsgEntity) + } + } + listDataChatMsgEntityList.postValue(chatMsgEntityList) +} + +fun addChatMsgEntity(filePath: String) { + + if (filePath.isNotEmpty()) { + var chatMsgEntityList: MutableList = ArrayList() + if (listDataChatMsgEntityList.value?.isEmpty() == false) { + chatMsgEntityList = listDataChatMsgEntityList.value!! + } + val chatMsgEntity = ChatMsgEntity() + chatMsgEntity.name = filePath.replace(Constant.USER_DATA_ATTACHEMNT_PATH, "").toString() + chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH + chatMsgEntityList.add(chatMsgEntity) + + + var attachmentList: RealmList = RealmList() + + //赋值处理 + if (liveDataQsRecordBean.value?.attachmentBeanList?.isEmpty() == false) { + attachmentList = liveDataQsRecordBean.value?.attachmentBeanList!! + } + + val attachmentBean = AttachmentBean() + attachmentBean.name = chatMsgEntity.name!! + attachmentBean.type = 1 + attachmentList.add(attachmentBean) + liveDataQsRecordBean.value?.attachmentBeanList = attachmentList + + listDataChatMsgEntityList.postValue(chatMsgEntityList) + } +} + +fun startSoundMetter(activity: Activity, v: View) { + + if (mSpeakMode == null) { + mSpeakMode = SpeakMode(activity) + } + + //语音识别动画 + if (pop == null) { + pop = PopupWindow() + pop!!.width = ViewGroup.LayoutParams.MATCH_PARENT + pop!!.height = ViewGroup.LayoutParams.WRAP_CONTENT + pop!!.setBackgroundDrawable(BitmapDrawable()) + val view = + View.inflate(activity as Context, R.layout.cv_card_voice_rcd_hint_window, null) + pop!!.contentView = view + volume = view.findViewById(R.id.volume) + } + + pop!!.update() + + Constant.IS_VIDEO_SPEED = true + //录音动画 + if (pop != null) { + pop!!.showAtLocation(v, Gravity.CENTER, 0, 0) + } + volume!!.setBackgroundResource(R.drawable.pop_voice_img) + val animation = volume!!.background as AnimationDrawable + animation.start() + + val name: String = DateTimeUtil.getTimeSSS().toString() + ".m4a" + if (mSoundMeter == null) { + mSoundMeter = SoundMeter() + } + mSoundMeter!!.setmListener(object : SoundMeter.OnSoundMeterListener { + @RequiresApi(Build.VERSION_CODES.Q) + override fun onSuccess(filePath: String?) { + if (!TextUtils.isEmpty(filePath) && File(filePath).exists()) { + if (File(filePath) == null || File(filePath).length() < 1600) { + ToastUtils.showLong("语音时间太短,无效!") + mSpeakMode!!.speakText("语音时间太短,无效") + stopSoundMeter() + return } } + + mSpeakMode!!.speakText("结束录音") + + addChatMsgEntity(filePath!!) } + + @RequiresApi(api = Build.VERSION_CODES.Q) + override fun onfaild(message: String?) { + ToastUtils.showLong("录制失败!") + mSpeakMode!!.speakText("录制失败") + stopSoundMeter() + } + }) + + mSoundMeter!!.start(Constant.USER_DATA_ATTACHEMNT_PATH + name) + ToastUtils.showLong("开始录音") + mSpeakMode!!.speakText("开始录音") +} + +//停止语音录制 +@RequiresApi(api = Build.VERSION_CODES.Q) +fun stopSoundMeter() { + //先重置标识,防止按钮抬起时触发语音结束 + Constant.IS_VIDEO_SPEED = false + if (mSoundMeter != null && mSoundMeter!!.isStartSound) { + mSoundMeter!!.stop() + } + pop?.let { + if (it.isShowing) { + it.dismiss() + } + } +} + + +fun savePhoto(bitmap: Bitmap) { + viewModelScope.launch(Dispatchers.IO) { + // 创建一个名为 "MyApp" 的文件夹 + val myAppDir = File(Constant.USER_DATA_ATTACHEMNT_PATH) + if (!myAppDir.exists()) myAppDir.mkdirs() // 确保文件夹已创建 + + // 创建一个名为 fileName 的文件 + val file = File(myAppDir, "${UUID.randomUUID()}.png") + file.createNewFile() // 创建文件 + + // 将 Bitmap 压缩为 JPEG 格式,并将其写入文件中 + val out = FileOutputStream(file) + bitmap.compress(Bitmap.CompressFormat.JPEG, 90, out) + out.flush() + out.close() + var picList = mutableListOf() + if (liveDataPictureList.value == null) { + picList.add(file.absolutePath) + } else { + picList.addAll(liveDataPictureList.value!!) + picList.add(file.absolutePath) + } + liveDataPictureList.postValue(picList) + } + } /** - * 查询问题类型列表 + * 监听任务选择变化 */ - @RequiresApi(Build.VERSION_CODES.N) - fun getChatMsgEntityList() { - val chatMsgEntityList: MutableList = ArrayList() - liveDataQsRecordBean.value?.attachmentBeanList?.forEach { - //1 录音 - if (it.type == 1) { - val chatMsgEntity = ChatMsgEntity() - chatMsgEntity.name = it.name - chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH - chatMsgEntityList.add(chatMsgEntity) - } - } - listDataChatMsgEntityList.postValue(chatMsgEntityList) - } - - fun addChatMsgEntity(filePath: String) { - - if (filePath.isNotEmpty()) { - var chatMsgEntityList: MutableList = ArrayList() - if (listDataChatMsgEntityList.value?.isEmpty() == false) { - chatMsgEntityList = listDataChatMsgEntityList.value!! - } - val chatMsgEntity = ChatMsgEntity() - chatMsgEntity.name = filePath.replace(Constant.USER_DATA_ATTACHEMNT_PATH, "").toString() - chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH - chatMsgEntityList.add(chatMsgEntity) - - - var attachmentList: RealmList = RealmList() - - //赋值处理 - if (liveDataQsRecordBean.value?.attachmentBeanList?.isEmpty() == false) { - attachmentList = liveDataQsRecordBean.value?.attachmentBeanList!! - } - - val attachmentBean = AttachmentBean() - attachmentBean.name = chatMsgEntity.name!! - attachmentBean.type = 1 - attachmentList.add(attachmentBean) - liveDataQsRecordBean.value?.attachmentBeanList = attachmentList - - listDataChatMsgEntityList.postValue(chatMsgEntityList) - } - } - - fun startSoundMetter(activity: Activity, v: View) { - - if (mSpeakMode == null) { - mSpeakMode = SpeakMode(activity) - } - - //语音识别动画 - if (pop == null) { - pop = PopupWindow() - pop!!.width = ViewGroup.LayoutParams.MATCH_PARENT - pop!!.height = ViewGroup.LayoutParams.WRAP_CONTENT - pop!!.setBackgroundDrawable(BitmapDrawable()) - val view = - View.inflate(activity as Context, R.layout.cv_card_voice_rcd_hint_window, null) - pop!!.contentView = view - volume = view.findViewById(R.id.volume) - } - - pop!!.update() - - Constant.IS_VIDEO_SPEED = true - //录音动画 - if (pop != null) { - pop!!.showAtLocation(v, Gravity.CENTER, 0, 0) - } - volume!!.setBackgroundResource(R.drawable.pop_voice_img) - val animation = volume!!.background as AnimationDrawable - animation.start() - - val name: String = DateTimeUtil.getTimeSSS().toString() + ".m4a" - if (mSoundMeter == null) { - mSoundMeter = SoundMeter() - } - mSoundMeter!!.setmListener(object : SoundMeter.OnSoundMeterListener { - @RequiresApi(Build.VERSION_CODES.Q) - override fun onSuccess(filePath: String?) { - if (!TextUtils.isEmpty(filePath) && File(filePath).exists()) { - if (File(filePath) == null || File(filePath).length() < 1600) { - ToastUtils.showLong("语音时间太短,无效!") - mSpeakMode!!.speakText("语音时间太短,无效") - stopSoundMeter() - return - } + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == Constant.SELECT_TASK_ID && oldBean == null) { + viewModelScope.launch(Dispatchers.IO) { + val taskId = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + val realm = Realm.getDefaultInstance() + val objects = realm.where(TaskBean::class.java).equalTo("id", taskId).findFirst() + if (objects != null) { + liveDataTaskBean.postValue(realm.copyFromRealm(objects)) } - - mSpeakMode!!.speakText("结束录音") - - addChatMsgEntity(filePath!!) } - - @RequiresApi(api = Build.VERSION_CODES.Q) - override fun onfaild(message: String?) { - ToastUtils.showLong("录制失败!") - mSpeakMode!!.speakText("录制失败") - stopSoundMeter() - } - }) - - mSoundMeter!!.start(Constant.USER_DATA_ATTACHEMNT_PATH + name) - ToastUtils.showLong("开始录音") - mSpeakMode!!.speakText("开始录音") - } - - //停止语音录制 - @RequiresApi(api = Build.VERSION_CODES.Q) - fun stopSoundMeter() { - //先重置标识,防止按钮抬起时触发语音结束 - Constant.IS_VIDEO_SPEED = false - if (mSoundMeter != null && mSoundMeter!!.isStartSound()) { - mSoundMeter!!.stop() } - if (pop != null && pop!!.isShowing) pop!!.dismiss() - } +} } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/LeftAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/LeftAdapter.kt index 9668f889..261b2106 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/LeftAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/LeftAdapter.kt @@ -1,16 +1,13 @@ package com.navinfo.omqs.ui.fragment.evaluationresult -import android.os.Build import android.view.LayoutInflater import android.view.ViewGroup -import androidx.annotation.RequiresApi -import com.navinfo.omqs.R import com.navinfo.omqs.bean.ScProblemTypeBean import com.navinfo.omqs.databinding.TextItemSelectBinding import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter import com.navinfo.omqs.ui.other.BaseViewHolder -class LeftAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : +class LeftAdapter(private var itemListener: ((Int, ScProblemTypeBean) -> Unit?)? = null) : BaseRecyclerViewAdapter() { private var selectTitle = "" @@ -21,7 +18,6 @@ class LeftAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : return BaseViewHolder(viewBinding) } - @RequiresApi(Build.VERSION_CODES.M) override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { val bd = holder.viewBinding as TextItemSelectBinding val title = data[position] @@ -32,7 +28,7 @@ class LeftAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) : selectTitle = title.classType notifyDataSetChanged() } - itemListener?.invoke(position, title.classType) + itemListener?.invoke(position, title) } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/MiddleAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/MiddleAdapter.kt index ee0b6044..6843dce8 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/MiddleAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/MiddleAdapter.kt @@ -20,7 +20,6 @@ class MiddleAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) return BaseViewHolder(viewBinding) } - @RequiresApi(Build.VERSION_CODES.M) override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { val bd = holder.viewBinding as TextItemSelectBinding val title = data[position] @@ -30,10 +29,10 @@ class MiddleAdapter(private var itemListener: ((Int, String) -> Unit?)? = null) bd.itemLayout.layoutParams = layoutParams if (selectTitle == title) { bd.itemId.setBackgroundResource(R.drawable.shape_bg_blue_bg_4_radius) - bd.itemId.setTextColor(holder.viewBinding.root.context.getColor(R.color.white)) + bd.itemId.setTextColor(holder.viewBinding.root.context.resources.getColor(R.color.white)) } else { bd.itemId.setBackgroundResource(R.drawable.shape_rect_white_2dp_bg) - bd.itemId.setTextColor(holder.viewBinding.root.context.getColor(R.color.black)) + bd.itemId.setTextColor(holder.viewBinding.root.context.resources.getColor(R.color.black)) } bd.root.setOnClickListener { if (selectTitle != title) { diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt index 0442c928..1c58c6e2 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/PhenomenonFragment.kt @@ -73,13 +73,11 @@ class PhenomenonFragment : } binding.phenomenonRightRecyclerview.adapter = rightAdapter //右侧菜单增加组标题 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - binding.phenomenonRightRecyclerview.addItemDecoration( - RightGroupHeaderDecoration( - requireContext() - ) + binding.phenomenonRightRecyclerview.addItemDecoration( + RightGroupHeaderDecoration( + requireContext() ) - } + ) //右侧菜单查询数据监听 viewModel.liveDataRightTypeList.observe(viewLifecycleOwner) { rightAdapter.setSelectTitle(viewModel.liveDataQsRecordBean.value!!.phenomenon) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt index 9af567f5..7355f34b 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/ProblemLinkFragment.kt @@ -1,8 +1,6 @@ package com.navinfo.omqs.ui.fragment.evaluationresult -import android.os.Build import android.os.Bundle -import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup @@ -50,13 +48,11 @@ class ProblemLinkFragment : BaseFragment() { } binding.linkRightRecyclerview.adapter = rightAdapter //右侧菜单增加组标题 - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - binding.linkRightRecyclerview.addItemDecoration( - RightGroupHeaderDecoration( - requireContext() - ) + binding.linkRightRecyclerview.addItemDecoration( + RightGroupHeaderDecoration( + requireContext() ) - } + ) //右侧菜单查询数据监听 viewModel.liveDataRightTypeList.observe(viewLifecycleOwner) { rightAdapter.setSelectTitle(viewModel.liveDataQsRecordBean.value!!.cause) diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/RightGroupHeaderDecoration.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/RightGroupHeaderDecoration.kt index 13b6fad4..41071b8c 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/RightGroupHeaderDecoration.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/RightGroupHeaderDecoration.kt @@ -5,9 +5,7 @@ import android.graphics.Canvas import android.graphics.Color import android.graphics.Paint import android.graphics.Rect -import android.os.Build import android.view.View -import androidx.annotation.RequiresApi import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView.ItemDecoration @@ -17,7 +15,6 @@ import com.navinfo.omqs.R /** * 自定义装饰器(实现分组+吸顶效果) */ -@RequiresApi(Build.VERSION_CODES.M) class RightGroupHeaderDecoration(context: Context) : ItemDecoration() { //头部的高 private val mItemHeaderHeight: Int @@ -35,7 +32,7 @@ class RightGroupHeaderDecoration(context: Context) : ItemDecoration() { mTextPaddingLeft = dp2px(context, 6f) mTextRect = Rect() mItemHeaderPaint = Paint(Paint.ANTI_ALIAS_FLAG) - mItemHeaderPaint.color = context.getColor(R.color.btn_bg_blue) + mItemHeaderPaint.color = context.resources.getColor(R.color.btn_bg_blue) mTextPaint = Paint(Paint.ANTI_ALIAS_FLAG) mTextPaint.textSize = 46f mTextPaint.color = Color.WHITE diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt new file mode 100644 index 00000000..dc865185 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasFragment.kt @@ -0,0 +1,152 @@ +package com.navinfo.omqs.ui.fragment.note + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.databinding.DataBindingUtil +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentCanvasBinding +import com.navinfo.omqs.databinding.FragmentNoteBinding +import com.navinfo.omqs.databinding.FragmentProblemLinkBinding +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.fragment.note.CanvasView.CanvasStyle +import com.navinfo.omqs.ui.fragment.note.CanvasView.OnCanvasChangeListener +import com.navinfo.omqs.ui.other.shareViewModels + +/** + * @author zhjch + * @version V1.0 + * @ClassName: CanvasFragment + * @Date 2016/5/10 + * @Description: ${TODO}(绘制画布) + */ +class CanvasFragment : BaseFragment() { + /** + * 获取画布 + * + * @return + */ + /** + * 画布 + */ + private val canvasView by lazy { binding.canvasView } + + /** + * 画笔线型 + */ + private var mStyle = CanvasStyle.FREE_LINE + + /** + * 画笔颜色 + */ + private var mColor = -1 + + /** + * 画笔粗细 + */ + private var width = 5 + + + /** + * 画布回调接口 + */ + private var listener: OnCanvasChangeListener? = null + + + private var _binding: FragmentCanvasBinding? = null + private val binding get() = _binding!! + + private val viewModel by shareViewModels("note") + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentCanvasBinding.inflate(inflater, container, false) + viewModel.initCanvasView(canvasView) + return binding.root + } + + override fun onDestroyView() { + super.onDestroyView() + _binding = null + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + + canvasView.setStyle(mStyle) + if (mColor == -1) { + mColor = resources.getColor(R.color.black) + } + canvasView.setPaintColor(mColor) + canvasView.setPaintWidth(width) + if (listener != null) { + canvasView.setOnCanvasChangeListener(listener) + } + +// * 开关橡皮擦 +// */ +// viewModel.liveEraserData.observe(viewLifecycleOwner) { +// canvasView.setEraser(it) +// } +// /** +// * 清除 +// */ +// viewModel.liveClearData.observe(viewLifecycleOwner) { +// canvasView.removeAllPaint() +// } +// /** +// * 回退上一笔 +// */ +// viewModel.liveBackData.observe(viewLifecycleOwner) { +// canvasView.back() +// } +// /** +// * 撤销回退 +// */ +// viewModel.liveForward.observe(viewLifecycleOwner) { +// canvasView.forward() +// } +// + + } + + + /** + * 将数据转化并绘制在画板上 + * + * @param value + */ + fun setDrawPathList(value: MutableList) { + if (value != null && value.isNotEmpty()) { + canvasView.setDrawPathList(value) + } + } + + + /** + * 设置草图画笔线型 + */ + fun setStyle(style: CanvasStyle) { + mStyle = style + canvasView.setStyle(style) + } + + /** + * 设置画笔颜色 + */ + fun setPaintColor(color: Int) { + mColor = color + canvasView.setPaintColor(mColor) + } + + /** + * 设置画笔粗细 + */ + fun setPaintWidth(width: Int) { + this.width = width + canvasView.setPaintWidth(width) + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt new file mode 100644 index 00000000..c8797d46 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasView.kt @@ -0,0 +1,1801 @@ +package com.navinfo.omqs.ui.fragment.note + +import android.content.Context +import android.graphics.* +import android.media.ThumbnailUtils +import android.util.AttributeSet +import android.util.Log +import android.view.MotionEvent +import android.view.View +import com.navinfo.collect.library.utils.GeometryTools +import com.navinfo.omqs.R +import com.navinfo.omqs.ui.other.BaseToast +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.sin + +/** + * @author zhjch + * @version V1.0 + * @ClassName: CanvasView + * @Date 2016/5/10 + * @Description: ${TODO}(画板) + */ +class CanvasView @JvmOverloads constructor( + private val mContext: Context, + attrs: AttributeSet? = null, + defStyle: Int = 0 +) : View( + mContext, attrs, defStyle +) { + /** + * 画笔类型 + */ + enum class CanvasStyle { + FREE_LINE, //自由线 + STRAIGHT_LINE, //直线 + RECT_LINE, //框 + RAILWAY_LINE, // 铁路线 + GREENLAND_LINE, //绿地线 + WATER_LINE, //水系线 + BUILDING_LINE, //建筑物 + POLY_LINE, //折线 + CIRCULAR_POINT, //圆点 + ELLIPSE_LINE, //椭圆 + PARKING_LINE + //停车场 + } + + /** + * 画布 + */ + private var mCanvas: Canvas? = null + + /** + * 控制线型 + */ + private var mPath: Path? = null + + /** + * 画布背景 + */ + private var mBitmapPaint: Paint? = null + + /** + * 画布前景 + */ + private var mBitmap: Bitmap? = null + + /** + * 普通画笔 + */ + private val mPaint: Paint? + + /** + * 圆形画笔 + */ + private val mPaintCircular: Paint + + /** + * 铁路画笔1 底色 + */ + private val mPaintRailway1: Paint + + /** + * 铁路画笔2 前景色 + */ + private val mPaintRailway2: Paint + + /** + * 水系画笔1 边框 + */ + private val mPaintWater: Paint + + /** + * 水系画笔2 填充 + */ + private val mPaintWater1: Paint + + /** + * 绿地画笔1 边框 + */ + private val mPaintGreenland: Paint + + /** + * 绿地画笔2 填充 + */ + private val mPaintGreenland1: Paint + + /** + * 停车场1 边框 + */ + private val mPaintParking: Paint + + /** + * 停车场 填充 + */ + private val mPaintParking1: Paint + + /** + * 准星图片 + */ + private val mStarBitmap: Bitmap + + /** + * 铁路栅格线样式 + */ + private val pathEffect1 = DashPathEffect(floatArrayOf(16f, 16f), 4f) + // private DashPathEffect pathEffect2 = new DashPathEffect(new float[] { 12,12 }, 12); + /** + * 需不需要记录画笔轨迹点 + */ + private var isSavePoint: Boolean + + /** + * 是否在move 中 + */ + private var isMove = false + + /** + * 是否打开橡皮擦 + */ + private var isEraser = false + + /** + * 绘制轨迹路径 + */ + private var mDrawPath: DrawPath? = null + + /** + * 画笔宽度 + */ + private var mPaintWidth = 5 + + /** + * 画笔颜色 + */ + private var mPaintColor = 0xFF5052 + + /** + * 圆形图案半径 + */ + private val mCircularP = 150 + /** + * 获取当前画笔样式 + * + * @return + */ + /** + * 线型 + */ + var canvasStyle = CanvasStyle.FREE_LINE + private set + + /** + * 当前画布上的每一笔 + */ + private var mCurrentPaths: MutableList = mutableListOf() + + /** + * 被撤销的每一笔 + */ + private val mDeletePaths: MutableList? + + /** + * 画布 宽度 + */ + private var viewWidth = 10 + + /** + * 画布 高度 + */ + private var viewHeight = 10 + + /** + * 画笔第一笔点下去的位置 + */ + private var mDownX = 0f + private var mDownY = 0f + + /** + * 画笔移动的位置 + */ + private var mMoveX = 0f + private var mMoveY = 0f + + /** + * 圆点图案移动矩阵 + */ + private val mMatrixCircular = Matrix() + private var mListener: OnCanvasChangeListener? = null + + /** + * 用来回馈上层页面 + */ + interface OnCanvasChangeListener { + /** + * 画布上有绘制操作 + */ + fun onDraw() + } + + fun setOnCanvasChangeListener(listener: OnCanvasChangeListener?) { + mListener = listener + } + + //路径对象 + class DrawPath( + startPoint: Point?, + var path: Path, + var width: Int, + var color: Int, + var style: CanvasStyle + ) { + var pointList: MutableList? + var isOver = true + + /** + * 外截矩形 + */ + var rect: Rect? = null + + /** + * @param startPoint 起点 + * @param path 路径 + * @param width 宽度 + * @param color 颜色 + * @param style 线型 + */ + init { + pointList = ArrayList() + if (startPoint != null) { + rect = Rect(startPoint.x, startPoint.y, startPoint.x + 1, startPoint.y + 1) + } + } + + /** + * 获取画笔 + * + * @param paint + * @return + */ + fun getPaint(paint: Paint?): Paint? { + paint!!.strokeWidth = width.toFloat() + paint.color = color + return paint + } + } + + /** + * 初始化画布 + */ + private fun initCanvas() { + //画布大小 + if (mBitmap != null) { + mBitmap!!.recycle() + } + mBitmap = Bitmap.createBitmap(viewWidth, viewHeight, Bitmap.Config.ARGB_8888) + mCanvas = Canvas(mBitmap!!) //所有mCanvas画的东西都被保存在了mBitmap中 + mPath = Path() + mBitmapPaint = Paint(Paint.DITHER_FLAG) + } + + /** + * 结束折线类型的操作 + */ + private fun setPolyLineOver() { + if (mCurrentPaths.size > 0) { + val path = mCurrentPaths[mCurrentPaths.size - 1] + if (path.style == CanvasStyle.RAILWAY_LINE + || path.style == CanvasStyle.POLY_LINE + ) { + //切换类型时,线少于2个点的不要 + if (path.pointList == null || path.pointList!!.size == 1) { + back() //只有一个点时会遗留一个起始圆点,先移除掉。然后清除 + mDeletePaths!!.clear() + if (path === mDrawPath) { + mDrawPath = null + // mDrawPath.pointList.clear(); +// mPath.reset(); + } + return + } + } else if (path.style == CanvasStyle.GREENLAND_LINE || path.style == CanvasStyle.WATER_LINE || path.style == CanvasStyle.PARKING_LINE) { + //切换类型时,面少于3个点的不要 + if (path.pointList == null || path.pointList!!.size < 4) { + mCurrentPaths.removeAt(mCurrentPaths.size - 1) + initCanvas() + } + invalidate() + return + } + path.isOver = true + } + } + + /** + * 切换线型 + * + * @param style + */ + fun setStyle(style: CanvasStyle) { + canvasStyle = style + setPolyLineOver() + } + + init { + mCurrentPaths = ArrayList() + mDeletePaths = ArrayList() + mPaint = Paint() + mPaint.isAntiAlias = true + mPaint.isDither = true + mPaint.color = mPaintColor + mPaint.style = Paint.Style.STROKE + mPaint.strokeJoin = Paint.Join.ROUND + mPaint.strokeCap = Paint.Cap.ROUND + mPaint.strokeWidth = mPaintWidth.toFloat() + mPaintRailway1 = Paint() + mPaintRailway1.isAntiAlias = true + mPaintRailway1.isDither = true + mPaintRailway1.color = -0xff8f40 + mPaintRailway1.style = Paint.Style.STROKE + mPaintRailway1.strokeWidth = 8f + mPaintRailway2 = Paint() + mPaintRailway2.isAntiAlias = true + mPaintRailway2.isDither = true + mPaintRailway2.color = Color.WHITE + mPaintRailway2.style = Paint.Style.STROKE + mPaintRailway2.strokeWidth = 4f + mPaintRailway2.pathEffect = pathEffect1 + mPaintWater = Paint() + mPaintWater.isAntiAlias = true + mPaintWater.isDither = true + mPaintWater.color = -0x543501 + mPaintWater.style = Paint.Style.FILL + mPaintWater.strokeWidth = 4f + mPaintWater1 = Paint() + mPaintWater1.isAntiAlias = true + mPaintWater1.isDither = true + mPaintWater1.color = -0xff4fb0 + mPaintWater1.style = Paint.Style.STROKE + mPaintWater1.strokeWidth = 4f + mPaintGreenland = Paint() + mPaintGreenland.isAntiAlias = true + mPaintGreenland.isDither = true + mPaintGreenland.color = -0x321c54 + mPaintGreenland.style = Paint.Style.FILL + mPaintGreenland.strokeWidth = 4f + mPaintGreenland1 = Paint() + mPaintGreenland1.isAntiAlias = true + mPaintGreenland1.isDither = true + mPaintGreenland1.color = -0xff4fb0 + mPaintGreenland1.style = Paint.Style.STROKE + mPaintGreenland1.strokeWidth = 4f + mPaintParking = Paint() + mPaintParking.isAntiAlias = true + mPaintParking.isDither = true + mPaintParking.color = -0x168 + mPaintParking.style = Paint.Style.FILL + mPaintParking.strokeWidth = 4f + mPaintParking1 = Paint() + mPaintParking1.isAntiAlias = true + mPaintParking1.isDither = true + mPaintParking1.color = -0x59595a + mPaintParking1.style = Paint.Style.STROKE + mPaintParking1.strokeWidth = 4f + mPaintCircular = Paint() + mPaintCircular.style = Paint.Style.FILL + mPaintCircular.color = mPaintColor + mStarBitmap = BitmapFactory.decodeResource(resources, R.drawable.home_map_center) + val a = mContext.obtainStyledAttributes(attrs, R.styleable.CanvasView) + isSavePoint = a.getBoolean(R.styleable.CanvasView_isSavePoint, true) + a.recycle() + } + + /** + * 手指移动的处理 + * + * @param x 坐标 + * @param y 坐标 + */ + private fun touch_move_line(x: Float, y: Float) { + if (x == mDownX && y == mDownY) return + val dx = abs(x - mDownX) + val dy = abs(y - mDownY) + if (dx >= TOUCH_TOLERANCE || dy >= TOUCH_TOLERANCE) { + //增加非空判断 + if (mPath != null) mPath!!.quadTo( + mDownX, + mDownY, + (x + mDownX) / 2, + (y + mDownY) / 2 + ) //源代码是这样写的,可是我没有弄明白,为什么要这样? + mDownX = x + mDownY = y + } + setBuilder(x.toInt(), y.toInt()) + } + + /** + * 多边形起点点下时的处理 + * + * @param x 坐标 + * @param y 坐标 + */ + private fun touch_start_poly(x: Int, y: Int) { + if (canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) { + if (mCurrentPaths.size > 0 && mCurrentPaths[0].isOver) { + BaseToast.makeText(mContext, "不允许存在多个!", BaseToast.LENGTH_SHORT).show() + return + } + if (mCurrentPaths.size > 0) { + val drawPath = mCurrentPaths[mCurrentPaths!!.size - 1] + if (drawPath.pointList!!.size > 3) { + if (!GeometryTools.isSimplePolygon(drawPath.pointList, Point(x, y))) { + BaseToast.makeText(mContext, "面不允许自相交图形!", BaseToast.LENGTH_SHORT).show() + return + } + } + } + } + if (mDeletePaths != null && mDeletePaths.size > 0) { + mDeletePaths.clear() + } + if (mCurrentPaths.size > 0) { + val drawPath = mCurrentPaths[mCurrentPaths.size - 1] + if (!drawPath.isOver) { + mDrawPath = drawPath + if (drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.PARKING_LINE) { + if (drawPath.pointList!!.size > 1) { + drawPath.pointList!!.removeAt(drawPath.pointList!!.size - 1) + } + setBuilder(x, y) + if (drawPath.pointList!!.size < 3) { + drawPath.path.lineTo(x.toFloat(), y.toFloat()) + } else { + initCanvas() + drawPath.path.reset() + for (i in drawPath.pointList!!.indices) { + if (i == 0) { + drawPath.path.moveTo( + drawPath.pointList!![0].x.toFloat(), + drawPath.pointList!![0].y.toFloat() + ) + } else { + drawPath.path.lineTo( + drawPath.pointList!![i].x.toFloat(), + drawPath.pointList!![i].y.toFloat() + ) + } + } + drawPath.path.lineTo( + drawPath.pointList!![0].x.toFloat(), + drawPath.pointList!![0].y.toFloat() + ) + } + drawPath.pointList!!.add(drawPath.pointList!![0]) + } else { + drawPath.path.lineTo(x.toFloat(), y.toFloat()) + setBuilder(x, y) + } + if (drawPath.style == CanvasStyle.POLY_LINE) { + mCanvas!!.drawPath(drawPath.path, drawPath.getPaint(mPaint)!!) + } else if (drawPath.style == CanvasStyle.RAILWAY_LINE) { + mCanvas!!.drawPath(drawPath.path, mPaintRailway1) + mCanvas!!.drawPath(drawPath.path, mPaintRailway2) + } else if (drawPath.style == CanvasStyle.GREENLAND_LINE) { + mCanvas!!.drawPath(drawPath.path, mPaintGreenland) + mCanvas!!.drawPath(drawPath.path, mPaintGreenland1) + } else if (drawPath.style == CanvasStyle.WATER_LINE) { + mCanvas!!.drawPath(drawPath.path, mPaintWater) + mCanvas!!.drawPath(drawPath.path, mPaintWater1) + } else if (drawPath.style == CanvasStyle.PARKING_LINE) { + mCanvas!!.drawPath(drawPath.path, mPaintParking) + mCanvas!!.drawPath(drawPath.path, mPaintParking1) + } + invalidate() + return + } + } + mPath = Path() + mDrawPath = DrawPath(Point(x, y), mPath!!, mPaintWidth, mPaintColor, canvasStyle) + mDrawPath!!.isOver = false + mPaintCircular.color = mPaintColor + mCanvas!!.drawCircle(x.toFloat(), y.toFloat(), 2f, mPaintCircular) + mPath!!.moveTo(x.toFloat(), y.toFloat()) + setBuilder(x, y) + if (canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.PARKING_LINE) { + setBuilder(x, y) + } + mCurrentPaths.add(mDrawPath!!) + if (mListener != null) { + mListener!!.onDraw() + } + invalidate() + return + } + + /** + * 手指按下的第一个点处理 + * + * @param x 坐标 + * @param y 坐标 + */ + private fun touch_start(x: Float, y: Float) { + mPath!!.reset() //清空path + mPath!!.moveTo(x, y) + mDownX = x + mDownY = y + if (canvasStyle != CanvasStyle.ELLIPSE_LINE) setBuilder(x.toInt(), y.toInt()) + if (mDeletePaths != null && mDeletePaths.size > 0) { + mDeletePaths.clear() + } + } + + /** + * 手指离开屏幕时的处理 + * + * @param x 坐标 + * @param y 坐标 + */ + private fun touch_up(x: Float, y: Float) { + try { + if (canvasStyle == CanvasStyle.POLY_LINE) { + val Y = y - mCircularP + mStarBitmap.height / 2 + mDrawPath!!.width = mPaintWidth + mDrawPath!!.color = mPaintColor + if (mDrawPath!!.pointList!!.size > 0) { + mDrawPath = mCurrentPaths[mCurrentPaths.size - 1] + mPath = mDrawPath!!.path + mPath!!.lineTo(x, Y) + mCanvas!!.drawPath(mPath!!, mPaint!!) + } else { + mCurrentPaths.add(mDrawPath!!) + if (mListener != null) { + mListener!!.onDraw() + } + mPath!!.moveTo(x, Y) + mPaintCircular.color = mPaintColor + mPaintCircular.strokeWidth = 4f + mCanvas!!.drawCircle(x, Y, 4f, mPaintCircular) + } + setBuilder(x.toInt(), Y.toInt()) + return + } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) { + val Y = y - mCircularP + mStarBitmap.height / 2 + setBuilder(x.toInt(), Y.toInt()) + mDrawPath!!.width = mPaintWidth + mDrawPath!!.color = mPaintColor + if (mDrawPath!!.pointList!!.size > 1) { + mDrawPath!!.isOver = true + mCurrentPaths.add(mDrawPath!!) + if (mListener != null) { + mListener!!.onDraw() + } + mPath!!.lineTo(x, Y) + mCanvas!!.drawPath(mPath!!, mPaint!!) + mPath = null + } else { + mPath!!.moveTo(x, Y) + mPaintCircular.color = mPaintColor + mPaintCircular.strokeWidth = 4f + mCanvas!!.drawCircle(x, Y, 4f, mPaintCircular) + } + return + } else if (canvasStyle == CanvasStyle.CIRCULAR_POINT) { + mPaintCircular.color = mPaintColor + val Y = y - mCircularP + mStarBitmap.height / 2 + mCanvas!!.drawCircle(x, Y, (mPaintWidth + 5).toFloat(), mPaintCircular) + mDrawPath!!.pointList!!.add(Point(x.toInt(), Y.toInt())) + mDrawPath!!.rect = Rect( + (x - mPaintWidth - 20).toInt(), + (Y - mPaintWidth - 20).toInt(), + (x + mPaintWidth + 20).toInt(), + (Y + mPaintWidth + 20).toInt() + ) + mCurrentPaths.add(mDrawPath!!) + if (mListener != null) { + mListener!!.onDraw() + } + } else if (canvasStyle == CanvasStyle.RECT_LINE) { + if (mDownX == x || mDownY == y) return + mPath!!.lineTo(x, mDownY) + setBuilder(x.toInt(), mDownY.toInt()) + mPath!!.lineTo(x, y) + setBuilder(x.toInt(), y.toInt()) + mPath!!.lineTo(mDownX, y) + setBuilder(mDownX.toInt(), y.toInt()) + mPath!!.lineTo(mDownX, mDownY) + setBuilder(mDownX.toInt(), mDownY.toInt()) + } else if (canvasStyle == CanvasStyle.ELLIPSE_LINE) { + if (mDownX == x || mDownY == y) { + return + } + val minX = if (x < mDownX) x else mDownX + val maxX = if (x > mDownX) x else mDownX + val minY = if (y < mDownY) y else mDownY + val maxY = if (y > mDownY) y else mDownY + mPath!!.moveTo(minX, minY) + mPath!!.addOval(RectF(minX, minY, maxX, maxY), Path.Direction.CW) + var a = 0.0 + val xR = ((maxX - minX) / 2).toDouble() + val yR = ((maxY - minY) / 2).toDouble() + var tempX = (xR * cos(a) + xR + minX).toInt() + val tempY = (yR * sin(a) + yR + minY).toInt() + val firstX = tempX + setBuilder(tempX, tempY) + var bLeft = false + var bRight = false + while (!bLeft || !bRight) { + a += 0.1 + val x1 = (xR * Math.cos(a) + xR + minX).toInt() + val y1 = (yR * Math.sin(a) + yR + minY).toInt() + if (!bLeft && x1 > tempX) { + bLeft = true + } + if (!bRight && bLeft && x1 <= tempX) { + bRight = true + setBuilder(firstX, tempY) + } else { + tempX = x1 + setBuilder(x1, y1) + } + } + } else { + if (!contains( + mDownX.toInt(), + mDownY.toInt(), + x.toInt(), + y.toInt(), + ChouXiBanJing + ) + ) { + mPath!!.lineTo(x, y) + setBuilder(x.toInt(), y.toInt()) + } + if (canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) { + val point = mDrawPath!!.pointList!![0] + mPath!!.lineTo(point.x.toFloat(), point.y.toFloat()) + setBuilder(point.x, point.y) + } + } + if (mDrawPath!!.pointList!!.size > 1) { + if (canvasStyle == CanvasStyle.RAILWAY_LINE) { + mCanvas!!.drawPath(mPath!!, mPaintRailway1) + mCanvas!!.drawPath(mPath!!, mPaintRailway2) + } else if (canvasStyle == CanvasStyle.GREENLAND_LINE) { + mCanvas!!.drawPath(mPath!!, mPaintGreenland) + mCanvas!!.drawPath(mPath!!, mPaintGreenland1) + } else if (canvasStyle == CanvasStyle.WATER_LINE) { + mCanvas!!.drawPath(mPath!!, mPaintWater) + mCanvas!!.drawPath(mPath!!, mPaintWater1) + } else if (canvasStyle == CanvasStyle.PARKING_LINE) { + mCanvas!!.drawPath(mPath!!, mPaintParking) + mCanvas!!.drawPath(mPath!!, mPaintParking1) + } else { + mCanvas!!.drawPath(mPath!!, mPaint!!) + } + mCurrentPaths.add(mDrawPath!!) + if (mListener != null) { + mListener!!.onDraw() + } + } + mPath = null + } catch (e: Exception) { + } + } + + /** + * 记录每一个点,并计算外截矩形 + * + * @param x + * @param y + */ + private fun setBuilder(x: Int, y: Int) { + if (!isSavePoint || mDrawPath == null) return + if (mDrawPath!!.pointList == null) mDrawPath!!.pointList = ArrayList() + if (mDrawPath!!.rect == null) { + mDrawPath!!.rect = Rect(x, y, x + 1, y + 1) + } + if (x < mDrawPath!!.rect!!.left) { + mDrawPath!!.rect!!.left = x + } else if (x > mDrawPath!!.rect!!.right) { + mDrawPath!!.rect!!.right = x + } + if (y < mDrawPath!!.rect!!.top) { + mDrawPath!!.rect!!.top = y + } else if (y > mDrawPath!!.rect!!.bottom) { + mDrawPath!!.rect!!.bottom = y + } + mDrawPath!!.pointList!!.add(Point(x, y)) + } + + /** + * 记录手指滑动中的坐标 + * + * @param x + * @param y + */ + private fun touch_move(x: Float, y: Float) { + mMoveX = x + mMoveY = y + } + + override fun onSizeChanged(w: Int, h: Int, oldw: Int, oldh: Int) { + super.onSizeChanged(w, h, oldw, oldh) + viewWidth = w + viewHeight = h + initCanvas() + if (mCurrentPaths.isNotEmpty()) { + for (dp in mCurrentPaths) { + if (dp.style == CanvasStyle.RAILWAY_LINE) { + mCanvas!!.drawPath(dp.path, mPaintRailway1) + mCanvas!!.drawPath(dp.path, mPaintRailway2) + } else if (dp.style == CanvasStyle.GREENLAND_LINE) { + mCanvas!!.drawPath(dp.path, mPaintGreenland) + mCanvas!!.drawPath(dp.path, mPaintGreenland1) + } else if (dp.style == CanvasStyle.WATER_LINE) { + mCanvas!!.drawPath(dp.path, mPaintWater) + mCanvas!!.drawPath(mPath!!, mPaintWater1) + } else if (dp.style == CanvasStyle.PARKING_LINE) { + mCanvas!!.drawPath(dp.path, mPaintParking) + mCanvas!!.drawPath(mPath!!, mPaintParking1) + } else { + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } + } + } + } + + /** + * 撤销上一步 + */ + fun back(): Boolean { + if (mCurrentPaths.size > 0) { + //调用初始化画布函数以清空画布 + initCanvas() + //将路径保存列表中的最后一个元素删除 ,并将其保存在路径删除列表中 + val drawPath = mCurrentPaths[mCurrentPaths.size - 1] + //如果是正在绘制的折线 + if ((drawPath.style == CanvasStyle.POLY_LINE + || drawPath.style == CanvasStyle.RAILWAY_LINE) + && !drawPath.isOver + ) { + val drawPathCopy: DrawPath? + if (mDeletePaths!!.size == 0 || mDeletePaths[mDeletePaths.size - 1]!!.style != drawPath.style || mDeletePaths[mDeletePaths.size - 1]!!.isOver) { + drawPathCopy = DrawPath( + null, + drawPath.path, + drawPath.width, + drawPath.color, + drawPath.style + ) + drawPathCopy.rect = drawPath.rect + drawPathCopy.isOver = false + mDeletePaths.add(drawPathCopy) + } else { + drawPathCopy = mDeletePaths[mDeletePaths.size - 1] + } + val point = drawPath.pointList!![drawPath.pointList!!.size - 1] + drawPathCopy!!.pointList!!.add(point) + if (drawPath.pointList!!.size == 1) { + mCurrentPaths.removeAt(mCurrentPaths.size - 1) + } else { + drawPath.pointList!!.remove(point) + } + } else if ((drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.PARKING_LINE) + && !drawPath.isOver + ) { + val drawPathCopy: DrawPath? + if (mDeletePaths!!.size == 0 || mDeletePaths[mDeletePaths.size - 1]!!.style != drawPath.style || mDeletePaths[mDeletePaths.size - 1]!!.isOver) { + drawPathCopy = DrawPath( + null, + drawPath.path, + drawPath.width, + drawPath.color, + drawPath.style + ) + drawPathCopy.rect = drawPath.rect + drawPathCopy.isOver = false + mDeletePaths.add(drawPathCopy) + } else { + drawPathCopy = mDeletePaths[mDeletePaths.size - 1] + } + val point = drawPath.pointList!![drawPath.pointList!!.size - 2] + drawPathCopy!!.pointList!!.add(point) + if (drawPath.pointList!!.size < 3) { + mCurrentPaths.removeAt(mCurrentPaths.size - 1) + } else { + drawPath.pointList!!.removeAt(drawPath.pointList!!.size - 1) + drawPath.pointList!!.remove(point) + drawPath.pointList!!.add(drawPath.pointList!![0]) + } + } else { + mDeletePaths!!.add(drawPath) + mCurrentPaths.removeAt(mCurrentPaths.size - 1) + } + if (mCurrentPaths.isNotEmpty()) { + //将路径保存列表中的路径重绘在画布上 + for (dp in mCurrentPaths) { + if (dp.style == CanvasStyle.POLY_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + val point = dp.pointList!![i] + mPaintCircular.color = dp.color + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } else if (dp.style == CanvasStyle.RAILWAY_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + val point = dp.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + mCanvas!!.drawPath(dp.path, mPaintRailway1) + mCanvas!!.drawPath(dp.path, mPaintRailway2) + } else if (dp.style == CanvasStyle.GREENLAND_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + val point = dp.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + mCanvas!!.drawPath(dp.path, mPaintGreenland) + mCanvas!!.drawPath(dp.path, mPaintGreenland1) + } else if (dp.style == CanvasStyle.WATER_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + val point = dp.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + mCanvas!!.drawPath(dp.path, mPaintWater) + mCanvas!!.drawPath(dp.path, mPaintWater1) + } else if (dp.style == CanvasStyle.PARKING_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + val point = dp.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + mCanvas!!.drawPath(dp.path, mPaintParking) + mCanvas!!.drawPath(dp.path, mPaintParking1) + } else if (dp.style == CanvasStyle.CIRCULAR_POINT) { + mPaintCircular.color = dp.color + mCanvas!!.drawCircle( + dp.pointList!![0].x.toFloat(), + dp.pointList!![0].y.toFloat(), + dp.width.toFloat(), + mPaintCircular + ) + } else { + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } + } + } + invalidate() // 刷新 + } + if (mCurrentPaths.size == 0) { + mDrawPath = null + } + return mCurrentPaths.size != 0 + } + + /** + * 恢复撤销的上一步 + */ + fun forward() { + if (mDeletePaths!!.size > 0) { + //将删除的路径列表中的最后一个,也就是最顶端路径取出(栈),并加入路径保存列表中 + val dp = mDeletePaths[mDeletePaths.size - 1] + if ((dp!!.style == CanvasStyle.POLY_LINE + || dp.style == CanvasStyle.RAILWAY_LINE) + && !dp.isOver + ) { + val dp2: DrawPath? + if (mCurrentPaths.size > 0 && mCurrentPaths[mCurrentPaths.size - 1].style == dp.style && !mCurrentPaths[mCurrentPaths.size - 1].isOver) { + dp2 = mCurrentPaths[mCurrentPaths.size - 1] + } else { + dp2 = DrawPath(null, dp.path, dp.width, dp.color, dp.style) + dp2.isOver = false + dp2.rect = dp.rect + mCurrentPaths.add(dp2) + if (mListener != null) { + mListener!!.onDraw() + } + } + val point = dp.pointList!![dp.pointList!!.size - 1] + dp2.pointList!!.add(point) + dp.pointList!!.remove(point) + } else if ((dp.style == CanvasStyle.WATER_LINE || dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.PARKING_LINE) + && !dp.isOver + ) { + val dp2: DrawPath? + if (mCurrentPaths.size > 0 && mCurrentPaths[mCurrentPaths.size - 1].style == dp.style && !mCurrentPaths[mCurrentPaths.size - 1].isOver) { + dp2 = mCurrentPaths[mCurrentPaths.size - 1] + } else { + dp2 = DrawPath(null, dp.path, dp.width, dp.color, dp.style) + dp2.isOver = false + dp2.rect = dp.rect + mCurrentPaths.add(dp2) + if (mListener != null) { + mListener!!.onDraw() + } + } + val point = dp.pointList!![dp.pointList!!.size - 1] + if (dp2.pointList!!.size > 1) { + dp2.pointList!!.removeAt(dp2.pointList!!.size - 1) + } + dp2.pointList!!.add(point) + dp2.pointList!!.add(dp2.pointList!![0]) + dp.pointList!!.remove(point) + } else { + mCurrentPaths.add(dp) + if (mListener != null) { + mListener!!.onDraw() + } + } + //将取出的路径重绘在画布上 + if (dp.style == CanvasStyle.POLY_LINE) { + val dp2 = mCurrentPaths[mCurrentPaths.size - 1] + dp2.path.reset() + if (dp2.pointList != null && dp2.pointList!!.size > 0) { + for (i in dp2.pointList!!.indices) { + val point = dp2.pointList!![i] + mPaintCircular.color = dp2.color + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp2.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp2.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + } + mCanvas!!.drawPath(dp2.path, dp2.getPaint(mPaint)!!) + } else if (dp.style == CanvasStyle.RAILWAY_LINE) { + val dp2 = mCurrentPaths[mCurrentPaths.size - 1] + dp2.path.reset() + if (dp2.pointList != null && dp2.pointList!!.size > 0) { + for (i in dp2.pointList!!.indices) { + val point = dp2.pointList!![i] + if (i == 0) { + dp2.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp2.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + } + mCanvas!!.drawPath(dp2.path, mPaintRailway1) + mCanvas!!.drawPath(dp2.path, mPaintRailway2) + } else if (dp.style == CanvasStyle.GREENLAND_LINE) { + val dp2 = mCurrentPaths[mCurrentPaths.size - 1] + initCanvas() + dp2.path.reset() + if (dp2.pointList != null && dp2.pointList!!.size > 0) { + for (i in dp2.pointList!!.indices) { + val point = dp2.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp2.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp2.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + } + mCanvas!!.drawPath(dp2.path, mPaintGreenland) + mCanvas!!.drawPath(dp2.path, mPaintGreenland1) + } else if (dp.style == CanvasStyle.WATER_LINE) { + val dp2 = mCurrentPaths[mCurrentPaths.size - 1] + initCanvas() + dp2.path.reset() + if (dp2.pointList != null && dp2.pointList!!.size > 0) { + for (i in dp2.pointList!!.indices) { + val point = dp2.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp2.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp2.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + } + mCanvas!!.drawPath(dp2.path, mPaintWater) + mCanvas!!.drawPath(dp2.path, mPaintWater1) + } else if (dp.style == CanvasStyle.PARKING_LINE) { + val dp2 = mCurrentPaths[mCurrentPaths.size - 1] + initCanvas() + dp2!!.path.reset() + if (dp2.pointList != null && dp2.pointList!!.size > 0) { + for (i in dp2.pointList!!.indices) { + val point = dp2.pointList!![i] + if (i == 0) { + mCanvas!!.drawCircle( + point.x.toFloat(), + point.y.toFloat(), + 2f, + mPaintCircular + ) + dp2.path.moveTo(point.x.toFloat(), point.y.toFloat()) + } else { + dp2.path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + } + } + mCanvas!!.drawPath(dp2.path, mPaintParking) + mCanvas!!.drawPath(dp2.path, mPaintParking1) + } else if (dp.style == CanvasStyle.CIRCULAR_POINT) { + mPaintCircular.color = dp.color + mCanvas!!.drawCircle( + dp.pointList!![0].x.toFloat(), + dp.pointList!![0].y.toFloat(), + dp.width.toFloat(), + mPaintCircular + ) + } else { + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } + //将该路径从删除的路径列表中去除 + if ((dp.style == CanvasStyle.POLY_LINE + || dp.style == CanvasStyle.RAILWAY_LINE) + && !dp.isOver + ) { + if (dp.pointList!!.size == 0) { + mDeletePaths.remove(dp) + } + } else if ((dp.style == CanvasStyle.WATER_LINE || dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.PARKING_LINE) + && !dp.isOver + ) { + if (dp.pointList!!.size == 0) { + mDeletePaths.remove(dp) + } + } else { + mDeletePaths.removeAt(mDeletePaths.size - 1) + } + invalidate() + } + } + + /* + * 清空的主要思想就是初始化画布 + * 将保存路径的两个List清空 + * */ + fun removeAllPaint() { + //调用初始化画布函数以清空画布 + initCanvas() + invalidate() //刷新 + mPath = null + mDrawPath = null + mCurrentPaths.clear() + mDeletePaths!!.clear() + } + + /** + * 设置画笔粗细 + * + * @param paintWidth + */ + fun setPaintWidth(paintWidth: Int) { + mPaintWidth = paintWidth + if (mPaint != null) { + mPaint.strokeWidth = mPaintWidth.toFloat() + } + } + + /** + * 设置画笔颜色 + * + * @param color + */ + fun setPaintColor(color: Int) { + mPaintColor = color + if (mPaint != null) { + mPaint.color = mPaintColor + } + } + + /** + * 将图形数据绘制在画布上 + * + * @param value + */ + fun setDrawPathList(value: MutableList) { + mPath = null + mCurrentPaths = value + if (mListener != null) { + mListener!!.onDraw() + } + mDeletePaths!!.clear() + if (mCanvas != null) { + if (mCurrentPaths.size > 0) { + for (dp in mCurrentPaths) { + if (dp.style == CanvasStyle.RAILWAY_LINE) { + dp.path.reset() + for (i in dp.pointList!!.indices) { + if (i == 0) { + dp.path.moveTo( + dp.pointList!![i].x.toFloat(), + dp.pointList!![i].y.toFloat() + ) + } else { + dp.path.lineTo( + dp.pointList!![i].x.toFloat(), + dp.pointList!![i].y.toFloat() + ) + } + } + mCanvas!!.drawPath(dp.path, mPaintRailway1) + mCanvas!!.drawPath(dp.path, mPaintRailway2) + } else if (dp.style == CanvasStyle.GREENLAND_LINE) { + mCanvas!!.drawPath(dp.path, mPaintGreenland) + mCanvas!!.drawPath(dp.path, mPaintGreenland1) + } else if (dp.style == CanvasStyle.WATER_LINE) { + mCanvas!!.drawPath(dp.path, mPaintWater) + mCanvas!!.drawPath(dp.path, mPaintWater1) + } else if (dp.style == CanvasStyle.PARKING_LINE) { + mCanvas!!.drawPath(dp.path, mPaintParking) + mCanvas!!.drawPath(dp.path, mPaintParking1) + } else if (dp.style == CanvasStyle.CIRCULAR_POINT) { + mPaintCircular.color = dp.color + mCanvas!!.drawCircle( + dp.pointList!![0].x.toFloat(), + dp.pointList!![0].y.toFloat(), + dp.width.toFloat(), + mPaintCircular + ) + } else if (dp.style == CanvasStyle.POLY_LINE) { + mPaintCircular.color = dp.color + dp.path.reset() + for (i in dp.pointList!!.indices) { + if (i == 0) { + mCanvas!!.drawCircle( + dp.pointList!![i].x.toFloat(), + dp.pointList!![i].y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo( + dp.pointList!![i].x.toFloat(), + dp.pointList!![i].y.toFloat() + ) + } else { + dp.path.lineTo( + dp.pointList!![i].x.toFloat(), + dp.pointList!![i].y.toFloat() + ) + } + } + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } else { + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } + canvasStyle = dp.style + } + } + mPaint!!.color = mPaintColor + } + invalidate() // 刷新 + } + + override fun onDraw(canvas: Canvas) { + super.onDraw(canvas) + canvas.drawBitmap(mBitmap!!, 0f, 0f, mBitmapPaint) //显示旧的画布 + if (mPath != null) { + // 实时的显示 + if (isMove) { + if (canvasStyle == CanvasStyle.CIRCULAR_POINT) { + mMatrixCircular.reset() + mMatrixCircular.postTranslate( + mMoveX - mStarBitmap.width / 2, + mMoveY - mCircularP + ) + canvas.drawBitmap(mStarBitmap, mMatrixCircular, null) + } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) { + if (mDrawPath != null && mDrawPath!!.pointList!!.size > 0) { + val x = mDrawPath!!.pointList!![0].x + val y = mDrawPath!!.pointList!![0].y + val Y2 = mMoveY - mCircularP + mStarBitmap.height / 2 + canvas.drawLine(x.toFloat(), y.toFloat(), mMoveX, Y2, mPaint!!) + } + mMatrixCircular.reset() + mMatrixCircular.postTranslate( + mMoveX - mStarBitmap.width / 2, + mMoveY - mCircularP + ) + canvas.drawBitmap(mStarBitmap, mMatrixCircular, null) + } else if (canvasStyle == CanvasStyle.POLY_LINE) { + if (mDrawPath != null && mDrawPath!!.pointList!!.size > 0) { + val x = mDrawPath!!.pointList!![mDrawPath!!.pointList!!.size - 1].x + val y = mDrawPath!!.pointList!![mDrawPath!!.pointList!!.size - 1].y + val Y2 = mMoveY - mCircularP + mStarBitmap.height / 2 + canvas.drawLine(x.toFloat(), y.toFloat(), mMoveX, Y2, mPaint!!) + } + mMatrixCircular.reset() + mMatrixCircular.postTranslate( + mMoveX - mStarBitmap.width / 2, + mMoveY - mCircularP + ) + canvas.drawBitmap(mStarBitmap, mMatrixCircular, null) + } else if (canvasStyle == CanvasStyle.RECT_LINE) { + if (mDownX > mMoveX && mDownY > mMoveY) { + canvas.drawRect(mMoveX, mMoveY, mDownX, mDownY, mPaint!!) + } else if (mDownX < mMoveX && mDownY > mMoveY) { + canvas.drawRect(mDownX, mMoveY, mMoveX, mDownY, mPaint!!) + } else if (mDownX > mMoveX && mDownY < mMoveY) { + canvas.drawRect(mMoveX, mDownY, mDownX, mMoveY, mPaint!!) + } else { + canvas.drawRect(mDownX, mDownY, mMoveX, mMoveY, mPaint!!) + } + } else if (canvasStyle == CanvasStyle.ELLIPSE_LINE) { + if (mDownX > mMoveX && mDownY > mMoveY) { + canvas.drawOval(RectF(mMoveX, mMoveY, mDownX, mDownY), mPaint!!) + } else if (mDownX < mMoveX && mDownY > mMoveY) { + canvas.drawOval(RectF(mDownX, mMoveY, mMoveX, mDownY), mPaint!!) + } else if (mDownX > mMoveX && mDownY < mMoveY) { + canvas.drawOval(RectF(mMoveX, mDownY, mDownX, mMoveY), mPaint!!) + } else { + canvas.drawOval(RectF(mDownX, mDownY, mMoveX, mMoveY), mPaint!!) + } + } else { + if (canvasStyle == CanvasStyle.RAILWAY_LINE) { + canvas.drawPath(mPath!!, mPaintRailway1) + canvas.drawPath(mPath!!, mPaintRailway2) + } else if (canvasStyle == CanvasStyle.GREENLAND_LINE) { + canvas.drawPath(mPath!!, mPaintGreenland1) + } else if (canvasStyle == CanvasStyle.WATER_LINE) { + canvas.drawPath(mPath!!, mPaintWater1) + } else if (canvasStyle == CanvasStyle.PARKING_LINE) { + canvas.drawPath(mPath!!, mPaintParking1) + } else { + canvas.drawPath(mPath!!, mPaint!!) + } + } + } else { + + +// if (mStyle == CanvasStyle.RAILWAY_LINE) { +// canvas.drawPath(mPath, mPaintRailway1); +// canvas.drawPath(mPath, mPaintRailway2); +// } else { +// canvas.drawPath(mPath, mPaint); +// } + } + } + } + + override fun onTouchEvent(event: MotionEvent): Boolean { + val x = event.x + val y = event.y + when (event.action) { + MotionEvent.ACTION_DOWN -> if (isEraser) { + clearLine(x.toInt(), y.toInt()) + } else { +// if (mCurrentPaths != null && mCurrentPaths.size() > 0) { +// for (DrawPath dp : mCurrentPaths) { +// if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) { +// return true; +// } +// } +// } + isMove = false + if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) { + touch_start_poly(x.toInt(), y.toInt()) + return true + } else if (canvasStyle == CanvasStyle.CIRCULAR_POINT) { + mPath = Path() + mDrawPath = DrawPath(null, mPath!!, mPaintWidth + 5, mPaintColor, canvasStyle) + return true + } else if (canvasStyle == CanvasStyle.POLY_LINE) { + if (mDeletePaths != null && mDeletePaths.size > 0) { + mDeletePaths.clear() + } + if (mDrawPath != null && mDrawPath!!.style == CanvasStyle.POLY_LINE && !mDrawPath!!.isOver) { + return true + } + mPath = Path() + mDrawPath = DrawPath( + Point(x.toInt(), y.toInt()), + mPath!!, + mPaintWidth, + mPaintColor, + canvasStyle + ) + mDrawPath!!.isOver = false + return true + } else if (canvasStyle == CanvasStyle.STRAIGHT_LINE) { + if (mDeletePaths != null && mDeletePaths.size > 0) { + mDeletePaths.clear() + } + if (mDrawPath != null && mDrawPath!!.style == CanvasStyle.STRAIGHT_LINE && !mDrawPath!!.isOver) { + return true + } + mPath = Path() + mDrawPath = DrawPath( + Point(x.toInt(), y.toInt()), + mPath!!, + mPaintWidth, + mPaintColor, + canvasStyle + ) + mDrawPath!!.isOver = false + return true + } + mPath = Path() + mDrawPath = DrawPath( + Point(x.toInt(), y.toInt()), + mPath!!, + mPaintWidth, + mPaintColor, + canvasStyle + ) + touch_start(x, y) + } + MotionEvent.ACTION_MOVE -> { + if (isEraser) { + return true + } + if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) return true + +// if (mCurrentPaths != null && mCurrentPaths.size() > 0) { +// for (DrawPath dp : mCurrentPaths) { +// if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) { +// return true; +// } +// } +// } + isMove = true + if (x == mDownX && y == mDownY) return false + if (canvasStyle == CanvasStyle.POLY_LINE || canvasStyle == CanvasStyle.STRAIGHT_LINE || canvasStyle == CanvasStyle.RECT_LINE || canvasStyle == CanvasStyle.ELLIPSE_LINE || canvasStyle == CanvasStyle.CIRCULAR_POINT) { + touch_move(x, y) + } else { + if (contains( + mDownX.toInt(), + mDownY.toInt(), + x.toInt(), + y.toInt(), + ChouXiBanJing + ) + ) { + return true + } + touch_move_line(x, y) + } + invalidate() + } + MotionEvent.ACTION_UP -> { + if (isEraser) { + return true + } + if (canvasStyle == CanvasStyle.RAILWAY_LINE || canvasStyle == CanvasStyle.GREENLAND_LINE || canvasStyle == CanvasStyle.WATER_LINE || canvasStyle == CanvasStyle.PARKING_LINE) return true + // if (mCurrentPaths != null && mCurrentPaths.size() > 0) { +// for (DrawPath dp : mCurrentPaths) { +// if (dp.style == CanvasStyle.GREENLAND_LINE || dp.style == CanvasStyle.WATER_LINE) { +// return true; +// } +// } +// } + touch_up(x, y) + invalidate() + isMove = false + } + } + return true + } + + /** + * 橡皮擦捕捉清除 + * + * @param x + * @param y + */ + private fun clearLine(x: Int, y: Int) { + if (mCurrentPaths.size == 0) return + val rect = Rect(x - EraserWight, y - EraserWight, x + EraserWight, y + EraserWight) + for (i in mCurrentPaths.size - 1 downTo -1 + 1) { + val path = mCurrentPaths[i] + if (path.rect != null && path.rect!!.intersects( + rect.left, + rect.top, + rect.right, + rect.bottom + ) + ) { + if (LineIntersectRect(path.pointList, rect)) { + initCanvas() + mDeletePaths!!.add(path) + mCurrentPaths.remove(path) + for (dp in mCurrentPaths) { + if (dp.style == CanvasStyle.RAILWAY_LINE) { + dp.path.reset() + if (dp.pointList != null && dp.pointList!!.size > 0) { + for (k in dp.pointList!!.indices) { + if (k == 0) { + dp.path.moveTo( + dp.pointList!![k].x.toFloat(), + dp.pointList!![k].y.toFloat() + ) + } else { + dp.path.lineTo( + dp.pointList!![k].x.toFloat(), + dp.pointList!![k].y.toFloat() + ) + } + } + } + mCanvas!!.drawPath(dp.path, mPaintRailway1) + mCanvas!!.drawPath(dp.path, mPaintRailway2) + } else if (dp.style == CanvasStyle.GREENLAND_LINE) { + mCanvas!!.drawPath(dp.path, mPaintGreenland) + mCanvas!!.drawPath(dp.path, mPaintGreenland1) + } else if (dp.style == CanvasStyle.WATER_LINE) { + mCanvas!!.drawPath(dp.path, mPaintWater) + mCanvas!!.drawPath(dp.path, mPaintWater1) + } else if (dp.style == CanvasStyle.PARKING_LINE) { + mCanvas!!.drawPath(dp.path, mPaintParking) + mCanvas!!.drawPath(dp.path, mPaintParking1) + } else if (dp.style == CanvasStyle.CIRCULAR_POINT) { + mPaintCircular.color = dp.color + mCanvas!!.drawCircle( + dp.pointList!![0].x.toFloat(), + dp.pointList!![0].y.toFloat(), + dp.width.toFloat(), + mPaintCircular + ) + } else if (dp.style == CanvasStyle.POLY_LINE) { + mPaintCircular.color = dp.color + dp.path.reset() + if (dp.pointList != null && dp.pointList!!.size > 0) { + for (k in dp.pointList!!.indices) { + if (k == 0) { + mCanvas!!.drawCircle( + dp.pointList!![k].x.toFloat(), + dp.pointList!![k].y.toFloat(), + 2f, + mPaintCircular + ) + dp.path.moveTo( + dp.pointList!![k].x.toFloat(), + dp.pointList!![k].y.toFloat() + ) + } else { + dp.path.lineTo( + dp.pointList!![k].x.toFloat(), + dp.pointList!![k].y.toFloat() + ) + } + } + } + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } else { + mCanvas!!.drawPath(dp.path, dp.getPaint(mPaint)!!) + } + } + invalidate() // 刷新 + return + } + } + } + invalidate() + } + + /** + * 整条线段是否穿过矩形 + * + * @param pointList + * @param rect + * @return + */ + private fun LineIntersectRect(pointList: List?, rect: Rect?): Boolean { + if (pointList != null && pointList.size > 1 && rect != null && !rect.isEmpty) { + for (i in 0 until pointList.size - 1) { + if (CheckRectLine(pointList[i], pointList[i + 1], rect)) return true + } + } else if (pointList != null && pointList.size == 1) { + val point = pointList[0] + if (point.x >= rect!!.left && point.x <= rect.right && point.y >= rect.top && point.y <= rect.bottom) { + return true + } + } + return false + } + + /** + * 获取画布上的图形数据 + * + * @return + */ + val paths: List? + get() { + setPolyLineOver() + if (mCurrentPaths.size > 0) { + var i = 0 + while (i < mCurrentPaths.size) { + val drawPath = mCurrentPaths[i] + if (drawPath.style == CanvasStyle.POLY_LINE + || drawPath.style == CanvasStyle.RAILWAY_LINE + ) { + if (drawPath.pointList!!.size < 2) { + mCurrentPaths.remove(drawPath) + i-- + } + } else if (drawPath.style == CanvasStyle.WATER_LINE || drawPath.style == CanvasStyle.GREENLAND_LINE || drawPath.style == CanvasStyle.PARKING_LINE) { + if (drawPath.pointList!!.size < 4) { + mCurrentPaths.remove(drawPath) + i-- + } + } + i++ + } + } + return mCurrentPaths + } + + /** + * 得到缩略图 + * + * @return + */ + val thumbnail: Bitmap? + get() { + if (mCurrentPaths == null || mCurrentPaths!!.size == 0) { + return null + } + val whiteBgBitmap = Bitmap.createBitmap( + mBitmap!!.width, + mBitmap!!.height, + Bitmap.Config.ARGB_8888 + ) + val canvas = Canvas(whiteBgBitmap) + canvas.drawColor(Color.WHITE) + canvas.drawBitmap(mBitmap!!, 0f, 0f, null) + return ThumbnailUtils.extractThumbnail(whiteBgBitmap, 100, 100) + } + + /** + * 设置是否保存中间点信息 + * + * @param b + */ + fun setIsSavePoint(b: Boolean) { + isSavePoint = b + } + + /** + * 设置橡皮擦按钮是否开启或关闭 + * + * @param b + */ + fun setEraser(b: Boolean) { + isEraser = b + if (b) { + setPolyLineOver() + } + } + + /** + * 判断线与垂直线相交 + * + * @param startX + * @param startY + * @param endX + * @param endY + * @param x0 + * @param y1 + * @param y2 + * @return + */ + private fun CheckRectLineV( + startX: Float, + startY: Float, + endX: Float, + endY: Float, + x0: Float, + y1: Float, + y2: Float + ): Boolean { + if (x0 < startX && x0 < endX) return false + if (x0 > startX && x0 > endX) return false + if (startX == endX) { + return if (x0 == startX) { + if (startY < y1 && endY < y1) return false + !(startY > y2 && endY > y2) + } else { + false + } + } + val y = (endY - startY) * (x0 - startX) / (endX - startX) + startY + return y in y1..y2 + } + + /** + * 判断线与水平线相交 + * + * @param startX + * @param startY + * @param endX + * @param endY + * @param y0 + * @param x1 + * @param x2 + * @return + */ + private fun CheckRectLineH( + startX: Float, + startY: Float, + endX: Float, + endY: Float, + y0: Float, + x1: Float, + x2: Float + ): Boolean { + //直线在点的上方 + if (y0 < startY && y0 < endY) return false + //直线在点的下方 + if (y0 > startY && y0 > endY) return false + //水平直线 + if (startY == endY) { + //水平直线与点处于同一水平。 + return if (y0 == startY) { + //直线在点的左边 + if (startX < x1 && endX < x1) return false + //直线在x2垂直线右边 + !(startX > x2 && endX > x2) + //直线的部分或者全部处于点与x2垂直线之间 + } else //水平直线与点不处于同一水平。 + { + false + } + } + //斜线 + val x = (endX - startX) * (y0 - startY) / (endY - startY) + startX + return x in x1..x2 + } + + /** + * 直线是否穿过矩形 + * + * @param start + * @param end + * @param rect + * @return + */ + private fun CheckRectLine(start: Point, end: Point, rect: Rect): Boolean { + var result = false + if (rect.contains(start.x, start.y) || rect.contains(end.x, end.y)) result = true else { + if (CheckRectLineH( + start.x.toFloat(), + start.y.toFloat(), + end.x.toFloat(), + end.y.toFloat(), + rect.top.toFloat(), + rect.left.toFloat(), + rect.right.toFloat() + ) + ) return true + if (CheckRectLineH( + start.x.toFloat(), + start.y.toFloat(), + end.x.toFloat(), + end.y.toFloat(), + rect.bottom.toFloat(), + rect.left.toFloat(), + rect.right.toFloat() + ) + ) return true + if (CheckRectLineV( + start.x.toFloat(), + start.y.toFloat(), + end.x.toFloat(), + end.y.toFloat(), + rect.left.toFloat(), + rect.top.toFloat(), + rect.bottom.toFloat() + ) + ) return true + if (CheckRectLineV( + start.x.toFloat(), + start.y.toFloat(), + end.x.toFloat(), + end.y.toFloat(), + rect.right.toFloat(), + rect.top.toFloat(), + rect.bottom.toFloat() + ) + ) return true + } + return result + } + + /** + * 两个矩形是否包含 + * + * @param x1 + * @param y1 + * @param x2 + * @param y2 + * @param dis + * @return + */ + private fun contains(x1: Int, y1: Int, x2: Int, y2: Int, dis: Int): Boolean { + val rect = Rect(x1 - dis, y1 - dis, x1 + dis, y1 + dis) + return rect.contains(x2, y2) + } + + companion object { + /* + 橡皮擦捕捉半径 + */ + private const val EraserWight = 12 + + /** + * 线点 抽稀半径 + */ + private const val ChouXiBanJing = 12 //抽稀半径 + + /** + * 手指滑动的距离,两点之间像素值少于4 的点不要 + */ + private const val TOUCH_TOLERANCE = 4f + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt new file mode 100644 index 00000000..fc0e1805 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/CanvasViewHelper.kt @@ -0,0 +1,424 @@ +package com.navinfo.omqs.ui.fragment.note + +import android.graphics.Path +import android.graphics.Point +import android.graphics.Rect +import android.graphics.RectF +import android.text.TextUtils +import com.navinfo.collect.library.data.entity.NoteBean +import com.navinfo.collect.library.data.entity.SketchAttachContent +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.utils.GeometryTools +import com.navinfo.omqs.ui.fragment.note.CanvasView.CanvasStyle +import io.realm.RealmList +import org.locationtech.jts.geom.Coordinate +import org.oscim.backend.canvas.Color +import org.oscim.core.GeoPoint +import java.util.UUID +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.sin + +/** + * @author zhjch + * @version V1.0 + * @ClassName: CanvasViewHelper + * @Date 2016/5/16 + * @Description: ${TODO}(用一句话描述该文件做什么) + */ +object CanvasViewHelper { + private const val mD2I = 3600000 + fun createNoteBean( + controller: NIMapController, + mCurrentPaths: List, + ): NoteBean { + val noteBean = NoteBean(UUID.randomUUID().toString()) + if (mCurrentPaths.isNotEmpty()) { + val list: RealmList = RealmList() + noteBean.list = list + for (index in mCurrentPaths.indices) { + val dp: CanvasView.DrawPath = mCurrentPaths[index] + val geo = SketchAttachContent(UUID.randomUUID().toString()) + val pointList = dp.pointList ?: continue + if (dp.style === CanvasStyle.GREENLAND_LINE || dp.style === CanvasStyle.WATER_LINE || dp.style === CanvasStyle.PARKING_LINE) { + val geoPointList = mutableListOf() + for (i in pointList.indices) { + val point = pointList[i] + val geoPoint: GeoPoint = controller.viewportHandler.fromScreenPoint( + point + ) + geoPointList.add(geoPoint) + if (index == 0 && i == 0) { + noteBean.guideGeometry = + GeometryTools.createGeometry(geoPoint).toText() + } + } + geo.style = createLineStyle(dp.style, dp.width, dp.color) + geo.geometry = GeometryTools.createPolygon(geoPointList).toText() + } else if (dp.style === CanvasStyle.CIRCULAR_POINT) { + val point = pointList[0] + val geoPoint: GeoPoint = controller.viewportHandler.fromScreenPoint(point) + geo.style = createLineStyle(dp.style, dp.width, dp.color) + geo.geometry = GeometryTools.createGeometry(geoPoint).toText() + noteBean.guideGeometry = geo.geometry + } else if (dp.style === CanvasStyle.ELLIPSE_LINE) { + dp.rect?.let { + val pointLT = Point(it.left, it.top) + val pointRB = Point(it.right, it.bottom) + val geoPointLT: GeoPoint = + controller.viewportHandler.fromScreenPoint(pointLT) + val geoPointRB: GeoPoint = + controller.viewportHandler.fromScreenPoint(pointRB) + val minX: Double + val maxX: Double + val minY: Double + val maxY: Double + if (geoPointLT.longitude < geoPointRB.longitude) { + minX = (geoPointLT.longitude * mD2I) + maxX = (geoPointRB.longitude * mD2I) + } else { + minX = (geoPointRB.longitude * mD2I) + maxX = (geoPointLT.longitude * mD2I) + } + if (geoPointLT.latitude < geoPointRB.latitude) { + minY = (geoPointLT.latitude * mD2I) + maxY = (geoPointRB.latitude * mD2I) + } else { + minY = (geoPointRB.latitude * mD2I) + maxY = (geoPointLT.latitude * mD2I) + } + val xR = (maxX - minX) / 2 + val yR = (maxY - minY) / 2 + var a = 0.0 + var tempX = xR * cos(a) + xR + minX + val tempY = yR * sin(a) + yR + minY + val firstX = tempX + val geoPointList = mutableListOf() + geoPointList.add(GeoPoint(tempX / mD2I, tempY / mD2I)) + var bLeft = false + var bRight = false + var zeng = 0.1 + if (controller.mMapView.mapLevel >= 20) { + zeng = 0.2 + } + while (!bLeft || !bRight) { + a += zeng + val x1 = (xR * cos(a) + xR + minX).toInt().toDouble() + val y1 = (yR * sin(a) + yR + minY).toInt().toDouble() + if (!bLeft && x1 > tempX) { + bLeft = true + } + if (!bRight && bLeft && x1 <= tempX) { + bRight = true + geoPointList.add( + GeoPoint( + firstX / mD2I, + tempY / mD2I + ) + ) + } else { + tempX = x1 + geoPointList.add(GeoPoint(x1 / mD2I, y1 / mD2I)) + } + } + if (index == 0) { + noteBean.guideGeometry = + GeometryTools.createGeometry(geoPointList[0]).toText() + } + geo.style = createLineStyle(dp.style, dp.width, dp.color) + geo.geometry = GeometryTools.createLineString(geoPointList).toText() + } + } else { + val geoPointList = mutableListOf() + for (i in pointList.indices) { + val point = pointList[i] + val geoPoint: GeoPoint = + controller.viewportHandler.fromScreenPoint(point) + geoPointList.add(geoPoint) + if (index == 0 && i == 0) { + noteBean.guideGeometry = + GeometryTools.createGeometry(geoPoint).toText() + } + } + geo.style = createLineStyle(dp.style, dp.width, dp.color) + geo.geometry = GeometryTools.createLineString(geoPointList).toText() + } + list.add(geo) + } + } + return noteBean + } + + fun createDrawPaths( + controller: NIMapController, + att: NoteBean + ): MutableList { + val contents: List = att.list + val drawPaths: MutableList = mutableListOf() + var width = 5 + var canvasStyle = CanvasStyle.FREE_LINE + var color = Color.BLACK + for (geo in contents) { + var max_x = 0 + var max_y = 0 + var min_x = 0 + var min_y = 0 + val style = geo.style + if (!TextUtils.isEmpty(style) && style.length > 3) { + try { + if (style.startsWith("4")) { + canvasStyle = CanvasStyle.RAILWAY_LINE + } else if (style.startsWith("5")) { + if (style.contains("cde3ac")) { + canvasStyle = CanvasStyle.GREENLAND_LINE + } else if (style.contains("abcaff")) { + canvasStyle = CanvasStyle.WATER_LINE + } else if (style.contains("fffe98")) { + canvasStyle = CanvasStyle.PARKING_LINE + } + } else { + val s = style.substring(0, 1) + if (TextUtils.equals(s, "2")) { + canvasStyle = CanvasStyle.STRAIGHT_LINE + } else if (TextUtils.equals(s, "3")) { + canvasStyle = CanvasStyle.RECT_LINE + } else if (TextUtils.equals(s, "6")) { + canvasStyle = CanvasStyle.POLY_LINE + } else if (TextUtils.equals(s, "7")) { + canvasStyle = CanvasStyle.ELLIPSE_LINE + } else if (TextUtils.equals(s, "9")) { + canvasStyle = CanvasStyle.CIRCULAR_POINT + } else if (TextUtils.equals(s, "1")) { + canvasStyle = CanvasStyle.FREE_LINE + } + width = style.substring(1, 3).toInt() + var colorStr = style.substring(3, style.length) + if (colorStr.length == 6) { + colorStr = "ff$colorStr" + } else if (colorStr.length == 8) { + } else { + colorStr = "ff000000" + } + color = colorStr.toLong(16).toInt() + } + } catch (e: Exception) { + e.printStackTrace() + } + val path = Path() + val pointList: MutableList = ArrayList() + if (canvasStyle === CanvasStyle.GREENLAND_LINE || canvasStyle === CanvasStyle.WATER_LINE || canvasStyle === CanvasStyle.PARKING_LINE) { +// val polygonGeometry: PolygonGeometry = geo.geo as PolygonGeometry +// if (polygonGeometry != null) { +// val xyz: Array> = polygonGeometry.getCoordinates() +// if (xyz != null && xyz.isNotEmpty() && xyz[0].size > 1) { +// var geoPoint: GeoPoint? = GeoPoint(xyz[0][0][0], xyz[0][0][1]) +// val movePoint: Point = .geoToScreen(geoPoint) +// max_x = movePoint.x +// max_y = movePoint.y +// min_x = movePoint.x +// min_y = movePoint.y +// path.reset() +// path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat()) +// pointList.add(Point(movePoint.x, movePoint.y)) +// for (i in 1 until xyz[0].size) { +// val x_y = xyz[0][i] +// if (x_y != null) { +// geoPoint = GeoPoint(x_y[0], x_y[1]) +// val point: Point = projection.geoToScreen(geoPoint) +// if (point.x > max_x) { +// max_x = point.x +// } +// if (point.x < min_x) { +// min_x = point.x +// } +// if (point.y > max_y) { +// max_y = point.y +// } +// if (point.y < min_y) { +// min_y = point.y +// } +// path.lineTo(point.x.toFloat(), point.y.toFloat()) +// pointList.add(point) +// } +// } +// path.close() +// } +// } +// val drawPath = +// CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle) +// val rect = Rect(min_x, min_y, max_x, max_y) +// drawPath.rect = rect +// drawPath.pointList = pointList +// drawPaths.add(drawPath) + } else if (canvasStyle === CanvasStyle.CIRCULAR_POINT) { +// val pointGeometry: PointGeometry = geo.geo as PointGeometry +// if (pointGeometry != null && pointGeometry.getCoordinates() != null) { +// val geoPoint: GeoPoint = GeoPoint( +// pointGeometry.getCoordinates().get(0), +// pointGeometry.getCoordinates().get(1) +// ) +// val movePoint: Point = projection.geoToScreen(geoPoint) +// pointList.add(movePoint) +// val drawPath = DrawPath(movePoint, path, width, color, canvasStyle) +// val rect = Rect( +// movePoint.x - width - 20, +// movePoint.y - width - 20, +// movePoint.x + width + 20, +// movePoint.y + width + 20 +// ) +// drawPath.rect = rect +// drawPath.pointList = pointList +// drawPaths.add(drawPath) +// } + } else if (canvasStyle === CanvasStyle.ELLIPSE_LINE) { +// val lineGeometry = GeometryTools.createGeometry(geo.geometry) +// if (lineGeometry != null) { +// val xys: Array = lineGeometry.coordinates +// if (xys != null && xys.size > 1) { +// var geoPoint: GeoPoint? = GeoPoint(xys[0].y, xys[0].x) +// val movePoint: Point = projection.geoToScreen(geoPoint) +// max_x = movePoint.x +// max_y = movePoint.y +// min_x = movePoint.x +// min_y = movePoint.y +// path.reset() +// path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat()) +// pointList.add(Point(movePoint.x, movePoint.y)) +// for (i in 1 until xys.size) { +// val x_y = xys[i] +// geoPoint = GeoPoint(x_y[0], x_y[1]) +// val point: Point = projection.geoToScreen(geoPoint) +// if (point.x > max_x) { +// max_x = point.x +// } +// if (point.x < min_x) { +// min_x = point.x +// } +// if (point.y > max_y) { +// max_y = point.y +// } +// if (point.y < min_y) { +// min_y = point.y +// } +// pointList.add(point) +// } +// path.addOval( +// RectF( +// min_x.toFloat(), +// min_y.toFloat(), +// max_x.toFloat(), +// max_y.toFloat() +// ), Path.Direction.CW +// ) +// } +// } +// val drawPath = +// CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle) +// val rect = Rect(min_x, min_y, max_x, max_y) +// drawPath.rect = rect +// drawPath.pointList = pointList +// drawPaths.add(drawPath) + } else { + val lineGeometry = GeometryTools.createGeometry(geo.geometry) + if (lineGeometry != null) { + val xys: Array = lineGeometry.coordinates + if (xys.size > 1) { + var geoPoint = GeoPoint(xys[0].y, xys[0].x) + val movePoint: Point = + controller.viewportHandler.toScreenPoint(geoPoint) + max_x = movePoint.x + max_y = movePoint.y + min_x = movePoint.x + min_y = movePoint.y + path.reset() + path.moveTo(movePoint.x.toFloat(), movePoint.y.toFloat()) + pointList.add(Point(movePoint.x, movePoint.y)) + for (i in 1 until xys.size) { + val x_y = xys[i] + geoPoint = GeoPoint(x_y.y, x_y.x) + val point: Point = + controller.viewportHandler.toScreenPoint(geoPoint) + if (point.x > max_x) { + max_x = point.x + } + if (point.x < min_x) { + min_x = point.x + } + if (point.y > max_y) { + max_y = point.y + } + if (point.y < min_y) { + min_y = point.y + } + if (canvasStyle === CanvasStyle.FREE_LINE) { + val dx = abs(point.x - movePoint.x).toFloat() + val dy = abs(point.y - movePoint.y).toFloat() + if (dx >= 4 || dy >= 4) { + path.quadTo( + movePoint.x.toFloat(), + movePoint.y.toFloat(), + ((point.x + movePoint.x) / 2).toFloat(), + ((point.y + movePoint.y) / 2).toFloat() + ) //源代码是这样写的,可是我没有弄明白,为什么要这样? + movePoint.x = point.x + movePoint.y = point.y + } + } else { + path.lineTo(point.x.toFloat(), point.y.toFloat()) + } + pointList.add(point) + } + } + } + val drawPath = + CanvasView.DrawPath(pointList[0], path, width, color, canvasStyle) + val rect = Rect(min_x, min_y, max_x, max_y) + drawPath.rect = rect + drawPath.pointList = pointList + drawPaths.add(drawPath) + } + } + } + return drawPaths + } + + private fun createLineStyle(canvasStyle: CanvasStyle, width: Int, color: Int): String { + val style = StringBuilder() + if (canvasStyle === CanvasStyle.RAILWAY_LINE) { + return "4060070c004ffffff16" + } else if (canvasStyle === CanvasStyle.GREENLAND_LINE) { + return "50200b050cde3ac" + } else if (canvasStyle === CanvasStyle.WATER_LINE) { + return "50200b050abcaff" + } else if (canvasStyle === CanvasStyle.PARKING_LINE) { + return "502a6a6a6fffe98" + } + if (canvasStyle === CanvasStyle.STRAIGHT_LINE) { + style.append("2") + } else if (canvasStyle === CanvasStyle.RECT_LINE) { + style.append("3") + } else if (canvasStyle === CanvasStyle.POLY_LINE) { + style.append("6") + } else if (canvasStyle === CanvasStyle.ELLIPSE_LINE) { + style.append("7") + } else if (canvasStyle === CanvasStyle.CIRCULAR_POINT) { + style.append("9") + } else { + style.append("1") + } + if (width < 10) { + style.append("0") + } + style.append(width.toString()) + try { + var colorString = Integer.toHexString(color).toString() + if (colorString.length == 8) { + colorString = TextUtils.substring(colorString, 2, 8) + } + style.append(colorString) + } catch (e: Exception) { + e.printStackTrace() + } + return style.toString() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt new file mode 100644 index 00000000..9d4cd8c2 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteFragment.kt @@ -0,0 +1,135 @@ +package com.navinfo.omqs.ui.fragment.note + +import android.os.Bundle +import android.text.Editable +import android.text.TextWatcher +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentNoteBinding +import com.navinfo.omqs.ui.dialog.FirstDialog +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels +import dagger.hilt.android.AndroidEntryPoint + +@AndroidEntryPoint +class NoteFragment : BaseFragment(), View.OnClickListener { + private var _binding: FragmentNoteBinding? = null + private val binding get() = _binding!! + + private val viewModel by shareViewModels("note") + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentNoteBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + binding.sketchEraser.setOnClickListener(this) + binding.sketchClear.setOnClickListener(this) + binding.sketchForward.setOnClickListener(this) + binding.sketchBack.setOnClickListener(this) + binding.noteBarSave.setOnClickListener(this) + binding.noteBarCancel.setOnClickListener(this) + binding.noteBarDelete.setOnClickListener(this) + binding.noteDescription.addTextChangedListener(object : TextWatcher { + override fun beforeTextChanged(s: CharSequence?, start: Int, count: Int, after: Int) { + } + + override fun onTextChanged(s: CharSequence?, start: Int, before: Int, count: Int) { + } + + override fun afterTextChanged(s: Editable?) { + viewModel.noteBeanDescription = s.toString() + } + }) + /** + * 数据操作结束 + */ + viewModel.liveDataFinish.observe(viewLifecycleOwner) { + if (it) + onBackPressed() + } + + /** + * 画布初始化完成 + */ + + viewModel.liveDataCanvasViewInitFinished.observe(viewLifecycleOwner) { + if (it) + arguments?.let { b -> + val id = b.getString("NoteId", "") + if (id.isNotEmpty()) { + viewModel.initData(id) + } + } + } + } + + + override fun onStart() { + super.onStart() + activity?.run { + findNavController( + R.id.main_activity_middle_fragment + ).navigate(R.id.CanvasFragment) + } + } + + override fun onStop() { + super.onStop() + activity?.run { + findNavController( + R.id.main_activity_middle_fragment + ).navigateUp() + } + } + + override fun onClick(v: View) { + when (v) { + binding.sketchEraser -> { + viewModel.onEraser() + binding.sketchEraser.isSelected = viewModel.isEraser + } + binding.sketchBack -> { + viewModel.onBack() + } + binding.sketchForward -> { + viewModel.onForward() + } + binding.sketchClear -> { + viewModel.onClear() + } + binding.noteBarSave -> { + viewModel.onSaveData() + } + binding.noteBarDelete -> { + viewModel.deleteData(requireContext()) + } + binding.noteBarCancel -> { + //返回按钮点击 + val mDialog = FirstDialog(context) + mDialog.setTitle("提示?") + mDialog.setMessage("是否退出,请确认!") + mDialog.setPositiveButton( + "确定" + ) { _, _ -> + mDialog.dismiss() + onBackPressed() + } + mDialog.setNegativeButton("取消", null) + mDialog.show() + } + } + } + + override fun onBackPressed(): Boolean { + findNavController().navigateUp() + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt new file mode 100644 index 00000000..d5fd927c --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/note/NoteViewModel.kt @@ -0,0 +1,171 @@ +package com.navinfo.omqs.ui.fragment.note + +import android.content.Context +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.navinfo.collect.library.data.entity.NoteBean +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.ui.dialog.FirstDialog +import dagger.hilt.android.lifecycle.HiltViewModel +import io.realm.Realm +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import javax.inject.Inject + +@HiltViewModel +class NoteViewModel @Inject constructor( + val mapController: NIMapController +) : ViewModel() { + + lateinit var canvasView: CanvasView + + + var mNoteBean: NoteBean? = null + + + var isEraser = false + + var noteBeanDescription = "" +// /** +// * 橡皮擦开关 +// */ +// val liveEraserData = MutableLiveData(false) +// +// /** +// * 清除按钮 +// */ +// val liveClearData = MutableLiveData(false) +// +// /** +// * 回退按钮 +// */ +// val liveBackData = MutableLiveData(false) +// +// /** +// * 撤销按钮 +// */ +// val liveForward = MutableLiveData(false) + + /** + * 处理结束关闭fragment + */ + val liveDataFinish = MutableLiveData(false) + + /** + * 通知页面画布初始化完成 + */ + val liveDataCanvasViewInitFinished = MutableLiveData(false) + + fun initCanvasView(canvasView: CanvasView) { + this.canvasView = canvasView + liveDataCanvasViewInitFinished.value = true + } + + + /** + * 通知橡皮擦开关 + */ + fun onEraser() { + isEraser = !isEraser + canvasView.setEraser(isEraser) +// liveEraserData.value = !liveEraserData.value!! + } + + /** + * 通知清除 + */ + fun onClear() { + canvasView.removeAllPaint() +// liveClearData.value = true + } + + /** + * 通知回退 + */ + fun onBack() { + canvasView.back() +// liveBackData.value = true + } + + /** + * 通知撤销回退 + */ + fun onForward() { + canvasView.forward() +// liveForward.value = true + } + + /** + * 保存数据 + */ + fun onSaveData() { + viewModelScope.launch(Dispatchers.IO) { + if (canvasView.paths != null && canvasView.paths!!.isNotEmpty()) { + var noteBean = + CanvasViewHelper.createNoteBean(mapController, canvasView.paths!!) + if (mNoteBean != null) { + noteBean.id = mNoteBean!!.id + noteBean.description = noteBeanDescription + } + mNoteBean = noteBean + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + it.copyToRealmOrUpdate(noteBean) + } + mapController.markerHandle.addOrUpdateNoteMark(mNoteBean!!) + liveDataFinish.postValue(true) + } + } + } + + /** + * 删除数据 + */ + fun deleteData(context: Context) { + if (mNoteBean == null) { + liveDataFinish.postValue(true) + return + } else { + val mDialog = FirstDialog(context) + mDialog.setTitle("提示?") + mDialog.setMessage("是否删除标签,请确认!") + mDialog.setPositiveButton( + "确定" + ) { dialog, _ -> + dialog.dismiss() + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val objects = it.where(NoteBean::class.java) + .equalTo("id", mNoteBean!!.id).findFirst() + objects?.deleteFromRealm() + } + mapController.markerHandle.removeNoteMark(mNoteBean!!) + liveDataFinish.postValue(true) + } + } + mDialog.setNegativeButton("取消", null) + mDialog.show() + } + } + + /** + * 初始化数据 + */ + fun initData(id: String) { + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { it -> + val objects = it.where(NoteBean::class.java) + .equalTo("id", id).findFirst() + mNoteBean = realm.copyFromRealm(objects) + mNoteBean?.let { bean -> + noteBeanDescription = bean.description + val list = CanvasViewHelper.createDrawPaths(mapController, bean) + canvasView.setDrawPathList(list) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt index ffc5e1f1..a2a7e4ad 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterFragment.kt @@ -15,14 +15,18 @@ import com.blankj.utilcode.util.UriUtils import com.github.k1rakishou.fsaf.FileChooser import com.github.k1rakishou.fsaf.callback.FSAFActivityCallbacks import com.github.k1rakishou.fsaf.callback.FileChooserCallback +import com.navinfo.collect.library.data.entity.TaskBean import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.Constant import com.navinfo.omqs.R import com.navinfo.omqs.databinding.FragmentPersonalCenterBinding import com.navinfo.omqs.db.ImportOMDBHelper import com.navinfo.omqs.hilt.ImportOMDBHiltFactory import com.navinfo.omqs.tools.CoroutineUtils +import com.navinfo.omqs.ui.activity.map.MainActivity import com.navinfo.omqs.ui.fragment.BaseFragment import com.navinfo.omqs.ui.activity.scan.QrCodeActivity +import com.navinfo.omqs.ui.fragment.console.ConsoleFragment import com.permissionx.guolindev.PermissionX import dagger.hilt.android.AndroidEntryPoint import org.oscim.core.GeoPoint @@ -32,7 +36,7 @@ import javax.inject.Inject * 个人中心 */ @AndroidEntryPoint -class PersonalCenterFragment(private var backListener: (() -> Unit?)? = null) : BaseFragment(), +class PersonalCenterFragment(private var indoorDataListener: ((Boolean) -> Unit?)? = null) : BaseFragment(), FSAFActivityCallbacks { private var _binding: FragmentPersonalCenterBinding? = null @@ -111,8 +115,15 @@ class PersonalCenterFragment(private var backListener: (() -> Unit?)? = null) : } }) } + R.id.personal_center_menu_open_auto_location -> { + Constant.AUTO_LOCATION = true + } + R.id.personal_center_menu_close_auto_location -> { + Constant.AUTO_LOCATION = false + } R.id.personal_center_menu_test -> { viewModel.readRealmData() + //116.25017070328308 40.061730653134696 // 定位到指定位置 niMapController.mMapView.vtmMap.animator() .animateTo(GeoPoint( 39.7991980627346,116.50936676873703 )) @@ -136,6 +147,9 @@ class PersonalCenterFragment(private var backListener: (() -> Unit?)? = null) : //跳转二维码扫描界面 checkPermission() } + R.id.personal_center_menu_scan_indoor_data -> { + indoorDataListener?.invoke(true) + } } true } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt index aebf7569..04093b8f 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/personalcenter/PersonalCenterViewModel.kt @@ -1,8 +1,9 @@ package com.navinfo.omqs.ui.fragment.personalcenter import android.net.Uri +import android.os.Build import android.util.Log -import androidx.appcompat.app.AppCompatActivity +import androidx.annotation.RequiresApi import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope @@ -16,17 +17,13 @@ import com.navinfo.omqs.bean.ScRootCauseAnalysisBean import com.navinfo.omqs.db.ImportOMDBHelper import com.navinfo.omqs.db.RealmOperateHelper import com.navinfo.omqs.db.RoomAppDatabase -import com.navinfo.omqs.tools.MetadataUtils import com.navinfo.omqs.tools.MetadataUtils.Companion.ScProblemTypeTitle import com.navinfo.omqs.tools.MetadataUtils.Companion.ScRootCauseAnalysisTitle import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import org.apache.commons.io.input.BOMInputStream import java.io.* -import java.nio.charset.Charset -import java.text.Normalizer import java.util.* import javax.inject.Inject @@ -43,6 +40,7 @@ class PersonalCenterViewModel @Inject constructor( /** * 导入OMDB数据 * */ + @RequiresApi(Build.VERSION_CODES.N) suspend fun obtainOMDBZipData(importOMDBHelper: ImportOMDBHelper) { Log.d("OMQSApplication", "开始生成数据") // Realm.getDefaultInstance().beginTransaction() @@ -164,11 +162,13 @@ class PersonalCenterViewModel @Inject constructor( /** * 导入OMDB数据 * */ - fun importOMDBData(importOMDBHelper: ImportOMDBHelper) { + fun importOMDBData(importOMDBHelper: ImportOMDBHelper,taskId:Int?=0) { viewModelScope.launch(Dispatchers.IO) { Log.d("OMQSApplication", "开始导入数据") - importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile).collect { - Log.d("importOMDBData", it) + if (taskId != null) { + importOMDBHelper.importOmdbZipFile(importOMDBHelper.omdbFile, taskId).collect { + Log.d("importOMDBData", it) + } } Log.d("OMQSApplication", "导入数据完成") } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkFragment.kt new file mode 100644 index 00000000..579382bb --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkFragment.kt @@ -0,0 +1,185 @@ +package com.navinfo.omqs.ui.fragment.tasklink + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.widget.Toast +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentTaskLinkBinding +import com.navinfo.omqs.ui.activity.map.MainActivity +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.other.shareViewModels +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class TaskLinkFragment : BaseFragment(), View.OnClickListener { + private var _binding: FragmentTaskLinkBinding? = null + private val binding get() = _binding!! + + @Inject + lateinit var mapController: NIMapController + + private val viewModel by shareViewModels("taskLink") + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentTaskLinkBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + + arguments?.let { + val id = it.getString("TaskLinkId") + if (id != null && id.isNotEmpty()){ + viewModel.initData(id) + } + } + + binding.taskLinkAddPoint.setOnClickListener(this) + binding.taskLinkKind.setOnClickListener(this) + binding.taskLinkFunctionalLevel.setOnClickListener(this) + binding.taskLinkDataLevel.setOnClickListener(this) + binding.taskLinkBarCancel.setOnClickListener(this) + binding.taskLinkBarSave.setOnClickListener(this) + binding.taskLinkBack.setOnClickListener(this) + binding.taskLinkClear.setOnClickListener(this) + binding.taskLinkBarDelete.setOnClickListener(this) + + /** + * 数据操作结束 + */ + viewModel.liveDataFinish.observe(viewLifecycleOwner) { + if (it) + onBackPressed() + } + /** + * 种别 + */ + viewModel.liveDataSelectKind.observe(viewLifecycleOwner) { + binding.taskLinkKind.text = it?.title + } + + /** + * 功能等级 + */ + viewModel.liveDataSelectFunctionLevel.observe(viewLifecycleOwner) { + binding.taskLinkFunctionalLevel.text = it?.title + } + + /** + * 数据等级 + */ + viewModel.liveDataSelectDataLevel.observe(viewLifecycleOwner) { + binding.taskLinkDataLevel.text = it?.title + } + + /** + * 当前选中任务 + */ + viewModel.liveDataTaskBean.observe(viewLifecycleOwner) { + binding.taskLinkTaskName.text = it?.evaluationTaskName + } + + /** + * viewModel 返回的文字信息 + */ + viewModel.liveDataToastMessage.observe(viewLifecycleOwner) { + Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() + } + /** + * 线长度 + */ + mapController.measureLayerHandler.lineLengthLiveData.observe(viewLifecycleOwner) { + binding.taskLinkLength.text = "${it}米" + } + mapController.measureLayerHandler.tempLineDistanceLiveData.observe(viewLifecycleOwner) { + (activity as MainActivity).setHomeCenterText(it) + } + } + + override fun onStart() { + super.onStart() + /** + * 显示地图准星 + */ + activity?.let { + (activity as MainActivity).setHomeCenterVisibility(View.VISIBLE) + } + } + + override fun onDestroy() { + super.onDestroy() + } + + override fun onStop() { + super.onStop() + /** + * 隐藏地图准星 + */ + requireActivity().findNavController(R.id.main_activity_middle_fragment).navigateUp() + activity?.let { + (activity as MainActivity).setHomeCenterVisibility(View.GONE) + } + } + + override fun onClick(v: View) { + when (v) { + binding.taskLinkAddPoint -> { + viewModel.addPoint() + } + binding.taskLinkKind -> { + showMiddleFragment() + viewModel.setAdapterList(1) + } + binding.taskLinkFunctionalLevel -> { + showMiddleFragment() + viewModel.setAdapterList(2) + } + binding.taskLinkDataLevel -> { + showMiddleFragment() + viewModel.setAdapterList(3) + } + binding.taskLinkBarCancel -> { + onBackPressed() + } + binding.taskLinkBarSave -> { + viewModel.saveData() + } + binding.taskLinkBack -> { + viewModel.removeLinkLastPoint() + } + binding.taskLinkClear -> { + viewModel.clearLink() + } + binding.taskLinkBarDelete ->{ + viewModel.deleteData(requireContext()) + } + } + } + + /** + * 显示中间面板 + */ + private fun showMiddleFragment() { + activity?.run { + val controller = findNavController( + R.id.main_activity_middle_fragment + ) + if (controller.currentDestination?.id == R.id.MiddleEmptyFragment) + controller.navigate(R.id.TaskLinkMiddleFragment) + } + } + + override fun onBackPressed(): Boolean { + findNavController().navigateUp() + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleAdapter.kt new file mode 100644 index 00000000..2536192a --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleAdapter.kt @@ -0,0 +1,48 @@ +package com.navinfo.omqs.ui.fragment.tasklink + +import android.os.Build +import android.view.LayoutInflater +import android.view.ViewGroup +import androidx.annotation.RequiresApi +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.AdapterElectronicEyeBinding +import com.navinfo.omqs.databinding.AdapterTaskLinkInfoBinding +import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter +import com.navinfo.omqs.ui.other.BaseViewHolder + +data class TaskLinkInfoAdapterItem( + val title: String, + val type: Int +) + +class TaskLinkMiddleAdapter(private var itemListener: ((Int, TaskLinkInfoAdapterItem) -> Unit?)? = null) : + BaseRecyclerViewAdapter() { + + private var selectTitle = "" + + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder { + val viewBinding = + AdapterTaskLinkInfoBinding.inflate(LayoutInflater.from(parent.context), parent, false) + return BaseViewHolder(viewBinding) + } + + override fun onBindViewHolder(holder: BaseViewHolder, position: Int) { + val binding = holder.viewBinding as AdapterTaskLinkInfoBinding + binding.title.text = data[position].title + + if (selectTitle == binding.title.text) { + binding.title.setBackgroundResource(R.drawable.shape_bg_blue_bg_4_radius) + binding.title.setTextColor(holder.viewBinding.root.context.resources.getColor(R.color.white)) + } else { + binding.title.setBackgroundResource(R.drawable.shape_rect_white_2dp_bg) + binding.title.setTextColor(holder.viewBinding.root.context.resources.getColor(R.color.black)) + } + binding.root.setOnClickListener { + if (selectTitle != data[position].title) { + selectTitle = data[position].title + notifyDataSetChanged() + } + itemListener?.invoke(position, data[position]) + } + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleFragment.kt new file mode 100644 index 00000000..3b8a23dc --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkMiddleFragment.kt @@ -0,0 +1,77 @@ +package com.navinfo.omqs.ui.fragment.tasklink + +import android.os.Build +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.fragment.app.viewModels +import androidx.navigation.findNavController +import androidx.navigation.fragment.findNavController +import androidx.recyclerview.widget.LinearLayoutManager +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.omqs.R +import com.navinfo.omqs.databinding.FragmentTaskLinkBinding +import com.navinfo.omqs.databinding.FragmentTaskLinkMiddleBinding +import com.navinfo.omqs.ui.activity.map.MainActivity +import com.navinfo.omqs.ui.fragment.BaseFragment +import com.navinfo.omqs.ui.fragment.evaluationresult.LeftAdapter +import com.navinfo.omqs.ui.other.shareViewModels +import dagger.hilt.android.AndroidEntryPoint +import javax.inject.Inject + +@AndroidEntryPoint +class TaskLinkMiddleFragment : BaseFragment(), View.OnClickListener { + private var _binding: FragmentTaskLinkMiddleBinding? = null + private val binding get() = _binding!! + + @Inject + lateinit var mapController: NIMapController + + private val viewModel by shareViewModels("taskLink") + + /** + * 监听左侧栏的点击事件 + */ + val adapter = TaskLinkMiddleAdapter { _, item -> + viewModel.setAdapterSelectValve(item) + if (activity != null) { + requireActivity().findNavController(R.id.main_activity_middle_fragment).navigateUp() + } + } + + override fun onCreateView( + inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle? + ): View { + _binding = FragmentTaskLinkMiddleBinding.inflate(inflater, container, false) + return binding.root + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + + binding.taskLinkMiddleRecyclerview.layoutManager = LinearLayoutManager(requireContext()) + binding.taskLinkMiddleRecyclerview.adapter = adapter + viewModel.liveDataLeftAdapterList.observe(viewLifecycleOwner) { + adapter.refreshData(it) + } + } + + + override fun onStart() { + super.onStart() + } + + override fun onStop() { + super.onStop() + } + + override fun onClick(v: View) { + when (v) { + } + } + + override fun onBackPressed(): Boolean { + findNavController().navigateUp() + return true + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkViewModel.kt new file mode 100644 index 00000000..a6e7d636 --- /dev/null +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklink/TaskLinkViewModel.kt @@ -0,0 +1,366 @@ +package com.navinfo.omqs.ui.fragment.tasklink + +import android.app.Dialog +import android.content.Context +import android.content.SharedPreferences +import android.os.Build +import androidx.annotation.RequiresApi +import androidx.lifecycle.MutableLiveData +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.navinfo.collect.library.data.entity.HadLinkDvoBean +import com.navinfo.collect.library.data.entity.LinkInfoBean +import com.navinfo.collect.library.data.entity.QsRecordBean +import com.navinfo.collect.library.data.entity.TaskBean +import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.utils.GeometryTools +import com.navinfo.omqs.Constant +import com.navinfo.omqs.ui.dialog.FirstDialog +import dagger.hilt.android.lifecycle.HiltViewModel +import io.realm.Realm +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.flow.collectLatest +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext +import org.bson.codecs.UuidCodec +import org.bson.internal.UuidHelper +import org.oscim.core.GeoPoint +import java.util.UUID +import javax.inject.Inject + +@HiltViewModel +class TaskLinkViewModel @Inject constructor( + private val mapController: NIMapController, + private val sharedPreferences: SharedPreferences +) : ViewModel(), SharedPreferences.OnSharedPreferenceChangeListener { + + /** + * 种别 + */ + private val kindList = listOf( + TaskLinkInfoAdapterItem("高速道路", 1), + TaskLinkInfoAdapterItem("城市高速", 2), + TaskLinkInfoAdapterItem("国道", 3), + TaskLinkInfoAdapterItem("省道", 4), + TaskLinkInfoAdapterItem("县道", 6), + TaskLinkInfoAdapterItem("乡镇村道路", 7), + TaskLinkInfoAdapterItem("其他道路", 8), + TaskLinkInfoAdapterItem("非引导道路", 9), + TaskLinkInfoAdapterItem("步行道路", 10), + TaskLinkInfoAdapterItem("人渡", 11), + TaskLinkInfoAdapterItem("轮渡", 13), + TaskLinkInfoAdapterItem("自行车道路", 15), + ) + + /** + * FunctionGrade 功能等级 + */ + private val functionLevelList = listOf( + TaskLinkInfoAdapterItem("等级1", 1), + TaskLinkInfoAdapterItem("等级2", 2), + TaskLinkInfoAdapterItem("等级3", 3), + TaskLinkInfoAdapterItem("等级4", 4), + TaskLinkInfoAdapterItem("等级5", 5), + ) + + /** + * 数据级别 + */ + private val dataLevelList = listOf( + TaskLinkInfoAdapterItem("Pro lane model(有高精车道模型覆盖的高速和城高link)", 1), + TaskLinkInfoAdapterItem("Lite lane model(有高精车道模型覆盖的普通路link)", 2), + TaskLinkInfoAdapterItem("Standard road model(其他link)", 3), + ) + + /** + * 处理结束关闭fragment` + */ + val liveDataFinish = MutableLiveData(false) + + /** + * 左侧面板展示内容 + */ + val liveDataLeftAdapterList = MutableLiveData>() + + /** + * 选择的种别 + */ + val liveDataSelectKind = MutableLiveData() + + /** + * 选择的功能等级 + */ + val liveDataSelectFunctionLevel = MutableLiveData() + + /** + * 选择的数据等级 + */ + val liveDataSelectDataLevel = MutableLiveData() + + /** + * 要提示的错误信息 + */ + val liveDataToastMessage = MutableLiveData() + + /** + * 当前选中的任务 + */ + val liveDataTaskBean = MutableLiveData() + + /** + * 当前正在编辑的线 + */ + private var hadLinkDvoBean: HadLinkDvoBean? = null + + /** + * 当前正在选择哪个数据 1:种别 2:功能等级 3:数据等级 + */ + private var selectType = 0 + + init { + getTaskBean() + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + } + + + private fun getTaskBean() { + viewModelScope.launch(Dispatchers.IO) { + val id = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + val realm = Realm.getDefaultInstance() + val res = realm.where(TaskBean::class.java).equalTo("id", id).findFirst() + liveDataTaskBean.postValue(res?.let { realm.copyFromRealm(it) }) + } + } + + /** + * 编辑点 + */ + fun addPoint() { + mapController.measureLayerHandler.drawLineOrPolygon(false) + } + + /** + * 设置左侧面板要显示的内容 + */ + fun setAdapterList(type: Int) { + selectType = type + when (type) { + 1 -> liveDataLeftAdapterList.value = kindList + 2 -> liveDataLeftAdapterList.value = functionLevelList + 3 -> liveDataLeftAdapterList.value = dataLevelList + } + } + + /** + * 返回左侧面板选择的内容 + */ + fun setAdapterSelectValve(item: TaskLinkInfoAdapterItem) { + when (selectType) { + 1 -> liveDataSelectKind.value = item + 2 -> liveDataSelectFunctionLevel.value = item + 3 -> liveDataSelectDataLevel.value = item + } + } + + override fun onCleared() { + mapController.measureLayerHandler.clear() + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) + super.onCleared() + } + + /** + * 保存数据 + */ + fun saveData() { + viewModelScope.launch(Dispatchers.Default) { + if (liveDataTaskBean.value == null) { + liveDataToastMessage.postValue("还没有选择任何一条任务!") + return@launch + } + + if (mapController.measureLayerHandler.mPathLayer.points.size < 2) { + liveDataToastMessage.postValue("道路点少于2个!") + return@launch + } + if (liveDataSelectKind.value == null) { + liveDataToastMessage.postValue("请选择种别!") + return@launch + } + if (liveDataSelectFunctionLevel.value == null) { + liveDataToastMessage.postValue("请选择功能等级!") + return@launch + } + if (liveDataSelectDataLevel.value == null) { + liveDataToastMessage.postValue("请选择数据等级!") + return@launch + } + val task: TaskBean = liveDataTaskBean.value!! + if (hadLinkDvoBean != null) { + hadLinkDvoBean!!.taskId = liveDataTaskBean.value!!.id + hadLinkDvoBean!!.length = + mapController.measureLayerHandler.lineLengthLiveData.value!! + hadLinkDvoBean!!.geometry = + GeometryTools.getLineString(mapController.measureLayerHandler.mPathLayer.points) + hadLinkDvoBean!!.linkInfo = LinkInfoBean( + kind = liveDataSelectKind.value!!.type, + functionLevel = liveDataSelectFunctionLevel.value!!.type, + dataLevel = liveDataSelectDataLevel.value!!.type, + ) + for (l in task.hadLinkDvoList) { + if (l.linkPid == hadLinkDvoBean!!.linkPid) { + task.hadLinkDvoList.remove(l) + task.hadLinkDvoList.add(hadLinkDvoBean) + break + } + } + } else { + hadLinkDvoBean = HadLinkDvoBean( + taskId = liveDataTaskBean.value!!.id, + linkPid = UUID.randomUUID().toString(), + linkStatus = 3, + length = mapController.measureLayerHandler.lineLengthLiveData.value!!, + geometry = GeometryTools.getLineString(mapController.measureLayerHandler.mPathLayer.points), + linkInfo = LinkInfoBean( + kind = liveDataSelectKind.value!!.type, + functionLevel = liveDataSelectFunctionLevel.value!!.type, + dataLevel = liveDataSelectDataLevel.value!!.type, + ) + ) + task.hadLinkDvoList.add(hadLinkDvoBean) + } + + + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + it.copyToRealmOrUpdate(hadLinkDvoBean) + it.copyToRealmOrUpdate(task) + } + mapController.lineHandler.addTaskLink(hadLinkDvoBean!!) + sharedPreferences.edit() + .putString(Constant.SHARED_SYNC_TASK_LINK_ID, hadLinkDvoBean!!.linkPid) + .apply() + liveDataFinish.postValue(true) + } + } + + /** + * 监听shared变化 + */ + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == Constant.SELECT_TASK_ID) { + getTaskBean() + } + } + + /** + * 绘制线的时候回退点 + */ + fun removeLinkLastPoint() { + mapController.measureLayerHandler.drawLineBackspace() + } + + /** + * 清除重绘 + */ + fun clearLink() { + mapController.measureLayerHandler.clear() + } + + /** + * 初始化数据 + */ + fun initData(id: String) { + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + val objects = + realm.where(HadLinkDvoBean::class.java).equalTo("linkPid", id) + .findFirst() + objects?.linkInfo?.let { + for (kind in kindList) { + if (kind.type == it.kind) { + liveDataSelectKind.postValue(kind) + break + } + } + for (function in functionLevelList) { + if (function.type == it.functionLevel) { + liveDataSelectFunctionLevel.postValue(function) + break + } + } + for (data in dataLevelList) { + if (data.type == it.dataLevel) { + liveDataSelectDataLevel.postValue(data) + break + } + } + } + val task = + realm.where(TaskBean::class.java).equalTo("id", objects?.taskId) + .findFirst() + + if (task != null) { + liveDataTaskBean.postValue(realm.copyFromRealm(task)) + } + hadLinkDvoBean = realm.copyFromRealm(objects) + withContext(Dispatchers.Main) { + mapController.measureLayerHandler.initPathLine(hadLinkDvoBean?.geometry!!) + } + } + } + + /** + * 删除数据 + */ + fun deleteData(context: Context) { + if (hadLinkDvoBean == null) { + liveDataFinish.value = true + return + } + val mDialog = FirstDialog(context) + mDialog.setTitle("提示?") + mDialog.setMessage("是否删除Mark,请确认!") + mDialog.setPositiveButton( + "确定" + ) { _, _ -> + mDialog.dismiss() + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + //先找到对应的任务 + val task = it.where(TaskBean::class.java).equalTo("id", hadLinkDvoBean!!.taskId) + .findFirst() + //维护任务删除当前link + if (task != null) { + for (h in task.hadLinkDvoList) { + if (h.linkPid == hadLinkDvoBean!!.linkPid) + task.hadLinkDvoList.remove(h) + break + } + realm.copyToRealmOrUpdate(task) + } + + //删除link + val objects = it.where(HadLinkDvoBean::class.java) + .equalTo("linkPid", hadLinkDvoBean!!.linkPid).findFirst() + objects?.deleteFromRealm() + //删除相关联的评测任务 + val qsRecordBeans = it.where(QsRecordBean::class.java) + .equalTo("linkId", hadLinkDvoBean!!.linkPid).and() + .equalTo("taskId", hadLinkDvoBean!!.taskId).findAll() + if (qsRecordBeans != null) { + for (b in qsRecordBeans) { + mapController.markerHandle.removeQsRecordMark(b) + } + qsRecordBeans.deleteAllFromRealm() + } + } + mapController.lineHandler.removeTaskLink(hadLinkDvoBean!!.linkPid) + mapController.mMapView.vtmMap.updateMap(true) + liveDataFinish.postValue(true) + } + } + mDialog.setNegativeButton("取消", null) + mDialog.show() + } +} \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskAdapter.kt index ee7ec323..8c164c07 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskAdapter.kt @@ -11,7 +11,7 @@ import com.navinfo.omqs.ui.other.BaseViewHolder interface TaskAdapterCallback { fun itemOnClick(bean: HadLinkDvoBean) - fun editOnclick(position: Int, bean: HadLinkDvoBean) + fun editOnClick(position: Int, bean: HadLinkDvoBean) } /** @@ -54,7 +54,7 @@ class TaskAdapter( } binding.taskEdit.isSelected = bean.reason != "" binding.taskEdit.setOnClickListener { - callback.editOnclick(position, bean) + callback.editOnClick(position, bean) } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskFragment.kt index 502b8ae3..e053d264 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskFragment.kt @@ -1,7 +1,6 @@ package com.navinfo.omqs.ui.fragment.tasklist -import android.app.AlertDialog -import android.content.DialogInterface +import android.os.Build import android.os.Bundle import android.text.Editable import android.text.TextWatcher @@ -16,14 +15,14 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.navinfo.collect.library.data.entity.HadLinkDvoBean import com.navinfo.omqs.R import com.navinfo.omqs.databinding.FragmentTaskBinding -import com.navinfo.omqs.databinding.FragmentTaskListBinding -import com.navinfo.omqs.http.taskdownload.TaskDownloadManager -import com.navinfo.omqs.http.taskupload.TaskUploadManager import com.navinfo.omqs.ui.fragment.BaseFragment import com.navinfo.omqs.ui.other.shareViewModels +import com.yanzhenjie.recyclerview.SwipeMenuBridge +import com.yanzhenjie.recyclerview.SwipeMenuCreator +import com.yanzhenjie.recyclerview.SwipeMenuItem import dagger.hilt.android.AndroidEntryPoint import kotlinx.coroutines.launch -import javax.inject.Inject +import org.videolan.vlc.Util /** * 当前任务的道路列表 @@ -41,14 +40,12 @@ class TaskFragment : BaseFragment() { private val adapter: TaskAdapter by lazy { TaskAdapter(object : TaskAdapterCallback { override fun itemOnClick(bean: HadLinkDvoBean) { - if(bean!=null){ + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { viewModel.showCurrentLink(bean) - }else{ - Toast.makeText(context, "数据错误,无法显示!", Toast.LENGTH_SHORT).show() } } - override fun editOnclick(position: Int, bean: HadLinkDvoBean) { + override fun editOnClick(position: Int, bean: HadLinkDvoBean) { showLinkEditDialog(position, bean) } }) @@ -66,11 +63,43 @@ class TaskFragment : BaseFragment() { override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + binding.taskAddLink.setOnClickListener { + viewModel.setSelectLink(!binding.taskAddLink.isSelected) + } + + viewModel.liveDataSelectNewLink.observe(viewLifecycleOwner) { + binding.taskAddLink.isSelected = it + } + + //注意:使用滑动菜单不能开启滑动删除,否则只有滑动删除没有滑动菜单 + val mSwipeMenuCreator = SwipeMenuCreator { _, rightMenu, _ -> + //添加菜单自动添加至尾部 + val deleteItem = SwipeMenuItem(context) + deleteItem.height = Util.convertDpToPx(requireContext(), 60) + deleteItem.width = Util.convertDpToPx(requireContext(), 80) + deleteItem.text = "删除" + deleteItem.background = requireContext().getDrawable(R.color.red) + deleteItem.setTextColor(requireContext().resources.getColor(R.color.white)) + rightMenu.addMenuItem(deleteItem) + } + val layoutManager = LinearLayoutManager(context) //// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能 binding.taskRecyclerview.setHasFixedSize(true) binding.taskRecyclerview.layoutManager = layoutManager + + //增加侧滑按钮 + binding.taskRecyclerview.setSwipeMenuCreator(mSwipeMenuCreator) + + + //单项点击 + binding.taskRecyclerview.setOnItemMenuClickListener { menuBridge, position -> + menuBridge.closeMenu() + viewModel.deleteTaskLink(requireContext(), adapter.data[position]) + } + binding.taskRecyclerview.adapter = adapter + binding.taskSearchClear.setOnClickListener { binding.taskSearch.setText("") } @@ -93,6 +122,7 @@ class TaskFragment : BaseFragment() { }) } + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt index 2a82c060..9d450b60 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListAdapter.kt @@ -1,16 +1,18 @@ package com.navinfo.omqs.ui.fragment.tasklist import android.annotation.SuppressLint -import android.app.Dialog import android.graphics.Color import android.util.Log import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast +import androidx.lifecycle.DefaultLifecycleObserver +import androidx.lifecycle.Lifecycle +import androidx.lifecycle.LifecycleEventObserver +import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView -import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.collect.library.data.entity.TaskBean import com.navinfo.omqs.R import com.navinfo.omqs.databinding.AdapterTaskListBinding @@ -19,15 +21,10 @@ import com.navinfo.omqs.http.taskupload.TaskUploadManager import com.navinfo.omqs.tools.FileManager import com.navinfo.omqs.tools.FileManager.Companion.FileDownloadStatus import com.navinfo.omqs.tools.FileManager.Companion.FileUploadStatus -import com.navinfo.omqs.ui.dialog.FirstDialog import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter import com.navinfo.omqs.ui.other.BaseViewHolder +import com.navinfo.omqs.ui.other.OnLifecycleStateListener import com.navinfo.omqs.ui.widget.LeftDeleteView -import io.realm.Realm -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext /** * 离线地图城市列表 RecyclerView 适配器 @@ -51,8 +48,7 @@ class TaskListAdapter( private var isShowDeleteView = false - private - val downloadBtnClick = View.OnClickListener() { + private val downloadBtnClick = View.OnClickListener() { if (it.tag != null) { val taskBean = data[it.tag as Int] if (taskBean.hadLinkDvoList.isNotEmpty()) { @@ -124,6 +120,7 @@ class TaskListAdapter( override fun onViewRecycled(holder: BaseViewHolder) { super.onViewRecycled(holder) //页面滑动时会用holder重构页面,但是对进度条的监听回调会一直返回,扰乱UI,所以当当前holder去重构的时候,移除监听 + //这里 BaseViewHolder 的LifecycleOwner 状态很早就DESTROYED 了,这个回调比较晚,起到的作用很小 downloadManager.removeObserver(holder.tag.toInt()) } @@ -131,6 +128,7 @@ class TaskListAdapter( holder: BaseViewHolder, @SuppressLint("RecyclerView") position: Int ) { + val binding: AdapterTaskListBinding = holder.viewBinding as AdapterTaskListBinding val taskBean = data[position] @@ -149,8 +147,22 @@ class TaskListAdapter( //tag 方便onclick里拿到数据 holder.tag = taskBean.id.toString() changeViews(binding, taskBean) + holder.addObserver(object : OnLifecycleStateListener { + override fun onState(tag: String, state: Lifecycle.State) { + when (state) { + Lifecycle.State.STARTED -> + downloadManager.observer( + taskBean.id, + holder, + DownloadObserver(taskBean.id, holder) + ) + Lifecycle.State.DESTROYED -> + downloadManager.removeObserver(tag.toInt()) + else -> {} + } + } + }) downloadManager.addTask(taskBean) - downloadManager.observer(taskBean.id, holder, DownloadObserver(taskBean.id, holder)) uploadManager.addTask(taskBean) uploadManager.observer(taskBean.id, holder, UploadObserver(taskBean.id, binding)) if (taskBean.status == FileDownloadStatus.NONE) { @@ -203,7 +215,12 @@ class TaskListAdapter( binding.taskDeleteLayout.setOnClickListener { //重置状态 leftDeleteView?.resetDeleteStatus() - itemListener?.invoke(position, ItemClickStatus.DELETE_LAYOUT_CLICK, taskBean) + if (taskBean.syncStatus != FileUploadStatus.DONE) { + Toast.makeText(binding.taskUploadBtn.context, "数据未上传,不允许关闭!", Toast.LENGTH_SHORT) + .show() + } else { + itemListener?.invoke(position, ItemClickStatus.DELETE_LAYOUT_CLICK, taskBean) + } } } @@ -212,7 +229,7 @@ class TaskListAdapter( * 重置item状态 * @param point */ - fun restoreItemView() { + private fun restoreItemView() { leftDeleteView?.let { if (isShowDeleteView) it.resetDeleteStatus() @@ -245,8 +262,9 @@ class TaskListAdapter( FileUploadStatus.DONE -> { binding.taskUploadBtn.stopAnimator() binding.taskUploadBtn.setText("已上传") + binding.taskUploadBtn.isEnabled = false binding.taskUploadBtn.setProgress(0) - binding.taskUploadBtn.setBackgroundColor(binding.root.resources.getColor(R.color.ripple_end_color)) + binding.taskUploadBtn.setBackgroundColor(binding.root.resources.getColor(R.color.gray_121)) } FileUploadStatus.ERROR -> { @@ -366,6 +384,17 @@ class TaskListAdapter( } } + fun initSelectTask(list: List, id: Int?) { + + for (i in list.indices) { + if (list[i].id == id) { + selectPosition = i + break + } + } + refreshData(list) + } + companion object { object ItemClickStatus { const val ITEM_LAYOUT_CLICK = 0 //条目点击 diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt index 58ad3124..1d83b1e9 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskListFragment.kt @@ -1,6 +1,5 @@ package com.navinfo.omqs.ui.fragment.tasklist -import android.app.Dialog import android.os.Build import android.os.Bundle import android.text.Editable @@ -9,26 +8,15 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import android.widget.Toast -import androidx.annotation.RequiresApi import androidx.recyclerview.widget.LinearLayoutManager -import com.navinfo.collect.library.data.entity.QsRecordBean -import com.navinfo.collect.library.data.entity.TaskBean import com.navinfo.omqs.databinding.FragmentTaskListBinding import com.navinfo.omqs.http.taskdownload.TaskDownloadManager import com.navinfo.omqs.http.taskupload.TaskUploadManager -import com.navinfo.omqs.tools.FileManager -import com.navinfo.omqs.ui.dialog.FirstDialog import com.navinfo.omqs.ui.fragment.BaseFragment import com.navinfo.omqs.ui.other.shareViewModels import dagger.hilt.android.AndroidEntryPoint -import io.realm.Realm -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.GlobalScope -import kotlinx.coroutines.launch -import kotlinx.coroutines.withContext import javax.inject.Inject -@RequiresApi(Build.VERSION_CODES.M) @AndroidEntryPoint class TaskListFragment : BaseFragment() { @@ -47,21 +35,28 @@ class TaskListFragment : BaseFragment() { private val adapter: TaskListAdapter by lazy { TaskListAdapter( - downloadManager, uploadManager,binding.taskListRecyclerview + downloadManager, uploadManager, binding.taskListRecyclerview ) { _, status, taskBean -> - if(taskBean.hadLinkDvoList.isEmpty()){ + if (taskBean.hadLinkDvoList.isEmpty()) { Toast.makeText(context, "数据错误,无Link数据!", Toast.LENGTH_SHORT).show() } - if(status==TaskListAdapter.Companion.ItemClickStatus.ITEM_LAYOUT_CLICK){ - viewModel.setSelectTaskBean(taskBean as TaskBean) - }else if(status==TaskListAdapter.Companion.ItemClickStatus.DELETE_LAYOUT_CLICK){ - context?.let { viewModel.removeTask(it, taskBean as TaskBean) } - }else if(status==TaskListAdapter.Companion.ItemClickStatus.UPLOAD_LAYOUT_CLICK){ - showLoadingDialog("正在校验") - Toast.makeText(context, "正在校验", Toast.LENGTH_SHORT).show() - viewModel.checkUploadTask(binding.root.context,taskBean) - } else { + when (status) { + TaskListAdapter.Companion.ItemClickStatus.ITEM_LAYOUT_CLICK -> { + viewModel.setSelectTaskBean(taskBean) + } + TaskListAdapter.Companion.ItemClickStatus.DELETE_LAYOUT_CLICK -> { + showLoadingDialog("正在关闭") + context?.let { viewModel.removeTask(it, taskBean) } + } + TaskListAdapter.Companion.ItemClickStatus.UPLOAD_LAYOUT_CLICK -> { + showLoadingDialog("正在校验") + Toast.makeText(context, "正在校验", Toast.LENGTH_SHORT).show() + viewModel.checkUploadTask(binding.root.context, taskBean) + } + else -> { + + } } } } @@ -85,13 +80,13 @@ class TaskListFragment : BaseFragment() { binding.taskListRecyclerview.layoutManager = layoutManager binding.taskListRecyclerview.adapter = adapter viewModel.liveDataTaskList.observe(viewLifecycleOwner) { - adapter.refreshData(it) + adapter.initSelectTask(it, viewModel.currentSelectTaskBean?.id) } //监听并调用上传 - viewModel.liveDataTaskUpload.observe(viewLifecycleOwner){ + viewModel.liveDataTaskUpload.observe(viewLifecycleOwner) { for ((key, value) in it) { - if(value){ + if (value) { adapter.uploadTask(key) } } @@ -111,6 +106,8 @@ class TaskListFragment : BaseFragment() { }) } + + override fun onDestroyView() { super.onDestroyView() _binding = null diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskManagerFragment.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskManagerFragment.kt index 63285395..5d1634c7 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskManagerFragment.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskManagerFragment.kt @@ -4,7 +4,9 @@ import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import android.widget.Toast import androidx.navigation.fragment.findNavController +import androidx.viewpager2.widget.ViewPager2 import com.google.android.material.tabs.TabLayoutMediator import com.navinfo.omqs.databinding.FragmentTaskManagerBinding import com.navinfo.omqs.ui.fragment.BaseFragment @@ -38,10 +40,20 @@ class TaskManagerFragment(private var backListener: ((TaskManagerFragment) -> Un override fun onViewCreated(view: View, savedInstanceState: Bundle?) { super.onViewCreated(view, savedInstanceState) + viewModel.liveDataToastMessage.observe(viewLifecycleOwner) { + Toast.makeText(requireContext(), it, Toast.LENGTH_SHORT).show() + } //禁止滑动,因为页面在抽屉里,和抽屉的滑动有冲突 binding.taskManagerViewpager.isUserInputEnabled = false //创建viewpager2的适配器 binding.taskManagerViewpager.adapter = activity?.let { TaskManagerAdapter(it) } + binding.taskManagerViewpager.registerOnPageChangeCallback(object : + ViewPager2.OnPageChangeCallback() { + override fun onPageSelected(position: Int) { + viewModel.setSelectLink(false) + } + }) + //绑定viewpager2与tabLayout TabLayoutMediator( binding.taskManagerTabLayout, diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskViewModel.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskViewModel.kt index 74ddacf6..e646eba1 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskViewModel.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/tasklist/TaskViewModel.kt @@ -1,20 +1,24 @@ package com.navinfo.omqs.ui.fragment.tasklist -import android.app.Dialog import android.content.Context -import android.graphics.Color +import android.content.SharedPreferences +import android.content.SharedPreferences.OnSharedPreferenceChangeListener import android.os.Build +import android.view.View import android.widget.Toast -import androidx.annotation.RequiresApi import androidx.lifecycle.MutableLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope +import com.navinfo.collect.library.data.dao.impl.TraceDataBase import com.navinfo.collect.library.data.entity.HadLinkDvoBean +import com.navinfo.collect.library.data.entity.NiLocation import com.navinfo.collect.library.data.entity.QsRecordBean import com.navinfo.collect.library.data.entity.TaskBean import com.navinfo.collect.library.map.NIMapController +import com.navinfo.collect.library.map.OnGeoPointClickListener import com.navinfo.collect.library.utils.GeometryTools import com.navinfo.omqs.Constant +import com.navinfo.omqs.db.RealmOperateHelper import com.navinfo.omqs.http.NetResult import com.navinfo.omqs.http.NetworkService import com.navinfo.omqs.tools.FileManager @@ -23,13 +27,19 @@ import com.navinfo.omqs.util.DateTimeUtil import dagger.hilt.android.lifecycle.HiltViewModel import io.realm.Realm import kotlinx.coroutines.* +import kotlinx.coroutines.flow.collectLatest +import org.oscim.core.GeoPoint import javax.inject.Inject @HiltViewModel class TaskViewModel @Inject constructor( - private val networkService: NetworkService, private val mapController: NIMapController -) : ViewModel() { + private val networkService: NetworkService, + private val mapController: NIMapController, + private val sharedPreferences: SharedPreferences, + private val realmOperateHelper: RealmOperateHelper, +) : ViewModel(), OnSharedPreferenceChangeListener { + private val TAG = "TaskViewModel" /** * 用来更新任务列表 @@ -46,13 +56,22 @@ class TaskViewModel @Inject constructor( */ val liveDataTaskUpload = MutableLiveData>() - private val colors = - arrayOf(Color.RED, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.GREEN, Color.CYAN) +// private val colors = +// arrayOf(Color.RED, Color.YELLOW, Color.BLUE, Color.MAGENTA, Color.GREEN, Color.CYAN) + /** + * 用来确定是否关闭 + */ + val liveDataCloseTask = MutableLiveData() + + /** + * 提示信息 + */ + val liveDataToastMessage = MutableLiveData() /** * 当前选中的任务 */ - private var currentSelectTaskBean: TaskBean? = null + var currentSelectTaskBean: TaskBean? = null /** * 任务列表查询协程 @@ -61,13 +80,21 @@ class TaskViewModel @Inject constructor( private var filterTaskJob: Job? = null + /** + * 是否开启了道路选择 + */ + var liveDataSelectNewLink = MutableLiveData(false) + + init { + sharedPreferences.registerOnSharedPreferenceChangeListener(this) + } + /** * 下载任务列表 */ fun getTaskList(context: Context) { viewModelScope.launch(Dispatchers.IO) { - var taskList: List = mutableListOf() when (val result = networkService.getTaskList(Constant.USER_ID)) { is NetResult.Success -> { if (result.data != null) { @@ -83,6 +110,7 @@ class TaskViewModel @Inject constructor( task.fileSize = item.fileSize task.status = item.status task.currentSize = item.currentSize + task.hadLinkDvoList = item.hadLinkDvoList //已上传后不在更新操作时间 if (task.syncStatus != FileManager.Companion.FileUploadStatus.DONE) { //赋值时间,用于查询过滤 @@ -116,35 +144,63 @@ class TaskViewModel @Inject constructor( is NetResult.Loading -> {} } - val realm = Realm.getDefaultInstance() - //过滤掉已上传的超过90天的数据 - var nowTime: Long = DateTimeUtil.getNowDate().time - var beginNowTime: Long = nowTime - 90 * 3600 * 24 * 1000L - var syncUpload: Int = FileManager.Companion.FileUploadStatus.DONE - val objects = - realm.where(TaskBean::class.java).notEqualTo("syncStatus", syncUpload).or() - .between("operationTime", beginNowTime, nowTime) - .equalTo("syncStatus", syncUpload).findAll() - taskList = realm.copyFromRealm(objects) + getLocalTaskList() + } + } + + /** + * 获取任务列表 + */ + private suspend fun getLocalTaskList() { + val realm = Realm.getDefaultInstance() + //过滤掉已上传的超过90天的数据 + val nowTime: Long = DateTimeUtil.getNowDate().time + val beginNowTime: Long = nowTime - 90 * 3600 * 24 * 1000L + val syncUpload: Int = FileManager.Companion.FileUploadStatus.DONE + val objects = + realm.where(TaskBean::class.java).notEqualTo("syncStatus", syncUpload).or() + .between("operationTime", beginNowTime, nowTime) + .equalTo("syncStatus", syncUpload).findAll().sort("id") + val taskList = realm.copyFromRealm(objects) + for (item in taskList) { + FileManager.checkOMDBFileInfo(item) + } + liveDataTaskList.postValue(taskList) + val id = sharedPreferences.getInt(Constant.SELECT_TASK_ID, -1) + if (id > -1) { for (item in taskList) { - FileManager.checkOMDBFileInfo(item) + if (item.id == id) { + currentSelectTaskBean = item + liveDataTaskLinks.postValue(currentSelectTaskBean!!.hadLinkDvoList) + withContext(Dispatchers.Main) { + showTaskLinks(currentSelectTaskBean!!) + } + break + } } - liveDataTaskList.postValue(taskList) } } /** * 设置当前选择的任务,并高亮当前任务的所有link */ - @RequiresApi(Build.VERSION_CODES.M) + fun setSelectTaskBean(taskBean: TaskBean) { + + sharedPreferences.edit().putInt(Constant.SELECT_TASK_ID, taskBean.id).apply() + currentSelectTaskBean = taskBean liveDataTaskLinks.value = taskBean.hadLinkDvoList + showTaskLinks(taskBean) + } - mapController.lineHandler.omdbTaskLinkLayer.removeAll() + private fun showTaskLinks(taskBean: TaskBean) { + + mapController.lineHandler.removeAllTaskLine() + mapController.markerHandle.clearNiLocationLayer() if (taskBean.hadLinkDvoList.isNotEmpty()) { - mapController.lineHandler.omdbTaskLinkLayer.addLineList(taskBean.hadLinkDvoList) + mapController.lineHandler.showTaskLines(taskBean.hadLinkDvoList) var maxX = 0.0 var maxY = 0.0 var minX = 0.0 @@ -174,14 +230,25 @@ class TaskViewModel @Inject constructor( ) } } + + //重新加载轨迹 + viewModelScope.launch(Dispatchers.IO) { + val list: List? = TraceDataBase.getDatabase( + mapController.mMapView.context, + Constant.USER_DATA_PATH + ).niLocationDao.findToTaskIdAll(taskBean.id.toString()) + list!!.forEach { + mapController.markerHandle.addNiLocationMarkerItem(it) + } + } } /** * 高亮当前选中的link */ - @RequiresApi(Build.VERSION_CODES.M) fun showCurrentLink(link: HadLinkDvoBean) { - mapController.lineHandler.omdbTaskLinkLayer.showSelectLine(link) + mapController.lineHandler.showLine(link.geometry) +// mapController.lineHandler.omdbTaskLinkLayer.showSelectLine(link) val geometry = GeometryTools.createGeometry(link.geometry) if (geometry != null) { val envelope = geometry.envelopeInternal @@ -196,13 +263,14 @@ class TaskViewModel @Inject constructor( } override fun onCleared() { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - mapController.lineHandler.omdbTaskLinkLayer.clearSelectLine() - } + mapController.lineHandler.removeLine() + sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) super.onCleared() } - + /** + * 保存link补作业原因 + */ suspend fun saveLinkReason(bean: HadLinkDvoBean, text: String) { withContext(Dispatchers.IO) { currentSelectTaskBean?.let { @@ -211,11 +279,12 @@ class TaskViewModel @Inject constructor( item.reason = text } } + val realm = Realm.getDefaultInstance() + realm.executeTransaction { r -> + r.copyToRealmOrUpdate(it) + } } - val realm = Realm.getDefaultInstance() - realm.executeTransaction { - realm.copyToRealmOrUpdate(currentSelectTaskBean) - } + } } @@ -239,6 +308,9 @@ class TaskViewModel @Inject constructor( } } + /** + * 筛选link + */ fun filterTask(pidKey: String) { if (currentSelectTaskBean == null) return @@ -256,53 +328,61 @@ class TaskViewModel @Inject constructor( } } + /** + * 关闭任务 + */ fun removeTask(context: Context, taskBean: TaskBean) { - if (taskBean != null) { - val mDialog = FirstDialog(context) - mDialog.setTitle("提示?") - mDialog.setMessage("是否关闭,请确认!") - mDialog.setPositiveButton("确定", object : FirstDialog.OnClickListener { - override fun onClick(dialog: Dialog?, which: Int) { - mDialog.dismiss() - viewModelScope.launch(Dispatchers.IO) { - val realm = Realm.getDefaultInstance() - realm.executeTransaction { - val objects = it.where(TaskBean::class.java) - .equalTo("id", taskBean.id).findFirst() - objects?.deleteFromRealm() + val mDialog = FirstDialog(context) + mDialog.setTitle("提示?") + mDialog.setMessage("是否关闭,请确认!") + mDialog.setPositiveButton( + "确定" + ) { dialog, _ -> + dialog.dismiss() + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + val objects = it.where(TaskBean::class.java) + .equalTo("id", taskBean.id).findFirst() + objects?.deleteFromRealm() + } + //遍历删除对应的数据 + taskBean.hadLinkDvoList.forEach { hadLinkDvoBean -> + val qsRecordList = realm.where(QsRecordBean::class.java) + .equalTo("linkId", hadLinkDvoBean.linkPid).and() + .equalTo("taskId", hadLinkDvoBean.taskId).findAll() + if (qsRecordList != null && qsRecordList.size > 0) { + val copyList = realm.copyFromRealm(qsRecordList) + copyList.forEach { + it.deleteFromRealm() + mapController.markerHandle.removeQsRecordMark(it) + mapController.mMapView.vtmMap.updateMap(true) } - //遍历删除对应的数据 - taskBean.hadLinkDvoList.forEach { hadLinkDvoBean -> - val qsRecordList = realm.where(QsRecordBean::class.java) - .equalTo("linkId", hadLinkDvoBean.linkPid).findAll() - if (qsRecordList != null && qsRecordList.size > 0) { - val copyList = realm.copyFromRealm(qsRecordList) - copyList.forEach { - it.deleteFromRealm() - mapController.markerHandle.removeQsRecordMark(it) - mapController.mMapView.vtmMap.updateMap(true) - } - } - } - //过滤掉已上传的超过90天的数据 - var nowTime: Long = DateTimeUtil.getNowDate().time - var beginNowTime: Long = nowTime - 90 * 3600 * 24 * 1000L - var syncUpload: Int = FileManager.Companion.FileUploadStatus.DONE - val objects = realm.where(TaskBean::class.java) - .notEqualTo("syncStatus", syncUpload).or() - .between("operationTime", beginNowTime, nowTime) - .equalTo("syncStatus", syncUpload).findAll() - val taskList = realm.copyFromRealm(objects) - for (item in taskList) { - FileManager.checkOMDBFileInfo(item) - } - liveDataTaskList.postValue(taskList) } } - }) - mDialog.setNegativeButton("取消", null) - mDialog.show() + //过滤掉已上传的超过90天的数据 + val nowTime: Long = DateTimeUtil.getNowDate().time + val beginNowTime: Long = nowTime - 90 * 3600 * 24 * 1000L + val syncUpload: Int = FileManager.Companion.FileUploadStatus.DONE + val objects = realm.where(TaskBean::class.java) + .notEqualTo("syncStatus", syncUpload).or() + .between("operationTime", beginNowTime, nowTime) + .equalTo("syncStatus", syncUpload).findAll() + val taskList = realm.copyFromRealm(objects) + for (item in taskList) { + FileManager.checkOMDBFileInfo(item) + } + liveDataTaskList.postValue(taskList) + liveDataCloseTask.postValue(true) + } } + mDialog.setNegativeButton( + "取消" + ) { _, _ -> + liveDataCloseTask.postValue(false) + mDialog.dismiss() + } + mDialog.show() } fun checkUploadTask(context: Context, taskBean: TaskBean) { @@ -310,7 +390,8 @@ class TaskViewModel @Inject constructor( val realm = Realm.getDefaultInstance() taskBean.hadLinkDvoList.forEach { hadLinkDvoBean -> val objects = realm.where(QsRecordBean::class.java) - .equalTo("linkId", hadLinkDvoBean.linkPid).findAll() + .equalTo("linkId", hadLinkDvoBean.linkPid).and() + .equalTo("taskId", hadLinkDvoBean.taskId).findAll() val map: MutableMap = HashMap() if (objects.isEmpty() && hadLinkDvoBean.reason.isEmpty()) { withContext(Dispatchers.Main) { @@ -319,19 +400,15 @@ class TaskViewModel @Inject constructor( mDialog.setTitle("提示?") mDialog.setMessage("此任务中存在未测评link,请确认!") mDialog.setPositiveButton( - "确定", - object : FirstDialog.OnClickListener { - override fun onClick(dialog: Dialog?, which: Int) { - mDialog.dismiss() - map[taskBean] = true - liveDataTaskUpload.postValue(map) - } - }) - mDialog.setNegativeButton("取消", object : FirstDialog.OnClickListener { - override fun onClick(dialog: Dialog?, which: Int) { - mDialog.dismiss() - } - }) + "确定" + ) { _, _ -> + mDialog.dismiss() + map[taskBean] = true + liveDataTaskUpload.postValue(map) + } + mDialog.setNegativeButton( + "取消" + ) { _, _ -> mDialog.dismiss() } mDialog.show() } return@launch @@ -341,4 +418,131 @@ class TaskViewModel @Inject constructor( } } } + + /** + * 监听新增的评测link + */ + override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences, key: String) { + if (key == Constant.SHARED_SYNC_TASK_LINK_ID) { + viewModelScope.launch(Dispatchers.IO) { + getLocalTaskList() + } + } + } + + /** + * 设置是否开启选择link + */ + fun setSelectLink(selected: Boolean) { + liveDataSelectNewLink.value = selected + //开始捕捉 + if (selected) { + mapController.mMapView.addOnNIMapClickListener(TAG, object : OnGeoPointClickListener { + override fun onMapClick(tag: String, point: GeoPoint) { + if (tag == TAG) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) { + viewModelScope.launch(Dispatchers.Default) { + if (currentSelectTaskBean == null) { + liveDataToastMessage.postValue("还没有开启任何任务") + } else { + val links = realmOperateHelper.queryLink( + point = point, + ) + if (links.isNotEmpty()) { + val l = links[0] + for (link in currentSelectTaskBean!!.hadLinkDvoList) { + if (link.linkPid == l.properties["linkPid"]) { + return@launch + } + } + val hadLinkDvoBean = HadLinkDvoBean( + taskId = currentSelectTaskBean!!.id, + linkPid = l.properties["linkPid"]!!, + geometry = l.geometry, + linkStatus = 2 + ) + currentSelectTaskBean!!.hadLinkDvoList.add( + hadLinkDvoBean + ) + val realm = Realm.getDefaultInstance() + realm.executeTransaction { r -> + r.copyToRealmOrUpdate(hadLinkDvoBean) + r.copyToRealmOrUpdate(currentSelectTaskBean!!) + } + liveDataTaskLinks.postValue(currentSelectTaskBean!!.hadLinkDvoList) + mapController.lineHandler.addTaskLink(hadLinkDvoBean) + } + } + } + } + } + } + + }) + } else { + mapController.mMapView.removeOnNIMapClickListener(TAG) + mapController.lineHandler.removeLine() + } + } + + /** + * 删除评测link + */ + fun deleteTaskLink(context: Context, hadLinkDvoBean: HadLinkDvoBean) { + if (hadLinkDvoBean.linkStatus == 1) { + val mDialog = FirstDialog(context) + mDialog.setTitle("提示") + mDialog.setMessage("当前要评测的link是任务原始规划的,不能删除,如果不进行作业请标记原因") + mDialog.setCancelVisibility(View.GONE) + mDialog.setPositiveButton( + "确定" + ) { _, _ -> + mDialog.dismiss() + } + mDialog.show() + } else { + val mDialog = FirstDialog(context) + mDialog.setTitle("提示") + mDialog.setMessage("是否删除当前link,与之相关联的评测任务会一起删除!!") + mDialog.setPositiveButton( + "确定" + ) { dialog, _ -> + dialog.dismiss() + viewModelScope.launch(Dispatchers.IO) { + val realm = Realm.getDefaultInstance() + realm.executeTransaction { + for (link in currentSelectTaskBean!!.hadLinkDvoList) { + if (link.linkPid == hadLinkDvoBean.linkPid) { + currentSelectTaskBean!!.hadLinkDvoList.remove(link) + break + } + } + realm.where(HadLinkDvoBean::class.java) + .equalTo("linkPid", hadLinkDvoBean.linkPid).findFirst() + ?.deleteFromRealm() + val markers = realm.where(QsRecordBean::class.java) + .equalTo("linkId", hadLinkDvoBean.linkPid) + .and().equalTo("taskId", hadLinkDvoBean.taskId) + .findAll() + if(markers != null){ + for(marker in markers){ + mapController.markerHandle.removeQsRecordMark(marker) + } + markers.deleteAllFromRealm() + } + + realm.copyToRealmOrUpdate(currentSelectTaskBean) + mapController.lineHandler.removeTaskLink(hadLinkDvoBean.linkPid) + liveDataTaskLinks.postValue(currentSelectTaskBean!!.hadLinkDvoList) + } + } + } + mDialog.setNegativeButton( + "取消" + ) { _, _ -> + mDialog.dismiss() + } + mDialog.show() + } + } } diff --git a/app/src/main/java/com/navinfo/omqs/ui/other/BaseViewHolder.kt b/app/src/main/java/com/navinfo/omqs/ui/other/BaseViewHolder.kt index 2544a7c0..7127a482 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/other/BaseViewHolder.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/other/BaseViewHolder.kt @@ -16,6 +16,8 @@ open class BaseViewHolder(val viewBinding: ViewBinding) : private val lifecycleRegistry = LifecycleRegistry(this) var tag = "" + private var listener: OnLifecycleStateListener? = null + init { // dataBinding.lifecycleOwner = this lifecycleRegistry.currentState = Lifecycle.State.INITIALIZED @@ -37,19 +39,33 @@ open class BaseViewHolder(val viewBinding: ViewBinding) : fun onStart() { lifecycleRegistry.currentState = Lifecycle.State.STARTED // lifecycleRegistry.currentState = Lifecycle.State.RESUMED // ON_RESUME EVENT + listener?.onState(tag,Lifecycle.State.STARTED) } fun onStop() { lifecycleRegistry.currentState = Lifecycle.State.STARTED // - lifecycleRegistry.currentState = Lifecycle.State.CREATED // ON_STOP EVENT + lifecycleRegistry.currentState = Lifecycle.State.CREATED // +// listener?.onState(tag,Lifecycle.State.STARTED)// ON_STOP EVENT } fun onDestroy() { lifecycleRegistry.currentState = Lifecycle.State.DESTROYED /// ON_DESTROY EVENT + listener?.onState(tag,Lifecycle.State.DESTROYED) } override fun getLifecycle(): Lifecycle { return lifecycleRegistry } + + fun addObserver(listener: OnLifecycleStateListener) { + this.listener = listener + } +} + +/** + * 生命周期变化 + */ +interface OnLifecycleStateListener { + fun onState(tag:String,state: Lifecycle.State) } \ No newline at end of file diff --git a/app/src/main/java/com/navinfo/omqs/ui/widget/SignUtil.kt b/app/src/main/java/com/navinfo/omqs/ui/widget/SignUtil.kt index fcfe77a1..3be3da98 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/widget/SignUtil.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/widget/SignUtil.kt @@ -45,7 +45,7 @@ class SignUtil { *获取道路功能等级文字 */ private fun getLinkFunctionClassText(data: RenderEntity): String { - return "等级${data.properties["functionClass"]}" + return "FC${data.properties["functionClass"]}" } /** @@ -55,9 +55,9 @@ class SignUtil { val direct = data.properties["direct"] when (direct?.toInt()) { 0 -> return "不应用" - 1 -> return "双方向" - 2 -> return "顺方向" - 3 -> return "逆方向" + 1 -> return "双" + 2 -> return "顺" + 3 -> return "逆" } return "" } diff --git a/app/src/main/java/com/navinfo/omqs/ui/widget/TextProgressButtonBar.kt b/app/src/main/java/com/navinfo/omqs/ui/widget/TextProgressButtonBar.kt index 3e687f45..d74c6580 100644 --- a/app/src/main/java/com/navinfo/omqs/ui/widget/TextProgressButtonBar.kt +++ b/app/src/main/java/com/navinfo/omqs/ui/widget/TextProgressButtonBar.kt @@ -122,18 +122,16 @@ class TextProgressButtonBar : View { * 绘制进度值 */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val shader = LinearGradient( - oval.left, - oval.top, - oval.right, - oval.bottom, - mStartColor, - mCurrentColor, - Shader.TileMode.MIRROR - ) - it.shader = shader - } + val shader = LinearGradient( + oval.left, + oval.top, + oval.right, + oval.bottom, + mStartColor, + mCurrentColor, + Shader.TileMode.MIRROR + ) + it.shader = shader canvas.drawRoundRect(oval, progress.toFloat(), progress.toFloat(), it) } else { oval = RectF( @@ -143,18 +141,16 @@ class TextProgressButtonBar : View { * 绘制进度值 */ - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { - val shader = LinearGradient( - oval.left, - oval.top, - oval.right, - oval.bottom, - mStartColor, - mCurrentColor, - Shader.TileMode.CLAMP - ) - it.shader = shader - } + val shader = LinearGradient( + oval.left, + oval.top, + oval.right, + oval.bottom, + mStartColor, + mCurrentColor, + Shader.TileMode.CLAMP + ) + it.shader = shader canvas.drawRoundRect(oval, corner.toFloat(), corner.toFloat(), it) } /*** diff --git a/app/src/main/java/com/navinfo/omqs/util/ShareUtil.java b/app/src/main/java/com/navinfo/omqs/util/ShareUtil.java index b3441a8b..cb54cdb2 100644 --- a/app/src/main/java/com/navinfo/omqs/util/ShareUtil.java +++ b/app/src/main/java/com/navinfo/omqs/util/ShareUtil.java @@ -370,4 +370,5 @@ public class ShareUtil { return null; } + } diff --git a/app/src/main/res/drawable-v24/baseline_add_24.xml b/app/src/main/res/drawable-v24/baseline_add_24.xml new file mode 100644 index 00000000..647e5ea9 --- /dev/null +++ b/app/src/main/res/drawable-v24/baseline_add_24.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable-v24/baseline_add_24_press.xml b/app/src/main/res/drawable-v24/baseline_add_24_press.xml new file mode 100644 index 00000000..c52171ce --- /dev/null +++ b/app/src/main/res/drawable-v24/baseline_add_24_press.xml @@ -0,0 +1,5 @@ + + + diff --git a/app/src/main/res/drawable-xxhdpi/home_map_center.png b/app/src/main/res/drawable-xxhdpi/home_map_center.png new file mode 100644 index 00000000..f255a8a3 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/home_map_center.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_menu_note.png b/app/src/main/res/drawable-xxhdpi/icon_menu_note.png new file mode 100644 index 00000000..9ba0d812 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_menu_note.png differ diff --git a/app/src/main/res/drawable-xxhdpi/icon_note.png b/app/src/main/res/drawable-xxhdpi/icon_note.png new file mode 100644 index 00000000..3d9fa526 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_note.png differ diff --git a/app/src/main/res/drawable-xxhdpi/sketch_back.png b/app/src/main/res/drawable-xxhdpi/sketch_back.png new file mode 100644 index 00000000..b42d27ec Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/sketch_back.png differ diff --git a/app/src/main/res/drawable-xxhdpi/sketch_eraser.png b/app/src/main/res/drawable-xxhdpi/sketch_eraser.png new file mode 100644 index 00000000..cb57b212 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/sketch_eraser.png differ diff --git a/app/src/main/res/drawable-xxhdpi/sketch_forward.png b/app/src/main/res/drawable-xxhdpi/sketch_forward.png new file mode 100644 index 00000000..c48f4029 Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/sketch_forward.png differ diff --git a/app/src/main/res/drawable/ripple_fragment_save_botton_bg.xml b/app/src/main/res/drawable/ripple_fragment_save_button_bg.xml similarity index 100% rename from app/src/main/res/drawable/ripple_fragment_save_botton_bg.xml rename to app/src/main/res/drawable/ripple_fragment_save_button_bg.xml diff --git a/app/src/main/res/drawable/selector_add_taskline.xml b/app/src/main/res/drawable/selector_add_taskline.xml new file mode 100644 index 00000000..45941ce4 --- /dev/null +++ b/app/src/main/res/drawable/selector_add_taskline.xml @@ -0,0 +1,5 @@ + + + + + diff --git a/app/src/main/res/drawable/selector_sketch_eraser_bg.xml b/app/src/main/res/drawable/selector_sketch_eraser_bg.xml new file mode 100644 index 00000000..d891b1c8 --- /dev/null +++ b/app/src/main/res/drawable/selector_sketch_eraser_bg.xml @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/shape_vertical_dashed_line.xml b/app/src/main/res/drawable/shape_vertical_dashed_line.xml index 5aa1a4bd..b9ca9aa6 100644 --- a/app/src/main/res/drawable/shape_vertical_dashed_line.xml +++ b/app/src/main/res/drawable/shape_vertical_dashed_line.xml @@ -4,7 +4,7 @@ android:left="-300dp" android:right="-300dp"> \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index eb4204c8..9772781f 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -31,6 +31,28 @@ app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> + + + + + app:constraint_referenced_ids="main_activity_serach,main_activity_2d_3d,main_activity_camera,main_activity_trace,main_activity_note,main_activity_task_line,main_activity_calc_disance" /> + + + + - - - - - - - - - - - - - - - - - + app:layout_constraintRight_toLeftOf="@id/main_activity_right_fragment" /> + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/adapter_task_link_info.xml b/app/src/main/res/layout/adapter_task_link_info.xml new file mode 100644 index 00000000..f8edde78 --- /dev/null +++ b/app/src/main/res/layout/adapter_task_link_info.xml @@ -0,0 +1,17 @@ + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/connect_out_camera.xml b/app/src/main/res/layout/connect_out_camera.xml index 0e79f412..a4a86803 100644 --- a/app/src/main/res/layout/connect_out_camera.xml +++ b/app/src/main/res/layout/connect_out_camera.xml @@ -150,6 +150,7 @@ android:layout_gravity="right|bottom" android:layout_margin="10dp" android:paddingLeft="@dimen/five" + android:visibility="invisible" android:textColor="@color/white" android:text="录像" android:button="@drawable/chk_icon_camera_record_or_takephoto_xml" diff --git a/app/src/main/res/layout/console_off.xml b/app/src/main/res/layout/console_off.xml index d1167c5a..e161ad00 100644 --- a/app/src/main/res/layout/console_off.xml +++ b/app/src/main/res/layout/console_off.xml @@ -278,7 +278,7 @@ + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_evaluation_result.xml b/app/src/main/res/layout/fragment_evaluation_result.xml index c1070d32..d1186c70 100644 --- a/app/src/main/res/layout/fragment_evaluation_result.xml +++ b/app/src/main/res/layout/fragment_evaluation_result.xml @@ -39,7 +39,7 @@ android:layout_height="40dp" android:layout_marginTop="9dp" android:layout_marginRight="14dp" - android:background="@drawable/ripple_fragment_save_botton_bg" + android:background="@drawable/ripple_fragment_save_button_bg" android:src="@drawable/icon_save" app:layout_constraintRight_toRightOf="parent" app:layout_constraintTop_toTopOf="parent" /> @@ -87,6 +87,24 @@ android:layout_marginBottom="7dp" android:orientation="vertical"> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_phenomenon.xml b/app/src/main/res/layout/fragment_phenomenon.xml index 47d18747..73c73788 100644 --- a/app/src/main/res/layout/fragment_phenomenon.xml +++ b/app/src/main/res/layout/fragment_phenomenon.xml @@ -1,26 +1,32 @@ - - + - - \ No newline at end of file + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_problem_link.xml b/app/src/main/res/layout/fragment_problem_link.xml index d31f06f3..508cfe3e 100644 --- a/app/src/main/res/layout/fragment_problem_link.xml +++ b/app/src/main/res/layout/fragment_problem_link.xml @@ -2,13 +2,15 @@ + android:layout_width="@dimen/fragment_problem_link_width" + android:layout_height="match_parent" + android:background="@drawable/shape_middle_fragment_bg" + app:layout_constraintRight_toRightOf="parent" /> \ No newline at end of file diff --git a/app/src/main/res/layout/fragment_task.xml b/app/src/main/res/layout/fragment_task.xml index 20c2876c..f27eb482 100644 --- a/app/src/main/res/layout/fragment_task.xml +++ b/app/src/main/res/layout/fragment_task.xml @@ -8,7 +8,7 @@ + + - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +