diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ee25edab..53e768a9 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -29,6 +29,7 @@
     <uses-permission android:name="android.permission.READ_PHONE_STATE" />
     <!-- 读取缓存数据 -->
     <uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
+    <uses-permission android:name="android.permission.RECORD_AUDIO" />
     <!--android:largeHeap="true" 大内存 128M -->
     <application
         android:name=".OMQSApplication"
diff --git a/app/src/main/java/com/navinfo/omqs/Constant.kt b/app/src/main/java/com/navinfo/omqs/Constant.kt
index e74563a6..43bad93b 100644
--- a/app/src/main/java/com/navinfo/omqs/Constant.kt
+++ b/app/src/main/java/com/navinfo/omqs/Constant.kt
@@ -32,6 +32,11 @@ class Constant {
          */
         lateinit var USER_DATA_PATH: String
 
+        /**
+         * 用户附件数据目录
+         */
+        lateinit var USER_DATA_ATTACHEMNT_PATH: String
+
         /**
          * 离线地图目录
          */
@@ -49,6 +54,8 @@ class Constant {
 
         const val DEBUG = true
 
+        var IS_VIDEO_SPEED by kotlin.properties.Delegates.notNull<Boolean>()
+
         const val message_status_late = "预约,待发送"
         const val message_status_send_over = "已发送"
         const val message_version_right_off = "1" //立即发送
diff --git a/app/src/main/java/com/navinfo/omqs/bean/Attachment.kt b/app/src/main/java/com/navinfo/omqs/bean/Attachment.kt
new file mode 100644
index 00000000..11088a69
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/bean/Attachment.kt
@@ -0,0 +1,43 @@
+package com.navinfo.omqs.bean
+
+import java.io.Serializable
+import java.util.*
+
+class Attachment(filename: String, type: Int) : Serializable,
+    Cloneable {
+    //内容
+    var filename: String = ""
+
+    //标识 默认照片0    录音1
+    var type: Int
+
+    override fun toString(): String {
+        return "TipsAttachment{" +
+                "filename='" + filename + '\'' +
+                ", type=" + type +
+                '}'
+    }
+
+    override fun equals(o: Any?): Boolean {
+        if (this === o) return true
+        if (o == null || javaClass != o.javaClass) return false
+        val that = o as Attachment
+        return type == that.type &&
+                filename == that.filename
+    }
+
+    override fun hashCode(): Int {
+        return Objects.hash(filename, type)
+    }
+
+    @kotlin.Throws(CloneNotSupportedException::class)
+    public override fun clone(): Any {
+        return super.clone()
+    }
+
+    init {
+        this.filename = filename
+        this.type = type
+    }
+}
+
diff --git a/app/src/main/java/com/navinfo/omqs/bean/ChatMsgEntity.kt b/app/src/main/java/com/navinfo/omqs/bean/ChatMsgEntity.kt
new file mode 100644
index 00000000..1a6f4548
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/bean/ChatMsgEntity.kt
@@ -0,0 +1,23 @@
+package com.navinfo.omqs.bean
+
+import java.io.Serializable
+
+class ChatMsgEntity : Serializable, Cloneable {
+    var voiceUri //声音存储地址
+            : String? = null
+    var voiceTimeLong //声音时间长度
+            : String? = null
+    var name //声音名字
+            : String? = null
+    var isDelete //是否被删除
+            = false
+
+    @kotlin.Throws(CloneNotSupportedException::class)
+    public override fun clone(): Any {
+        return super.clone()
+    }
+
+    companion object {
+        private val TAG: String = ChatMsgEntity::class.java.getSimpleName()
+    }
+}
diff --git a/app/src/main/java/com/navinfo/omqs/ui/activity/CheckPermissionsActivity.java b/app/src/main/java/com/navinfo/omqs/ui/activity/CheckPermissionsActivity.java
index be35ad09..6690ea72 100644
--- a/app/src/main/java/com/navinfo/omqs/ui/activity/CheckPermissionsActivity.java
+++ b/app/src/main/java/com/navinfo/omqs/ui/activity/CheckPermissionsActivity.java
@@ -37,6 +37,7 @@ public class CheckPermissionsActivity extends BaseActivity {
 			Manifest.permission.ACCESS_FINE_LOCATION,
 			Manifest.permission.WRITE_EXTERNAL_STORAGE,
 			Manifest.permission.READ_EXTERNAL_STORAGE,
+			Manifest.permission.RECORD_AUDIO
 	};
 
 	private static final int PERMISSON_REQUESTCODE = 0;
@@ -51,6 +52,7 @@ public class CheckPermissionsActivity extends BaseActivity {
 					Manifest.permission.ACCESS_FINE_LOCATION,
 					Manifest.permission.WRITE_EXTERNAL_STORAGE,
 					Manifest.permission.READ_EXTERNAL_STORAGE,
+					Manifest.permission.RECORD_AUDIO,
 					BACKGROUND_LOCATION_PERMISSION
 			};
 		}
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 94d4f611..1b054ca1 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
@@ -156,12 +156,17 @@ class LoginViewModel @Inject constructor(
      * 创建用户目录
      */
     private fun createUserFolder(context: Context, userId: String) {
+        Constant.IS_VIDEO_SPEED = false
         Constant.USER_ID = userId
         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/"
         // 在SD卡创建用户目录,解压资源等
         val userFolder = File(Constant.USER_DATA_PATH)
         if (!userFolder.exists()) userFolder.mkdirs()
+        //创建附件目录
+        val userAttachmentFolder = File(Constant.USER_DATA_ATTACHEMNT_PATH)
+        if (!userAttachmentFolder.exists()) userAttachmentFolder.mkdirs()
         // 初始化Realm
         Realm.init(context.applicationContext)
         val password = "encryp".encodeToByteArray().copyInto(ByteArray(64))
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 44732ade..cb0a6eb3 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
@@ -1,7 +1,12 @@
 package com.navinfo.omqs.ui.activity.map
 
+import android.os.Build
 import android.os.Bundle
+import android.util.Log
+import android.view.MotionEvent
+import android.view.View
 import androidx.activity.viewModels
+import androidx.annotation.RequiresApi
 import androidx.core.view.WindowCompat
 import androidx.databinding.DataBindingUtil
 import androidx.navigation.findNavController
@@ -52,6 +57,26 @@ class MainActivity : BaseActivity() {
         //给xml传递viewModel对象
         binding.viewModel = viewModel
 
+        binding.mainActivityVoice.setOnTouchListener(object : View.OnTouchListener {
+            @RequiresApi(Build.VERSION_CODES.Q)
+            override fun onTouch(v: View?, event: MotionEvent?): Boolean {
+                Log.e("qj",event?.action.toString())
+                when (event?.action) {
+                    MotionEvent.ACTION_DOWN ->{
+                        voiceOnTouchStart()//Do Something
+                        Log.e("qj","voiceOnTouchStart")
+                    }
+                    MotionEvent.ACTION_UP ->{
+                        voiceOnTouchStop()//Do Something
+                        Log.e("qj","voiceOnTouchStop")
+                    }
+                }
+
+
+                return v?.onTouchEvent(event) ?: true
+            }
+        })
+
         viewModel.liveDataQsRecordIdList.observe(this) {
             //处理页面跳转
             viewModel.navigation(this, it)
@@ -68,8 +93,8 @@ class MainActivity : BaseActivity() {
         mapController.locationLayerHandler.setNiLocationListener(NiLocationListener {
             //ToastUtils.showLong("定位${it.longitude}")
             binding!!.viewModel!!.addSaveTrace(it)
-            binding!!.viewModel!!.startSaveTraceThread(this)
         })
+        binding!!.viewModel!!.startSaveTraceThread(this)
         //显示轨迹图层
 //        mapController.layerManagerHandler.showNiLocationLayer(Constant.DATA_PATH+ SystemConstant.USER_ID+"/trace.sqlite")
         mapController.layerManagerHandler.showNiLocationLayer()
@@ -110,8 +135,19 @@ class MainActivity : BaseActivity() {
      * 点击录音按钮
      */
     fun voiceOnclick() {
-        val naviController = findNavController(R.id.main_activity_right_fragment)
-        naviController.navigate(R.id.EvaluationResultFragment)
+/*        val naviController = findNavController(R.id.main_activity_right_fragment)
+        naviController.navigate(R.id.EvaluationResultFragment)*/
+    }
+
+    fun voiceOnTouchStart(){
+        binding!!.viewModel!!.startSoundMetter(this,mapController.locationLayerHandler.getCurrentNiLocation(),binding.mainActivityVoice)
+    }
+
+    @RequiresApi(Build.VERSION_CODES.Q)
+    fun voiceOnTouchStop(){
+        if(Constant.IS_VIDEO_SPEED){
+            binding!!.viewModel!!.stopSoundMeter()
+        }
     }
 
 //    override fun onBackPressed() {
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 e1bc8b5f..8447afb9 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
@@ -1,12 +1,24 @@
 package com.navinfo.omqs.ui.activity.map
 
+import android.app.Activity
 import android.content.Context
 import android.content.DialogInterface
+import android.graphics.drawable.AnimationDrawable
+import android.graphics.drawable.BitmapDrawable
+import android.os.Build
 import android.os.Bundle
+import android.text.TextUtils
 import android.util.Log
+import android.view.Gravity
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import android.widget.PopupWindow
+import androidx.annotation.RequiresApi
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.navigation.findNavController
+import com.blankj.utilcode.util.ToastUtils
 import com.navinfo.collect.library.data.dao.impl.TraceDataBase
 import com.navinfo.collect.library.data.entity.NiLocation
 import com.navinfo.collect.library.map.NIMapController
@@ -17,10 +29,15 @@ import com.navinfo.omqs.Constant
 import com.navinfo.omqs.R
 import com.navinfo.omqs.ui.dialog.CommonDialog
 import com.navinfo.omqs.ui.manager.TakePhotoManager
+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.RealmSet
 import org.oscim.core.GeoPoint
 import org.videolan.libvlc.LibVlcUtil
+import java.io.File
+import java.util.*
 import javax.inject.Inject
 
 /**
@@ -28,14 +45,23 @@ import javax.inject.Inject
  */
 @HiltViewModel
 class MainViewModel @Inject constructor(
-    private val mapController: NIMapController,
+    private val mapController: NIMapController
 ) : ViewModel() {
 
     val liveDataQsRecordIdList = MutableLiveData<List<String>>()
     private var mCameraDialog: CommonDialog? = null
 
+    //语音窗体
+    private var pop: PopupWindow? = null
+
+    private var mSpeakMode: SpeakMode? = null
+
     private var niLocationList: MutableList<NiLocation> = ArrayList<NiLocation>()
 
+    //录音图标
+    var volume: ImageView? = null
+    var mSoundMeter: SoundMeter? = null
+
     init {
         mapController.markerHandle.setOnQsRecordItemClickListener(object :
             OnQsRecordItemClickListener {
@@ -43,6 +69,7 @@ class MainViewModel @Inject constructor(
                 liveDataQsRecordIdList.value = list
             }
         })
+
     }
 
     /**
@@ -61,8 +88,6 @@ class MainViewModel @Inject constructor(
 
         Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString())
 
-        //ToastUtils.showShort("点击了相机")
-
         if (mCameraDialog == null) {
             mCameraDialog = CommonDialog(
                 context,
@@ -100,11 +125,15 @@ class MainViewModel @Inject constructor(
         Thread(Runnable {
             try {
                 while (true) {
-
                     if (niLocationList != null && niLocationList.size > 0) {
 
                         var niLocation = niLocationList[0]
-                        val geometry = GeometryTools.createGeometry(GeoPoint(niLocation.latitude,niLocation.longitude))
+                        val geometry = GeometryTools.createGeometry(
+                            GeoPoint(
+                                niLocation.latitude,
+                                niLocation.longitude
+                            )
+                        )
                         val tileX = RealmSet<Int>()
                         GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX)
                         val tileY = RealmSet<Int>()
@@ -118,11 +147,16 @@ class MainViewModel @Inject constructor(
                             }
                         }
 
-                        TraceDataBase.getDatabase(context, Constant.USER_DATA_PATH + "/trace.sqlite").niLocationDao.insert(niLocation)
-
+                        TraceDataBase.getDatabase(
+                            context,
+                            Constant.USER_DATA_PATH + "/trace.sqlite"
+                        ).niLocationDao.insert(niLocation)
+                        val list = TraceDataBase.getDatabase(
+                            context,
+                            Constant.USER_DATA_PATH + "/trace.sqlite"
+                        ).niLocationDao.findAll()
                         niLocationList.remove(niLocation)
-
-                        Log.e("qj", "saveTrace==${niLocationList.size}")
+                        Log.e("qj", "saveTrace==${niLocationList.size}===${list.size}")
 
                     }
                     Thread.sleep(30)
@@ -141,6 +175,90 @@ class MainViewModel @Inject constructor(
         }
     }
 
+    fun startSoundMetter(context: Context, niLocation: NiLocation?, v: View) {
+        if (niLocation == null) {
+            ToastUtils.showLong("未获取到GPS信息,请检查GPS是否正常!")
+            //停止录音动画
+            if (pop != null && pop!!.isShowing())
+                pop!!.dismiss();
+            return;
+        }
+
+        if(mSpeakMode==null){
+            mSpeakMode = SpeakMode(context as 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(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("结束录音")
+                //获取右侧fragment容器
+                val naviController = (context as Activity).findNavController(R.id.main_activity_right_fragment)
+                val bundle = Bundle()
+                bundle.putString("filePath", filePath)
+                naviController.navigate(R.id.EvaluationResultFragment, bundle)
+            }
+
+            @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()
+    }
+
+
     fun navigation(activity: MainActivity, list: List<String>) {
         //获取右侧fragment容器
         val naviController = activity.findNavController(R.id.main_activity_right_fragment)
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 65ca7324..c0ff35a9 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
@@ -7,9 +7,11 @@ import android.view.ViewGroup
 import androidx.databinding.DataBindingUtil
 import androidx.navigation.NavOptions
 import androidx.navigation.findNavController
+import androidx.recyclerview.widget.LinearLayoutManager
 import com.navinfo.omqs.R
 import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding
 import com.navinfo.omqs.ui.fragment.BaseFragment
+import com.navinfo.omqs.ui.fragment.tasklist.TaskListAdapter
 import com.navinfo.omqs.ui.other.shareViewModels
 import dagger.hilt.android.AndroidEntryPoint
 
@@ -17,15 +19,26 @@ import dagger.hilt.android.AndroidEntryPoint
 class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
     private lateinit var binding: FragmentEvaluationResultBinding
     private val viewModel by shareViewModels<EvaluationResultViewModel>("QsRecode")
-
+    private val adapter: SoundtListAdapter by lazy {
+        SoundtListAdapter()
+    }
     override fun onCreateView(
         inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
     ): View {
         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
+        binding.evaluationVoiceRecyclerview.adapter = adapter
+        viewModel.listDataChatMsgEntityList.observe(viewLifecycleOwner) {
+            adapter.refreshData(it)
+        }
+        viewModel.getChatMsgEntityList()
         return binding.root
     }
 
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 f1914053..50233488 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
@@ -1,12 +1,17 @@
 package com.navinfo.omqs.ui.fragment.evaluationresult
 
+import android.os.Build
 import android.util.Log
+import androidx.annotation.RequiresApi
 import androidx.lifecycle.MutableLiveData
 import androidx.lifecycle.ViewModel
 import androidx.lifecycle.viewModelScope
 import com.navinfo.collect.library.data.entity.QsRecordBean
 import com.navinfo.collect.library.map.NIMapController
 import com.navinfo.collect.library.utils.GeometryTools
+import com.navinfo.omqs.Constant
+import com.navinfo.omqs.bean.Attachment
+import com.navinfo.omqs.bean.ChatMsgEntity
 import com.navinfo.omqs.db.RealmOperateHelper
 import com.navinfo.omqs.db.RoomAppDatabase
 import dagger.hilt.android.lifecycle.HiltViewModel
@@ -14,7 +19,6 @@ import io.realm.Realm
 import io.realm.kotlin.where
 import kotlinx.coroutines.Dispatchers
 import kotlinx.coroutines.launch
-import org.locationtech.jts.geom.Point
 import java.util.*
 import javax.inject.Inject
 
@@ -47,9 +51,12 @@ class EvaluationResultViewModel @Inject constructor(
      */
     val liveDataRightTypeList = MutableLiveData<List<RightBean>>()
 
-
     var liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
 
+    var listDataChatMsgEntityList = MutableLiveData<MutableList<ChatMsgEntity>>()
+
+    var listDataAttachmentList = MutableLiveData<MutableList<Attachment>>()
+
     var oldBean: QsRecordBean? = null
 
     init {
@@ -274,4 +281,22 @@ class EvaluationResultViewModel @Inject constructor(
             }
         }
     }
+
+    /**
+     * 查询问题类型列表
+     */
+    @RequiresApi(Build.VERSION_CODES.N)
+    fun getChatMsgEntityList() {
+        val chatMsgEntityList: MutableList<ChatMsgEntity> = ArrayList()
+        liveDataQsRecordBean.value!!.attachments.forEach{
+            //1 录音
+            if(it.type==1){
+                val chatMsgEntity = ChatMsgEntity()
+                chatMsgEntity.name = it.filename
+                chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH
+                chatMsgEntityList.add(chatMsgEntity)
+            }
+        }
+        listDataChatMsgEntityList.postValue(chatMsgEntityList)
+    }
 }
\ No newline at end of file
diff --git a/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/SoundtListAdapter.kt b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/SoundtListAdapter.kt
new file mode 100644
index 00000000..8b39ef35
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/SoundtListAdapter.kt
@@ -0,0 +1,241 @@
+package com.navinfo.omqs.ui.fragment.evaluationresult
+
+import android.graphics.drawable.AnimationDrawable
+import android.media.AudioFormat
+import android.media.AudioManager
+import android.media.AudioTrack
+import android.media.MediaPlayer
+import android.text.TextUtils
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.ImageView
+import com.navinfo.omqs.R
+import com.navinfo.omqs.bean.ChatMsgEntity
+import com.navinfo.omqs.databinding.AdapterSoundListBinding
+import com.navinfo.omqs.ui.other.BaseRecyclerViewAdapter
+import com.navinfo.omqs.ui.other.BaseViewHolder
+import java.io.*
+
+/**
+ * 语音 RecyclerView 适配器
+ *
+ * 在 RecycleView 的 ViewHolder 中监听 ViewModel 的 LiveData,然后此时传递的 lifecycleOwner 是对应的 Fragment。由于 ViewHolder 的生命周期是比 Fragment 短的,所以当 ViewHolder 销毁时,由于 Fragment 的 Lifecycle 还没有结束,此时 ViewHolder 会发生内存泄露(监听的 LiveData 没有解绑)
+ *   这种场景下有两种解决办法:
+ *使用 LiveData 的 observeForever 然后在 ViewHolder 销毁前手动调用 removeObserver
+ *使用 LifecycleRegistry 给 ViewHolder 分发生命周期(这里使用了这个)
+ */
+class SoundtListAdapter(
+) : BaseRecyclerViewAdapter<ChatMsgEntity>() {
+
+    //媒体播放器
+    private val mMediaPlayer = MediaPlayer()
+
+    //媒体播放器
+    private var md: MediaPlayer? = null
+
+    private var mAudioTrack: AudioTrack? = null
+
+    //录音结束后,右侧显示图片
+    private var animView: View? = null
+
+    //录音时动画效果
+    private var animaV: AnimationDrawable? = null
+
+    private var itemClick: OnItemClickListner? = null
+
+    //最大宽度
+    private val maxWidth = 0
+
+    override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BaseViewHolder {
+        val viewBinding =
+            AdapterSoundListBinding.inflate(LayoutInflater.from(parent.context), parent, false)
+        return BaseViewHolder(viewBinding)
+    }
+
+    override fun onViewRecycled(holder: BaseViewHolder) {
+        super.onViewRecycled(holder)
+
+    }
+
+    override fun onBindViewHolder(holder: BaseViewHolder, position: Int) {
+        val binding: AdapterSoundListBinding =
+            holder.viewBinding as AdapterSoundListBinding
+        val entity = data[position]
+        //tag 方便onclick里拿到数据
+        holder.tag = entity.name.toString()
+        holder.viewBinding.tvTime.isSelected =  entity.isDelete
+        holder.viewBinding.rlSoundContent.isSelected = entity.isDelete
+        holder.viewBinding.ivSoundAnim.setBackgroundResource(R.drawable.icon_sound_03)
+        if (itemClick != null) {
+            holder.viewBinding.rlSoundContent.setOnClickListener {
+                itemClick!!.onItemClick(it.findViewById<View>(R.id.rl_sound_content), position)
+            }
+        }
+        //mixWidth
+        if (!TextUtils.isEmpty(entity.name)) {
+            if (entity.name.indexOf(".pcm") > 0) {
+                val file: File = File(entity.voiceUri + entity.name)
+                if (file != null) {
+                    val time = (file.length() / 16000).toInt()
+                    val layoutParams: ViewGroup.LayoutParams =
+                        holder.viewBinding.rlSoundContent.getLayoutParams()
+                    layoutParams.width = 115 + time * 10
+                    layoutParams.width =
+                        if (layoutParams.width > layoutParams.width) maxWidth else layoutParams.width
+                    holder.viewBinding.rlSoundContent.setLayoutParams(layoutParams)
+                    holder.viewBinding.tvTime.text = time.toString() + "\""
+                }
+            } else {
+                try {
+                    md = MediaPlayer()
+                    md.reset()
+                    md.setDataSource(entity.getVoiceUri() + entity.getName())
+                    md.prepare()
+                } catch (e: Exception) {
+                    // TODO Auto-generated catch block
+                    e.printStackTrace()
+                }
+                var time =
+                    if (entity.getVoiceTimeLong() == null) md!!.duration.toString() + "" else entity.getVoiceTimeLong()
+                        .toString() + ""
+                if (!TextUtils.isEmpty(time)) {
+                    val i = md!!.duration / 1000
+                    time = i.toString() + "\""
+                    val layoutParams: ViewGroup.LayoutParams =
+                        holder.viewBinding.rlSoundContent.getLayoutParams()
+                    layoutParams.width = 115 + i * 10
+                    layoutParams.width =
+                        if (layoutParams.width > layoutParams.width) maxWidth else layoutParams.width
+                    holder.viewBinding.rlSoundContent.setLayoutParams(layoutParams)
+                }
+                holder.viewBinding.tvTime.text = time
+                md!!.release()
+            }
+    }
+
+
+    override fun getItemViewRes(position: Int): Int {
+        return R.layout.adapter_sound_list
+    }
+
+    /**
+     * 播放某段录音
+     *
+     * @param view  显示动画
+     * @param index 录音在集合中索引
+     */
+    fun setPlayerIndex(view: View, index: Int) {
+        val imageV = view.findViewById<View>(R.id.iv_sound_anim) as ImageView
+        val width = view.width
+        if (animView != null) {
+            animaV?.stop()
+            animView!!.setBackgroundResource(R.drawable.icon_sound_03)
+        }
+        animView = imageV
+        val entity: ChatMsgEntity = data.get(index)
+        playMusic(entity.voiceUri + entity.name, imageV)
+    }
+
+    /**
+     * 播放录音
+     *
+     * @param name 录音名称
+     * @Description
+     */
+    private fun playMusic(name: String, imageV: ImageView) {
+        imageV.setBackgroundResource(R.drawable.sound_anim)
+        animaV = imageV.background as AnimationDrawable
+        animaV!!.start()
+        if (name.indexOf(".pcm") > 0) {
+            audioTrackPlay(name, imageV)
+        } else {
+            mediaPlayer(name, imageV)
+        }
+    }
+
+    private fun mediaPlayer(name: String, imageV: ImageView) {
+        try {
+            if (mMediaPlayer.isPlaying) {
+                mMediaPlayer.stop()
+            }
+            mMediaPlayer.reset()
+            mMediaPlayer.setDataSource(name)
+            mMediaPlayer.prepare()
+            mMediaPlayer.start()
+            //播放结束
+            mMediaPlayer.setOnCompletionListener {
+                animaV!!.stop()
+                imageV.setBackgroundResource(R.drawable.icon_sound_03)
+            }
+        } catch (e: Exception) {
+            e.printStackTrace()
+        }
+    }
+
+    private fun audioTrackPlay(name: String, imageV: ImageView) {
+        var dis: DataInputStream? = null
+        try {
+            //从音频文件中读取声音
+            dis = DataInputStream(BufferedInputStream(FileInputStream(name)))
+        } catch (e: FileNotFoundException) {
+            e.printStackTrace()
+        }
+        //最小缓存区
+        val bufferSizeInBytes = AudioTrack.getMinBufferSize(
+            16000,
+            AudioFormat.CHANNEL_OUT_MONO,
+            AudioFormat.ENCODING_PCM_16BIT
+        )
+        //创建AudioTrack对象   依次传入 :流类型、采样率(与采集的要一致)、音频通道(采集是IN 播放时OUT)、量化位数、最小缓冲区、模式
+        if (mAudioTrack != null) {
+            mAudioTrack!!.stop()
+            mAudioTrack!!.release()
+            mAudioTrack = null
+        }
+        mAudioTrack = AudioTrack(
+            AudioManager.STREAM_MUSIC,
+            16000,
+            AudioFormat.CHANNEL_OUT_MONO,
+            AudioFormat.ENCODING_PCM_16BIT,
+            bufferSizeInBytes,
+            AudioTrack.MODE_STREAM
+        )
+        val data = ByteArray(bufferSizeInBytes)
+        mAudioTrack!!.play() //开始播放
+        while (true) {
+            var i = 0
+            try {
+                while (dis!!.available() > 0 && i < data.size) {
+                    data[i] = dis.readByte() //录音时write Byte 那么读取时就该为readByte要相互对应
+                    i++
+                }
+            } catch (e: IOException) {
+                // TODO Auto-generated catch block
+                e.printStackTrace()
+            }
+            mAudioTrack!!.write(data, 0, data.size)
+            if (i != bufferSizeInBytes) //表示读取完了
+            {
+                break
+            }
+        }
+        mAudioTrack!!.stop() //停止播放
+        mAudioTrack!!.release() //释放资源
+        mAudioTrack = null
+        imageV.post {
+            animaV?.stop()
+            imageV.setBackgroundResource(R.drawable.icon_sound_03)
+        }
+    }
+
+    fun setOnItemClickListener(clickListner: OnItemClickListner) {
+        itemClick = clickListner
+    }
+
+    interface OnItemClickListner {
+        fun onItemClick(view: View?, postion: Int)
+    }
+}
+
+
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 5195f2d3..53e75ae7 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
@@ -198,7 +198,7 @@ class TaskListAdapter(
     }
 
     override fun getItemViewRes(position: Int): Int {
-        return R.layout.adapter_offline_map_city
+        return R.layout.adapter_task_list
     }
 }
 
diff --git a/app/src/main/java/com/navinfo/omqs/util/SoundMeter.java b/app/src/main/java/com/navinfo/omqs/util/SoundMeter.java
new file mode 100644
index 00000000..44efd859
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/util/SoundMeter.java
@@ -0,0 +1,161 @@
+package com.navinfo.omqs.util;
+
+import android.media.MediaRecorder;
+import android.os.Environment;
+import android.text.TextUtils;
+import android.util.Log;
+import java.io.File;
+import java.io.IOException;
+
+/**
+ * 录音接口
+ */
+public class SoundMeter {
+    static final private double EMA_FILTER = 0.6;
+
+    private static final String TAG = "SoundMeter";
+    private String mFilePath;
+    private MediaRecorder mRecorder = null;
+    private double mEMA = 0.0;
+    //监听
+    private OnSoundMeterListener mListener;
+    //是否开启了语音录制
+    private boolean isStartSound;
+    /**
+     * 开始录音
+     *
+     * @param name 录音文件保存路径
+     */
+    public void start(final String name) {
+        mFilePath = name;
+        isStartSound = false;
+        //执行录音操作
+        if (!Environment.getExternalStorageState().equals(
+                Environment.MEDIA_MOUNTED) || TextUtils.isEmpty(name)) {
+            if(mListener!=null)
+                mListener.onfaild("权限失败或者文件名称错误");
+            return;
+        }
+
+        if (mRecorder == null) {
+            mRecorder = new MediaRecorder();
+            mRecorder.setAudioSource(MediaRecorder.AudioSource.MIC);
+            mRecorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4);
+            mRecorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC);
+            mRecorder.setOutputFile(name);
+            Log.w(TAG, "录音" + name);
+
+            try {
+                mRecorder.prepare();
+                mRecorder.start();
+                mEMA = 0.0;
+                isStartSound = true;
+            } catch (IllegalStateException e) {
+                if(mListener!=null)
+                    mListener.onfaild(e.getMessage());
+                if (mRecorder != null)
+                    mRecorder.release();
+                //启动异常释放资源
+                isStartSound = false;
+                mRecorder = null;
+                System.out.print(e.getMessage());
+            } catch (IOException e) {
+                System.out.print(e.getMessage());
+                if(mListener!=null)
+                    mListener.onfaild(e.getMessage());
+                //启动异常释放资源
+                isStartSound = false;
+                if (mRecorder != null)
+                    mRecorder.release();
+                mRecorder = null;
+            }finally {
+
+            }
+        }
+    }
+
+    /**
+     * 结束录音接,释放录音对象
+     */
+    public void stop() {
+        isStartSound = false;
+        try {
+            if (mRecorder != null) {
+                mRecorder.stop();
+            }
+            if(new File(mFilePath).exists()){
+                if(mListener!=null)
+                    mListener.onSuccess(mFilePath);
+            }
+        } catch (Exception e) {
+            if(mListener!=null)
+                mListener.onfaild(e.getMessage());
+        } finally {
+            if (mRecorder != null)
+                mRecorder.release();
+            mRecorder = null;
+        }
+    }
+
+    /**
+     * 停止录音
+     */
+    public void pause() {
+        if (mRecorder != null) {
+            mRecorder.stop();
+        }
+    }
+
+    /**
+     * 开始录音
+     */
+    public void start() {
+        if (mRecorder != null) {
+            mRecorder.start();
+        }
+    }
+
+    /**
+     * 获取录音基准值
+     *
+     * @return
+     */
+    public double getAmplitude() {
+        if (mRecorder != null)
+            return (mRecorder.getMaxAmplitude() / 2700.0);
+        else
+            return 0;
+
+    }
+
+    /**
+     * 获取EMA基准值
+     *
+     * @return
+     */
+    public double getAmplitudeEMA() {
+        double amp = getAmplitude();
+        mEMA = EMA_FILTER * amp + (1.0 - EMA_FILTER) * mEMA;
+        return mEMA;
+    }
+
+    public OnSoundMeterListener getmListener() {
+        return mListener;
+    }
+
+    public void setmListener(OnSoundMeterListener mListener) {
+        this.mListener = mListener;
+    }
+
+    //是否开启了语音录制
+    public boolean isStartSound(){
+        return isStartSound;
+    }
+
+    //录音监听
+    public interface OnSoundMeterListener{
+        public void onSuccess(String filePath);
+        public void onfaild(String message);
+    }
+
+}
diff --git a/app/src/main/java/com/navinfo/omqs/util/SoundRecordeUtils.java b/app/src/main/java/com/navinfo/omqs/util/SoundRecordeUtils.java
new file mode 100644
index 00000000..1a902cad
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/util/SoundRecordeUtils.java
@@ -0,0 +1,129 @@
+package com.navinfo.omqs.util;
+
+import android.app.Activity;
+import android.graphics.drawable.AnimationDrawable;
+import android.graphics.drawable.BitmapDrawable;
+import android.view.Gravity;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.ImageView;
+import android.widget.PopupWindow;
+import android.widget.Toast;
+import com.blankj.utilcode.util.ToastUtils;
+import com.navinfo.omqs.Constant;
+import com.navinfo.omqs.R;
+import java.io.File;
+
+public class SoundRecordeUtils {
+    private static SoundRecordeUtils instance;
+    private SoundMeter mSensor; // 系统录音组件
+    private PopupWindow pop;
+    private ImageView volume;
+    private SpeakMode mSpeakMode;
+    private Activity mActivity;
+
+    public static SoundRecordeUtils getInstance(Activity context) {
+        if (instance == null) {
+            instance = new SoundRecordeUtils(context);
+        }
+        return instance;
+    }
+
+    public SoundRecordeUtils(Activity mContext) {
+        this.mActivity = mContext;
+        mSpeakMode = new SpeakMode(mContext);
+        initVoicePop();
+    }
+
+    private void initVoicePop() {
+        //语音识别动画
+        pop = new PopupWindow();
+        pop.setWidth(ViewGroup.LayoutParams.MATCH_PARENT);
+        pop.setHeight(ViewGroup.LayoutParams.WRAP_CONTENT);
+        pop.setBackgroundDrawable(new BitmapDrawable());
+        View view = View.inflate(mActivity, R.layout.cv_card_voice_rcd_hint_window, null);
+        pop.setContentView(view);
+
+        pop.update();
+        volume = (ImageView) view.findViewById(R.id.volume);
+    }
+
+    //启动录音
+    public String startSoundMeter(View v, SoundRecordeCallback soundRecordeCallback, boolean isRemind/*是否需要默认语音提醒开始和结束录音*/) {
+        //录音动画
+        if (pop != null)
+            pop.showAtLocation(v, Gravity.CENTER, 0, 0);
+        volume.setBackgroundResource(R.drawable.pop_voice_img);
+        AnimationDrawable animation = (AnimationDrawable) volume.getBackground();
+        animation.start();
+
+        final String name = DateTimeUtil.getTimeSSS() + ".m4a";
+        if (mSensor == null) {
+            mSensor = new SoundMeter();
+        }
+        mSensor.setmListener(new SoundMeter.OnSoundMeterListener() {
+            @Override
+            public void onSuccess(String filePath) {
+                if (isRemind) {
+                    mSpeakMode.speakText("结束录音");
+                }
+                if (soundRecordeCallback!=null) {
+                    soundRecordeCallback.onSuccess(filePath, name);
+                }
+            }
+
+            @Override
+            public void onfaild(String message) {
+                if (isRemind) {
+                    ToastUtils.showLong("录制失败!");
+                    mSpeakMode.speakText("录制失败");
+                }
+                if (soundRecordeCallback!=null) {
+                    soundRecordeCallback.onfaild(message);
+                }
+            }
+        });
+        //增加下目录创建,防止由于目录导致无法录制文件
+        if (!new File(Constant.USER_DATA_ATTACHEMNT_PATH).exists()) {
+            new File(Constant.USER_DATA_ATTACHEMNT_PATH).mkdirs();
+        }
+        if (mSensor.isStartSound()) {
+            ToastUtils.showLong("已自动结束上一段录音");
+            mSpeakMode.speakText("已自动结束上一段录音");
+            return null;
+        }
+        //启动定时器
+        mSensor.start(Constant.USER_DATA_ATTACHEMNT_PATH + name);
+        if (isRemind) {
+            ToastUtils.showLong("开始录音");
+            mSpeakMode.speakText("开始录音");
+        }
+        return name;
+    }
+
+    //判断是否启动了录音
+    public boolean isStartSound(){
+        if(mSensor!=null){
+            return mSensor.isStartSound();
+        }
+
+        return false;
+    }
+
+    //停止语音录制
+    public void stopSoundMeter() {
+        //先重置标识,防止按钮抬起时触发语音结束
+
+        if (mSensor != null && mSensor.isStartSound()) {
+            mSensor.stop();
+        }
+
+        if (pop != null && pop.isShowing())
+            pop.dismiss();
+    }
+
+    public interface SoundRecordeCallback{
+        public void onSuccess(String filePath, String fileName);
+        public void onfaild(String message);
+    }
+}
diff --git a/app/src/main/java/com/navinfo/omqs/util/SpeakMode.java b/app/src/main/java/com/navinfo/omqs/util/SpeakMode.java
new file mode 100644
index 00000000..1930e611
--- /dev/null
+++ b/app/src/main/java/com/navinfo/omqs/util/SpeakMode.java
@@ -0,0 +1,149 @@
+package com.navinfo.omqs.util;
+
+import android.app.Activity;
+import android.app.Dialog;
+import android.content.Intent;
+import android.os.Bundle;
+import android.os.Handler;
+import android.os.Message;
+import android.speech.tts.TextToSpeech;
+import android.util.Log;
+import android.view.View;
+
+import com.navinfo.omqs.ui.dialog.FirstDialog;
+
+import java.util.HashMap;
+import java.util.Locale;
+
+//语音类
+public class SpeakMode extends Activity implements TextToSpeech.OnInitListener{
+    private Activity mActivity;
+    private TextToSpeech mTextToSpeech;//TTS对象
+    private int status;
+    private int MY_DATA_CHECK_CODE = 0;
+
+    private Handler mHandler = new Handler() {
+
+        @Override
+        public void handleMessage(Message msg) {
+            switch (msg.what) {
+                case 0x11:
+                    try {
+                        HashMap<String, String> params = new HashMap<String, String>();
+
+                        params.put(TextToSpeech.Engine.KEY_PARAM_STREAM, "STREAM_NOTIFICATION");//设置播放类型(音频流类型)
+
+                        mTextToSpeech.speak(msg.obj + "", TextToSpeech.QUEUE_ADD, params);//将这个发音任务添加当前任务之后
+
+                        //BaseToast.makeText(mActivity,msg.obj+"",Toast.LENGTH_LONG).show();
+
+                        mTextToSpeech.playSilence(100, TextToSpeech.QUEUE_ADD, params);//间隔多长时间
+
+                    } catch (Exception e) {
+
+                    }
+                    break;
+            }
+        }
+    };
+
+    public SpeakMode(Activity activity) {
+
+        mActivity = activity;
+
+        if (mActivity != null && !mActivity.isFinishing())
+            this.mTextToSpeech = new TextToSpeech(this.mActivity, this);
+    }
+
+    @Override
+    protected void onCreate(Bundle savedInstanceState) {
+        super.onCreate(savedInstanceState);
+
+        Intent checkIntent = new Intent();
+        checkIntent.setAction(TextToSpeech.Engine.ACTION_CHECK_TTS_DATA);
+        startActivityForResult(checkIntent, MY_DATA_CHECK_CODE);
+    }
+
+    public void setData(String json) {
+
+    }
+
+    @Override
+    public void onInit(int status) {
+        this.status = status;
+        if (this.mTextToSpeech != null) {
+            int result = this.mTextToSpeech.setLanguage(Locale.CHINESE);
+            if (result == TextToSpeech.LANG_MISSING_DATA
+                    || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+                if (mActivity != null && !mActivity.isFinishing()) {
+                    FirstDialog firstDialog = new FirstDialog(mActivity);
+                    firstDialog.setTitle("提示");
+                    firstDialog.setMessage("设备不支持语音播报,请先下载语音助手。");
+                    firstDialog.setConfirmListener(new FirstDialog.OnClickListener() {
+                        @Override
+                        public void onClick(Dialog dialog, int which) {
+                                dialog.dismiss();
+                            }
+                    });
+                    firstDialog.setNegativeView(View.GONE);
+                    firstDialog.show();
+                }
+            }
+        }
+        Log.i("TextToSpeechDemo", String.valueOf(status));
+    }
+
+    //读语音处理
+    public void speakText(final String message) {
+        new Thread(new Runnable() {
+            @Override
+            public void run() {
+
+                if (mTextToSpeech != null) {
+
+                    int result = mTextToSpeech.setLanguage(Locale.CHINESE);
+
+                    if (result == TextToSpeech.LANG_MISSING_DATA
+                            || result == TextToSpeech.LANG_NOT_SUPPORTED) {
+
+                    } else {
+                        if (mTextToSpeech != null && mTextToSpeech.isSpeaking()) {
+
+                            while (mTextToSpeech.isSpeaking()) {
+
+                                try {
+                                    //增加播报停止,解决不能播报最新内容问题
+                                    mTextToSpeech.stop();
+
+                                    Thread.sleep(100);
+
+                                } catch (Exception e) {
+
+                                }
+                            }
+                        }
+
+                        Message msg = new Message();
+                        msg.what = 0x11;
+                        msg.obj = message;
+                        mHandler.sendMessage(msg);
+
+                    }
+                }
+            }
+        }).start();
+
+    }
+
+    public void stopSpeek() {
+        try {
+
+            if (this.mTextToSpeech != null && this.mTextToSpeech.isSpeaking()) {
+                this.mTextToSpeech.stop();
+            }
+        } catch (Exception e) {
+
+        }
+    }
+
+}
diff --git a/app/src/main/res/color/font_blue_reg.xml b/app/src/main/res/color/font_blue_reg.xml
new file mode 100644
index 00000000..99b71983
--- /dev/null
+++ b/app/src/main/res/color/font_blue_reg.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:color="#FF3030"></item>
+    <item android:state_selected="true" android:color="#FF3030"></item>
+    <item android:color="#1ABBFE"></item>
+</selector>
diff --git a/app/src/main/res/drawable-hdpi/rcd_cancel_icon.png b/app/src/main/res/drawable-hdpi/rcd_cancel_icon.png
new file mode 100644
index 00000000..b1b2b06a
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/rcd_cancel_icon.png differ
diff --git a/app/src/main/res/drawable-hdpi/voice_rcd_cancel_bg_focused.png b/app/src/main/res/drawable-hdpi/voice_rcd_cancel_bg_focused.png
new file mode 100644
index 00000000..4190494f
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice_rcd_cancel_bg_focused.png differ
diff --git a/app/src/main/res/drawable-hdpi/voice_rcd_hint_bg.9.png b/app/src/main/res/drawable-hdpi/voice_rcd_hint_bg.9.png
new file mode 100644
index 00000000..aa325e17
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice_rcd_hint_bg.9.png differ
diff --git a/app/src/main/res/drawable-hdpi/voice_to_short.png b/app/src/main/res/drawable-hdpi/voice_to_short.png
new file mode 100644
index 00000000..ae7bc3e9
Binary files /dev/null and b/app/src/main/res/drawable-hdpi/voice_to_short.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp1.png b/app/src/main/res/drawable-xhdpi/amp1.png
new file mode 100644
index 00000000..3a94df9b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp1.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp2.png b/app/src/main/res/drawable-xhdpi/amp2.png
new file mode 100644
index 00000000..f1b1adf7
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp2.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp3.png b/app/src/main/res/drawable-xhdpi/amp3.png
new file mode 100644
index 00000000..beb72b00
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp3.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp4.png b/app/src/main/res/drawable-xhdpi/amp4.png
new file mode 100644
index 00000000..8d41ae8c
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp4.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp5.png b/app/src/main/res/drawable-xhdpi/amp5.png
new file mode 100644
index 00000000..6791bd7b
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp5.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp6.png b/app/src/main/res/drawable-xhdpi/amp6.png
new file mode 100644
index 00000000..5afef7e2
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp6.png differ
diff --git a/app/src/main/res/drawable-xhdpi/amp7.png b/app/src/main/res/drawable-xhdpi/amp7.png
new file mode 100644
index 00000000..d2ad7fd3
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/amp7.png differ
diff --git a/app/src/main/res/drawable-xhdpi/icon_sound_01.png b/app/src/main/res/drawable-xhdpi/icon_sound_01.png
new file mode 100644
index 00000000..2862f172
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_sound_01.png differ
diff --git a/app/src/main/res/drawable-xhdpi/icon_sound_02.png b/app/src/main/res/drawable-xhdpi/icon_sound_02.png
new file mode 100644
index 00000000..f9e67e63
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_sound_02.png differ
diff --git a/app/src/main/res/drawable-xhdpi/icon_sound_03.png b/app/src/main/res/drawable-xhdpi/icon_sound_03.png
new file mode 100644
index 00000000..3e5eadb4
Binary files /dev/null and b/app/src/main/res/drawable-xhdpi/icon_sound_03.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp1.png b/app/src/main/res/drawable-xxhdpi/amp1.png
new file mode 100644
index 00000000..3a94df9b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp1.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp2.png b/app/src/main/res/drawable-xxhdpi/amp2.png
new file mode 100644
index 00000000..f1b1adf7
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp2.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp3.png b/app/src/main/res/drawable-xxhdpi/amp3.png
new file mode 100644
index 00000000..beb72b00
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp3.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp4.png b/app/src/main/res/drawable-xxhdpi/amp4.png
new file mode 100644
index 00000000..8d41ae8c
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp4.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp5.png b/app/src/main/res/drawable-xxhdpi/amp5.png
new file mode 100644
index 00000000..6791bd7b
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp5.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp6.png b/app/src/main/res/drawable-xxhdpi/amp6.png
new file mode 100644
index 00000000..5afef7e2
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp6.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/amp7.png b/app/src/main/res/drawable-xxhdpi/amp7.png
new file mode 100644
index 00000000..d2ad7fd3
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/amp7.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/icon_sound_01.png b/app/src/main/res/drawable-xxhdpi/icon_sound_01.png
new file mode 100644
index 00000000..2862f172
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_sound_01.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/icon_sound_02.png b/app/src/main/res/drawable-xxhdpi/icon_sound_02.png
new file mode 100644
index 00000000..f9e67e63
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_sound_02.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/icon_sound_03.png b/app/src/main/res/drawable-xxhdpi/icon_sound_03.png
new file mode 100644
index 00000000..3e5eadb4
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/icon_sound_03.png differ
diff --git a/app/src/main/res/drawable/bg_select_sound_xml.xml b/app/src/main/res/drawable/bg_select_sound_xml.xml
new file mode 100644
index 00000000..46b9cf09
--- /dev/null
+++ b/app/src/main/res/drawable/bg_select_sound_xml.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="utf-8"?>
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+    <item android:state_checked="true" android:drawable="@drawable/icon_select_sound_press"/>
+    <item android:state_selected="true" android:drawable="@drawable/icon_select_sound_press"/>
+    <item android:drawable="@drawable/icon_select_sound_defaule"/>
+</selector>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/drawable_bg_blue_frame_black_bg_4_radius.xml b/app/src/main/res/drawable/drawable_bg_blue_frame_black_bg_4_radius.xml
new file mode 100644
index 00000000..b722d9fb
--- /dev/null
+++ b/app/src/main/res/drawable/drawable_bg_blue_frame_black_bg_4_radius.xml
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="utf-8"?>
+<shape xmlns:android="http://schemas.android.com/apk/res/android" >
+    <solid android:color="@color/black" />
+    <stroke
+        android:width="1dp"
+        android:color="@color/deepskyblue" />
+    <corners
+        android:bottomLeftRadius="5dp"
+        android:bottomRightRadius="5dp"
+        android:topLeftRadius="5dp"
+        android:topRightRadius="5dp" />
+    <padding
+        android:bottom="1dp"
+        android:left="1dp"
+        android:right="1dp"
+        android:top="1dp"/>
+</shape>
\ No newline at end of file
diff --git a/app/src/main/res/drawable/icon_select_sound_defaule.9.png b/app/src/main/res/drawable/icon_select_sound_defaule.9.png
new file mode 100644
index 00000000..61f976cd
Binary files /dev/null and b/app/src/main/res/drawable/icon_select_sound_defaule.9.png differ
diff --git a/app/src/main/res/drawable/icon_select_sound_press.9.png b/app/src/main/res/drawable/icon_select_sound_press.9.png
new file mode 100644
index 00000000..a7d26589
Binary files /dev/null and b/app/src/main/res/drawable/icon_select_sound_press.9.png differ
diff --git a/app/src/main/res/drawable/pop_voice_img.xml b/app/src/main/res/drawable/pop_voice_img.xml
new file mode 100644
index 00000000..270b69d9
--- /dev/null
+++ b/app/src/main/res/drawable/pop_voice_img.xml
@@ -0,0 +1,13 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false"
+    >
+   <item android:drawable="@drawable/amp7" android:duration="90" /> 
+   <item android:drawable="@drawable/amp1" android:duration="90" /> 
+   <item android:drawable="@drawable/amp2" android:duration="90" /> 
+   <item android:drawable="@drawable/amp3" android:duration="90" /> 
+   <item android:drawable="@drawable/amp4" android:duration="90" /> 
+   <item android:drawable="@drawable/amp5" android:duration="90" /> 
+   <item android:drawable="@drawable/amp6" android:duration="90" /> 
+   <item android:drawable="@drawable/amp7" android:duration="90" /> 
+	
+</animation-list>
diff --git a/app/src/main/res/drawable/sound_anim.xml b/app/src/main/res/drawable/sound_anim.xml
new file mode 100644
index 00000000..d2fbaf94
--- /dev/null
+++ b/app/src/main/res/drawable/sound_anim.xml
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<animation-list xmlns:android="http://schemas.android.com/apk/res/android" android:oneshot="false">
+   <item android:drawable="@drawable/icon_sound_03" android:duration="200" /> 
+   <item android:drawable="@drawable/icon_sound_02" android:duration="200" /> 
+   <item android:drawable="@drawable/icon_sound_01" android:duration="200" /> 
+	
+</animation-list>
\ 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 40393b4f..2c7894e8 100644
--- a/app/src/main/res/layout/activity_main.xml
+++ b/app/src/main/res/layout/activity_main.xml
@@ -63,7 +63,6 @@
                 android:layout_height="48dp"
                 android:layout_marginRight="20dp"
                 android:layout_marginBottom="120dp"
-                android:onClick="@{()->mainActivity.voiceOnclick()}"
                 android:src="@drawable/baseline_keyboard_voice_24"
                 app:layout_constraintBottom_toBottomOf="parent"
                 app:layout_constraintRight_toRightOf="parent" />
diff --git a/app/src/main/res/layout/adapter_sound_list.xml b/app/src/main/res/layout/adapter_sound_list.xml
new file mode 100644
index 00000000..752ae50d
--- /dev/null
+++ b/app/src/main/res/layout/adapter_sound_list.xml
@@ -0,0 +1,45 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    xmlns:app="http://schemas.android.com/apk/res-auto"
+    xmlns:tools="http://schemas.android.com/tools"
+    android:layout_width="match_parent"
+    android:layout_height="wrap_content"
+    android:background="@color/ivory"
+    android:paddingLeft="10dp"
+    android:paddingTop="5dp"
+    android:paddingRight="10dp"
+    android:paddingBottom="5dp"
+    tools:context="com.navinfo.omqs.ui.fragment.evaluationresult.SoundListAdapter">
+
+    <LinearLayout
+        android:id="@+id/rl_sound_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:background="@drawable/bg_select_sound_xml"
+        android:gravity="center_vertical"
+        android:minWidth="50dp"
+        android:orientation="horizontal"
+        android:padding="5dp">
+
+        <TextView
+            android:id="@+id/tv_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginLeft="10dp"
+            android:layout_marginRight="6dp"
+            android:layout_toRightOf="@id/tv_chatcontent"
+            android:gravity="left|center"
+            android:lineSpacingExtra="2dp"
+            android:text=""
+            android:textColor="@color/font_blue_reg"
+            android:textSize="15sp" />
+
+        <ImageView
+            android:id="@+id/iv_sound_anim"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:background="@drawable/sound_anim" />
+
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/cv_card_chatting_item_msg_text_left.xml b/app/src/main/res/layout/cv_card_chatting_item_msg_text_left.xml
new file mode 100644
index 00000000..0d409ad7
--- /dev/null
+++ b/app/src/main/res/layout/cv_card_chatting_item_msg_text_left.xml
@@ -0,0 +1,41 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="wrap_content"
+    android:orientation="vertical"
+    android:paddingLeft="10dp"
+    android:paddingRight="10dp"
+    android:paddingTop="2dp"
+    android:paddingBottom="2dp"
+     >
+
+    <LinearLayout
+        android:id="@+id/rl_sound_content"
+        android:layout_width="wrap_content"
+        android:layout_height="wrap_content"
+        android:padding="5dp"
+        android:minWidth="50dp"
+        android:orientation="horizontal"
+        android:gravity="center_vertical"
+        android:background="@drawable/bg_select_sound_xml" >
+
+        <TextView
+            android:layout_marginLeft="10dp"
+            android:id="@+id/tv_time"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginRight="6dp"
+            android:layout_toRightOf="@id/tv_chatcontent"
+            android:gravity="left|center"
+            android:lineSpacingExtra="2dp"
+            android:text=""
+            android:textColor="@color/font_blue_reg"
+            android:textSize="15sp" />
+        <ImageView
+            android:id="@+id/iv_sound_anim"
+            android:background="@drawable/sound_anim"
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content" />
+    </LinearLayout>
+
+</LinearLayout>
\ No newline at end of file
diff --git a/app/src/main/res/layout/cv_card_voice_rcd_hint_window.xml b/app/src/main/res/layout/cv_card_voice_rcd_hint_window.xml
new file mode 100644
index 00000000..013f836d
--- /dev/null
+++ b/app/src/main/res/layout/cv_card_voice_rcd_hint_window.xml
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="utf-8"?>
+<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="fill_parent"
+    android:layout_height="fill_parent"
+    android:gravity="center" >
+
+    <LinearLayout
+        android:id="@+id/voice_rcd_hint_rcding"
+        android:layout_width="wrap_content"
+        android:layout_height="140.0dip"
+        android:gravity="bottom|center"
+        android:orientation="horizontal"
+         >
+
+        <LinearLayout
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:gravity="center"
+            android:background="@drawable/drawable_bg_blue_frame_black_bg_4_radius"
+            android:orientation="horizontal"
+            >
+            <ImageView
+                android:background="@drawable/pop_voice_img"
+                android:scaleType="center"
+                android:id="@+id/volume"
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="bottom"
+                 />
+        </LinearLayout>
+
+        <LinearLayout
+            android:id="@+id/del_re"
+            android:layout_width="140.0dip"
+            android:layout_height="140.0dip"
+            android:layout_marginLeft="10.0dip"
+            android:background="@drawable/voice_rcd_cancel_bg_focused"
+            android:gravity="center"
+            android:orientation="vertical"
+            android:visibility="gone" >
+
+            <TextView
+                android:layout_width="wrap_content"
+                android:layout_height="wrap_content"
+                android:layout_gravity="center_horizontal"
+                android:text="取消"
+                android:textColor="#ffffff"
+                android:textSize="13.0dip" />
+
+            <ImageView
+                android:id="@+id/sc_img1"
+                android:layout_width="75.0dip"
+                android:layout_height="75.0dip"
+                android:layout_marginTop="12.0dip"
+                android:src="@drawable/rcd_cancel_icon"
+                
+                 />      
+        </LinearLayout>
+    </LinearLayout>
+
+    <LinearLayout
+        android:id="@+id/voice_rcd_hint_tooshort"
+        android:layout_width="140.0dip"
+        android:layout_height="140.0dip"
+        android:background="@drawable/voice_rcd_hint_bg"
+        android:gravity="center"
+        android:orientation="vertical"
+        android:visibility="gone" >
+
+        <ImageView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:src="@drawable/voice_to_short" />
+
+        <TextView
+            android:layout_width="wrap_content"
+            android:layout_height="wrap_content"
+            android:layout_marginBottom="15.0dip"
+            android:text="时间太短"
+            android:textColor="#ffffff" />
+    </LinearLayout>
+
+</LinearLayout>
\ 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 bd0896f0..88a6d93e 100644
--- a/app/src/main/res/layout/fragment_evaluation_result.xml
+++ b/app/src/main/res/layout/fragment_evaluation_result.xml
@@ -125,10 +125,16 @@
                     android:layout_margin="5dp"
                     android:background="@color/gray_121" />
 
+                <TextView
+                    android:layout_width="wrap_content"
+                    android:layout_height="wrap_content"
+                    android:text="多媒体" />
+
                 <androidx.recyclerview.widget.RecyclerView
                     android:id="@+id/evaluation_voice_recyclerview"
                     android:layout_width="match_parent"
-                    android:layout_height="80dp" />
+                    android:layout_height="120dp" />
+
             </LinearLayout>
         </androidx.core.widget.NestedScrollView>
 
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/dao/impl/INiLocationDao.java b/collect-library/src/main/java/com/navinfo/collect/library/data/dao/impl/INiLocationDao.java
index 4bc28536..89c6c185 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/data/dao/impl/INiLocationDao.java
+++ b/collect-library/src/main/java/com/navinfo/collect/library/data/dao/impl/INiLocationDao.java
@@ -43,4 +43,7 @@ public interface INiLocationDao {
 
     @Query("SELECT * FROM niLocation where tilex>=:minx and tilex<=:maxx and tiley>=:miny and tiley <=:maxy and time>=:startTime and time<=:endTime")
     List<NiLocation> timeTofindList(int minx, int maxx, int miny, int maxy,long startTime,long endTime);
+
+    @Query("SELECT * FROM niLocation")
+    List<NiLocation> findAll();
 }
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt
index a70856ef..d985c1af 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt
+++ b/collect-library/src/main/java/com/navinfo/collect/library/data/entity/QsRecordBean.kt
@@ -1,6 +1,7 @@
 package com.navinfo.collect.library.data.entity
 
 import com.navinfo.collect.library.utils.GeometryToolsKt
+import com.navinfo.omqs.bean.Attachment
 import io.realm.RealmObject
 import io.realm.RealmSet
 import io.realm.annotations.PrimaryKey
@@ -86,6 +87,8 @@ open class QsRecordBean @JvmOverloads constructor(
      */
     var guideGeometry: String = "",
 
+    var attachments:RealmSet<Attachment>,
+
     ) : RealmObject() {
 
     fun copy(): QsRecordBean {
@@ -104,6 +107,7 @@ open class QsRecordBean @JvmOverloads constructor(
             confirmUserId = confirmUserId,
             t_lifecycle = t_lifecycle,
             t_status = t_status,
+            attachments = attachments,
         )
         qs.geometry = geometry
         return qs
diff --git a/collect-library/src/main/java/com/navinfo/collect/library/map/source/MapLifeNiLocationTileDataSource.java b/collect-library/src/main/java/com/navinfo/collect/library/map/source/MapLifeNiLocationTileDataSource.java
index 52909ec7..fb672ff5 100644
--- a/collect-library/src/main/java/com/navinfo/collect/library/map/source/MapLifeNiLocationTileDataSource.java
+++ b/collect-library/src/main/java/com/navinfo/collect/library/map/source/MapLifeNiLocationTileDataSource.java
@@ -53,7 +53,8 @@ public class MapLifeNiLocationTileDataSource implements ITileDataSource {
             if(mEndTime!=0){
                 list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().timeTofindList(xStart, xEnd, yStart, yEnd,mStartTime,mEndTime);
             }else{
-                list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().findList(xStart, xEnd, yStart, yEnd);
+                //list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().findList(xStart, xEnd, yStart, yEnd);
+                list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().findAll();
             }
 
             Log.e("qj","query"+(list==null?0:list.size())+"==="+xStart+"==="+xEnd+"==="+yStart+"==="+yEnd);