Merge branch 'master' of gitlab.navinfo.com:vivo/navinfovivo

 Conflicts:
	app/build.gradle
	app/src/main/java/com/navinfo/volvo/database/entity/GreetingMessage.kt
	app/src/main/java/com/navinfo/volvo/ui/fragments/message/ObtainMessageViewModel.kt
This commit is contained in:
squallzhjch
2023-01-06 13:48:58 +08:00
11 changed files with 672 additions and 146 deletions

3
.idea/gradle.xml generated
View File

@@ -7,8 +7,7 @@
<option name="testRunner" value="GRADLE" /> <option name="testRunner" value="GRADLE" />
<option name="distributionType" value="DEFAULT_WRAPPED" /> <option name="distributionType" value="DEFAULT_WRAPPED" />
<option name="externalProjectPath" value="$PROJECT_DIR$" /> <option name="externalProjectPath" value="$PROJECT_DIR$" />
<option name="gradleHome" value="$USER_HOME$/.gradle/wrapper/dists/gradle-7.2-all/260hg96vuh6ex27h9vo47iv4d/gradle-7.2" /> <option name="gradleJvm" value="11" />
<option name="gradleJvm" value="JDK" />
<option name="modules"> <option name="modules">
<set> <set>
<option value="$PROJECT_DIR$" /> <option value="$PROJECT_DIR$" />

View File

@@ -149,8 +149,10 @@ dependencies {
implementation("com.github.bumptech.glide:glide:4.11.0") { implementation("com.github.bumptech.glide:glide:4.11.0") {
exclude group: "com.android.support" exclude group: "com.android.support"
} }
// 多媒体播放库 https://github.com/JagarYousef/ChatVoicePlayer
implementation 'com.github.JagarYousef:ChatVoicePlayer:1.1.0'
// 图片查看器 https://github.com/XiaoGe-1996/ImageViewer
implementation 'com.github.XiaoGe-1996:ImageViewer:v1.0.0'
} }
kapt { kapt {

View File

@@ -4,10 +4,12 @@
<!-- 拍照 --> <!-- 拍照 -->
<uses-permission <uses-permission
android:name="android.permission.CAMERA" android:name="android.permission.CAMERA"
android:required="false" /> <!-- 网络请求 --> android:required="false" />
<!-- 网络请求 -->
<uses-permission <uses-permission
android:name="android.permission.INTERNET" android:name="android.permission.INTERNET"
android:required="false" /> <!-- 录音 --> android:required="false" />
<!-- 录音 -->
<uses-permission <uses-permission
android:name="android.permission.RECORD_AUDIO" android:name="android.permission.RECORD_AUDIO"
android:required="false" /> <!-- 读写文件 --> android:required="false" /> <!-- 读写文件 -->

View File

@@ -38,9 +38,9 @@ data class GreetingMessage @JvmOverloads constructor(
var sendType: String? = "", var sendType: String? = "",
var del: String? = "", var del: String? = "",
var version: String? = "", var version: String? = "",
/** // /**
* 附件列表 // * 附件列表
*/ // */
var attachment: MutableList<Attachment> = mutableListOf(), // var attachment: MutableList<Attachment> = mutableListOf(),
var read: Boolean = false, var read: Boolean = false,
) )

View File

@@ -0,0 +1,86 @@
package com.navinfo.volvo.http
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.catch
import kotlinx.coroutines.flow.flow
import kotlinx.coroutines.flow.flowOn
import okhttp3.ResponseBody
import retrofit2.Retrofit
import java.io.File
import java.io.IOException
object DownloadManager {
suspend fun download(url: String, file: File): Flow<DownloadState> {
return flow {
val retrofit = Retrofit.Builder()
.baseUrl(UrlUtils.getBaseUrl(url))
.build()
val response = retrofit.create(NavinfoVolvoService::class.java).downloadFile(url).execute()
if (response.isSuccessful) {
saveToFile(response.body()!!, file) {
emit(DownloadState.InProgress(it))
}
emit(DownloadState.Success(file))
} else {
emit(DownloadState.Error(IOException(response.toString())))
}
}.catch {
emit(DownloadState.Error(it))
}.flowOn(Dispatchers.IO)
}
private inline fun saveToFile(responseBody: ResponseBody, file: File, progressListener: (Int) -> Unit) {
val total = responseBody.contentLength()
var bytesCopied = 0
var emittedProgress = 0
file.outputStream().use { output ->
val input = responseBody.byteStream()
val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
var bytes = input.read(buffer)
while (bytes >= 0) {
output.write(buffer, 0, bytes)
bytesCopied += bytes
bytes = input.read(buffer)
val progress = (bytesCopied * 100 / total).toInt()
if (progress - emittedProgress > 0) {
progressListener(progress)
emittedProgress = progress
}
}
}
}
}
sealed class DownloadState {
data class InProgress(val progress: Int) : DownloadState()
data class Success(val file: File) : DownloadState()
data class Error(val throwable: Throwable) : DownloadState()
}
interface DownloadCallback {
fun progress(progress: Int)
fun error(throwable: Throwable)
fun success(file: File)
}
object UrlUtils {
/**
* 从url分割出BaseUrl
*/
fun getBaseUrl(url: String): String {
var mutableUrl = url
var head = ""
var index = mutableUrl.indexOf("://")
if (index != -1) {
head = mutableUrl.substring(0, index + 3)
mutableUrl = mutableUrl.substring(index + 3)
}
index = mutableUrl.indexOf("/")
if (index != -1) {
mutableUrl = mutableUrl.substring(0, index + 1)
}
return head + mutableUrl
}
}

View File

@@ -2,18 +2,16 @@ package com.navinfo.volvo.http
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.ResponseBody
import retrofit2.Call import retrofit2.Call
import retrofit2.http.Body import retrofit2.http.*
import retrofit2.http.Multipart
import retrofit2.http.POST
import retrofit2.http.Part
import java.io.File import java.io.File
interface NavinfoVolvoService { interface NavinfoVolvoService {
@POST("/navi/cardDelivery/insertCardByApp") @POST("/navi/cardDelivery/insertCardByApp")
fun insertCardByApp(@Body insertData: MutableMap<String, String>) suspend fun insertCardByApp(@Body insertData: Map<String, String>):DefaultResponse<String>
@POST("/navi/cardDelivery/updateCardByApp") @POST("/navi/cardDelivery/updateCardByApp")
fun updateCardByApp(@Body updateData: MutableMap<String, String>) suspend fun updateCardByApp(@Body updateData: Map<String, String>):DefaultResponse<String>
@POST("/navi/cardDelivery/queryCardListByApp") @POST("/navi/cardDelivery/queryCardListByApp")
fun queryCardListByApp(@Body queryData: MutableMap<String, String>) fun queryCardListByApp(@Body queryData: MutableMap<String, String>)
@POST("/navi/cardDelivery/deleteCardByApp") @POST("/navi/cardDelivery/deleteCardByApp")
@@ -22,5 +20,8 @@ interface NavinfoVolvoService {
@Multipart @Multipart
suspend fun uploadAttachment(@Part attachmentFile: MultipartBody.Part):DefaultResponse<MutableMap<String, String>> suspend fun uploadAttachment(@Part attachmentFile: MultipartBody.Part):DefaultResponse<MutableMap<String, String>>
@POST("/img/download") @POST("/img/download")
fun downLoadAttachment(@Body downloadData: MutableMap<String, String>):Call<DefaultResponse<MutableMap<String, String>>> suspend fun downLoadAttachment(@Body downloadData: Map<String, String>):DefaultResponse<String>
@Streaming
@GET
fun downloadFile(@Url url: String): Call<ResponseBody>
} }

View File

@@ -1,9 +1,12 @@
package com.navinfo.volvo.ui.fragments.message package com.navinfo.volvo.ui.fragments.message
import android.content.DialogInterface import android.content.DialogInterface
import android.graphics.Paint
import android.net.Uri
import android.os.Bundle import android.os.Bundle
import android.text.TextUtils import android.text.TextUtils
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View import android.view.View
import android.view.View.* import android.view.View.*
import android.view.ViewGroup import android.view.ViewGroup
@@ -13,11 +16,18 @@ import android.widget.ArrayAdapter
import android.widget.Toast import android.widget.Toast
import androidx.core.widget.addTextChangedListener import androidx.core.widget.addTextChangedListener
import androidx.fragment.app.Fragment import androidx.fragment.app.Fragment
import androidx.fragment.app.viewModels
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import androidx.navigation.Navigation import androidx.navigation.Navigation
import com.bumptech.glide.Glide import com.bumptech.glide.Glide
import com.bumptech.glide.load.engine.DiskCacheStrategy
import com.easytools.tools.DateUtils import com.easytools.tools.DateUtils
import com.easytools.tools.DeviceUtils
import com.easytools.tools.DisplayUtils
import com.easytools.tools.FileIOUtils
import com.easytools.tools.FileUtils
import com.easytools.tools.ResourceUtils
import com.easytools.tools.ToastUtils import com.easytools.tools.ToastUtils
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.dialog.MaterialAlertDialogBuilder
@@ -25,17 +35,25 @@ import com.gredicer.datetimepicker.DateTimePickerFragment
import com.hjq.permissions.OnPermissionCallback import com.hjq.permissions.OnPermissionCallback
import com.hjq.permissions.Permission import com.hjq.permissions.Permission
import com.hjq.permissions.XXPermissions import com.hjq.permissions.XXPermissions
import com.navinfo.volvo.R
import com.navinfo.volvo.RecorderLifecycleObserver import com.navinfo.volvo.RecorderLifecycleObserver
import com.navinfo.volvo.databinding.FragmentObtainMessageBinding import com.navinfo.volvo.database.entity.Attachment
import com.navinfo.volvo.database.entity.AttachmentType import com.navinfo.volvo.database.entity.AttachmentType
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import com.navinfo.volvo.databinding.FragmentObtainMessageBinding
import com.navinfo.volvo.http.DownloadCallback
import com.navinfo.volvo.ui.markRequiredInRed import com.navinfo.volvo.ui.markRequiredInRed
import com.navinfo.volvo.util.PhotoLoader
import com.navinfo.volvo.utils.EasyMediaFile import com.navinfo.volvo.utils.EasyMediaFile
import com.navinfo.volvo.utils.SystemConstant import com.navinfo.volvo.utils.SystemConstant
import com.nhaarman.supertooltips.ToolTip import com.nhaarman.supertooltips.ToolTip
import indi.liyi.viewer.Utils
import indi.liyi.viewer.ViewData
import top.zibin.luban.Luban import top.zibin.luban.Luban
import top.zibin.luban.OnCompressListener import top.zibin.luban.OnCompressListener
import java.io.File import java.io.File
import java.io.FileInputStream
import java.io.FileOutputStream
import java.util.* import java.util.*
@@ -52,6 +70,9 @@ class ObtainMessageFragment: Fragment() {
RecorderLifecycleObserver() RecorderLifecycleObserver()
} }
private val dateSendFormat = "yyyy-MM-dd HH:mm:ss"
private val dateShowFormat = "yyyy-MM-dd HH:mm"
// This property is only valid between onCreateView and // This property is only valid between onCreateView and
// onDestroyView. // onDestroyView.
private val binding get() = _binding!! private val binding get() = _binding!!
@@ -72,37 +93,63 @@ class ObtainMessageFragment: Fragment() {
if(it.name?.isNotEmpty() == true) if(it.name?.isNotEmpty() == true)
binding.tvMessageTitle?.setText(it.name) binding.tvMessageTitle?.setText(it.name)
if (it.sendDate?.isNotEmpty() == true) { if (it.sendDate?.isNotEmpty() == true) {
binding.btnSendTime.text = it.sendDate // 获取当前发送时间,如果早于当前时间,则显示现在
val sendDate = DateUtils.str2Date(it.sendDate, dateSendFormat)
if (sendDate<=Date()) {
binding.btnSendTime.text = "现在"
} else {
binding.btnSendTime.text = it.sendDate
}
} else { // 如果发送时间此时为空,自动设置发送时间为当前时间
it.sendDate = DateUtils.date2Str(Date(), dateSendFormat)
} }
var hasPhoto = false var hasPhoto = false
var hasAudio = false var hasAudio = false
if (it.attachment.isNotEmpty()) { if (it.imageUrl!=null&&it.imageUrl?.isNotEmpty() == true) {
// 展示照片文件或录音文件 hasPhoto = true
for (attachment in it.attachment) { // Glide.with(this@ObtainMessageFragment)
if (attachment.attachmentType == AttachmentType.PIC) { // .asBitmap().fitCenter()
Glide.with(context!!) // .load(it.imageUrl)
.asBitmap().fitCenter() // .diskCacheStrategy(DiskCacheStrategy.ALL)
.load(attachment.pathUrl) // .into(binding.imgMessageAttachment)
.into(binding.imgMessageAttachment) // 如果当前attachment文件是本地文件开始尝试网络上传
// 显示名称 val str = it.imageUrl?.replace("\\", "/")
binding.tvPhotoName.text = attachment.pathUrl.replace("\\", "/").substringAfterLast("/") binding.tvPhotoName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG )
hasPhoto = true if (!str!!.startsWith("http")) {
obtainMessageViewModel.uploadAttachment(File(it.imageUrl), AttachmentType.PIC)
// 如果当前attachment文件是本地文件开始尝试网络上传 binding.tvPhotoName.text = str.substringAfterLast("/", "picture.jpg")
if (!attachment.pathUrl.startsWith("http")) { } else {
obtainMessageViewModel.uploadAttachment(File(attachment.pathUrl)) if (str.contains("?")) {
} binding.tvPhotoName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?"))
} else {
binding.tvPhotoName.text = str.substringAfterLast("/")
} }
if (attachment.attachmentType == AttachmentType.AUDIO) { }
binding.tvAudioName.text = attachment.pathUrl.replace("\\", "/").substringAfterLast("/") }
hasAudio = true
if (it.mediaUrl!=null&&it.mediaUrl?.isNotEmpty() == true) {
hasAudio = true
binding.tvAudioName.getPaint().setFlags(Paint.UNDERLINE_TEXT_FLAG )
// 如果当前attachment文件是本地文件开始尝试网络上传
val str = it.mediaUrl?.replace("\\", "/")
if (!str!!.startsWith("http")) {
obtainMessageViewModel.uploadAttachment(File(it.mediaUrl),AttachmentType.AUDIO)
binding.tvAudioName.text = str.substringAfterLast("/", "audio.m4a")
} else {
if (str.contains("?")) {
binding.tvAudioName.text = str.substring(str.lastIndexOf("/")+1, str.indexOf("?"))
} else {
binding.tvAudioName.text = str.substringAfterLast("/")
} }
} }
} }
binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE binding.layerPhotoResult.visibility = if (hasPhoto) VISIBLE else GONE
binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE binding.layerGetPhoto.visibility = if (hasPhoto) GONE else VISIBLE
// binding.imgMessageAttachment.visibility = if (hasPhoto) VISIBLE else GONE
binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE binding.layerAudioResult.visibility = if (hasAudio) VISIBLE else GONE
binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE binding.layerGetAudio.visibility = if (hasAudio) GONE else VISIBLE
// binding.llAudioPlay.visibility = if (hasAudio) VISIBLE else GONE
} }
) )
lifecycle.addObserver(recorderLifecycleObserver) lifecycle.addObserver(recorderLifecycleObserver)
@@ -113,16 +160,20 @@ class ObtainMessageFragment: Fragment() {
fun initView() { fun initView() {
// 设置问候信息提示的红色星号 // 设置问候信息提示的红色星号
binding.tiLayoutTitle.markRequiredInRed() binding.tiLayoutTitle.markRequiredInRed()
binding.tvMessageTitle.addTextChangedListener { binding.tvMessageTitle.addTextChangedListener(afterTextChanged = {
obtainMessageViewModel.updateMessageTitle(it.toString()) obtainMessageViewModel.getMessageLiveData().value?.name = it.toString()
} })
binding.edtSendFrom.addTextChangedListener (afterTextChanged = {
obtainMessageViewModel.getMessageLiveData().value?.who = it.toString()
})
binding.imgPhotoDelete.setOnClickListener { binding.imgPhotoDelete.setOnClickListener {
obtainMessageViewModel.updateMessagePic(null) obtainMessageViewModel.updateMessagePic("")
} }
binding.imgAudioDelete.setOnClickListener { binding.imgAudioDelete.setOnClickListener {
obtainMessageViewModel.updateMessageAudio(null) obtainMessageViewModel.updateMessageAudio("")
} }
val sendToArray = mutableListOf<String>("绑定车辆1(LYVXFEFEXNL754427)") val sendToArray = mutableListOf<String>("绑定车辆1(LYVXFEFEXNL754427)")
@@ -143,11 +194,11 @@ class ObtainMessageFragment: Fragment() {
val dialog = DateTimePickerFragment.newInstance().mode(0) val dialog = DateTimePickerFragment.newInstance().mode(0)
dialog.listener = object : DateTimePickerFragment.OnClickListener { dialog.listener = object : DateTimePickerFragment.OnClickListener {
override fun onClickListener(selectTime: String) { override fun onClickListener(selectTime: String) {
val sendDate = DateUtils.str2Date(selectTime, "yyyy-MM-dd HH:mm") val sendDate = DateUtils.str2Date(selectTime, dateShowFormat)
if (sendDate <= Date()) { if (sendDate <= Date()) {
obtainMessageViewModel.updateMessageSendTime("现在") obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(Date(), dateSendFormat))
} else { } else {
obtainMessageViewModel.updateMessageSendTime(selectTime) obtainMessageViewModel.updateMessageSendTime(DateUtils.date2Str(sendDate, dateSendFormat))
} }
} }
@@ -193,12 +244,22 @@ class ObtainMessageFragment: Fragment() {
// 用户选择录音文件 // 用户选择录音文件
binding.btnSelectSound.setOnClickListener { binding.btnSelectSound.setOnClickListener {
photoHelper.setCrop(false).selectAudio(activity!!) photoHelper.setCrop(false).selectAudio(activity!!)
// SingleAudioPicker.showPicker(context!!) {
// val audioFile = File(it.contentUri.path)
// ToastUtils.showToast(audioFile.absolutePath)
// if (!audioFile.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) {
// val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, audioFile.name), FileInputStream(audioFile))
// XLog.e("拷贝结果:"+copyResult)
// obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, audioFile.name).absolutePath)
// } else {
// obtainMessageViewModel.updateMessageAudio(audioFile.absolutePath)
// }
// }
} }
// 开始录音 binding.btnStartRecord.setOnTouchListener { view, motionEvent ->
binding.btnStartRecord.setOnClickListener {
// 申请权限 // 申请权限
XXPermissions.with(this) XXPermissions.with(this@ObtainMessageFragment)
// 申请单个权限 // 申请单个权限
.permission(Permission.RECORD_AUDIO) .permission(Permission.RECORD_AUDIO)
.request(object : OnPermissionCallback { .request(object : OnPermissionCallback {
@@ -207,17 +268,23 @@ class ObtainMessageFragment: Fragment() {
Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show() Toast.makeText(activity, "获取部分权限成功,但部分权限未正常授予", Toast.LENGTH_SHORT).show()
return return
} }
when(motionEvent.action) {
if (it.isSelected) { MotionEvent.ACTION_DOWN-> {
it.isSelected = false // 申请权限
val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder() recorderLifecycleObserver.initAndStartRecorder()
if (File(recorderAudioPath).exists()) { ToastUtils.showToast("开始录音!")
obtainMessageViewModel.updateMessageAudio(recorderAudioPath) false
}
MotionEvent.ACTION_UP -> {
val recorderAudioPath = recorderLifecycleObserver.stopAndReleaseRecorder()
if (File(recorderAudioPath).exists()) {
obtainMessageViewModel.updateMessageAudio(recorderAudioPath)
}
false
}
else -> {
false
} }
} else{
it.isSelected = true
recorderLifecycleObserver.initAndStartRecorder()
} }
} }
@@ -232,6 +299,7 @@ class ObtainMessageFragment: Fragment() {
} }
} }
}) })
false
} }
// 获取照片文件和音频文件 // 获取照片文件和音频文件
@@ -260,8 +328,16 @@ class ObtainMessageFragment: Fragment() {
if (!it.absolutePath.equals(file?.absolutePath)) { if (!it.absolutePath.equals(file?.absolutePath)) {
it?.delete() it?.delete()
} }
// 跳转回原Fragment展示拍摄的照片 // 如果当前文件不在camera缓存文件夹下则移动该文件
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath) if (!file!!.parentFile.absolutePath.equals(SystemConstant.CameraFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.CameraFolder, fileName), FileInputStream(file))
XLog.e("拷贝结果:"+copyResult)
// 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(File(SystemConstant.CameraFolder, fileName).absolutePath)
} else {
// 跳转回原Fragment展示拍摄的照片
ViewModelProvider(requireActivity()).get(ObtainMessageViewModel::class.java).updateMessagePic(file!!.absolutePath)
}
} }
override fun onError(e: Throwable) { override fun onError(e: Throwable) {
@@ -270,7 +346,13 @@ class ObtainMessageFragment: Fragment() {
}).launch() }).launch()
} else if (fileName.endsWith(".mp3")||fileName.endsWith(".wav")||fileName.endsWith(".amr")||fileName.endsWith(".m4a")) { } else if (fileName.endsWith(".mp3")||fileName.endsWith(".wav")||fileName.endsWith(".amr")||fileName.endsWith(".m4a")) {
ToastUtils.showToast(it.absolutePath) ToastUtils.showToast(it.absolutePath)
obtainMessageViewModel.updateMessageAudio(it.absolutePath) if (!it.parentFile.parentFile.absolutePath.equals(SystemConstant.SoundFolder)) {
val copyResult = FileIOUtils.writeFileFromIS(File(SystemConstant.SoundFolder, fileName), FileInputStream(it))
XLog.e("拷贝结果:"+copyResult)
obtainMessageViewModel.updateMessageAudio(File(SystemConstant.SoundFolder, fileName).absolutePath)
} else {
obtainMessageViewModel.updateMessageAudio(it.absolutePath)
}
} }
} }
@@ -279,11 +361,37 @@ class ObtainMessageFragment: Fragment() {
ToastUtils.showToast(it.message) ToastUtils.showToast(it.message)
} }
binding.tvAudioName.setOnClickListener {
binding.llAudioPlay.visibility = if (binding.llAudioPlay.visibility == VISIBLE) GONE else VISIBLE
// 判断当前播放的文件是否在缓存文件夹内,如果不在首先下载该文件
val fileUrl = obtainMessageViewModel.getMessageLiveData().value!!.mediaUrl!!
val localFile = obtainMessageViewModel.getLocalFileFromNetUrl(fileUrl, AttachmentType.AUDIO)
if (!localFile.exists()) {
obtainMessageViewModel.downLoadFile(fileUrl, localFile, object: DownloadCallback {
override fun progress(progress: Int) {
}
override fun error(throwable: Throwable) {
}
override fun success(file: File) {
binding.voicePlayerView.setAudio(localFile.absolutePath)
}
})
} else {
binding.voicePlayerView.setAudio(localFile.absolutePath)
}
}
binding.btnObtainMessageBack.setOnClickListener { binding.btnObtainMessageBack.setOnClickListener {
Navigation.findNavController(it).popBackStack() Navigation.findNavController(it).popBackStack()
} }
binding.btnObtainMessageConfirm.setOnClickListener { binding.btnObtainMessageConfirm.setOnClickListener {
var checkResult = true
val toolTipBackColor = ResourceUtils.getColor(R.color.teal_200)
val toolTipTextColor = ResourceUtils.getColor(R.color.black)
// 检查当前输入数据 // 检查当前输入数据
val messageData = obtainMessageViewModel.getMessageLiveData().value val messageData = obtainMessageViewModel.getMessageLiveData().value
if (messageData?.name?.isEmpty() == true) { if (messageData?.name?.isEmpty() == true) {
@@ -291,40 +399,49 @@ class ObtainMessageFragment: Fragment() {
binding.ttTitle binding.ttTitle
val toolTip = ToolTip() val toolTip = ToolTip()
.withText("请输入问候信息") .withText("请输入问候信息")
.withColor(com.navinfo.volvo.R.color.purple_200) .withColor(toolTipBackColor)
.withShadow() .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW) .withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle) toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle)
} checkResult = false
var hasPic = false } else {
var hasAudio = false if (messageData?.name!!.length>10) {
for (attachment in messageData?.attachment!!) { val toolTipRelativeLayout =
if (attachment.attachmentType == AttachmentType.PIC) { binding.ttTitle
hasPic = true val toolTip = ToolTip()
} .withText("问候信息长度不能超过10")
if (attachment.attachmentType == AttachmentType.AUDIO) { .withColor(toolTipBackColor)
hasAudio = true .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tiLayoutTitle)
checkResult = false
} }
} }
if (!hasPic) { if (messageData?.imageUrl?.isEmpty() == true) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttPic binding.ttPic
val toolTip = ToolTip() val toolTip = ToolTip()
.withText("需要提供照片文件") .withText("需要提供照片文件")
.withColor(com.navinfo.volvo.R.color.purple_200) .withColor(toolTipBackColor)
.withShadow() .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW) .withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic) toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic)
checkResult = false
} }
if (!hasAudio) { if (messageData?.mediaUrl?.isEmpty() == true) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttAudio binding.ttAudio
val toolTip = ToolTip() val toolTip = ToolTip()
.withText("需要提供音频文件") .withText("需要提供音频文件")
.withColor(com.navinfo.volvo.R.color.purple_200) .withColor(toolTipBackColor)
.withShadow() .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW) .withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic) toolTipRelativeLayout.showToolTipForView(toolTip, binding.tvUploadPic)
checkResult = false
} }
if (messageData?.who?.isEmpty()==true) { if (messageData?.who?.isEmpty()==true) {
@@ -332,21 +449,86 @@ class ObtainMessageFragment: Fragment() {
binding.ttSendFrom binding.ttSendFrom
val toolTip = ToolTip() val toolTip = ToolTip()
.withText("请输入您的名称") .withText("请输入您的名称")
.withColor(com.navinfo.volvo.R.color.purple_200) .withColor(toolTipBackColor)
.withShadow() .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW) .withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom) toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendFrom)
checkResult = false
} }
if (messageData?.toWho?.isEmpty()==true) { if (messageData?.toWho?.isEmpty()==true) {
val toolTipRelativeLayout = val toolTipRelativeLayout =
binding.ttSendTo binding.ttSendTo
val toolTip = ToolTip() val toolTip = ToolTip()
.withText("请选择要发送的车辆") .withText("请选择要发送的车辆")
.withColor(com.navinfo.volvo.R.color.purple_200) .withColor(toolTipBackColor)
.withShadow() .withTextColor(toolTipTextColor)
.withoutShadow()
.withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW) .withAnimationType(ToolTip.AnimationType.FROM_MASTER_VIEW)
toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendTo) toolTipRelativeLayout.showToolTipForView(toolTip, binding.edtSendTo)
checkResult = false
} }
if (checkResult) { // 检查通过
// 检查attachment是否为本地数据如果是本地则弹出对话框尝试上传
val localAttachmentList = mutableListOf<Attachment>()
if (messageData?.imageUrl?.startsWith("http") == false) {
val imageAttachment = Attachment("", messageData.imageUrl!!, AttachmentType.PIC)
localAttachmentList.add(imageAttachment)
}
if (messageData?.mediaUrl?.startsWith("http") == false) {
val audioAttachment = Attachment("", messageData.mediaUrl!!, AttachmentType.AUDIO)
localAttachmentList.add(audioAttachment)
}
if (localAttachmentList.isNotEmpty()) {
MaterialAlertDialogBuilder(context!!)
.setTitle("提示")
.setMessage("当前照片及音频内容需首先上传,是否尝试上传?")
.setPositiveButton("确定", DialogInterface.OnClickListener { dialogInterface, i ->
dialogInterface.dismiss()
for (attachment in localAttachmentList) {
obtainMessageViewModel.uploadAttachment(File(attachment.pathUrl), attachment.attachmentType)
}
})
.setNegativeButton("取消", DialogInterface.OnClickListener {
dialogInterface, i -> dialogInterface.dismiss()
})
.show()
return@setOnClickListener
}
// 检查发送时间
val sendDate = DateUtils.str2Date(messageData?.sendDate, dateSendFormat)
val cal = Calendar.getInstance()
cal.time = Date()
cal.set(Calendar.MINUTE, cal.get(Calendar.MINUTE)+1)
if (cal.time.time < sendDate.time) { // 发送时间设置小于当前时间1分钟前Toast提示用户并自动设置发送时间
messageData?.sendDate = DateUtils.date2Str(cal.time, dateSendFormat)
ToastUtils.showToast("自动调整发送时间为1分钟后发送")
}
// 开始网络提交数据
if (obtainMessageViewModel.getMessageLiveData().value?.id==0L) { // 如果网络id为空则调用更新操作
obtainMessageViewModel.insertCardByApp()
} else {
obtainMessageViewModel.updateCardByApp()
}
}
}
// 点击照片名称
binding.tvPhotoName.setOnClickListener {
val viewData = ViewData()
viewData.imageSrc = obtainMessageViewModel.getMessageLiveData().value!!.imageUrl
viewData.targetX = Utils.dp2px(context, 10F).toFloat()
viewData.targetWidth = DisplayUtils.getScreenWidthPixels(activity) - Utils.dp2px(context, 20F)
viewData.targetHeight = Utils.dp2px(context, 200F)
val viewDataList = listOf(viewData)
binding.imageViewer.overlayStatusBar(true) // ImageViewer 是否会占据 StatusBar 的空间
.viewData(viewDataList) // 图片数据
.imageLoader(PhotoLoader()) // 设置图片加载方式
.showIndex(true) // 是否显示图片索引默认为true
.watch(0) // 开启浏览
} }
} }
@@ -395,4 +577,6 @@ class ObtainMessageFragment: Fragment() {
super.onDestroy() super.onDestroy()
lifecycle.removeObserver(recorderLifecycleObserver) lifecycle.removeObserver(recorderLifecycleObserver)
} }
companion object
} }

View File

@@ -1,22 +1,30 @@
package com.navinfo.volvo.ui.fragments.message package com.navinfo.volvo.ui.fragments.message
import androidx.lifecycle.* import androidx.lifecycle.*
import com.easytools.tools.FileIOUtils
import com.easytools.tools.FileUtils
import com.easytools.tools.ToastUtils import com.easytools.tools.ToastUtils
import com.elvishew.xlog.XLog import com.elvishew.xlog.XLog
import com.navinfo.volvo.database.entity.Attachment import com.navinfo.volvo.database.entity.Attachment
import com.navinfo.volvo.database.entity.AttachmentType import com.navinfo.volvo.database.entity.AttachmentType
import com.navinfo.volvo.database.entity.GreetingMessage import com.navinfo.volvo.database.entity.GreetingMessage
import com.navinfo.volvo.http.DownloadCallback
import com.navinfo.volvo.http.DownloadManager
import com.navinfo.volvo.http.DownloadState
import com.navinfo.volvo.http.NavinfoVolvoCall import com.navinfo.volvo.http.NavinfoVolvoCall
import com.navinfo.volvo.utils.SystemConstant
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import okhttp3.MediaType
import okhttp3.MediaType.Companion.toMediaTypeOrNull import okhttp3.MediaType.Companion.toMediaTypeOrNull
import okhttp3.MultipartBody import okhttp3.MultipartBody
import okhttp3.RequestBody import okhttp3.RequestBody
import java.io.File import java.io.File
import java.io.FileInputStream
import java.util.* import java.util.*
import javax.inject.Inject
class ObtainMessageViewModel @Inject constructor() : ViewModel() { class ObtainMessageViewModel: ViewModel() {
private val msgLiveData: MutableLiveData<GreetingMessage> by lazy { private val msgLiveData: MutableLiveData<GreetingMessage> by lazy {
MutableLiveData<GreetingMessage>() MutableLiveData<GreetingMessage>()
} }
@@ -37,52 +45,42 @@ class ObtainMessageViewModel @Inject constructor() : ViewModel() {
// 更新消息附件中的照片文件 // 更新消息附件中的照片文件
fun updateMessagePic(picUrl: String?) { fun updateMessagePic(picUrl: String?) {
var hasPic = false // var hasPic = false
for (attachment in this.msgLiveData.value!!.attachment) { this.msgLiveData.value?.imageUrl = picUrl
if (attachment.attachmentType == AttachmentType.PIC) { // for (attachment in this.msgLiveData.value!!.attachment) {
if (picUrl == null || picUrl.isEmpty()) { // if (attachment.attachmentType == AttachmentType.PIC) {
this.msgLiveData.value!!.attachment.remove(attachment) // if (picUrl==null||picUrl.isEmpty()) {
} else { // this.msgLiveData.value!!.attachment.remove(attachment)
attachment.pathUrl = picUrl // } else {
} // attachment.pathUrl = picUrl
hasPic = true // }
} // hasPic = true
} // }
if (!hasPic && picUrl != null) { // }
this.msgLiveData.value!!.attachment.add( // if (!hasPic&&picUrl!=null) {
Attachment( // this.msgLiveData.value!!.attachment.add(Attachment(UUID.randomUUID().toString(), picUrl, AttachmentType.PIC))
UUID.randomUUID().toString(), // }
picUrl,
AttachmentType.PIC
)
)
}
this.msgLiveData.postValue(this.msgLiveData.value) this.msgLiveData.postValue(this.msgLiveData.value)
} }
// 更新消息附件中的录音文件 // 更新消息附件中的录音文件
fun updateMessageAudio(audioUrl: String?) { fun updateMessageAudio(audioUrl: String?) {
var hasAudio = false // var hasAudio = false
for (attachment in this.msgLiveData.value!!.attachment) { // for (attachment in this.msgLiveData.value!!.attachment) {
if (attachment.attachmentType == AttachmentType.AUDIO) { // if (attachment.attachmentType == AttachmentType.AUDIO) {
if (audioUrl == null || audioUrl.isEmpty()) { // if (audioUrl==null||audioUrl.isEmpty()) {
this.msgLiveData.value!!.attachment.remove(attachment) // this.msgLiveData.value!!.attachment.remove(attachment)
} else { // } else {
attachment.pathUrl = audioUrl // attachment.pathUrl = audioUrl
} // }
hasAudio = true // hasAudio = true
} // }
} // }
if (!hasAudio && audioUrl != null) { // if (!hasAudio&&audioUrl!=null) {
this.msgLiveData.value!!.attachment.add( // this.msgLiveData.value!!.attachment.add(Attachment(UUID.randomUUID().toString(), audioUrl, AttachmentType.AUDIO))
Attachment( // }
UUID.randomUUID().toString(), this.msgLiveData.value?.mediaUrl = audioUrl
audioUrl,
AttachmentType.AUDIO
)
)
}
this.msgLiveData.postValue(this.msgLiveData.value) this.msgLiveData.postValue(this.msgLiveData.value)
} }
@@ -104,21 +102,59 @@ class ObtainMessageViewModel @Inject constructor() : ViewModel() {
this.msgLiveData.postValue(this.msgLiveData.value) this.msgLiveData.postValue(this.msgLiveData.value)
} }
fun uploadAttachment(attachmentFile: File) { // // 获取照片url
// fun getImageAttachment(attachementList: List<Attachment>): Attachment? {
// for (attachment in attachementList) {
// if (attachment.attachmentType == AttachmentType.PIC) {
// return attachment
// }
// }
// return null
// }
//
// // 获取音频url
// fun getAudioAttachment(attachementList: List<Attachment>): Attachment? {
// for (attachment in attachementList) {
// if (attachment.attachmentType == AttachmentType.AUDIO) {
// return attachment
// }
// }
// return null
// }
// 上传附件文件
fun uploadAttachment(attachmentFile: File, attachmentType: AttachmentType) {
// 启用协程调用网络请求 // 启用协程调用网络请求
viewModelScope.launch { viewModelScope.launch {
try { try {
val requestFile: RequestBody = val requestFile: RequestBody =
RequestBody.create("multipart/form-data".toMediaTypeOrNull(), attachmentFile) RequestBody.create("multipart/form-data".toMediaTypeOrNull(), attachmentFile)
val body = MultipartBody.Part.createFormData( val body = MultipartBody.Part.createFormData("picture", attachmentFile.getName(), requestFile)
"picture",
attachmentFile.getName(),
requestFile
)
val result = NavinfoVolvoCall.getApi().uploadAttachment(body) val result = NavinfoVolvoCall.getApi().uploadAttachment(body)
XLog.d(result.code) XLog.d(result.code)
if (result.code == 200) { // 请求成功 if (result.code == 200) { // 请求成功
// 获取上传后的结果 // 获取上传后的结果
val fileKey = result.data?.get("fileKey")
val newFileName = fileKey!!.substringAfterLast("/")
// 修改缓存文件名
if (attachmentType == AttachmentType.PIC) { // 修改当前文件在缓存文件夹的名称
val destFile = File(SystemConstant.CameraFolder, newFileName)
if (destFile.exists()) {
FileUtils.deleteFile(destFile)
}
val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile))
XLog.e("拷贝结果:"+copyResult)
} else {
val destFile = File(SystemConstant.SoundFolder, newFileName)
if (destFile.exists()) {
FileUtils.deleteFile(destFile)
}
val copyResult = FileIOUtils.writeFileFromIS(destFile, FileInputStream(attachmentFile))
XLog.e("拷贝结果:"+copyResult)
}
if (fileKey!=null) {
downloadAttachment(fileKey, attachmentType)
}
} else { } else {
ToastUtils.showToast(result.msg) ToastUtils.showToast(result.msg)
} }
@@ -128,4 +164,139 @@ class ObtainMessageViewModel @Inject constructor() : ViewModel() {
} }
} }
} }
// 下载附件文件
fun downloadAttachment(fileKey: String, attachmentType: AttachmentType) {
// 启用协程调用网络请求
viewModelScope.launch {
try {
val downloadParam = mapOf("fileKey" to fileKey)
val result = NavinfoVolvoCall.getApi().downLoadAttachment(downloadParam)
XLog.d(result.code)
if (result.code == 200) { // 请求成功
// 获取上传后的结果
val imageUrl = result.data
if (imageUrl!=null) {
XLog.d("downloadAttachment-imageUrl:${imageUrl}")
// 获取到图片的网络地址
if (attachmentType == AttachmentType.PIC) {
updateMessagePic(imageUrl)
} else {
updateMessageAudio(imageUrl)
}
}
} else {
ToastUtils.showToast(result.msg)
}
} catch (e: Exception) {
ToastUtils.showToast(e.message)
XLog.d(e.message)
}
}
}
fun downLoadFile(url: String, destFile: File, downloadCallback: DownloadCallback){
viewModelScope.launch {
DownloadManager.download(
url,
destFile
).collect {
when (it) {
is DownloadState.InProgress -> {
XLog.d("~~~", "download in progress: ${it.progress}.")
downloadCallback.progress(it.progress)
}
is DownloadState.Success -> {
XLog.d("~~~", "download finished.")
downloadCallback.success(it.file)
}
is DownloadState.Error -> {
XLog.d("~~~", "download error: ${it.throwable}.")
downloadCallback.error(it.throwable)
}
}
}
}
}
fun insertCardByApp() {
viewModelScope.launch {
try {
// TODO 首先保存数据到本地
val message = msgLiveData.value
val insertData = mapOf(
"name" to message?.name,
"imageUrl" to message?.imageUrl,
"mediaUrl" to message?.mediaUrl,
"who" to message?.who,
"toWho" to message?.toWho,
"sendDate" to message?.sendDate
)
val result = NavinfoVolvoCall.getApi().insertCardByApp(insertData as Map<String, String>)
XLog.d("insertCardByApp:${result.code}")
if (result.code == 200) { // 请求成功
// 获取上传后的结果
val netId = result.data
message?.id = netId!!.toLong()
ToastUtils.showToast("保存成功")
// TODO 尝试更新本地数据
} else {
ToastUtils.showToast(result.msg)
}
} catch (e: Exception) {
ToastUtils.showToast(e.message)
XLog.d(e.message)
}
}
}
fun updateCardByApp() {
viewModelScope.launch {
try {
val message = msgLiveData.value
val updateData = mapOf(
"id" to message?.id,
"name" to message?.name,
"imageUrl" to message?.imageUrl,
"mediaUrl" to message?.mediaUrl,
"who" to message?.who,
"toWho" to message?.toWho,
"sendDate" to message?.sendDate
)
val result = NavinfoVolvoCall.getApi().updateCardByApp(updateData as Map<String, String>)
XLog.d("updateCardByApp:${result.code}")
if (result.code == 200) { // 请求成功
// 数据更新成功
ToastUtils.showToast("更新成功")
// 尝试保存数据到本地
} else {
ToastUtils.showToast(result.msg)
}
} catch (e: Exception) {
ToastUtils.showToast(e.message)
XLog.d(e.message)
}
}
}
/**
* 根据网络地址获取本地的缓存文件路径
* */
fun getLocalFileFromNetUrl(url: String, attachmentType: AttachmentType):File {
if (url.startsWith("http")) {
val folder = when(attachmentType) {
AttachmentType.PIC-> SystemConstant.CameraFolder
else -> SystemConstant.SoundFolder
}
var name = if (url.contains("?")) {
url.substring(url.lastIndexOf("/")+1, url.indexOf("?"))
} else {
url.substringAfterLast("/")
}
return File(folder, name)
} else {
return File(url)
}
}
} }

View File

@@ -0,0 +1,50 @@
package com.navinfo.volvo.util;
import android.graphics.drawable.Drawable;
import android.widget.ImageView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.bumptech.glide.Glide;
import com.bumptech.glide.request.target.CustomViewTarget;
import com.bumptech.glide.request.transition.Transition;
import indi.liyi.viewer.ImageLoader;
public class PhotoLoader extends ImageLoader {
@Override
public void displayImage(final Object src, ImageView imageView, final LoadCallback callback) {
Glide.with(imageView.getContext())
.load(src)
.into(new CustomViewTarget<ImageView, Drawable>(imageView) {
@Override
protected void onResourceLoading(@Nullable Drawable placeholder) {
super.onResourceLoading(placeholder);
if(callback!=null){
callback.onLoadStarted(placeholder);
}
}
@Override
public void onResourceReady(@NonNull Drawable resource, @Nullable Transition<? super Drawable> transition) {
if(callback!=null) {
callback.onLoadSucceed(resource);
}
}
@Override
public void onLoadFailed(@Nullable Drawable errorDrawable) {
if(callback!=null) {
callback.onLoadFailed(errorDrawable);
}
}
@Override
protected void onResourceCleared(@Nullable Drawable placeholder) {
}
});
}
}

View File

@@ -103,7 +103,7 @@ class EasyMediaFile {
fun selectAudio(activity: Activity) { fun selectAudio(activity: Activity) {
isCrop = false isCrop = false
val intent = Intent(Intent.ACTION_PICK, null).apply { val intent = Intent(Intent.ACTION_PICK, null).apply {
setDataAndType(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "audio/*") setDataAndType(MediaStore.Audio.Media.EXTERNAL_CONTENT_URI, "*/*")
} }
if (Looper.myLooper() == Looper.getMainLooper()) { if (Looper.myLooper() == Looper.getMainLooper()) {
selectFileInternal(intent, activity, 1) selectFileInternal(intent, activity, 1)
@@ -131,7 +131,7 @@ class EasyMediaFile {
* 选择文件 * 选择文件
*/ */
private fun selectFileInternal(intent: Intent, activity: Activity, type: Int) { private fun selectFileInternal(intent: Intent, activity: Activity, type: Int) {
val resolveInfoList = activity.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY) var resolveInfoList = activity.packageManager.queryIntentActivities(intent, PackageManager.MATCH_DEFAULT_ONLY)
if (resolveInfoList.isEmpty()) { if (resolveInfoList.isEmpty()) {
error?.invoke(IllegalStateException("No Activity found to handle Intent ")) error?.invoke(IllegalStateException("No Activity found to handle Intent "))
} else { } else {

View File

@@ -27,7 +27,7 @@
app:counterEnabled="true" app:counterEnabled="true"
app:counterMaxLength="10" app:counterMaxLength="10"
app:errorEnabled="true" app:errorEnabled="true"
android:hint="请输入问候信息" android:hint="问候信息"
app:layout_constraintLeft_toLeftOf="parent" app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent" app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" app:layout_constraintTop_toTopOf="parent"
@@ -121,9 +121,11 @@
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/tv_photo_name" android:id="@+id/tv_photo_name"
android:layout_width="wrap_content" android:textColor="@android:color/holo_blue_dark"
android:padding="@dimen/default_widget_padding"
android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:text=""></com.google.android.material.textview.MaterialTextView> android:layout_weight="1"></com.google.android.material.textview.MaterialTextView>
<Space <Space
android:layout_width="@dimen/default_widget_padding" android:layout_width="@dimen/default_widget_padding"
android:layout_height="wrap_content"></Space> android:layout_height="wrap_content"></Space>
@@ -147,7 +149,6 @@
<com.nhaarman.supertooltips.ToolTipRelativeLayout <com.nhaarman.supertooltips.ToolTipRelativeLayout
android:id="@+id/tt_pic" android:id="@+id/tt_pic"
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@@ -156,6 +157,7 @@
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:gravity="center_vertical" android:gravity="center_vertical"
android:layout_marginVertical="@dimen/default_widget_padding"
android:orientation="horizontal"> android:orientation="horizontal">
<TextView <TextView
@@ -180,7 +182,7 @@
android:layout_height="wrap_content" android:layout_height="wrap_content"
style="@style/Widget.Material3.Button.ElevatedButton" style="@style/Widget.Material3.Button.ElevatedButton"
app:icon="@drawable/ic_baseline_fiber_manual_record_24" app:icon="@drawable/ic_baseline_fiber_manual_record_24"
android:text="录制音频" android:text="长按录音"
android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton> android:padding="@dimen/default_widget_padding"></com.google.android.material.button.MaterialButton>
<Space <Space
android:layout_width="@dimen/default_widget_padding" android:layout_width="@dimen/default_widget_padding"
@@ -204,8 +206,11 @@
android:orientation="horizontal"> android:orientation="horizontal">
<com.google.android.material.textview.MaterialTextView <com.google.android.material.textview.MaterialTextView
android:id="@+id/tv_audio_name" android:id="@+id/tv_audio_name"
android:layout_width="wrap_content" android:layout_width="0dp"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:textColor="@android:color/holo_blue_dark"
android:padding="@dimen/default_widget_padding"
android:layout_weight="1"
android:text=""></com.google.android.material.textview.MaterialTextView> android:text=""></com.google.android.material.textview.MaterialTextView>
<Space <Space
android:layout_width="@dimen/default_widget_padding" android:layout_width="@dimen/default_widget_padding"
@@ -219,16 +224,36 @@
</LinearLayout> </LinearLayout>
</LinearLayout> </LinearLayout>
<com.google.android.material.imageview.ShapeableImageView <!--增加音频播放按钮-->
android:id="@+id/img_sound_play" <LinearLayout
android:layout_width="wrap_content" android:id="@+id/ll_audio_play"
android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:src="@drawable/ic_baseline_volume_up_24" android:layout_gravity="center"
android:visibility="gone" android:visibility="gone"
android:layout_gravity="center"></com.google.android.material.imageview.ShapeableImageView> android:orientation="horizontal">
<me.jagar.chatvoiceplayerlibrary.VoicePlayerView
android:id="@+id/voicePlayerView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
app:enableVisualizer="true"
app:visualizationPlayedColor="@color/teal_200"
app:visualizationNotPlayedColor="@color/teal_700"
app:playPauseBackgroundColor="@color/teal_700"
app:timingBackgroundColor="@color/purple_200"
app:seekBarProgressColor="@color/purple_500"
app:showShareButton="false"
app:shareCornerRadius="100"
app:playPauseCornerRadius="100"
app:showTiming="true"
app:viewCornerRadius="100"
app:viewBackground="@android:color/transparent"
app:progressTimeColor="@color/purple_500"
app:seekBarThumbColor="@color/teal_200"/>
</LinearLayout>
<com.nhaarman.supertooltips.ToolTipRelativeLayout <com.nhaarman.supertooltips.ToolTipRelativeLayout
android:id="@+id/tt_audio" android:id="@+id/tt_audio"
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
</LinearLayout> </LinearLayout>
@@ -283,7 +308,6 @@
<com.nhaarman.supertooltips.ToolTipRelativeLayout <com.nhaarman.supertooltips.ToolTipRelativeLayout
android:id="@+id/tt_send_from" android:id="@+id/tt_send_from"
app:layout_constraintTop_toBottomOf="@id/ti_layout_title"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" /> android:layout_height="wrap_content" />
@@ -379,4 +403,11 @@
android:layout_weight="1" android:layout_weight="1"
android:text="确认提交"></com.google.android.material.button.MaterialButton> android:text="确认提交"></com.google.android.material.button.MaterialButton>
</LinearLayout> </LinearLayout>
<indi.liyi.viewer.ImageViewer
android:id="@+id/imageViewer"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@android:color/transparent"
app:ivr_dragMode="agile" />
</androidx.constraintlayout.widget.ConstraintLayout> </androidx.constraintlayout.widget.ConstraintLayout>