Răsfoiți Sursa

1、开发循环指示器(自定义View)修改图片浏览控件,关联位置指示器
2、案件上传(所需文件已准备)上传功能尚未完成(正在调试中...)

不会爬树的猴 1 an în urmă
părinte
comite
69fd975c94

+ 1 - 4
app/src/main/java/com/cr/adapter/DownloadDataAdapter.kt

@@ -2,9 +2,6 @@ package com.cr.adapter
 
 import android.content.Context
 import android.graphics.Color
-import android.os.Handler
-import android.os.Looper
-import android.os.Message
 import android.view.View
 import android.view.ViewGroup
 import android.widget.ImageView
@@ -96,7 +93,7 @@ class DownloadDataAdapter @JvmOverloads constructor(
                 var holder = view.tag as ViewHolder
                 var url = "${NetManager.getServerUrl()}/resource/download/${holder.model?.fileName}"
                 var fullName = CrUtil.CONFIG_PATH + holder.model?.fileName
-                TCPDataTask.getInstance().sendDownloadFile(context!!,fullName,url,object:TCPDataTask.OnDownloadListener{
+                TCPDataTask.getInstance().sendDownloadFile(context!!,fullName,url,object:TCPDataTask.IProgressCallback{
                     // todo: 2023/4/12 开始下载
                     override fun onStart() {
                         holder.progressBar?.progress = 0

+ 13 - 0
app/src/main/java/com/cr/common/CrFileManager.kt

@@ -180,5 +180,18 @@ class CrFileManager {
                 }
             }
         }
+
+        /**
+         * 获取截取的图片名称集合
+         * @param baseName String 基础名称
+         * @return List<String> 图片名称集合
+         */
+        fun getCaptureNames(baseName:String):List<String>{
+            var captureNames = mutableListOf<String>()
+            captureNames.add("${baseName}_yx.jpg")
+            captureNames.add("${baseName}_xz.jpg")
+            captureNames.add("${baseName}_gh.jpg")
+            return captureNames
+        }
     }
 }

+ 1 - 3
app/src/main/java/com/cr/cruav/AvLogin.kt

@@ -3,7 +3,6 @@ package com.cr.cruav
 import android.Manifest
 import android.app.Activity
 import android.content.pm.PackageManager
-import android.graphics.Color
 import android.os.Build
 import android.os.Bundle
 import android.util.Log
@@ -28,7 +27,6 @@ import com.cr.widget.CrEditTextWidget
 import com.squareup.otto.Subscribe
 import org.json.JSONArray
 import kotlinx.android.synthetic.main.av_login.*
-import kotlinx.coroutines.*
 
 class AvLogin : CrActivity(), OnClickListener {
     // define: 2023/3/31 权限列表
@@ -180,7 +178,7 @@ class AvLogin : CrActivity(), OnClickListener {
         TCPDataTask.getInstance().sendJSON(
             NetManager.getServerUrl("appQueryUser"),
             user,
-            object : TCPDataTask.OnChangeListener {
+            object : TCPDataTask.IChangeCallback {
                 // todo: 2023/4/10 成功
                 override fun onSuccess(jsonArray: JSONArray) {
                     CrConfig.user = UserModel.toModel(jsonArray.optString(0))

+ 1 - 1
app/src/main/java/com/cr/map/CaseModel.kt

@@ -18,7 +18,7 @@ class CaseModel {
     var imgName: String? = "" // define: 2023/6/12 案件照片字符串
     var date: String? = "" // define: 2023/6/12 创建日期
     var isDown: Boolean? = false  // define: 2023/6/12 是否已下载
-    var imgArray: List<String>? = null // define: 2023/6/12 照片数组
+    var imgArray: MutableList<String>? = null // define: 2023/6/12 照片数组
     var caseType: CaseType? = CaseType.Non // define: 2023/6/12 案件点类型
     var joinPolygon: CaseLocalPolygonModel? = null // define: 2023/6/12 关联的案件图斑
     var isCheck: Boolean? = false // define: 2023/6/12 是否已选中

+ 8 - 0
app/src/main/java/com/cr/models/UserModel.kt

@@ -13,6 +13,10 @@ class UserModel constructor():iNetDataModel<UserModel>{
     var userId: String? = ""   // define: 2023/4/6 账号Id
     var userPwd: String? = ""   // define: 2023/4/6 密码
     var userName: String? = ""   // define: 2023/4/6 账号名称
+    var imagePublishUrl:String?= null // define: 2023/8/25 图片发布地址
+    var imageThumbnailPublishUrl:String?=null // define: 2023/8/25 图片缩略图发布地址
+    var videoPublishUrl:String?= null // define: 2023/8/25 视频发布地址
+    var netCaseServiceUrl:String?=null // define: 2023/8/25 在线案件服务地址
 
     /**
      * 构造方法
@@ -37,6 +41,10 @@ class UserModel constructor():iNetDataModel<UserModel>{
             model.userId = obj.getString("userId")
             model.userPwd = obj.getString("userPwd")
             model.userName = obj.getString("userName")
+            model.imagePublishUrl = obj.getString("imagePublishUrl")
+            model.imageThumbnailPublishUrl = obj.getString("imageThumbnailPublishUrl")
+            model.videoPublishUrl = obj.getString("videoPublishUrl")
+            model.netCaseServiceUrl = obj.getString("netCaseServiceUrl")
             return model
         }
     }

+ 112 - 0
app/src/main/java/com/cr/network/CrProgressRequestBody.kt

@@ -0,0 +1,112 @@
+package com.cr.network
+
+import okhttp3.MediaType
+import okhttp3.RequestBody
+import okio.*
+import java.io.IOException
+import kotlin.jvm.JvmOverloads
+
+/**
+ * 操作系统:MAC系统
+ * 创建者:王成
+ * 创建日期:2023/8/26 16:56
+ * 描述:自定义上传体 用于监听上传进度
+ */
+class CrProgressRequestBody @JvmOverloads constructor(
+    requestBody:RequestBody,
+    listener:ICallback?=null
+) : RequestBody() {
+    /**
+     * 进度接口
+     */
+    interface ICallback{
+        // todo: 2023/8/26 进度回调
+        fun onProgress(currentBytes:Long,totalBytes:Long)
+
+        // todo: 2023/8/26 错误回调
+        fun onFailed(error:String)
+    }
+    private var requestBody:RequestBody?= null  // define: 2023/8/26 上传body
+    private var listener:ICallback?= null // define: 2023/8/26 监听
+
+    /**
+     * 初始化
+     */
+    init {
+        this.requestBody = requestBody
+        this.listener = listener
+    }
+
+    /**
+     * 覆写设置类型
+     * @return MediaType?
+     */
+    override fun contentType(): MediaType? {
+        return requestBody?.contentType()
+    }
+
+    /**
+     * 覆写内容长度
+     * @return Long
+     */
+    override fun contentLength(): Long {
+        return requestBody!!.contentLength()
+    }
+
+    /**
+     * 覆写写入进度
+     * @param sink BufferedSink
+     */
+    override fun writeTo(sink: BufferedSink) {
+        try {
+            var bufferedSink = CountingSink(sink,object:ICallback{
+                // todo: 2023/8/26 进度回调
+                override fun onProgress(currentBytes: Long, totalBytes: Long) {
+                    listener?.onProgress(currentBytes,contentLength())
+                }
+
+                // todo: 2023/8/26 错误
+                override fun onFailed(error: String) {
+                    listener?.onFailed(error)
+                }
+            }).buffer()
+            requestBody?.writeTo(bufferedSink)
+            bufferedSink.flush()
+        }catch (e:IOException){
+            listener?.onFailed(e.message!!)
+        }
+    }
+
+    /**
+     * ForwardingSink 实现类
+     * @property bytesWritten Long
+     * @property listener IProgressListener?
+     * @constructor
+     */
+    private class CountingSink @JvmOverloads constructor(
+        delegate:Sink,
+        listener:ICallback?=null
+    ) : ForwardingSink(delegate) {
+        // define: 2023/8/26 长度
+        private var bytesWritten:Long = 0
+        private var listener:ICallback?= null
+
+        /**
+         * 初始化
+         */
+        init {
+            this.listener = listener
+        }
+
+        /**
+         * 覆写写入方法
+         * @param source Buffer
+         * @param byteCount Long
+         */
+        override fun write(source: Buffer, byteCount: Long) {
+            super.write(source, byteCount)
+            bytesWritten += byteCount
+            listener?.onProgress(bytesWritten,0)
+        }
+    }
+}

+ 153 - 75
app/src/main/java/com/cr/network/TCPDataTask.kt

@@ -10,6 +10,7 @@ import com.cr.dialog.DialogNormal
 import com.cr.models.iNetDataModel
 import okhttp3.*
 import okhttp3.MediaType.Companion.toMediaType
+import okhttp3.RequestBody.Companion.asRequestBody
 import okhttp3.RequestBody.Companion.toRequestBody
 import org.json.JSONArray
 import org.json.JSONException
@@ -29,28 +30,32 @@ import java.util.concurrent.TimeUnit
  */
 class TCPDataTask {
     // define: 2023/4/10 定义显示或关闭等待条
-    val PROGRESS_SHOW:Int = 1001
-    val PROGRESS_CLOSE:Int = 1002
-    val SHOW_MESSAGE:Int = 1003
-    var DOWNLOAD_ERROR:Int = 1004
-    var DOWNLOAD_PROGRESS:Int = 1005
-    var DOWNLOAD_COMPLETE:Int = 1006
+    val PROGRESS_SHOW: Int = 1001
+    val PROGRESS_CLOSE: Int = 1002
+    val SHOW_MESSAGE: Int = 1003
+    var DOWNLOAD_ERROR: Int = 1004
+    var DOWNLOAD_PROGRESS: Int = 1005
+    var DOWNLOAD_COMPLETE: Int = 1006
 
     // define: 2023/4/10 定义变量
-    var context:Context? = null
+    var context: Context? = null
 
     /**
      * 多线程管理器
      */
-    private var handler: Handler = object:Handler(Looper.getMainLooper()){
+    private var handler: Handler = object : Handler(Looper.getMainLooper()) {
         // todo: 2023/4/10 处理消息
         override fun handleMessage(msg: Message) {
-            if(msg.what == PROGRESS_SHOW){
-                DialogLoadingUtil.show(context!!,msg.obj.toString())
-            }else if(msg.what == PROGRESS_CLOSE){
-                DialogLoadingUtil.dismiss()
-            }else if(msg.what == SHOW_MESSAGE){
-                DialogNormal(context!!,"警告",msg.obj.toString()).show()
+            when (msg.what) {
+                PROGRESS_SHOW -> {
+                    DialogLoadingUtil.show(context!!, msg.obj.toString())
+                }
+                PROGRESS_CLOSE -> {
+                    DialogLoadingUtil.dismiss()
+                }
+                SHOW_MESSAGE -> {
+                    DialogNormal(context!!, "警告", msg.obj.toString()).show()
+                }
             }
         }
     }
@@ -58,26 +63,26 @@ class TCPDataTask {
     /**
      * 对外接口
      */
-    interface OnChangeListener{
-        fun onSuccess(jsonArray:JSONArray)
-        fun onFailed(message:String)
+    interface IChangeCallback {
+        fun onSuccess(jsonArray: JSONArray)
+        fun onFailed(message: String)
     }
 
     /**
      * 下载监听
      */
-    interface OnDownloadListener{
+    interface IProgressCallback {
         // todo: 2023/4/12 开始下载
         fun onStart()
+
         // todo: 2023/4/12 下载失败
-        fun onFailed(message:String)
+        fun onFailed(message: String)
 
         // todo: 2023/4/12 下载进度
-        fun onProgress(progress:Int,total:Int)
+        fun onProgress(progress: Int, total: Int)
 
         // todo: 2023/4/12 下载完成
         fun onComplete()
-
     }
 
     // todo: 2023/4/8 网络连接
@@ -86,11 +91,11 @@ class TCPDataTask {
     /**
      * 单例方法
      */
-    companion object{
+    companion object {
         fun getInstance() = InstanceHelper.self
     }
 
-    object InstanceHelper{
+    object InstanceHelper {
         var self = TCPDataTask()
     }
 
@@ -98,23 +103,40 @@ class TCPDataTask {
      * 初始化
      * @constructor
      */
-    constructor(){
+    constructor() {
         /**
          * 初始化连接
          */
         okHttp = OkHttpClient.Builder()
-            .connectTimeout(6*1000,TimeUnit.MILLISECONDS)
-            .readTimeout(6*1000,TimeUnit.MILLISECONDS)
-            .writeTimeout(6*1000,TimeUnit.MILLISECONDS)
-            .build()
+            .connectTimeout(6 * 1000, TimeUnit.MILLISECONDS)
+            .readTimeout(6 * 1000, TimeUnit.MILLISECONDS)
+            .writeTimeout(6 * 1000, TimeUnit.MILLISECONDS)
+            .addNetworkInterceptor(interceptor).build()
+    }
+
+    /**
+     * 创建拦截器
+     */
+    private val interceptor = object :Interceptor{
+        // todo: 2023/8/26 拦截器监听
+        override fun intercept(chain: Interceptor.Chain): Response {
+            var originalRequest = chain.request()
+            if(originalRequest.body == null){
+                return chain.proceed(originalRequest)
+            }
+            var progressRequest = originalRequest.newBuilder()
+                .method(originalRequest.method,CrProgressRequestBody(originalRequest.body!!))
+                .build()
+            return chain.proceed(progressRequest)
+        }
     }
 
     /**
      * 显示等待框
      * @param message String 等待提示内容
      */
-    private fun showLoading(message:String){
-        var msg:Message = Message();
+    private fun showLoading(message: String) {
+        var msg: Message = Message();
         msg.what = PROGRESS_SHOW
         msg.obj = message
         handler.sendMessage(msg)
@@ -123,8 +145,8 @@ class TCPDataTask {
     /**
      * 关闭等待框
      */
-    private fun dismissLoading(){
-        var msg:Message = Message();
+    private fun dismissLoading() {
+        var msg: Message = Message();
         msg.what = PROGRESS_CLOSE
         handler.sendMessage(msg)
     }
@@ -133,8 +155,8 @@ class TCPDataTask {
      * 显示提示消息
      * @param message String
      */
-    private fun showMessage(message:String){
-        var msg:Message = Message();
+    private fun showMessage(message: String) {
+        var msg: Message = Message();
         msg.what = SHOW_MESSAGE
         msg.obj = message
         handler.sendMessage(msg)
@@ -146,8 +168,8 @@ class TCPDataTask {
      * @param iModel iNetDataModel<T> 发送数据
      * @param callback OnChangeListener 回调
      */
-    fun<T> sendJSON(url:String, iModel:iNetDataModel<T>, callback:OnChangeListener){
-        sendJSON(url,iModel,callback,null,null)
+    fun <T> sendJSON(url: String, iModel: iNetDataModel<T>, callback: IChangeCallback) {
+        sendJSON(url, iModel, callback, null, null)
     }
 
     /**
@@ -158,60 +180,79 @@ class TCPDataTask {
      * @param context Context 上下文  如果不显示等待框 可以传null
      * @param loadingMessage String 等待消息内容
      */
-    fun<T> sendJSON(url:String, iModel:iNetDataModel<T>, callback:OnChangeListener, context:Context?, loadingMessage:String?){
+    fun <T> sendJSON(
+        url: String,
+        iModel: iNetDataModel<T>,
+        callback: IChangeCallback,
+        context: Context?,
+        loadingMessage: String?
+    ) {
         this.context = context
         // todo: 2023/4/8 确定传输类型
-        var mediaType:MediaType = "application/json;charset=utf-8".toMediaType()
+        var mediaType: MediaType = "application/json;charset=utf-8".toMediaType()
         // todo: 2023/4/8 创建数据传输内容
-        var requestBody:RequestBody = iModel.toJSON().toRequestBody(mediaType)
-        var request:Request = Request.Builder().url(url).post(requestBody).build()
+        var requestBody: RequestBody = iModel.toJSON().toRequestBody(mediaType)
+        var request: Request = Request.Builder().url(url).post(requestBody).build()
         // todo: 2023/4/10 判断是否需要显示等待框
-        if(loadingMessage != null){
+        if (loadingMessage != null) {
             showLoading(loadingMessage)
         }
         // todo: 2023/4/8 连接发送
-        okHttp?.newCall(request)?.enqueue(object :Callback{
+        okHttp?.newCall(request)?.enqueue(object : Callback {
             // todo: 2023/4/8 失败
             override fun onFailure(call: Call, e: IOException) {
                 // todo: 2023/4/10 关闭等待框
                 dismissLoading()
-                if(e is ConnectException){
-                    if(callback != null) callback.onFailed("服务器异常,连接超时!")
-                }else{
+                if (e is ConnectException) {
+                    if (callback != null) callback.onFailed("服务器异常,连接超时!")
+                } else {
                     var errMessage: String? = e.message
-                    if(callback != null) callback.onFailed(errMessage!!)
+                    if (callback != null) callback.onFailed(errMessage!!)
                 }
             }
+
             // todo: 2023/4/8 成功
             override fun onResponse(call: Call, response: Response) {
                 // todo: 2023/4/10 关闭等待框
                 dismissLoading()
-                if(response.code == 200){
-                    try{
-                        var successMessage:String = response.body!!.string()
-                        checkJSON(successMessage,callback)
-                    }catch (e:java.lang.Exception){
-                        if(callback != null) callback.onFailed(e.message!!)
+                if (response.code == 200) {
+                    try {
+                        var successMessage: String = response.body!!.string()
+                        checkJSON(successMessage, callback)
+                    } catch (e: java.lang.Exception) {
+                        if (callback != null) callback.onFailed(e.message!!)
                     }
-                }else{
-                    if(callback != null) callback.onFailed("服务器连接异常!")
+                } else {
+                    if (callback != null) callback.onFailed("服务器连接异常!")
                 }
             }
         })
     }
 
-    fun sendDownloadFile(context: Context, fullName:String, url: String, callBack:OnDownloadListener){
-        if(callBack != null) callBack.onStart()
+    /**
+     * 下载文件
+     * @param context Context 上下文
+     * @param fullName String 下载文件名称
+     * @param url String 下载地址
+     * @param callBack OnDownloadListener 回调
+     */
+    fun sendDownloadFile(
+        context: Context,
+        fullName: String,
+        url: String,
+        callBack: IProgressCallback
+    ) {
+        if (callBack != null) callBack.onStart()
         // todo: 2023/4/12 上下文
         this.context = context
         // todo: 2023/4/12 定义下载的Url
-        var request:Request = Request.Builder().url(url).build()
+        var request: Request = Request.Builder().url(url).build()
         // todo: 2023/4/12 开始调用下载
-        okHttp?.newCall(request)?.enqueue(object:Callback{
+        okHttp?.newCall(request)?.enqueue(object : Callback {
             // todo: 2023/4/12 下载失败
             override fun onFailure(call: Call, e: IOException) {
                 handler.post(Runnable {
-                    if(callBack!= null) {
+                    if (callBack != null) {
                         callBack.onComplete()
                         callBack.onFailed(e.message!!)
                     }
@@ -220,36 +261,36 @@ class TCPDataTask {
 
             // todo: 2023/4/12 下载成功
             override fun onResponse(call: Call, response: Response) {
-                var inputStream:InputStream? = null
+                var inputStream: InputStream? = null
                 var buf = ByteArray(2048)
-                var len:Int
-                var fos:FileOutputStream?=null
-                try{
+                var len: Int
+                var fos: FileOutputStream? = null
+                try {
                     inputStream = response.body!!.byteStream()
-                    var total:Long = response.body!!.contentLength()
-                    var sum:Long = 0
+                    var total: Long = response.body!!.contentLength()
+                    var sum: Long = 0
                     // todo: 2023/4/12 创建文件
-                    var file:File = File(fullName)
+                    var file: File = File(fullName)
                     fos = FileOutputStream(file)
-                    while (true){
+                    while (true) {
                         len = inputStream.read(buf)
-                        if(len == -1) break
-                        fos.write(buf,0,len)
+                        if (len == -1) break
+                        fos.write(buf, 0, len)
                         sum += len
                         handler.post(Runnable {
-                            if(callBack != null) callBack.onProgress(sum.toInt(), total.toInt())
+                            if (callBack != null) callBack.onProgress(sum.toInt(), total.toInt())
                         })
                     }
                     fos.flush()
-                }catch (e: IOException){
+                } catch (e: IOException) {
                     handler.post(Runnable {
-                        if(callBack!= null) callBack.onFailed(e.message!!)
+                        if (callBack != null) callBack.onFailed(e.message!!)
                     })
-                }finally {
+                } finally {
                     inputStream!!.close()
                     fos!!.close()
                     handler.post(Runnable {
-                        if(callBack != null) callBack.onComplete()
+                        if (callBack != null) callBack.onComplete()
                     })
                 }
             }
@@ -257,12 +298,49 @@ class TCPDataTask {
         })
     }
 
+    fun <T> sendUploadFiles(
+        context: Context,
+        url:String,
+        filePathList: List<String>,
+        iModel: iNetDataModel<T>,
+        callback:IProgressCallback?
+    ) {
+        // todo: 2023/8/26 设置媒体格式
+        val mediaType = "application/form-data;charset=utf-8".toMediaType()
+        // todo: 2023/8/26 创建数据body
+        var dataBody: RequestBody = iModel.toJSON().toRequestBody(mediaType)
+        // todo: 2023/8/26 创建body
+        var requestBodyBuilder = MultipartBody.Builder().setType(MultipartBody.FORM)
+            //.addFormDataPart("data", "", dataBody)
+        // todo: 2023/8/26 添加文件
+        for (filePath in filePathList){
+            val file = File(filePath)
+            requestBodyBuilder.addFormDataPart("files",file.name, file.asRequestBody(mediaType))
+        }
+        // todo: 2023/8/26 创建执行体
+        var requestBody = requestBodyBuilder.build()
+        var request = Request.Builder().url(url).post(requestBody).build()
+        okHttp?.newCall(request)?.enqueue(object:Callback{
+            // todo: 2023/8/26 执行失败
+            override fun onFailure(call: Call, e: IOException) {
+                handler.post(Runnable {
+                    callback?.onFailed(e.message!!)
+                })
+            }
+
+            // todo: 2023/8/26 执行结果
+            override fun onResponse(call: Call, response: Response) {
+
+            }
+        })
+    }
+
     /**
      * 解析JSON数据
      * @param JSON String JSON字符串
      * @param callback OnChangeListener
      */
-    private fun checkJSON(JSON: String,callback:OnChangeListener) {
+    private fun checkJSON(JSON: String, callback: IChangeCallback) {
         val jsonObject: JSONObject
         try {
             jsonObject = JSONObject(JSON)

+ 3 - 1
app/src/main/java/com/cr/pages/CrFragment.kt

@@ -91,7 +91,9 @@ open class CrFragment :Fragment(){
 
                 // todo: 2023/8/15 关闭操作
                 override fun close() {
-
+                    callback?.let {
+                        it.onCompletion(CompletionModel(false,""))
+                    }
                 }
 
             })

+ 104 - 51
app/src/main/java/com/cr/pages/FragmentMap.kt

@@ -11,6 +11,7 @@ import androidx.fragment.app.activityViewModels
 import com.cr.common.*
 import com.cr.cruav.CrApplication
 import com.cr.cruav.R
+import com.cr.data.CrConfig
 import com.cr.data.CrUtil
 import com.cr.dialog.DialogInput
 import com.cr.dialog.DialogNormal
@@ -23,9 +24,11 @@ import com.esri.arcgisruntime.arcgisservices.LabelDefinition
 import com.esri.arcgisruntime.data.*
 import com.esri.arcgisruntime.geometry.*
 import com.esri.arcgisruntime.layers.ArcGISMapImageLayer
+import com.esri.arcgisruntime.layers.ArcGISMapImageSublayer
 import com.esri.arcgisruntime.layers.ArcGISTiledLayer
 import com.esri.arcgisruntime.layers.FeatureLayer
 import com.esri.arcgisruntime.layers.Layer
+import com.esri.arcgisruntime.loadable.LoadStatus
 import com.esri.arcgisruntime.mapping.ArcGISMap
 import com.esri.arcgisruntime.mapping.view.*
 import com.esri.arcgisruntime.symbology.*
@@ -50,7 +53,8 @@ import kotlin.math.abs
  */
 class FragmentMap : CrAnimationFragment() {
     // todo: 2023/8/17 飞行器模型
-    private val flightControlVm:CrFlightControlVM by activityViewModels()
+    private val flightControlVm: CrFlightControlVM by activityViewModels()
+
     /**
      * 事件监听接口
      */
@@ -167,7 +171,7 @@ class FragmentMap : CrAnimationFragment() {
     private var fTableCasePolygon: FeatureTable? = null // define: 2023/4/14 永久案件表
     private var fLayerCasePolygon: FeatureLayer? = null // define: 2023/4/14 永久案件图层
     private var fTableNetCasePolygon: ServiceFeatureTable? = null // define: 2023/4/14 网路案件表
-    private var fLayerNetCasePolygon: ArcGISMapImageLayer? = null // define: 2023/4/14 网络案件图层
+    private var fLayerNetCasePolygon: ArcGISMapImageSublayer? = null // define: 2023/4/14 网络案件图层
 
     // todo: 2023/4/18 地图Touch事件
     private var mapTouch: MapTouch? = null
@@ -381,6 +385,8 @@ class FragmentMap : CrAnimationFragment() {
         addBaseGeoDatabaseToMap()
         // todo: 2023/4/14 添加可编辑矢量数据
         addEditGeoDatabaseToMap()
+        // todo: 2023/8/25 加载在线网络案件
+        addNetCaseLayerToMap()
         // todo: 2023/4/19 添加动态图层
         addGraphicOverlayToMap()
         // todo: 2023/8/17 添加飞行相关图层
@@ -544,49 +550,93 @@ class FragmentMap : CrAnimationFragment() {
         // todo: 2023/8/16 初始化航线图层
         gLayerAirplaneLine = GraphicsOverlay()
         gLayerAirplaneLine?.let {
-            overlaysAddToMap(it,LAYER_NAME_AIR_LINE,LayerManager.LayerGroup.LAYER_NAME_AIR)
+            overlaysAddToMap(it, LAYER_NAME_AIR_LINE, LayerManager.LayerGroup.LAYER_NAME_AIR)
         }
         // todo: 2023/8/16 初始化历史航线图层
         gLayerHistoryAirplaneLine = GraphicsOverlay()
         gLayerHistoryAirplaneLine?.let {
-            overlaysAddToMap(it,LAYER_NAME_AIR_HISTORY_LINE,LayerManager.LayerGroup.LAYER_NAME_AIR)
+            overlaysAddToMap(
+                it,
+                LAYER_NAME_AIR_HISTORY_LINE,
+                LayerManager.LayerGroup.LAYER_NAME_AIR
+            )
         }
         // todo: 2023/8/17 初始化返航点图层
         gLayerAirplaneHomeLocation = GraphicsOverlay()
         gLayerAirplaneHomeLocation?.let {
-            overlaysAddToMap(it,LAYER_NAME_AIR_HOME,LayerManager.LayerGroup.LAYER_NAME_AIR)
+            overlaysAddToMap(it, LAYER_NAME_AIR_HOME, LayerManager.LayerGroup.LAYER_NAME_AIR)
         }
         // todo: 2023/8/17 初始化飞行器图层
         gLayerAirplaneLocation = GraphicsOverlay()
         gLayerAirplaneLocation?.let {
-            overlaysAddToMap(it,LAYER_NAME_AIR_LOCATION,LayerManager.LayerGroup.LAYER_NAME_AIR)
+            overlaysAddToMap(it, LAYER_NAME_AIR_LOCATION, LayerManager.LayerGroup.LAYER_NAME_AIR)
         }
         // todo: 2023/8/17 符号化
         initAirplaneSymbolInfo()
     }
 
     /**
+     * 加载网络案件图斑到地图
+     */
+    private fun addNetCaseLayerToMap() {
+        // todo: 2023/8/25 创建地图服务
+        var mapService = ArcGISMapImageLayer(CrConfig.user!!.netCaseServiceUrl!!)
+        // todo: 2023/8/25 添加监听
+        mapService.addDoneLoadingListener {
+            if (mapService.loadStatus == LoadStatus.LOADED) {
+                fLayerNetCasePolygon = mapService.sublayers[0] as ArcGISMapImageSublayer
+                fLayerNetCasePolygon?.let {
+                    // todo: 2023/8/25 案件图斑图层加载监听
+                    it.addDoneLoadingListener {
+                        if (it.loadStatus == LoadStatus.LOADED) {
+                            fTableNetCasePolygon = it.table
+                        }
+                    }
+                    // todo: 2023/8/25 加载 必须添加该方法 否则无法获取数据表
+                    it.loadAsync()
+                    // todo: 2023/8/25 设置
+                    it.name = LAYER_NAME_NET_CASE
+                    it.isVisible = true
+                    it.isLabelsEnabled = false
+                    LayerManager.getInstance().addLayer(
+                        LayerModel(
+                            it.name,
+                            it.isVisible,
+                            LayerType.LAYER_TYPE_SERVER,
+                            it
+                        ), LayerManager.LayerGroup.LAYER_NAME_DAILY
+                    )
+                }
+            }
+        }
+        // todo: 2023/8/25 添加到地图中
+        mMap?.operationalLayers?.add(mapService)
+    }
+
+    /**
      * 飞行相关符号化
      */
-    private fun initAirplaneSymbolInfo(){
+    private fun initAirplaneSymbolInfo() {
         // todo: 2023/8/17 符号化航线
-        symbolAirplaneLine = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID,Color.WHITE,2.0f)
-        symbolAirplaneLink = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID,Color.GREEN,2.0f)
+        symbolAirplaneLine = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.WHITE, 2.0f)
+        symbolAirplaneLink = SimpleLineSymbol(SimpleLineSymbol.Style.SOLID, Color.GREEN, 2.0f)
         builderAirplaneLine = PolylineBuilder(spatialReferenceWGS84)
         builderAirplaneLink = PolylineBuilder(spatialReferenceWGS84)
-        graAirplaneLine = Graphic(builderAirplaneLine!!.toGeometry(),symbolAirplaneLine)
-        graAirplaneLink = Graphic(builderAirplaneLink!!.toGeometry(),symbolAirplaneLink)
+        graAirplaneLine = Graphic(builderAirplaneLine!!.toGeometry(), symbolAirplaneLine)
+        graAirplaneLink = Graphic(builderAirplaneLink!!.toGeometry(), symbolAirplaneLink)
         gLayerAirplaneLine?.graphics?.add(graAirplaneLine)
         gLayerAirplaneLine?.graphics?.add(graAirplaneLink)
         // todo: 2023/8/17 飞行器符号化
         builderAirplaneLocation = PointBuilder(spatialReferenceWGS84)
-        symbolAirplaneLocation = createPictureMarkerSymbol(R.drawable.ico_air,25f,40f)
-        graAirplaneLocation = Graphic(builderAirplaneLocation!!.toGeometry(),symbolAirplaneLocation)
+        symbolAirplaneLocation = createPictureMarkerSymbol(R.drawable.ico_air, 25f, 40f)
+        graAirplaneLocation =
+            Graphic(builderAirplaneLocation!!.toGeometry(), symbolAirplaneLocation)
         gLayerAirplaneLocation?.graphics?.add(graAirplaneLocation)
         // todo: 2023/8/17 返航位置初始化
         builderAirplaneHomeLocation = PointBuilder(spatialReferenceWGS84)
-        var symbolAirplaneHomeLocation = createPictureMarkerSymbol(R.drawable.ico_home,30f,30f)
-        graAirplaneHomeLocation = Graphic(builderAirplaneHomeLocation!!.toGeometry(),symbolAirplaneHomeLocation)
+        var symbolAirplaneHomeLocation = createPictureMarkerSymbol(R.drawable.ico_home, 30f, 30f)
+        graAirplaneHomeLocation =
+            Graphic(builderAirplaneHomeLocation!!.toGeometry(), symbolAirplaneHomeLocation)
         gLayerAirplaneHomeLocation?.graphics?.add(graAirplaneHomeLocation)
     }
 
@@ -596,7 +646,11 @@ class FragmentMap : CrAnimationFragment() {
      * @param layerName String 图层名称
      * @param group LayerGroup 分组
      */
-    private fun overlaysAddToMap(layer:GraphicsOverlay,layerName:String,group:LayerManager.LayerGroup){
+    private fun overlaysAddToMap(
+        layer: GraphicsOverlay,
+        layerName: String,
+        group: LayerManager.LayerGroup
+    ) {
         // todo: 2023/8/17 设置可见
         layer.isVisible = true
         // todo: 2023/8/17 加入到地图中
@@ -1638,9 +1692,10 @@ class FragmentMap : CrAnimationFragment() {
         showLoading("初始化...")
         // todo: 2023/8/14 初始化图片信息
         captureNames.clear()
-        captureNames.add("${joinCaseName}_yx.jpg")
-        captureNames.add("${joinCaseName}_xz.jpg")
-        captureNames.add("${joinCaseName}_gh.jpg")
+        var tempNames = CrFileManager.getCaptureNames(joinCaseName)
+        for(name in tempNames){
+            captureNames.add(name)
+        }
         // todo: 2023/8/14 初始化信息
         var opNameList = mutableListOf<MutableList<String>>()
         var ovLayerList = mutableListOf<MutableList<GraphicsOverlay>>()
@@ -1682,13 +1737,15 @@ class FragmentMap : CrAnimationFragment() {
                 updateLoading("恢复地图状态...")
                 restoreMapLayerState()
                 // todo: 2023/8/14 发送截图完成事件
-                CrApplication.getEventBus().post(
-                    EventMapCapture(
-                        EventMapCapture.CaptureAction.CAPTURE_ACTION_COMPLETE,
-                        null,
-                        captureNames
+                mainHandler.post {
+                    CrApplication.getEventBus().post(
+                        EventMapCapture(
+                            EventMapCapture.CaptureAction.CAPTURE_ACTION_COMPLETE,
+                            null,
+                            captureNames
+                        )
                     )
-                )
+                }
                 // todo: 2023/8/14 关闭等待框
                 closeLoading()
             }
@@ -1798,22 +1855,22 @@ class FragmentMap : CrAnimationFragment() {
      * @param longitude Double 经度
      * @param latitude Double 纬度
      */
-    private fun aircraftCenter(longitude:Double,latitude:Double){
+    private fun aircraftCenter(longitude: Double, latitude: Double) {
         var visiblePolygon = mapView?.visibleArea
-        var airPoint = Point(longitude,latitude,spatialReferenceWGS84)
-        var targetPoint = GeometryEngine.project(airPoint,mapView?.spatialReference) as Point
-        if(!GeometryEngine.within(targetPoint,visiblePolygon)){
-            setMapCenter(longitude,latitude,0.0)
+        var airPoint = Point(longitude, latitude, spatialReferenceWGS84)
+        var targetPoint = GeometryEngine.project(airPoint, mapView?.spatialReference) as Point
+        if (!GeometryEngine.within(targetPoint, visiblePolygon)) {
+            setMapCenter(longitude, latitude, 0.0)
         }
     }
 
     /**
      * 初始化订阅
      */
-    private fun initObserver(){
-        flightControlVm.flightControlInfo.observe(requireActivity()){
+    private fun initObserver() {
+        flightControlVm.flightControlInfo.observe(requireActivity()) {
             it?.let {
-                if(it.longitude > 10 && it.latitude > 10){
+                if (it.longitude > 10 && it.latitude > 10) {
                     // todo: 2023/8/17 更新位置
                     updateAirplaneLocation(it)
                     // todo: 2023/8/17 更新返航点
@@ -1829,16 +1886,16 @@ class FragmentMap : CrAnimationFragment() {
      * 更新飞行器位置
      * @param obj CrFlightControlInfo 飞行器信息
      */
-    private fun updateAirplaneLocation(obj:CrFlightControlInfo){
+    private fun updateAirplaneLocation(obj: CrFlightControlInfo) {
         // todo: 2023/8/17 更新飞行器位置
-        builderAirplaneLocation?.setXY(obj.longitude,obj.latitude)
+        builderAirplaneLocation?.setXY(obj.longitude, obj.latitude)
         graAirplaneLocation?.geometry = builderAirplaneLocation!!.toGeometry()
         // todo: 2023/8/17 更新角度
         symbolAirplaneLocation?.angle = obj.yaw.toFloat()
         // todo: 2023/8/17 超边界更新
-        aircraftCenter(obj.longitude,obj.latitude)
+        aircraftCenter(obj.longitude, obj.latitude)
         // todo: 2023/8/17 更新航线
-        builderAirplaneLine?.addPoint(Point(obj.longitude,obj.latitude))
+        builderAirplaneLine?.addPoint(Point(obj.longitude, obj.latitude))
         graAirplaneLine?.geometry = builderAirplaneLine!!.toGeometry()
     }
 
@@ -1846,15 +1903,15 @@ class FragmentMap : CrAnimationFragment() {
      * 更新返航位置
      * @param obj CrFlightControlInfo 飞行器信息
      */
-    private fun updateAirplaneHomeLocation(obj:CrFlightControlInfo){
-        if(obj.isUpdateHomeLocation){
+    private fun updateAirplaneHomeLocation(obj: CrFlightControlInfo) {
+        if (obj.isUpdateHomeLocation) {
             CrUtil.print("返航点已刷新,请留意返航位置!")
             CrAudioUtil.getInstance().play("返航点已刷新,请留意返航位置!")
-            builderAirplaneHomeLocation?.setXY(obj.homeLongitude,obj.homeLatitude)
+            builderAirplaneHomeLocation?.setXY(obj.homeLongitude, obj.homeLatitude)
             graAirplaneHomeLocation?.geometry = builderAirplaneHomeLocation!!.toGeometry()
             // todo: 2023/8/17 如果返航点已刷新 则认为是更换了起飞位置 则重新初始化航线
             builderAirplaneLine = PolylineBuilder(spatialReferenceWGS84)
-            builderAirplaneLine?.addPoint(Point(obj.homeLongitude,obj.homeLatitude))
+            builderAirplaneLine?.addPoint(Point(obj.homeLongitude, obj.homeLatitude))
             graAirplaneLine?.geometry = builderAirplaneLine!!.toGeometry()
             // todo: 2023/8/17 设置更新标志 否则一直更新
             obj.isUpdateHomeLocation = false
@@ -1865,11 +1922,11 @@ class FragmentMap : CrAnimationFragment() {
      * 更新连接线
      * @param obj CrFlightControlInfo 飞行器信息
      */
-    private fun updateAirplaneLinkLine(obj:CrFlightControlInfo){
-        if(obj.isHomeLocationValid && obj.isLocationValid){
+    private fun updateAirplaneLinkLine(obj: CrFlightControlInfo) {
+        if (obj.isHomeLocationValid && obj.isLocationValid) {
             builderAirplaneLink = PolylineBuilder(spatialReferenceWGS84)
-            builderAirplaneLink?.addPoint(Point(obj.longitude,obj.latitude))
-            builderAirplaneLink?.addPoint(Point(obj.homeLongitude,obj.homeLatitude))
+            builderAirplaneLink?.addPoint(Point(obj.longitude, obj.latitude))
+            builderAirplaneLink?.addPoint(Point(obj.homeLongitude, obj.homeLatitude))
             graAirplaneLink?.geometry = builderAirplaneLink!!.toGeometry()
         }
     }
@@ -2031,10 +2088,8 @@ class FragmentMap : CrAnimationFragment() {
      */
     @Subscribe
     fun onCapture(event: EventMapCapture) {
-        event?.let {
-            if (it.action == EventMapCapture.CaptureAction.CAPTURE_ACTION_START) {
-                captures(it.joinCaseName!!)
-            }
+        if (event.action == EventMapCapture.CaptureAction.CAPTURE_ACTION_START) {
+            captures(event.joinCaseName!!)
         }
     }
 
@@ -2062,6 +2117,4 @@ class FragmentMap : CrAnimationFragment() {
         // todo: 2023/8/17 初始化订阅
         initObserver()
     }
-
-
 }

+ 69 - 4
app/src/main/java/com/cr/pages/FragmentUploadAction.kt

@@ -2,11 +2,11 @@ package com.cr.pages
 
 import android.graphics.Color
 import android.os.Bundle
-import android.provider.Settings.Global
 import android.view.LayoutInflater
 import android.view.View
 import android.view.ViewGroup
 import android.widget.Button
+import android.widget.LinearLayout
 import android.widget.TextView
 import com.bigkoo.pickerview.adapter.ArrayWheelAdapter
 import com.contrarywind.listener.OnItemSelectedListener
@@ -16,12 +16,13 @@ import com.cr.cruav.CrApplication
 import com.cr.cruav.R
 import com.cr.data.CrConfig
 import com.cr.data.CrUtil
+import com.cr.map.CaseModel
 import com.cr.models.SelModel
 import com.cr.network.NetManager
 import com.cr.network.TCPDataTask
 import com.cr.view.CrViewWheel
+import com.cr.widget.CrImageBrowserWidget
 import dji.v5.utils.common.ContextUtil
-import okhttp3.internal.notify
 import org.json.JSONArray
 
 /**
@@ -37,6 +38,12 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
     private var wheelCaseType:CrViewWheel?= null // define: 2023/8/18 案件类型选择器
     private var wheelCaseDesc:CrViewWheel?=null // define: 2023/8/18 案件描述选择器
     private var lblMessage:TextView?=null  // define: 2023/8/23 信息显示
+    private var lblLongitude:TextView?= null // define: 2023/8/25 经度
+    private var lblLatitude:TextView?=null // define: 2023/8/25 纬度
+    private var lblAngle:TextView?=null // define: 2023/8/25 角度
+    private var lblAltitude:TextView?=null // define: 2023/8/25 高度
+    private var imageBrowser:CrImageBrowserWidget?=null // define: 2023/8/26 图片浏览器
+    private var btnSubmit:LinearLayout?= null // define: 2023/8/26 上传按钮
 
     // todo: 2023/8/23 变量定义
     private var caseSubmitDescriptionList:List<SelModel>?=null // define: 2023/8/23 案件上传描述信息列表
@@ -45,6 +52,7 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
     private var adapterCaseDesc:ArrayWheelAdapter<String>?=null // define: 2023/8/24 案件描述适配器
     private var itemsCaseType:MutableList<String>?= null // define: 2023/8/24 案件类型数据集
     private var itemsCaseDesc:MutableList<String>?=null // define: 2023/8/24 案件描述信息数据集
+    private var joinCase:CaseModel?=null // define: 2023/8/25 关联的案件
     /**
      * 初始化
      * @param inflater LayoutInflater
@@ -81,6 +89,15 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
             setWheel(wheelCaseType!!,listenerWheelCaseType)
             setWheel(wheelCaseDesc!!,listenerWheelCaseDesc)
             lblMessage = it.findViewById(R.id.lbl_message)
+            lblLongitude = it.findViewById(R.id.lbl_longitude)
+            lblLatitude = it.findViewById(R.id.lbl_latitude)
+            lblAngle = it.findViewById(R.id.lbl_angle)
+            lblAltitude = it.findViewById(R.id.lbl_altitude)
+            // todo: 2023/8/26 挂接图片浏览器
+            imageBrowser = it.findViewById(R.id.image_browser)
+            // todo: 2023/8/26 挂载上传按钮
+            btnSubmit = it.findViewById(R.id.btn_submit)
+            btnSubmit?.setOnClickListener(this)
         }
     }
 
@@ -93,12 +110,17 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
         itemsCaseDesc = mutableListOf()
         adapterCaseDesc = ArrayWheelAdapter(itemsCaseDesc)
         wheelCaseDesc?.adapter = adapterCaseDesc
+        // todo: 2023/8/25 同步本地案件描述
         syncLocalCaseDescription()
 
         itemsCaseType = mutableListOf()
         adapterCaseType = ArrayWheelAdapter(itemsCaseType)
         wheelCaseType?.adapter = adapterCaseType
+        // todo: 2023/8/25 同步显示本地案件类型
         syncLocalCaseType()
+
+        // todo: 2023/8/25 同步关联案件显示
+        updateCaseView()
     }
 
     /**
@@ -167,7 +189,7 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
      * 同步案件描述信息
      */
     private fun asyncCaseDescription(){
-        TCPDataTask.getInstance().sendJSON(NetManager.getServerUrl("appQueryCaseDescription"),CrConfig.user!!,object:TCPDataTask.OnChangeListener{
+        TCPDataTask.getInstance().sendJSON(NetManager.getServerUrl("appQueryCaseDescription"),CrConfig.user!!,object:TCPDataTask.IChangeCallback{
             // todo: 2023/8/23 成功
             override fun onSuccess(jsonArray: JSONArray) {
                 var list = SelModel.fromJSONArray(jsonArray)
@@ -191,7 +213,7 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
      * 同步案件类型信息
      */
     private fun asyncCaseType(){
-        TCPDataTask.getInstance().sendJSON(NetManager.getServerUrl("appQueryCaseSource"),CrConfig.user!!,object:TCPDataTask.OnChangeListener{
+        TCPDataTask.getInstance().sendJSON(NetManager.getServerUrl("appQueryCaseSource"),CrConfig.user!!,object:TCPDataTask.IChangeCallback{
             // todo: 2023/8/23 成功
             override fun onSuccess(jsonArray: JSONArray) {
                 var list = SelModel.fromJSONArray(jsonArray)
@@ -225,6 +247,49 @@ class FragmentUploadAction:CrNavigationFragment(), View.OnClickListener {
                 // todo: 2023/8/24 同步案件类型
                 asyncCaseType()
             }
+            R.id.btn_submit->{
+                // todo: 2023/8/26 提报
+                submit()
+            }
         }
     }
+
+    /**
+     * 提报
+     */
+    private fun submit(){
+        var filePathList = mutableListOf<String>()
+        for (fileName in joinCase!!.imgArray!!){
+            filePathList.add("${CrUtil.IMAGE_PATH}${fileName}")
+        }
+        TCPDataTask.getInstance().sendUploadFiles(CrApplication.getContext(),NetManager.getServerUrl("appUploadCase"),filePathList,CrConfig.user!!)
+    }
+
+    /**
+     * 更新关联案件信息展示
+     */
+    private fun updateCaseView(){
+        this.joinCase?.let {
+            // todo: 2023/8/25 设置显示
+            lblLongitude?.text = String.format("%.6f",it.longitude)
+            lblLatitude?.text = String.format("%.6f",it.latitude)
+            lblAngle?.text = String.format("%.3f",it.angle)
+            lblAltitude?.text = String.format("%.3f",it.altitude)
+            // todo: 2023/8/26 挂接关联照片
+            for(fileName in it.imgArray!!){
+                val filePath= "${CrUtil.IMAGE_PATH}${fileName}"
+                CrUtil.print(filePath)
+                imageBrowser?.crAppendImage(filePath)
+            }
+        }
+    }
+
+    /**
+     * 设置关联的案件
+     * @param joinCase CaseModel 案件
+     */
+    fun crSetJoinCase(joinCase:CaseModel){
+        this.joinCase = joinCase;
+        updateCaseView()
+    }
 }

+ 3 - 2
app/src/main/java/com/cr/pages/FragmentUploadCase.kt

@@ -74,7 +74,7 @@ class FragmentUploadCase : CrNavigationFragment() {
      */
     override fun initPage() {
         // todo: 2023/6/15 初始化页面适配器
-        adapter = CrPageAdapter(this.activity!!)
+        adapter = CrPageAdapter(this.requireActivity())
         // todo: 2023/6/15 初始化主页面
         fragmentUploadCaseMain = FragmentUploadCaseMain()
         fragmentUploadCaseMain?.crSetListener(caseMainListener)
@@ -89,7 +89,6 @@ class FragmentUploadCase : CrNavigationFragment() {
      */
     private fun showPage(fragment: CrNavigationFragment) {
         var fragments = adapter?.getFragments()
-        CrUtil.print("页面数量---" + fragments?.size)
         for (i in 0 until fragments!!.size) {
             if (fragments?.get(i) == fragment) {
                 fragment.nvBar?.visibility = View.GONE
@@ -134,6 +133,8 @@ class FragmentUploadCase : CrNavigationFragment() {
             adapter?.addFragment(fragmentUploadCaseAction!!)
             // todo: 2023/8/18 显示页面
             showPage(fragmentUploadCaseAction!!)
+            // todo: 2023/8/25 设置关联案件
+            fragmentUploadCaseAction?.crSetJoinCase(joinCase)
         }
     }
 

+ 127 - 5
app/src/main/java/com/cr/pages/FragmentUploadCaseMain.kt

@@ -13,12 +13,17 @@ import com.cr.common.CrPictureManager
 import com.cr.common.CrFileManager
 import com.cr.common.CrUnitManager
 import com.cr.common.DataManager
+import com.cr.cruav.CrApplication
 import com.cr.cruav.R
+import com.cr.data.CrConfig
 import com.cr.data.CrUtil
+import com.cr.event.EventMapCapture
 import com.cr.map.CaseModel
 import com.cr.models.CompletionModel
 import com.cr.models.ICompletion
 import com.cr.widget.CrImageBrowserWidget
+import com.squareup.otto.Subscribe
+import java.io.File
 
 /**
  * 操作系统:MAC系统
@@ -33,8 +38,9 @@ class FragmentUploadCaseMain : CrNavigationFragment(), View.OnClickListener {
     interface IOperationListener {
         // todo: 2023/6/20 编辑照片
         fun onEditImage(imagePath: String)
+
         // todo: 2023/8/18
-        fun onUploadCase(joinCase:CaseModel)
+        fun onUploadCase(joinCase: CaseModel)
     }
 
     companion object {
@@ -75,6 +81,8 @@ class FragmentUploadCaseMain : CrNavigationFragment(), View.OnClickListener {
         if (this.joinCase != null) {
             crSetJoinCase(this.joinCase!!)
         }
+        // todo: 2023/8/25 订阅事件
+        CrApplication.getEventBus().register(this)
         return mainView
     }
 
@@ -121,10 +129,16 @@ class FragmentUploadCaseMain : CrNavigationFragment(), View.OnClickListener {
         when (view?.id) {
             // todo: 2023/6/15 编辑照片
             R.id.case_btn_edit -> {
-                if (cursorImageName == null){
+                if (cursorImageName == null) {
                     showWarning("未选择需要编辑的照片!")
-                }else{
-                    if(listener != null) listener?.onEditImage(String.format("%s%s",CrUtil.IMAGE_PATH,cursorImageName))
+                } else {
+                    if (listener != null) listener?.onEditImage(
+                        String.format(
+                            "%s%s",
+                            CrUtil.IMAGE_PATH,
+                            cursorImageName
+                        )
+                    )
                 }
             }
             // todo: 2023/6/15 打开相册
@@ -136,16 +150,77 @@ class FragmentUploadCaseMain : CrNavigationFragment(), View.OnClickListener {
             }
             // todo: 2023/6/15 上传
             R.id.case_btn_upload -> {
-                if(listener != null) listener?.onUploadCase(this.joinCase!!)
+                checkDataAndUpdateCase()
             }
         }
     }
 
     /**
+     * 检查数据并长传案件
+     */
+    private fun checkDataAndUpdateCase() {
+        // todo: 2023/6/20 查询数据库中数据
+        DataManager.appQueryImages(this.joinCase!!.name!!, object : ICompletion<List<String>> {
+            override fun onCompletion(completion: CompletionModel<List<String>>) {
+                if (completion.isSuccess == true) {
+                    // todo: 2023/8/25 进行判断
+                    if (completion.result!!.size > 1) {
+                        checkCaseCaptureData()
+                    } else {
+                        showWarning("不存在取证照片,无法上传!");
+                    }
+                }
+            }
+        })
+    }
+
+    /**
+     * 检查案件截屏数据
+     */
+    private fun checkCaseCaptureData() {
+        // todo: 2023/8/25 先调用地图 进行截图 截图之前进行判断 是否已经截取
+        var isExists = true
+        val names = CrFileManager.getCaptureNames(joinCase!!.name!!)
+        for (name in names) {
+            val filePath = "${CrUtil.IMAGE_PATH}${name}"
+            isExists = isExists && CrFileManager.isExists(filePath)
+        }
+        if (isExists) {
+            showConfirm("截图文件已存在,是否直接使用?", arrayOf("是","否"),object:ICompletion<String>{
+                override fun onCompletion(completion: CompletionModel<String>) {
+                    if(completion.isSuccess == true){
+                        sendListener()
+                    }else{
+                        // todo: 2023/8/25 发送截屏事件
+                        sendCaptureEvent()
+                    }
+                }
+            })
+        } else {
+            // todo: 2023/8/25 发送截屏事件
+            sendCaptureEvent()
+        }
+    }
+
+    /**
+     * 发送截屏事件
+     */
+    private fun sendCaptureEvent(){
+        CrApplication.getEventBus().post(
+            EventMapCapture(
+                EventMapCapture.CaptureAction.CAPTURE_ACTION_START,
+                joinCase!!.name
+            )
+        )
+    }
+
+    /**
      * 设置关联的案件点
      * @param joinCase CaseModel 案件点
      */
     fun crSetJoinCase(joinCase: CaseModel) {
+        // todo: 2023/8/26 初始化图片浏览器 必须的
+        imageBrowser?.crInit()
         this.joinCase = joinCase
         // todo: 2023/6/15 设置显示内容
         lblCaseName?.text = this.joinCase?.name
@@ -228,8 +303,55 @@ class FragmentUploadCaseMain : CrNavigationFragment(), View.OnClickListener {
         }
     }
 
+    /**
+     * 发送监听
+     */
+    private fun sendListener(){
+        // todo: 2023/8/25 对关联案件进行设置
+        joinCase?.let { itCase ->
+            // todo: 2023/8/25 清理数组
+            if(itCase.imgArray == null) itCase.imgArray = mutableListOf()
+            else itCase.imgArray?.clear()
+            // todo: 2023/8/25 追加图片
+            DataManager.appQueryImages(this.joinCase!!.name!!, object : ICompletion<List<String>> {
+                override fun onCompletion(completion: CompletionModel<List<String>>) {
+                    if (completion.isSuccess == true) {
+                        for(imgName in completion.result!!){
+                            itCase.imgArray?.add(imgName)
+                        }
+                        // todo: 2023/8/25 追截截图
+                        var captureNames = CrFileManager.getCaptureNames(itCase.name!!)
+                        for(imgName in captureNames){
+                            itCase.imgArray?.add(imgName)
+                        }
+                        // todo: 2023/8/25 回调打开窗口
+                        listener?.let { it.onUploadCase(joinCase!!) }
+                    }
+                }
+            })
+        }
+    }
+
+    /**
+     * 订阅截屏事件
+     */
+    @Subscribe
+    fun onCapture(event: EventMapCapture) {
+        if (event.action == EventMapCapture.CaptureAction.CAPTURE_ACTION_COMPLETE) {
+            // todo: 2023/8/26 发送监听 打开页面
+            sendListener()
+        }
+    }
+
     // todo: 2023/6/20 设置监听
     fun crSetListener(listener: IOperationListener) {
         this.listener = listener
     }
+
+    // todo: 2023/8/25 生命周期
+    override fun onDestroy() {
+        super.onDestroy()
+        // todo: 2023/8/25 取消订阅事件
+        CrApplication.getEventBus().unregister(this)
+    }
 }

+ 197 - 0
app/src/main/java/com/cr/view/CrIndicator.kt

@@ -0,0 +1,197 @@
+package com.cr.view
+
+import android.content.Context
+import android.graphics.Canvas
+import android.graphics.Color
+import android.graphics.Paint
+import android.graphics.Rect
+import android.util.AttributeSet
+import android.view.View
+import com.cr.common.CrColorManager
+import com.cr.common.CrFontManager
+import com.cr.common.CrUnitManager
+import com.cr.cruav.R
+
+/**
+ * 操作系统:MAC系统
+ * 创建者:王成
+ * 创建日期:2023/8/26 11:23
+ * 描述:自定义指示器视图
+ */
+class CrIndicator @JvmOverloads constructor(
+    context: Context,
+    attrs: AttributeSet? = null,
+    defStyleAtr: Int = 0
+) : View(context, attrs, defStyleAtr) {
+    // todo: 2023/8/26 视图尺寸定义
+    private var viewWidth: Int = 0  // define: 2023/8/26 视图宽度
+    private var viewHeight: Int = 0  // define: 2023/8/26 视图高度
+
+    // todo: 2023/8/26 定义画布
+    private var canvas: Canvas? = null
+
+    // todo: 2023/8/26 定义画笔
+    private var paintNormalBackground: Paint? = null  // define: 2023/8/26 普通画笔
+    private var paintActiveBackground: Paint? = null  // define: 2023/8/26 激活画笔
+    private var paintNormalText: Paint? = null // define: 2023/8/26 普通文字画笔
+    private var paintActiveText: Paint? = null // define: 2023/8/26 激活文字画笔
+
+    // todo: 2023/8/26 变量定义
+    private var mCount: Int = 5  // define: 2023/8/26 指示器数量
+    private var mSpace: Int = 10 // define: 2023/8/26 指示器间隔
+    private var mIndex: Int = 2  // define: 2023/8/26 档期激活位置
+
+    /**
+     * 初始化
+     */
+    init {
+        // todo: 2023/8/26 获取属性
+        val attributes = context.obtainStyledAttributes(attrs,R.styleable.CrIndicator)
+        if(attributes != null){
+            mCount = attributes.getInt(R.styleable.CrIndicator_cr_count,0)
+            mIndex = attributes.getInt(R.styleable.CrIndicator_cr_index,1)
+            // todo: 2023/8/26 使属性生效
+            attributes.recycle()
+        }
+    }
+
+    /**
+     * 生命周期函数
+     * 当XML加载完毕后会调用此方法
+     */
+    override fun onFinishInflate() {
+        super.onFinishInflate()
+        this.setBackgroundColor(Color.argb(0, 70, 71, 81))
+    }
+
+    /**
+     * 重置
+     * @param widthMeasureSpec Int
+     * @param heightMeasureSpec Int
+     */
+    override fun onMeasure(widthMeasureSpec: Int, heightMeasureSpec: Int) {
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec)
+        // todo: 2023/8/26 获取视图宽度和高度 单位是像素
+        this.viewWidth = MeasureSpec.getSize(widthMeasureSpec)
+        this.viewHeight = MeasureSpec.getSize(heightMeasureSpec)
+    }
+
+    /**
+     * 重绘
+     * @param canvas Canvas 画布
+     */
+    override fun onDraw(canvas: Canvas?) {
+        super.onDraw(canvas)
+        this.canvas = canvas
+        // todo: 2023/8/26 初始化画笔
+        initPaint()
+        // todo: 2023/8/26 绘制
+        draw()
+    }
+
+    /**
+     * 绘制
+     */
+    private fun draw() {
+        if (mCount <= 0) return
+        // todo: 2023/8/26 半径大小
+        val mRadius = viewHeight / 2 - 4
+        // todo: 2023/8/26 计算指示器绘制内容占据的宽度
+        val mWidth = mRadius * 2 * mCount + mSpace * (mCount - 1)
+        // todo: 2023/8/26 计算左侧位置
+        var mLeft = (viewWidth - mWidth) / 2
+        // todo: 2023/8/26 绘制圆
+        for (i in 0 until mCount) {
+            // todo: 2023/8/26 计算圆心位置
+            val cx = mLeft + mRadius * (i * 2 + 1) + mSpace * i
+            val cy = mRadius * 1f + 4
+            // todo: 2023/8/26 计算文字位置
+            val title = "${i + 1}"
+            var titleRect = Rect()
+            paintNormalText?.getTextBounds(title, 0, title!!.length, titleRect)
+            val dx = cx - titleRect.width() / 2
+            val dy = cy + titleRect.height() / 2
+            if (i != mIndex) {
+                // todo: 2023/8/26 绘制普通
+                canvas?.drawCircle(cx * 1f, cy, mRadius * 1f, paintNormalBackground!!)
+                canvas?.drawText(title, dx * 1f, dy * 1f, paintNormalText!!)
+            } else {
+                // todo: 2023/8/26 绘制当前激活
+                canvas?.drawCircle(cx * 1f, cy, mRadius * 1f, paintActiveBackground!!)
+                canvas?.drawText(title, dx * 1f, dy * 1f, paintActiveText!!)
+            }
+        }
+    }
+
+    /**
+     * 画笔初始化
+     */
+    private fun initPaint() {
+        // todo: 2023/8/26 普通背景画笔
+        paintNormalBackground = Paint(Paint.ANTI_ALIAS_FLAG)
+        paintNormalBackground?.let {
+            it.isAntiAlias = true  // todo: 2023/7/31 抗锯齿
+            it.color = Color.argb(255, 36, 110, 140)
+            it.alpha = 255
+            it.strokeJoin = Paint.Join.ROUND
+            it.strokeCap = Paint.Cap.BUTT
+            it.style = Paint.Style.FILL  // define: 2023/7/31 画笔类型
+        }
+
+        // todo: 2023/8/26 选中背景画笔
+        paintActiveBackground = Paint(Paint.ANTI_ALIAS_FLAG)
+        paintActiveBackground?.let {
+            it.isAntiAlias = true  // todo: 2023/7/31 抗锯齿
+            it.color = Color.argb(255, 188, 14, 28)
+            it.alpha = 255
+            it.strokeJoin = Paint.Join.ROUND
+            it.strokeCap = Paint.Cap.BUTT
+            it.style = Paint.Style.FILL  // define: 2023/7/31 画笔类型
+        }
+
+        // todo: 2023/8/26 普通文字画笔
+        paintNormalText = Paint()
+        paintNormalText?.let {
+            it.isAntiAlias = true  // todo: 2023/7/31 抗锯齿
+            it.textAlign = Paint.Align.LEFT  // define: 2023/7/31 文字对齐方式
+            it.color = CrColorManager.getColor(context, R.color.white)  // define: 2023/7/31 文字颜色
+            it.textSize =
+                CrUnitManager.dp2px(CrUnitManager.getDimens(context, R.dimen.cr_4_dp)).toFloat()
+            it.isFakeBoldText = true  // define: 2023/7/31 加粗字体
+            it.typeface = CrFontManager.getTTGBTypeface(context)
+        }
+
+        // todo: 2023/8/26 选中文字画笔
+        paintActiveText = Paint()
+        paintActiveText?.let {
+            it.isAntiAlias = true  // todo: 2023/7/31 抗锯齿
+            it.textAlign = Paint.Align.LEFT  // define: 2023/7/31 文字对齐方式
+            it.color = CrColorManager.getColor(context, R.color.yellow)  // define: 2023/7/31 文字颜色
+            it.textSize =
+                CrUnitManager.dp2px(CrUnitManager.getDimens(context, R.dimen.cr_4_dp)).toFloat()
+            it.isFakeBoldText = true  // define: 2023/7/31 加粗字体
+            it.typeface = CrFontManager.getTTGBTypeface(context)
+        }
+    }
+
+    /**
+     * 设置指示器数量
+     * @param count Int
+     */
+    fun crSetCount(count: Int) {
+        if (count > 0) {
+            mCount = count
+            invalidate()
+        }
+    }
+
+    /**
+     * 设置当前激活
+     * @param index Int
+     */
+    fun crSetIndex(index:Int){
+        if(index > mCount) return
+        mIndex = index
+        invalidate()
+    }
+}

+ 17 - 0
app/src/main/java/com/cr/widget/CrImageBrowserWidget.kt

@@ -13,6 +13,7 @@ import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
 import com.cr.common.CrFileManager
 import com.cr.cruav.R
 import com.cr.data.CrUtil
+import com.cr.view.CrIndicator
 import java.io.FileInputStream
 import java.io.FileNotFoundException
 
@@ -31,6 +32,7 @@ class CrImageBrowserWidget @JvmOverloads constructor(
     private var dataList: MutableList<String> = mutableListOf() // define: 2023/6/16 数据集合
     private var adapter: BaseAdapter? = null // define: 2023/6/16 适配器
     private var listener:iChangeListener?=null // define: 2023/6/17 对外接口
+    private var wgIndicator:CrIndicator?= null // define: 2023/8/26 指示器
 
     // todo: 2023/6/17 对外接口
     interface iChangeListener{
@@ -52,6 +54,8 @@ class CrImageBrowserWidget @JvmOverloads constructor(
         super.joinControls()
         // todo: 2023/6/16 挂载视图控件
         viewPager = findViewById(R.id.wg_pager)
+        // todo: 2023/8/26 挂载指示图控件
+        wgIndicator = findViewById(R.id.wg_indicator)
         // todo: 2023/6/16 设置适配器
         adapter = BaseAdapter(dataList)
         viewPager?.adapter = adapter
@@ -69,6 +73,8 @@ class CrImageBrowserWidget @JvmOverloads constructor(
             if(fileModel != null){
                 if(listener != null) listener?.onChange(fileModel)
             }
+            // todo: 2023/8/26 设置指示器
+            wgIndicator?.crSetIndex(position)
         }
     }
 
@@ -123,6 +129,7 @@ class CrImageBrowserWidget @JvmOverloads constructor(
          * 初始化
          */
         init {
+            CrUtil.print("初始化...")
             this.dataList = dataList
             initFileList()
         }
@@ -182,11 +189,21 @@ class CrImageBrowserWidget @JvmOverloads constructor(
     }
 
     /**
+     * 初始化
+     */
+    fun crInit(){
+        this.dataList.clear()
+        adapter?.crUpdate(this.dataList)
+        viewPager?.unregisterOnPageChangeCallback(pageChangeListener)
+    }
+
+    /**
      * 添加图片
      * @param imagePath String 图片路径
      */
     fun crAppendImage(imagePath: String) {
         this.dataList.add(imagePath)
+        wgIndicator?.crSetCount(dataList.size)
         adapter?.crUpdate(this.dataList)
         // todo: 2023/6/17 追加了一条数据后注册监听
         if(this.dataList.size == 1){

+ 2 - 0
app/src/main/java/com/cr/widget/CrSpeedWidget.kt

@@ -80,6 +80,8 @@ class CrSpeedWidget @JvmOverloads constructor(
             val floatProgress = attributes.getInt(R.styleable.CrSpeedWidget_speedProgress,0)
             progressAngle = floatProgress
             progressValue = floatProgress
+            // todo: 2023/8/26 使属性生效
+            attributes.recycle()
         }
     }
 

+ 4 - 2
app/src/main/res/layout/dig_normal.xml

@@ -49,12 +49,14 @@
                 style="@style/btn_default"
                 android:background="@drawable/btn_red_selector"
                 android:text="@string/dig_normal_btn_ok"
-                android:id="@+id/dig_btn_ok"/>
+                android:id="@+id/dig_btn_ok"
+                android:minWidth="@dimen/cr_70_dp"/>
             <Button
                 style="@style/btn_default"
                 android:background="@drawable/btn_blue_selector"
                 android:text="@string/dig_normal_btn_cancel"
-                android:id="@+id/dig_btn_cancel"/>
+                android:id="@+id/dig_btn_cancel"
+                android:minWidth="@dimen/cr_70_dp"/>
         </LinearLayout>
     </LinearLayout>
 </LinearLayout>

+ 74 - 42
app/src/main/res/layout/frag_case_upload_main.xml

@@ -1,9 +1,12 @@
 <?xml version="1.0" encoding="utf-8"?>
 <LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-    android:orientation="vertical"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
-    android:background="@drawable/shape_back_fragment">
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    android:background="@drawable/shape_back_fragment"
+    android:orientation="vertical"
+    android:rotationX="-360">
+
     <LinearLayout
         android:layout_width="match_parent"
         android:layout_height="@dimen/cr_40_dp"
@@ -12,99 +15,128 @@
         <LinearLayout
             android:layout_width="match_parent"
             android:layout_height="match_parent"
-            android:orientation="vertical"
-            android:layout_weight="1">
+            android:layout_weight="1"
+            android:orientation="vertical">
+
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_weight="1">
+
                 <ImageView
                     android:layout_width="@dimen/cr_18_dp"
                     android:layout_height="@dimen/cr_18_dp"
-                    android:src="@drawable/ico_media_cur"
-                    android:layout_marginLeft="@dimen/common_margin"/>
+                    android:layout_marginLeft="@dimen/common_margin"
+                    android:src="@drawable/ico_media_cur" />
+
                 <TextView
+                    android:id="@+id/case_name"
                     style="@style/text_case_upload"
-                    android:textColor="@color/white"
-                    android:text="@string/ucm_lbl_name"
                     android:layout_weight="1"
-                    android:textStyle="bold"
-                    android:id="@+id/case_name"/>
+                    android:text="@string/ucm_lbl_name"
+                    android:textColor="@color/white"
+                    android:textStyle="bold" />
+
                 <TextView
+                    android:id="@+id/case_image_count"
                     style="@style/text_case_upload"
-                    android:textColor="@color/green"
                     android:text="@string/ucm_lbl_description"
-                    android:id="@+id/case_image_count"/>
+                    android:textColor="@color/green" />
+
                 <TextView
+                    android:id="@+id/case_create_date"
                     style="@style/text_case_upload"
-                    android:textColor="@color/yellow"
                     android:text="@string/ucm_lbl_date"
-                    android:id="@+id/case_create_date"/>
+                    android:textColor="@color/yellow" />
             </LinearLayout>
-            <View style="@style/view_split_h1"/>
+
+            <View style="@style/view_split_h1" />
+
             <LinearLayout
                 android:layout_width="match_parent"
                 android:layout_height="match_parent"
                 android:layout_weight="1">
+
                 <TextView
                     style="@style/text_case_upload"
                     android:text="@string/ucm_lbl_media_name"
-                    android:textStyle="bold"/>
+                    android:textStyle="bold" />
+
                 <TextView
+                    android:id="@+id/case_media_name"
                     style="@style/text_case_upload"
-                    android:textColor="@color/white"
-                    android:text="@string/ucm_lbl_media_name_value"
                     android:layout_weight="1"
-                    android:id="@+id/case_media_name"/>
+                    android:text="@string/ucm_lbl_media_name_value"
+                    android:textColor="@color/white" />
+
                 <TextView
                     style="@style/text_case_upload"
                     android:text="@string/ucm_lbl_media_size"
-                    android:textStyle="bold"/>
+                    android:textStyle="bold" />
+
                 <TextView
+                    android:id="@+id/case_media_size"
                     style="@style/text_case_upload"
-                    android:textColor="@color/white"
                     android:text="@string/ucm_lbl_media_size_value"
-                    android:id="@+id/case_media_size"/>
+                    android:textColor="@color/white" />
             </LinearLayout>
         </LinearLayout>
-        <View style="@style/view_split_v1"/>
+
+        <View style="@style/view_split_v1" />
+
         <LinearLayout
-            style="@style/button_panel"
-            android:id="@+id/case_btn_edit">
+            android:id="@+id/case_btn_edit"
+            style="@style/button_panel">
+
             <ImageView
                 style="@style/button_image"
-                android:src="@drawable/ty_save"/>
+                android:src="@drawable/ty_save" />
+
             <TextView
                 style="@style/button_title"
-                android:text="@string/ucm_btn_edit"/>
+                android:text="@string/ucm_btn_edit" />
         </LinearLayout>
-        <View style="@style/view_split_v1"/>
+
+        <View style="@style/view_split_v1" />
+
         <LinearLayout
-            style="@style/button_panel"
-            android:id="@+id/case_btn_photo">
+            android:id="@+id/case_btn_photo"
+            style="@style/button_panel">
+
             <ImageView
                 style="@style/button_image"
-                android:src="@drawable/ico_multimage"/>
+                android:src="@drawable/ico_multimage" />
+
             <TextView
                 style="@style/button_title"
-                android:text="@string/ucm_btn_photo"/>
+                android:text="@string/ucm_btn_photo" />
         </LinearLayout>
-        <View style="@style/view_split_v1"/>
+
+        <View style="@style/view_split_v1" />
+
         <LinearLayout
-            style="@style/button_panel"
-            android:id="@+id/case_btn_upload">
+            android:id="@+id/case_btn_upload"
+            style="@style/button_panel">
+
             <ImageView
                 style="@style/button_image"
-                android:src="@drawable/ico_upload_wj"/>
+                android:src="@drawable/ico_upload_wj" />
+
             <TextView
                 style="@style/button_title"
-                android:text="@string/ucm_btn_upload"/>
+                android:text="@string/ucm_btn_upload" />
         </LinearLayout>
     </LinearLayout>
-    <View style="@style/view_split_h1"/>
-    <com.cr.widget.CrImageBrowserWidget
+
+    <View style="@style/view_split_h1" />
+
+    <RelativeLayout
         android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:layout_weight="1"
-        android:id="@+id/case_image_browser"/>
+        android:layout_height="0dp"
+        android:layout_weight="1">
+        <com.cr.widget.CrImageBrowserWidget
+            android:id="@+id/case_image_browser"
+            android:layout_width="match_parent"
+            android:layout_height="match_parent" />
+    </RelativeLayout>
 </LinearLayout>

+ 12 - 6
app/src/main/res/layout/frag_case_upload_upload.xml

@@ -26,28 +26,32 @@
                     android:text="@string/up_longitude"/>
                 <TextView
                     style="@style/lbl_value_common"
-                    android:text="@string/value_6"/>
+                    android:text="@string/value_6"
+                    android:id="@+id/lbl_longitude"/>
                 <TextView
                     style="@style/lbl_title_common"
                     android:text="@string/up_latitude"
                     android:layout_marginStart="@dimen/cr_10_dp"/>
                 <TextView
                     style="@style/lbl_value_common"
-                    android:text="@string/value_6"/>
+                    android:text="@string/value_6"
+                    android:id="@+id/lbl_latitude"/>
                 <TextView
                     style="@style/lbl_title_common"
                     android:text="@string/up_yaw"
                     android:layout_marginStart="@dimen/cr_10_dp"/>
                 <TextView
                     style="@style/lbl_value_common"
-                    android:text="@string/value_3"/>
+                    android:text="@string/value_3"
+                    android:id="@+id/lbl_angle"/>
                 <TextView
                     style="@style/lbl_title_common"
                     android:text="@string/up_altitude"
                     android:layout_marginStart="@dimen/cr_10_dp"/>
                 <TextView
                     style="@style/lbl_value_common"
-                    android:text="@string/value_3"/>
+                    android:text="@string/value_3"
+                    android:id="@+id/lbl_altitude"/>
             </LinearLayout>
             <View style="@style/view_split_h1"/>
             <LinearLayout
@@ -125,7 +129,8 @@
                     android:layout_weight="1">
                     <com.cr.widget.CrImageBrowserWidget
                         android:layout_width="match_parent"
-                        android:layout_height="match_parent"/>
+                        android:layout_height="match_parent"
+                        android:id="@+id/image_browser"/>
                 </LinearLayout>
             </LinearLayout>
         </LinearLayout>
@@ -232,7 +237,8 @@
                 android:layout_marginLeft="@dimen/common_margin"
                 android:layout_marginRight="@dimen/common_margin"
                 android:background="@drawable/btn_red_selector"
-                android:clickable="true">
+                android:clickable="true"
+                android:id="@+id/btn_submit">
                 <com.cr.view.CrICON
                     android:layout_width="wrap_content"
                     android:layout_height="wrap_content"

+ 11 - 9
app/src/main/res/layout/wg_image_browser.xml

@@ -11,16 +11,18 @@
         app:layout_constraintLeft_toLeftOf="parent"
         app:layout_constraintRight_toRightOf="parent"
         app:layout_constraintTop_toTopOf="parent"/>
-    <androidx.constraintlayout.widget.Guideline
-        android:layout_width="match_parent"
-        android:layout_height="wrap_content"
-        android:id="@+id/wg_line"
-        android:orientation="horizontal"
-        app:layout_constraintGuide_percent="0.95"/>
-    <androidx.constraintlayout.widget.ConstraintLayout
+    <LinearLayout
         android:layout_width="wrap_content"
         android:layout_height="wrap_content"
-        app:layout_constraintTop_toBottomOf="@id/wg_line"
         app:layout_constraintRight_toRightOf="parent"
-        android:id="@+id/wg_indicator"/>
+        app:layout_constraintBottom_toBottomOf="parent">
+        <com.cr.view.CrIndicator
+            android:layout_width="match_parent"
+            android:layout_height="@dimen/cr_24_dp"
+            android:layout_alignParentBottom="true"
+            android:layout_marginBottom="@dimen/cr_10_dp"
+            android:id="@+id/wg_indicator"
+            app:cr_count="1"
+            app:cr_index="0"/>
+    </LinearLayout>
 </androidx.constraintlayout.widget.ConstraintLayout>

+ 6 - 0
app/src/main/res/values/themes.xml

@@ -447,4 +447,10 @@
     <declare-styleable name="CrICON">
         <attr name="cr_font" format="string"></attr>
     </declare-styleable>
+
+    <!--指示器组件-->
+    <declare-styleable name="CrIndicator">
+        <attr name="cr_count" format="integer"></attr>
+        <attr name="cr_index" format="integer"></attr>
+    </declare-styleable>
 </resources>