Merge branch 'master' of gitlab.navinfo.com:CollectVehicle/OneMapQS
Conflicts: app/src/main/java/com/navinfo/omqs/ui/activity/map/MainActivity.kt app/src/main/java/com/navinfo/omqs/ui/activity/map/MainViewModel.kt app/src/main/java/com/navinfo/omqs/ui/activity/map/SignAdapter.kt app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultFragment.kt app/src/main/java/com/navinfo/omqs/ui/fragment/evaluationresult/EvaluationResultViewModel.kt
This commit is contained in:
@@ -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
|
||||
};
|
||||
}
|
||||
|
||||
@@ -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))
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package com.navinfo.omqs.ui.activity.map
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import androidx.activity.viewModels
|
||||
import androidx.core.view.WindowCompat
|
||||
import androidx.databinding.DataBindingUtil
|
||||
@@ -70,6 +73,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)
|
||||
@@ -127,7 +150,19 @@ class MainActivity : BaseActivity() {
|
||||
* 点击录音按钮
|
||||
*/
|
||||
fun voiceOnclick() {
|
||||
rightController.navigate(R.id.EvaluationResultFragment)
|
||||
/* val naviController = findNavController(R.id.main_activity_right_fragment)
|
||||
naviController.navigate(R.id.EvaluationResultFragment)*/
|
||||
}
|
||||
|
||||
fun voiceOnTouchStart(){
|
||||
viewModel!!.startSoundMetter(this,binding.mainActivityVoice)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
fun voiceOnTouchStop(){
|
||||
if(Constant.IS_VIDEO_SPEED){
|
||||
viewModel!!.stopSoundMeter()
|
||||
}
|
||||
}
|
||||
|
||||
// override fun onBackPressed() {
|
||||
|
||||
@@ -1,14 +1,25 @@
|
||||
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.lifecycle.viewModelScope
|
||||
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.RenderEntity
|
||||
import com.navinfo.collect.library.map.NIMapController
|
||||
@@ -20,12 +31,18 @@ import com.navinfo.omqs.bean.SignBean
|
||||
import com.navinfo.omqs.db.RealmOperateHelper
|
||||
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 kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oscim.core.GeoPoint
|
||||
import org.videolan.libvlc.LibVlcUtil
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
@@ -47,9 +64,15 @@ class MainViewModel @Inject constructor(
|
||||
//看板数据
|
||||
val liveDataSignList = MutableLiveData<List<SignBean>>()
|
||||
|
||||
|
||||
// private var niLocationList: MutableList<NiLocation> = ArrayList<NiLocation>()
|
||||
var testPoint = GeoPoint(0, 0)
|
||||
//语音窗体
|
||||
private var pop: PopupWindow? = null
|
||||
|
||||
private var mSpeakMode: SpeakMode? = null
|
||||
|
||||
//录音图标
|
||||
var volume: ImageView? = null
|
||||
var mSoundMeter: SoundMeter? = null
|
||||
|
||||
init {
|
||||
mapController.markerHandle.setOnQsRecordItemClickListener(object :
|
||||
@@ -97,11 +120,12 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
Log.e("jingo", "定位点插入 ${Thread.currentThread().name}")
|
||||
traceDataBase.niLocationDao.insert(location)
|
||||
mapController.mMapView.vtmMap.updateMap(true)
|
||||
}
|
||||
}
|
||||
//用于定位点捕捉道路
|
||||
viewModelScope.launch(Dispatchers.Default) {
|
||||
mapController.locationLayerHandler.niLocationFlow.collect { location ->
|
||||
mapController.locationLayerHandler.niLocationFlow.collectLatest { location ->
|
||||
Log.e("jingo", "定位点绑定道路 ${Thread.currentThread().name}")
|
||||
location.longitude = testPoint.longitude
|
||||
location.latitude = testPoint.latitude
|
||||
@@ -148,6 +172,7 @@ class MainViewModel @Inject constructor(
|
||||
|
||||
//显示轨迹图层
|
||||
mapController.layerManagerHandler.showNiLocationLayer()
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -169,8 +194,6 @@ class MainViewModel @Inject constructor(
|
||||
|
||||
Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString())
|
||||
|
||||
//ToastUtils.showShort("点击了相机")
|
||||
|
||||
if (mCameraDialog == null) {
|
||||
mCameraDialog = CommonDialog(
|
||||
context,
|
||||
@@ -204,57 +227,82 @@ class MainViewModel @Inject constructor(
|
||||
})
|
||||
}
|
||||
|
||||
fun startSoundMetter(context: Context, v: View) {
|
||||
|
||||
// fun startSaveTraceThread(context: Context) {
|
||||
// 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 tileX = RealmSet<Int>()
|
||||
// GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX)
|
||||
// val tileY = RealmSet<Int>()
|
||||
// GeometryToolsKt.getTileYByGeometry(geometry.toString(), tileY)
|
||||
//
|
||||
// //遍历存储tile对应的x与y的值
|
||||
// tileX.forEach { x ->
|
||||
// tileY.forEach { y ->
|
||||
// niLocation.tilex = x
|
||||
// niLocation.tiley = y
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// TraceDataBase.getDatabase(
|
||||
// context,
|
||||
// Constant.USER_DATA_PATH + "/trace.sqlite"
|
||||
// ).niLocationDao.insert(niLocation)
|
||||
// niLocationList.remove(niLocation)
|
||||
//
|
||||
// Log.e("qj", "saveTrace==${niLocationList.size}")
|
||||
// }
|
||||
// Thread.sleep(30)
|
||||
// }
|
||||
// } catch (e: InterruptedException) {
|
||||
// e.printStackTrace()
|
||||
// Log.e("qj", "异常==${e.message}")
|
||||
// }
|
||||
// }).start()
|
||||
// }
|
||||
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 addSaveTrace(niLocation: NiLocation) {
|
||||
// if (niLocation != null && niLocationList != null) {
|
||||
// niLocationList.add(niLocation)
|
||||
// }
|
||||
// }
|
||||
|
||||
/**
|
||||
* 处理页面调转
|
||||
@@ -290,6 +338,5 @@ class MainViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@@ -1,18 +1,24 @@
|
||||
package com.navinfo.omqs.ui.fragment.evaluationresult
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.navigation.NavOptions
|
||||
import androidx.navigation.findNavController
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.bean.SignBean
|
||||
import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding
|
||||
import com.navinfo.omqs.ui.fragment.BaseFragment
|
||||
import com.navinfo.omqs.ui.other.shareViewModels
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import androidx.navigation.findNavController
|
||||
|
||||
@AndroidEntryPoint
|
||||
class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
||||
@@ -26,8 +32,25 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
||||
binding =
|
||||
DataBindingUtil.inflate(inflater, R.layout.fragment_evaluation_result, container, false)
|
||||
binding.fragment = this
|
||||
val layoutManager = LinearLayoutManager(context)
|
||||
binding.viewModel = viewModel
|
||||
binding.lifecycleOwner = this
|
||||
//// 设置 RecyclerView 的固定大小,避免在滚动时重新计算视图大小和布局,提高性能
|
||||
binding.evaluationVoiceRecyclerview.setHasFixedSize(true)
|
||||
binding.evaluationVoiceRecyclerview.layoutManager = layoutManager
|
||||
/**
|
||||
* 监听左侧栏的点击事件
|
||||
*/
|
||||
val adapter = SoundtListAdapter { _, view ->
|
||||
|
||||
}
|
||||
|
||||
binding.evaluationVoiceRecyclerview.adapter = adapter
|
||||
viewModel.listDataChatMsgEntityList.observe(viewLifecycleOwner) {
|
||||
adapter.refreshData(it)
|
||||
}
|
||||
|
||||
viewModel.getChatMsgEntityList()
|
||||
return binding.root
|
||||
}
|
||||
|
||||
@@ -55,6 +78,26 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
binding.evaluationVoice.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
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* 读取元数据
|
||||
*/
|
||||
@@ -235,4 +278,16 @@ class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun voiceOnTouchStart(){
|
||||
viewModel!!.startSoundMetter(requireActivity(),binding.evaluationVoice)
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
fun voiceOnTouchStop(){
|
||||
if(Constant.IS_VIDEO_SPEED){
|
||||
viewModel!!.stopSoundMeter()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,24 +1,48 @@
|
||||
package com.navinfo.omqs.ui.fragment.evaluationresult
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.graphics.drawable.AnimationDrawable
|
||||
import android.graphics.drawable.BitmapDrawable
|
||||
import android.net.Uri
|
||||
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.core.util.rangeTo
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.navigation.findNavController
|
||||
import com.blankj.utilcode.util.ToastUtils
|
||||
import com.navinfo.collect.library.data.entity.AttachmentBean
|
||||
import com.navinfo.collect.library.data.entity.QsRecordBean
|
||||
import com.navinfo.collect.library.data.entity.RenderEntity.Companion.LinkTable
|
||||
import com.navinfo.collect.library.map.NIMapController
|
||||
import com.navinfo.collect.library.utils.GeometryTools
|
||||
import com.navinfo.omqs.bean.SignBean
|
||||
import com.navinfo.omqs.Constant
|
||||
import com.navinfo.omqs.R
|
||||
import com.navinfo.omqs.bean.ChatMsgEntity
|
||||
import com.navinfo.omqs.db.RealmOperateHelper
|
||||
import com.navinfo.omqs.db.RoomAppDatabase
|
||||
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.Realm
|
||||
import io.realm.RealmList
|
||||
import io.realm.kotlin.where
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import org.oscim.core.GeoPoint
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import javax.inject.Inject
|
||||
|
||||
@@ -51,11 +75,22 @@ class EvaluationResultViewModel @Inject constructor(
|
||||
*/
|
||||
val liveDataRightTypeList = MutableLiveData<List<RightBean>>()
|
||||
|
||||
|
||||
var liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
|
||||
|
||||
var listDataChatMsgEntityList = MutableLiveData<MutableList<ChatMsgEntity>>()
|
||||
|
||||
var oldBean: QsRecordBean? = null
|
||||
|
||||
//语音窗体
|
||||
private var pop: PopupWindow? = null
|
||||
|
||||
private var mSpeakMode: SpeakMode? = null
|
||||
|
||||
//录音图标
|
||||
var volume: ImageView? = null
|
||||
|
||||
var mSoundMeter: SoundMeter? = null
|
||||
|
||||
init {
|
||||
liveDataQsRecordBean.value = QsRecordBean(id = UUID.randomUUID().toString())
|
||||
Log.e("jingo", "EvaluationResultViewModel 创建了 ${hashCode()}")
|
||||
@@ -83,7 +118,7 @@ class EvaluationResultViewModel @Inject constructor(
|
||||
/**
|
||||
* 查询数据库,获取问题分类
|
||||
*/
|
||||
fun initNewData(bean: SignBean?) {
|
||||
fun initNewData(bean: SignBean?,filePath: String) {
|
||||
viewModelScope.launch(Dispatchers.IO) {
|
||||
getClassTypeList()
|
||||
getProblemLinkList()
|
||||
@@ -115,6 +150,8 @@ class EvaluationResultViewModel @Inject constructor(
|
||||
val point = GeometryTools.createGeoPoint(bean.geometry)
|
||||
mapController.markerHandle.addMarker(point, markerTitle)
|
||||
}
|
||||
|
||||
addChatMsgEntity(filePath)
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -324,4 +361,126 @@ class EvaluationResultViewModel @Inject constructor(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 查询问题类型列表
|
||||
*/
|
||||
@RequiresApi(Build.VERSION_CODES.N)
|
||||
fun getChatMsgEntityList() {
|
||||
val chatMsgEntityList: MutableList<ChatMsgEntity> = ArrayList()
|
||||
liveDataQsRecordBean.value?.attachmentBeanList?.forEach {
|
||||
//1 录音
|
||||
if (it.type == 1) {
|
||||
val chatMsgEntity = ChatMsgEntity()
|
||||
chatMsgEntity.name = it.name
|
||||
chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH
|
||||
chatMsgEntityList.add(chatMsgEntity)
|
||||
}
|
||||
}
|
||||
listDataChatMsgEntityList.postValue(chatMsgEntityList)
|
||||
}
|
||||
|
||||
fun addChatMsgEntity(filePath: String) {
|
||||
|
||||
if(filePath!=null){
|
||||
var chatMsgEntityList: MutableList<ChatMsgEntity> = ArrayList()
|
||||
if(listDataChatMsgEntityList.value?.isEmpty() == false){
|
||||
chatMsgEntityList = listDataChatMsgEntityList.value!!
|
||||
}
|
||||
val chatMsgEntity = ChatMsgEntity()
|
||||
chatMsgEntity.name = filePath.replace(Constant.USER_DATA_ATTACHEMNT_PATH,"").toString()
|
||||
chatMsgEntity.voiceUri = Constant.USER_DATA_ATTACHEMNT_PATH
|
||||
chatMsgEntityList.add(chatMsgEntity)
|
||||
|
||||
|
||||
var attachmentList: RealmList<AttachmentBean> = RealmList()
|
||||
|
||||
//赋值处理
|
||||
if(liveDataQsRecordBean.value?.attachmentBeanList?.isEmpty() == false){
|
||||
attachmentList = liveDataQsRecordBean.value?.attachmentBeanList!!
|
||||
}
|
||||
|
||||
val attachmentBean = AttachmentBean()
|
||||
attachmentBean.name = chatMsgEntity.name!!
|
||||
attachmentBean.type = 1
|
||||
attachmentList.add(attachmentBean)
|
||||
liveDataQsRecordBean.value?.attachmentBeanList = attachmentList
|
||||
|
||||
listDataChatMsgEntityList.postValue(chatMsgEntityList)
|
||||
}
|
||||
}
|
||||
|
||||
fun startSoundMetter(activity: Activity, v: View) {
|
||||
|
||||
if(mSpeakMode==null){
|
||||
mSpeakMode = SpeakMode(activity)
|
||||
}
|
||||
|
||||
//语音识别动画
|
||||
if (pop == null) {
|
||||
pop = PopupWindow()
|
||||
pop!!.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
pop!!.height = ViewGroup.LayoutParams.WRAP_CONTENT
|
||||
pop!!.setBackgroundDrawable(BitmapDrawable())
|
||||
val view = View.inflate(activity as Context, R.layout.cv_card_voice_rcd_hint_window, null)
|
||||
pop!!.contentView = view
|
||||
volume = view.findViewById(R.id.volume)
|
||||
}
|
||||
|
||||
pop!!.update()
|
||||
|
||||
Constant.IS_VIDEO_SPEED = true
|
||||
//录音动画
|
||||
//录音动画
|
||||
if (pop != null) {
|
||||
pop!!.showAtLocation(v, Gravity.CENTER, 0, 0)
|
||||
}
|
||||
volume!!.setBackgroundResource(R.drawable.pop_voice_img)
|
||||
val animation = volume!!.background as AnimationDrawable
|
||||
animation.start()
|
||||
|
||||
val name: String = DateTimeUtil.getTimeSSS().toString() + ".m4a"
|
||||
if (mSoundMeter == null) {
|
||||
mSoundMeter = SoundMeter()
|
||||
}
|
||||
mSoundMeter!!.setmListener(object : SoundMeter.OnSoundMeterListener {
|
||||
@RequiresApi(Build.VERSION_CODES.Q)
|
||||
override fun onSuccess(filePath: String?) {
|
||||
if (!TextUtils.isEmpty(filePath) && File(filePath).exists()) {
|
||||
if (File(filePath) == null || File(filePath).length() < 1600) {
|
||||
ToastUtils.showLong("语音时间太短,无效!")
|
||||
mSpeakMode!!.speakText("语音时间太短,无效")
|
||||
stopSoundMeter()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
mSpeakMode!!.speakText("结束录音")
|
||||
|
||||
addChatMsgEntity(filePath!!)
|
||||
}
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
override fun onfaild(message: String?) {
|
||||
ToastUtils.showLong("录制失败!")
|
||||
mSpeakMode!!.speakText("录制失败")
|
||||
stopSoundMeter()
|
||||
}
|
||||
})
|
||||
|
||||
mSoundMeter!!.start(Constant.USER_DATA_ATTACHEMNT_PATH + name)
|
||||
ToastUtils.showLong("开始录音")
|
||||
mSpeakMode!!.speakText("开始录音")
|
||||
}
|
||||
|
||||
//停止语音录制
|
||||
@RequiresApi(api = Build.VERSION_CODES.Q)
|
||||
fun stopSoundMeter() {
|
||||
//先重置标识,防止按钮抬起时触发语音结束
|
||||
Constant.IS_VIDEO_SPEED = false
|
||||
if (mSoundMeter != null && mSoundMeter!!.isStartSound()) {
|
||||
mSoundMeter!!.stop()
|
||||
}
|
||||
if (pop != null && pop!!.isShowing) pop!!.dismiss()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,233 @@
|
||||
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(
|
||||
private var itemListener: ((Int, View) -> Unit?)? = null
|
||||
) : 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 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)
|
||||
|
||||
holder.viewBinding.rlSoundContent.setOnClickListener {
|
||||
setPlayerIndex(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.voiceUri + entity.name)
|
||||
md!!.prepare()
|
||||
} catch (e: Exception) {
|
||||
// TODO Auto-generated catch block
|
||||
e.printStackTrace()
|
||||
}
|
||||
var time =
|
||||
if (entity.voiceTimeLong == null) md!!.duration.toString() + "" else entity.voiceTimeLong
|
||||
.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.layoutParams = layoutParams
|
||||
}
|
||||
holder.viewBinding.tvTime.text = time
|
||||
md!!.release()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 播放某段录音
|
||||
*
|
||||
* @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
|
||||
*/
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
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()
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun getItemViewRes(position: Int): Int {
|
||||
return R.layout.adapter_sound_list
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user