增加语音部分业务
@ -29,6 +29,7 @@
|
|||||||
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
<uses-permission android:name="android.permission.READ_PHONE_STATE" />
|
||||||
<!-- 读取缓存数据 -->
|
<!-- 读取缓存数据 -->
|
||||||
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
|
||||||
|
<uses-permission android:name="android.permission.RECORD_AUDIO" />
|
||||||
<!--android:largeHeap="true" 大内存 128M -->
|
<!--android:largeHeap="true" 大内存 128M -->
|
||||||
<application
|
<application
|
||||||
android:name=".OMQSApplication"
|
android:name=".OMQSApplication"
|
||||||
|
@ -32,6 +32,11 @@ class Constant {
|
|||||||
*/
|
*/
|
||||||
lateinit var USER_DATA_PATH: String
|
lateinit var USER_DATA_PATH: String
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 用户附件数据目录
|
||||||
|
*/
|
||||||
|
lateinit var USER_DATA_ATTACHEMNT_PATH: String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 离线地图目录
|
* 离线地图目录
|
||||||
*/
|
*/
|
||||||
@ -49,6 +54,8 @@ class Constant {
|
|||||||
|
|
||||||
const val DEBUG = true
|
const val DEBUG = true
|
||||||
|
|
||||||
|
var IS_VIDEO_SPEED by kotlin.properties.Delegates.notNull<Boolean>()
|
||||||
|
|
||||||
const val message_status_late = "预约,待发送"
|
const val message_status_late = "预约,待发送"
|
||||||
const val message_status_send_over = "已发送"
|
const val message_status_send_over = "已发送"
|
||||||
const val message_version_right_off = "1" //立即发送
|
const val message_version_right_off = "1" //立即发送
|
||||||
|
43
app/src/main/java/com/navinfo/omqs/bean/Attachment.kt
Normal file
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
23
app/src/main/java/com/navinfo/omqs/bean/ChatMsgEntity.kt
Normal file
@ -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()
|
||||||
|
}
|
||||||
|
}
|
@ -37,6 +37,7 @@ public class CheckPermissionsActivity extends BaseActivity {
|
|||||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.RECORD_AUDIO
|
||||||
};
|
};
|
||||||
|
|
||||||
private static final int PERMISSON_REQUESTCODE = 0;
|
private static final int PERMISSON_REQUESTCODE = 0;
|
||||||
@ -51,6 +52,7 @@ public class CheckPermissionsActivity extends BaseActivity {
|
|||||||
Manifest.permission.ACCESS_FINE_LOCATION,
|
Manifest.permission.ACCESS_FINE_LOCATION,
|
||||||
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
Manifest.permission.WRITE_EXTERNAL_STORAGE,
|
||||||
Manifest.permission.READ_EXTERNAL_STORAGE,
|
Manifest.permission.READ_EXTERNAL_STORAGE,
|
||||||
|
Manifest.permission.RECORD_AUDIO,
|
||||||
BACKGROUND_LOCATION_PERMISSION
|
BACKGROUND_LOCATION_PERMISSION
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -156,12 +156,17 @@ class LoginViewModel @Inject constructor(
|
|||||||
* 创建用户目录
|
* 创建用户目录
|
||||||
*/
|
*/
|
||||||
private fun createUserFolder(context: Context, userId: String) {
|
private fun createUserFolder(context: Context, userId: String) {
|
||||||
|
Constant.IS_VIDEO_SPEED = false
|
||||||
Constant.USER_ID = userId
|
Constant.USER_ID = userId
|
||||||
Constant.VERSION_ID = userId
|
Constant.VERSION_ID = userId
|
||||||
Constant.USER_DATA_PATH = Constant.DATA_PATH + Constant.USER_ID + "/" + Constant.VERSION_ID
|
Constant.USER_DATA_PATH = Constant.DATA_PATH + Constant.USER_ID + "/" + Constant.VERSION_ID
|
||||||
|
Constant.USER_DATA_ATTACHEMNT_PATH = Constant.USER_DATA_PATH + "/attachment/"
|
||||||
// 在SD卡创建用户目录,解压资源等
|
// 在SD卡创建用户目录,解压资源等
|
||||||
val userFolder = File(Constant.USER_DATA_PATH)
|
val userFolder = File(Constant.USER_DATA_PATH)
|
||||||
if (!userFolder.exists()) userFolder.mkdirs()
|
if (!userFolder.exists()) userFolder.mkdirs()
|
||||||
|
//创建附件目录
|
||||||
|
val userAttachmentFolder = File(Constant.USER_DATA_ATTACHEMNT_PATH)
|
||||||
|
if (!userAttachmentFolder.exists()) userAttachmentFolder.mkdirs()
|
||||||
// 初始化Realm
|
// 初始化Realm
|
||||||
Realm.init(context.applicationContext)
|
Realm.init(context.applicationContext)
|
||||||
val password = "encryp".encodeToByteArray().copyInto(ByteArray(64))
|
val password = "encryp".encodeToByteArray().copyInto(ByteArray(64))
|
||||||
|
@ -1,7 +1,12 @@
|
|||||||
package com.navinfo.omqs.ui.activity.map
|
package com.navinfo.omqs.ui.activity.map
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.util.Log
|
||||||
|
import android.view.MotionEvent
|
||||||
|
import android.view.View
|
||||||
import androidx.activity.viewModels
|
import androidx.activity.viewModels
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
@ -52,6 +57,26 @@ class MainActivity : BaseActivity() {
|
|||||||
//给xml传递viewModel对象
|
//给xml传递viewModel对象
|
||||||
binding.viewModel = 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.liveDataQsRecordIdList.observe(this) {
|
||||||
//处理页面跳转
|
//处理页面跳转
|
||||||
viewModel.navigation(this, it)
|
viewModel.navigation(this, it)
|
||||||
@ -68,8 +93,8 @@ class MainActivity : BaseActivity() {
|
|||||||
mapController.locationLayerHandler.setNiLocationListener(NiLocationListener {
|
mapController.locationLayerHandler.setNiLocationListener(NiLocationListener {
|
||||||
//ToastUtils.showLong("定位${it.longitude}")
|
//ToastUtils.showLong("定位${it.longitude}")
|
||||||
binding!!.viewModel!!.addSaveTrace(it)
|
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(Constant.DATA_PATH+ SystemConstant.USER_ID+"/trace.sqlite")
|
||||||
mapController.layerManagerHandler.showNiLocationLayer()
|
mapController.layerManagerHandler.showNiLocationLayer()
|
||||||
@ -110,8 +135,19 @@ class MainActivity : BaseActivity() {
|
|||||||
* 点击录音按钮
|
* 点击录音按钮
|
||||||
*/
|
*/
|
||||||
fun voiceOnclick() {
|
fun voiceOnclick() {
|
||||||
val naviController = findNavController(R.id.main_activity_right_fragment)
|
/* val naviController = findNavController(R.id.main_activity_right_fragment)
|
||||||
naviController.navigate(R.id.EvaluationResultFragment)
|
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() {
|
// override fun onBackPressed() {
|
||||||
|
@ -1,12 +1,24 @@
|
|||||||
package com.navinfo.omqs.ui.activity.map
|
package com.navinfo.omqs.ui.activity.map
|
||||||
|
|
||||||
|
import android.app.Activity
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.DialogInterface
|
import android.content.DialogInterface
|
||||||
|
import android.graphics.drawable.AnimationDrawable
|
||||||
|
import android.graphics.drawable.BitmapDrawable
|
||||||
|
import android.os.Build
|
||||||
import android.os.Bundle
|
import android.os.Bundle
|
||||||
|
import android.text.TextUtils
|
||||||
import android.util.Log
|
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.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.navigation.findNavController
|
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.dao.impl.TraceDataBase
|
||||||
import com.navinfo.collect.library.data.entity.NiLocation
|
import com.navinfo.collect.library.data.entity.NiLocation
|
||||||
import com.navinfo.collect.library.map.NIMapController
|
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.R
|
||||||
import com.navinfo.omqs.ui.dialog.CommonDialog
|
import com.navinfo.omqs.ui.dialog.CommonDialog
|
||||||
import com.navinfo.omqs.ui.manager.TakePhotoManager
|
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 dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import io.realm.RealmSet
|
import io.realm.RealmSet
|
||||||
import org.oscim.core.GeoPoint
|
import org.oscim.core.GeoPoint
|
||||||
import org.videolan.libvlc.LibVlcUtil
|
import org.videolan.libvlc.LibVlcUtil
|
||||||
|
import java.io.File
|
||||||
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -28,14 +45,23 @@ import javax.inject.Inject
|
|||||||
*/
|
*/
|
||||||
@HiltViewModel
|
@HiltViewModel
|
||||||
class MainViewModel @Inject constructor(
|
class MainViewModel @Inject constructor(
|
||||||
private val mapController: NIMapController,
|
private val mapController: NIMapController
|
||||||
) : ViewModel() {
|
) : ViewModel() {
|
||||||
|
|
||||||
val liveDataQsRecordIdList = MutableLiveData<List<String>>()
|
val liveDataQsRecordIdList = MutableLiveData<List<String>>()
|
||||||
private var mCameraDialog: CommonDialog? = null
|
private var mCameraDialog: CommonDialog? = null
|
||||||
|
|
||||||
|
//语音窗体
|
||||||
|
private var pop: PopupWindow? = null
|
||||||
|
|
||||||
|
private var mSpeakMode: SpeakMode? = null
|
||||||
|
|
||||||
private var niLocationList: MutableList<NiLocation> = ArrayList<NiLocation>()
|
private var niLocationList: MutableList<NiLocation> = ArrayList<NiLocation>()
|
||||||
|
|
||||||
|
//录音图标
|
||||||
|
var volume: ImageView? = null
|
||||||
|
var mSoundMeter: SoundMeter? = null
|
||||||
|
|
||||||
init {
|
init {
|
||||||
mapController.markerHandle.setOnQsRecordItemClickListener(object :
|
mapController.markerHandle.setOnQsRecordItemClickListener(object :
|
||||||
OnQsRecordItemClickListener {
|
OnQsRecordItemClickListener {
|
||||||
@ -43,6 +69,7 @@ class MainViewModel @Inject constructor(
|
|||||||
liveDataQsRecordIdList.value = list
|
liveDataQsRecordIdList.value = list
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -61,8 +88,6 @@ class MainViewModel @Inject constructor(
|
|||||||
|
|
||||||
Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString())
|
Log.e("qj", LibVlcUtil.hasCompatibleCPU(context).toString())
|
||||||
|
|
||||||
//ToastUtils.showShort("点击了相机")
|
|
||||||
|
|
||||||
if (mCameraDialog == null) {
|
if (mCameraDialog == null) {
|
||||||
mCameraDialog = CommonDialog(
|
mCameraDialog = CommonDialog(
|
||||||
context,
|
context,
|
||||||
@ -100,11 +125,15 @@ class MainViewModel @Inject constructor(
|
|||||||
Thread(Runnable {
|
Thread(Runnable {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
|
|
||||||
if (niLocationList != null && niLocationList.size > 0) {
|
if (niLocationList != null && niLocationList.size > 0) {
|
||||||
|
|
||||||
var niLocation = niLocationList[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>()
|
val tileX = RealmSet<Int>()
|
||||||
GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX)
|
GeometryToolsKt.getTileXByGeometry(geometry.toString(), tileX)
|
||||||
val tileY = RealmSet<Int>()
|
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)
|
niLocationList.remove(niLocation)
|
||||||
|
Log.e("qj", "saveTrace==${niLocationList.size}===${list.size}")
|
||||||
Log.e("qj", "saveTrace==${niLocationList.size}")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
Thread.sleep(30)
|
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>) {
|
fun navigation(activity: MainActivity, list: List<String>) {
|
||||||
//获取右侧fragment容器
|
//获取右侧fragment容器
|
||||||
val naviController = activity.findNavController(R.id.main_activity_right_fragment)
|
val naviController = activity.findNavController(R.id.main_activity_right_fragment)
|
||||||
|
@ -7,9 +7,11 @@ import android.view.ViewGroup
|
|||||||
import androidx.databinding.DataBindingUtil
|
import androidx.databinding.DataBindingUtil
|
||||||
import androidx.navigation.NavOptions
|
import androidx.navigation.NavOptions
|
||||||
import androidx.navigation.findNavController
|
import androidx.navigation.findNavController
|
||||||
|
import androidx.recyclerview.widget.LinearLayoutManager
|
||||||
import com.navinfo.omqs.R
|
import com.navinfo.omqs.R
|
||||||
import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding
|
import com.navinfo.omqs.databinding.FragmentEvaluationResultBinding
|
||||||
import com.navinfo.omqs.ui.fragment.BaseFragment
|
import com.navinfo.omqs.ui.fragment.BaseFragment
|
||||||
|
import com.navinfo.omqs.ui.fragment.tasklist.TaskListAdapter
|
||||||
import com.navinfo.omqs.ui.other.shareViewModels
|
import com.navinfo.omqs.ui.other.shareViewModels
|
||||||
import dagger.hilt.android.AndroidEntryPoint
|
import dagger.hilt.android.AndroidEntryPoint
|
||||||
|
|
||||||
@ -17,15 +19,26 @@ import dagger.hilt.android.AndroidEntryPoint
|
|||||||
class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
class EvaluationResultFragment : BaseFragment(), View.OnClickListener {
|
||||||
private lateinit var binding: FragmentEvaluationResultBinding
|
private lateinit var binding: FragmentEvaluationResultBinding
|
||||||
private val viewModel by shareViewModels<EvaluationResultViewModel>("QsRecode")
|
private val viewModel by shareViewModels<EvaluationResultViewModel>("QsRecode")
|
||||||
|
private val adapter: SoundtListAdapter by lazy {
|
||||||
|
SoundtListAdapter()
|
||||||
|
}
|
||||||
override fun onCreateView(
|
override fun onCreateView(
|
||||||
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?
|
||||||
): View {
|
): View {
|
||||||
binding =
|
binding =
|
||||||
DataBindingUtil.inflate(inflater, R.layout.fragment_evaluation_result, container, false)
|
DataBindingUtil.inflate(inflater, R.layout.fragment_evaluation_result, container, false)
|
||||||
binding.fragment = this
|
binding.fragment = this
|
||||||
|
val layoutManager = LinearLayoutManager(context)
|
||||||
binding.viewModel = viewModel
|
binding.viewModel = viewModel
|
||||||
binding.lifecycleOwner = this
|
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
|
return binding.root
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +1,17 @@
|
|||||||
package com.navinfo.omqs.ui.fragment.evaluationresult
|
package com.navinfo.omqs.ui.fragment.evaluationresult
|
||||||
|
|
||||||
|
import android.os.Build
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.annotation.RequiresApi
|
||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.navinfo.collect.library.data.entity.QsRecordBean
|
import com.navinfo.collect.library.data.entity.QsRecordBean
|
||||||
import com.navinfo.collect.library.map.NIMapController
|
import com.navinfo.collect.library.map.NIMapController
|
||||||
import com.navinfo.collect.library.utils.GeometryTools
|
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.RealmOperateHelper
|
||||||
import com.navinfo.omqs.db.RoomAppDatabase
|
import com.navinfo.omqs.db.RoomAppDatabase
|
||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
@ -14,7 +19,6 @@ import io.realm.Realm
|
|||||||
import io.realm.kotlin.where
|
import io.realm.kotlin.where
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import org.locationtech.jts.geom.Point
|
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@ -47,9 +51,12 @@ class EvaluationResultViewModel @Inject constructor(
|
|||||||
*/
|
*/
|
||||||
val liveDataRightTypeList = MutableLiveData<List<RightBean>>()
|
val liveDataRightTypeList = MutableLiveData<List<RightBean>>()
|
||||||
|
|
||||||
|
|
||||||
var liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
|
var liveDataQsRecordBean = MutableLiveData<QsRecordBean>()
|
||||||
|
|
||||||
|
var listDataChatMsgEntityList = MutableLiveData<MutableList<ChatMsgEntity>>()
|
||||||
|
|
||||||
|
var listDataAttachmentList = MutableLiveData<MutableList<Attachment>>()
|
||||||
|
|
||||||
var oldBean: QsRecordBean? = null
|
var oldBean: QsRecordBean? = null
|
||||||
|
|
||||||
init {
|
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)
|
||||||
|
}
|
||||||
}
|
}
|
@ -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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -198,7 +198,7 @@ class TaskListAdapter(
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun getItemViewRes(position: Int): Int {
|
override fun getItemViewRes(position: Int): Int {
|
||||||
return R.layout.adapter_offline_map_city
|
return R.layout.adapter_task_list
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
161
app/src/main/java/com/navinfo/omqs/util/SoundMeter.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
129
app/src/main/java/com/navinfo/omqs/util/SoundRecordeUtils.java
Normal file
@ -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);
|
||||||
|
}
|
||||||
|
}
|
149
app/src/main/java/com/navinfo/omqs/util/SpeakMode.java
Normal file
@ -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) {
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
6
app/src/main/res/color/font_blue_reg.xml
Normal file
@ -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>
|
BIN
app/src/main/res/drawable-hdpi/rcd_cancel_icon.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable-hdpi/voice_rcd_cancel_bg_focused.png
Normal file
After Width: | Height: | Size: 2.6 KiB |
BIN
app/src/main/res/drawable-hdpi/voice_rcd_hint_bg.9.png
Normal file
After Width: | Height: | Size: 308 B |
BIN
app/src/main/res/drawable-hdpi/voice_to_short.png
Normal file
After Width: | Height: | Size: 1010 B |
BIN
app/src/main/res/drawable-xhdpi/amp1.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp2.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp3.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp4.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp5.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp6.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
app/src/main/res/drawable-xhdpi/amp7.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
app/src/main/res/drawable-xhdpi/icon_sound_01.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
app/src/main/res/drawable-xhdpi/icon_sound_02.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/res/drawable-xhdpi/icon_sound_03.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp1.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp2.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp3.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp4.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp5.png
Normal file
After Width: | Height: | Size: 22 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp6.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
app/src/main/res/drawable-xxhdpi/amp7.png
Normal file
After Width: | Height: | Size: 23 KiB |
BIN
app/src/main/res/drawable-xxhdpi/icon_sound_01.png
Normal file
After Width: | Height: | Size: 17 KiB |
BIN
app/src/main/res/drawable-xxhdpi/icon_sound_02.png
Normal file
After Width: | Height: | Size: 18 KiB |
BIN
app/src/main/res/drawable-xxhdpi/icon_sound_03.png
Normal file
After Width: | Height: | Size: 18 KiB |
6
app/src/main/res/drawable/bg_select_sound_xml.xml
Normal file
@ -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>
|
@ -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>
|
BIN
app/src/main/res/drawable/icon_select_sound_defaule.9.png
Normal file
After Width: | Height: | Size: 66 KiB |
BIN
app/src/main/res/drawable/icon_select_sound_press.9.png
Normal file
After Width: | Height: | Size: 65 KiB |
13
app/src/main/res/drawable/pop_voice_img.xml
Normal file
@ -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>
|
7
app/src/main/res/drawable/sound_anim.xml
Normal file
@ -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>
|
@ -63,7 +63,6 @@
|
|||||||
android:layout_height="48dp"
|
android:layout_height="48dp"
|
||||||
android:layout_marginRight="20dp"
|
android:layout_marginRight="20dp"
|
||||||
android:layout_marginBottom="120dp"
|
android:layout_marginBottom="120dp"
|
||||||
android:onClick="@{()->mainActivity.voiceOnclick()}"
|
|
||||||
android:src="@drawable/baseline_keyboard_voice_24"
|
android:src="@drawable/baseline_keyboard_voice_24"
|
||||||
app:layout_constraintBottom_toBottomOf="parent"
|
app:layout_constraintBottom_toBottomOf="parent"
|
||||||
app:layout_constraintRight_toRightOf="parent" />
|
app:layout_constraintRight_toRightOf="parent" />
|
||||||
|
45
app/src/main/res/layout/adapter_sound_list.xml
Normal file
@ -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>
|
@ -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>
|
83
app/src/main/res/layout/cv_card_voice_rcd_hint_window.xml
Normal file
@ -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>
|
@ -125,10 +125,16 @@
|
|||||||
android:layout_margin="5dp"
|
android:layout_margin="5dp"
|
||||||
android:background="@color/gray_121" />
|
android:background="@color/gray_121" />
|
||||||
|
|
||||||
|
<TextView
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:text="多媒体" />
|
||||||
|
|
||||||
<androidx.recyclerview.widget.RecyclerView
|
<androidx.recyclerview.widget.RecyclerView
|
||||||
android:id="@+id/evaluation_voice_recyclerview"
|
android:id="@+id/evaluation_voice_recyclerview"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="80dp" />
|
android:layout_height="120dp" />
|
||||||
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
</androidx.core.widget.NestedScrollView>
|
</androidx.core.widget.NestedScrollView>
|
||||||
|
|
||||||
|
@ -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")
|
@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);
|
List<NiLocation> timeTofindList(int minx, int maxx, int miny, int maxy,long startTime,long endTime);
|
||||||
|
|
||||||
|
@Query("SELECT * FROM niLocation")
|
||||||
|
List<NiLocation> findAll();
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package com.navinfo.collect.library.data.entity
|
package com.navinfo.collect.library.data.entity
|
||||||
|
|
||||||
import com.navinfo.collect.library.utils.GeometryToolsKt
|
import com.navinfo.collect.library.utils.GeometryToolsKt
|
||||||
|
import com.navinfo.omqs.bean.Attachment
|
||||||
import io.realm.RealmObject
|
import io.realm.RealmObject
|
||||||
import io.realm.RealmSet
|
import io.realm.RealmSet
|
||||||
import io.realm.annotations.PrimaryKey
|
import io.realm.annotations.PrimaryKey
|
||||||
@ -86,6 +87,8 @@ open class QsRecordBean @JvmOverloads constructor(
|
|||||||
*/
|
*/
|
||||||
var guideGeometry: String = "",
|
var guideGeometry: String = "",
|
||||||
|
|
||||||
|
var attachments:RealmSet<Attachment>,
|
||||||
|
|
||||||
) : RealmObject() {
|
) : RealmObject() {
|
||||||
|
|
||||||
fun copy(): QsRecordBean {
|
fun copy(): QsRecordBean {
|
||||||
@ -104,6 +107,7 @@ open class QsRecordBean @JvmOverloads constructor(
|
|||||||
confirmUserId = confirmUserId,
|
confirmUserId = confirmUserId,
|
||||||
t_lifecycle = t_lifecycle,
|
t_lifecycle = t_lifecycle,
|
||||||
t_status = t_status,
|
t_status = t_status,
|
||||||
|
attachments = attachments,
|
||||||
)
|
)
|
||||||
qs.geometry = geometry
|
qs.geometry = geometry
|
||||||
return qs
|
return qs
|
||||||
|
@ -53,7 +53,8 @@ public class MapLifeNiLocationTileDataSource implements ITileDataSource {
|
|||||||
if(mEndTime!=0){
|
if(mEndTime!=0){
|
||||||
list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().timeTofindList(xStart, xEnd, yStart, yEnd,mStartTime,mEndTime);
|
list = TraceDataBase.getDatabase(mCon, dbName).getNiLocationDao().timeTofindList(xStart, xEnd, yStart, yEnd,mStartTime,mEndTime);
|
||||||
}else{
|
}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);
|
Log.e("qj","query"+(list==null?0:list.size())+"==="+xStart+"==="+xEnd+"==="+yStart+"==="+yEnd);
|
||||||
|