Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。

Kotlin高仿微信-项目实践58篇,点击查看详情

效果图:

详细的聊天功能请查看Kotlin高仿微信-第8篇-单聊,这里是提示文本功能的部分实现。

实现代码:

我的语言布局:

<LinearLayoutandroid:id="@+id/chat_item_me_voice_layout"app:layout_constraintEnd_toStartOf="@+id/chat_item_me_avatar"app:layout_constraintTop_toTopOf="@+id/chat_item_me_avatar"app:layout_constraintBottom_toBottomOf="@+id/chat_item_me_avatar"android:layout_width="120dp"android:layout_height="40dp"android:layout_marginEnd="10dp"android:background="@drawable/wc_base_voice_green_normal"android:gravity="center"android:visibility="visible"android:orientation="horizontal"><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/chat_item_me_voice_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="1'"android:layout_marginRight="4dp"android:textSize="20sp"/><ImageViewandroid:id="@+id/chat_item_me_voice"android:layout_width="22dp"android:layout_height="22dp"android:src="@drawable/wc_chat_me_volume_2"android:visibility="visible"/></LinearLayout>

好友的语音布局:

<LinearLayoutandroid:id="@+id/chat_item_other_voice_layout"app:layout_constraintStart_toEndOf="@+id/chat_item_other_avatar"app:layout_constraintTop_toTopOf="@+id/chat_item_other_avatar"app:layout_constraintBottom_toBottomOf="@+id/chat_item_other_avatar"android:layout_width="120dp"android:layout_height="40dp"android:layout_marginStart="10dp"android:gravity="center"android:visibility="visible"android:background="@drawable/wc_base_voice_green_normal"android:orientation="horizontal"><ImageViewandroid:id="@+id/chat_item_other_voice"android:layout_width="22dp"android:layout_height="22dp"android:src="@drawable/wc_chat_other_volume_2"android:visibility="visible"/><androidx.appcompat.widget.AppCompatTextViewandroid:id="@+id/chat_item_other_voice_second"android:layout_width="wrap_content"android:layout_height="wrap_content"android:text="1'"android:layout_marginLeft="4dp"android:textSize="20sp"/></LinearLayout>
<com.wn.wechatclientdemo.view.ChatRecordButtonandroid:id="@+id/chat_record_btn"android:layout_width="0dp"android:layout_height="38dp"android:visibility="gone"android:layout_marginHorizontal="10dp"android:background="@drawable/wc_chat_voice_selector"app:layout_constraintStart_toEndOf="@+id/chat_audio"app:layout_constraintEnd_toStartOf="@+id/chat_emoji"app:layout_constraintTop_toTopOf="@+id/chat_audio"app:layout_constraintBottom_toBottomOf="@+id/chat_audio"android:text="按住说话"/>
/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/5/4 17:36* Description : 聊天语言按钮*/
class ChatRecordButton : AppCompatButton {constructor(context: Context) : super(context)constructor(context: Context, attributeSet: AttributeSet) :super(context, attributeSet)constructor(context: Context, attributeSet: AttributeSet, defStyle : Int) : super(context, attributeSet, defStyle)private var mFile : String = ""private var mFileMp3 : String = ""private var finishedListener: OnFinishedRecordListener? = null/*** 最短录音时间*/private val MIN_INTERVAL_TIME = 1000/*** 最长录音时间*/private val MAX_INTERVAL_TIME = 1000 * 60private var view: View? = nullprivate var mStateTV: TextView? = nullprivate var mStateIV: ImageView? = nullcompanion object var mRecorder: MediaRecorder? = nullprivate val mThread: ObtainDecibelThread? = nullprivate var volumeHandler: Handler? = nullprivate var y1 : Float = 0fprivate var anim: AnimationDrawable? = nullfun setOnFinishedRecordListener(listener: OnFinishedRecordListener?) {finishedListener = listener}private var startTime: Long = 0private var recordDialog: Dialog? = nullprivate val res = intArrayOf(R.drawable.wc_chat_volume_0,R.drawable.wc_chat_volume_1,R.drawable.wc_chat_volume_2,R.drawable.wc_chat_volume_3,R.drawable.wc_chat_volume_4,R.drawable.wc_chat_volume_5,R.drawable.wc_chat_volume_6,R.drawable.wc_chat_volume_7,R.drawable.wc_chat_volume_8)//private var audioRecordTool: AudioRecordTool? = nullinit {volumeHandler = object : Handler() {override fun handleMessage(msg: Message) {if (msg.what == -100) {stopRecording()recordDialog!!.dismiss()} else if (msg.what != -1) {mStateIV!!.setImageResource(res.get(msg.what))}}}}override fun onTouchEvent(event: MotionEvent?): Boolean {val action = event!!.actiony1 = event.yif (mStateTV != null && mStateIV != null && y1 < 0) {mStateTV?.text = "松开手指,取消发送"mStateIV?.setImageDrawable(resources.getDrawable(R.drawable.wc_chat_volume_cancel))} else if (mStateTV != null) {mStateTV?.text = "手指上滑,取消发送"}when (action) {MotionEvent.ACTION_DOWN -> {text = "松开发送"initDialogAndStartRecord()}MotionEvent.ACTION_UP, MotionEvent.ACTION_CANCEL -> {this.text = "按住录音"if (y1 >= 0 && System.currentTimeMillis() - startTime <= MAX_INTERVAL_TIME) {TagUtils.d("结束录音:")finishRecord()} else if (y1 < 0) {  //当手指向上滑,会cancelcancelRecord()}}}return true}/*** 初始化录音对话框 并 开始录音*/private fun initDialogAndStartRecord() {startTime = System.currentTimeMillis()recordDialog = Dialog(context, R.style.like_toast_dialog_style)// view = new ImageView(getContext());view = inflate(context, R.layout.wc_chat_dialog_record, null)mStateIV = view?.findViewById<ImageView>(R.id.rc_audio_state_image)mStateTV = view?.findViewById<TextView>(R.id.rc_audio_state_text)mStateIV?.setImageDrawable(resources.getDrawable(R.drawable.anim_mic))anim = mStateIV?.getDrawable() as AnimationDrawableanim?.start()mStateIV?.setVisibility(VISIBLE)//mStateIV.setImageResource(R.drawable.ic_volume_1);mStateTV?.setVisibility(VISIBLE)mStateTV?.setText("手指上滑,取消发送")recordDialog?.setContentView(view!!, LinearLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,ViewGroup.LayoutParams.WRAP_CONTENT))recordDialog?.setOnDismissListener(onDismiss)val lp = recordDialog?.getWindow()!!.attributeslp.gravity = Gravity.CENTERstartRecording()recordDialog?.show()}/*** 放开手指,结束录音处理*/private fun finishRecord() {val intervalTime: Long = System.currentTimeMillis() - startTimeif (intervalTime < MIN_INTERVAL_TIME) {TagUtils.d("录音时间太短")volumeHandler!!.sendEmptyMessageDelayed(-100, 500)//view.setBackgroundResource(R.drawable.ic_voice_cancel);mStateIV!!.setImageDrawable(resources.getDrawable(R.drawable.wc_chat_volume_wraning))mStateTV!!.text = "录音时间太短"anim!!.stop()val file = File(mFile!!)file.delete()return} else {stopRecording()recordDialog!!.dismiss()}TagUtils.d("录音完成的路径:$mFile")val mediaPlayer = MediaPlayer()try {mediaPlayer.setDataSource(mFile)mediaPlayer.prepare()mediaPlayer.durationTagUtils.d("获取到的时长:" + mediaPlayer.duration / 1000)} catch (e: Exception) {}//if (finishedListener != null) finishedListener!!.onFinishedRecord( mFile, mediaPlayer.duration / 1000)//删除原文件TagUtils.d("amr录音保存路径:${mFile}")File(mFile).delete()/*TagUtils.d("pcm录音保存路径:${audioRecordTool?.pcmFileName}")audioRecordTool?.deletePcmFile()//最终使用的是wav录音文件,音质非常的好mFile = audioRecordTool?.wavFileName!!TagUtils.d("wav录音保存路径:${audioRecordTool?.wavFileName!!}")*/TagUtils.d("mp3录音保存路径:${mFileMp3}")if (finishedListener != null) finishedListener!!.onFinishedRecord( mFileMp3, mediaPlayer.duration / 1000)}/*** 取消录音对话框和停止录音*/fun cancelRecord() {stopRecording()recordDialog!!.dismiss()val file = File(mFile)file.delete()LameMp3Manager.instance.cancelRecorder()var mp3File = File(mFileMp3)mp3File.delete()}/*** 执行录音操作*/private fun startRecording() {if(ContextCompat.checkSelfPermission(WcApp.getContext(), Manifest.permission.WRITE_EXTERNAL_STORAGE) == PackageManager.PERMISSION_GRANTED){mFile = FileUtils.getBaseFile("voice_" + System.currentTimeMillis() + ".amr").absolutePathmFileMp3 = FileUtils.getBaseFile("voice_" + System.currentTimeMillis() + ".mp3").absolutePath} else {ToastUtils.makeText(R.string.wc_permission_tip_record)return}//使用MP3文件代替wav,因为mp3语音清晰,占用空间小LameMp3Manager.instance.startRecorder(mFileMp3)/*audioRecordTool = AudioRecordTool()//初始化audioRecordTool?.createAudioRecord()audioRecordTool?.start()*/if (mRecorder != null) {mRecorder?.reset()} else {mRecorder = MediaRecorder()}mRecorder?.setAudioSource(MediaRecorder.AudioSource.MIC)mRecorder?.setOutputFormat(MediaRecorder.OutputFormat.AMR_NB)mRecorder?.setAudioEncoder(MediaRecorder.AudioEncoder.AMR_NB)mRecorder?.setOutputFile(mFile)try {mRecorder?.prepare()mRecorder?.start()} catch (e: Exception) {TagUtils.d("preparestart异常,重新开始录音:$e")e.printStackTrace()mRecorder?.release()mRecorder = nullstartRecording()}}private fun stopRecording() {//audioRecordTool?.stop()LameMp3Manager.instance.stopRecorder()if (mThread != null) {mThread.exit()//mThread = null}if (mRecorder != null) {try {mRecorder?.stop() //停止时没有prepare,就会报stop failedmRecorder?.reset()mRecorder?.release()mRecorder = null} catch (pE: RuntimeException) {pE.printStackTrace()} finally {if (recordDialog!!.isShowing) {recordDialog!!.dismiss()}}}}private class ObtainDecibelThread(val obj: ChatRecordButton) : Thread() {@Volatileprivate var running = truefun exit() {running = false}override fun run() {TagUtils.d("检测到的分贝001:")while (running) {if (obj.mRecorder == null || !running) {break}// int x = recorder.getMaxAmplitude(); //振幅val db: Int = obj.mRecorder!!.getMaxAmplitude() / 600TagUtils.d("检测到的分贝002:$")if (db != 0 && obj.y1 >= 0) {val f = (db / 5)if (f == 0) obj.volumeHandler?.sendEmptyMessage(0) else if (f == 1) obj.volumeHandler?.sendEmptyMessage(1) else if (f == 2) obj.volumeHandler?.sendEmptyMessage(2) else if (f == 3) obj.volumeHandler?.sendEmptyMessage(3) else if (f == 4) obj.volumeHandler?.sendEmptyMessage(4) else if (f == 5) obj.volumeHandler?.sendEmptyMessage(5) else if (f == 6) obj.volumeHandler?.sendEmptyMessage(6) else obj.volumeHandler?.sendEmptyMessage(7)}obj.volumeHandler?.sendEmptyMessage(-1)if (System.currentTimeMillis() - obj.startTime > 20000) {obj.finishRecord()}try {sleep(200)} catch (e: InterruptedException) {e.printStackTrace()}}}}private val onDismiss = DialogInterface.OnDismissListener { stopRecording() }interface OnFinishedRecordListener{fun onFinishedRecord(audioPath : String, time : Int)}}
//录音完成回调
chat_record_btn.setOnFinishedRecordListener(object : ChatRecordButton.OnFinishedRecordListener{override fun onFinishedRecord(audioPath: String, time: Int) {chat_content.isFocusable = falseSoftInputUtils.hideSoftInput(chat_record_btn)AddFileListener.sendFile(ChatBean.CONTENT_TYPE_VOICE, audioPath, toUserId, time)}
})
/*** 发送图片、小视频、语音* @param fileType Int* @param picPath String* @param toUserID String*/
fun sendFile(fileType: Int, filePath : String, toUserId : String, time: Int) {CoroutineScope(Dispatchers.IO).launch {//语音、小视频多少秒val toId: String = BaseUtils.getChatId(toUserId) + "/Smack"var resultFilePath = ""if(TextUtils.isEmpty(filePath)){return@launch}if(!File(filePath).exists()){return@launch}var second = timeif(fileType == ChatBean.CONTENT_TYPE_VOICE){second = time} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){second = CommonUtils.Media.getMediaTime(filePath, fileType)}//先刷新页面,再慢慢上传文件var chatBean = processSendChatBean(toUserId, fileType, filePath, second)if(fileType == ChatBean.CONTENT_TYPE_IMG){//图片//压缩图片后路径var resultPicPath = UploadFileUtils.getCompressFile(filePath)resultFilePath = resultPicPath} else if(fileType == ChatBean.CONTENT_TYPE_VOICE){//语音resultFilePath = filePathTagUtils.d("语音时间:${second}")} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){//小视频var videoFilePath = UploadFileUtils.getVideoCompressFile()//TagUtils.d("开始发送小视频压缩前文件2:${videoFilePath}")var compressResult = VideoController.getInstance().convertVideo(filePath, videoFilePath, VideoController.COMPRESS_QUALITY_LOW, object : VideoController.CompressProgressListener {override fun onProgress(percent: Float) {//TagUtils.d("压缩小视频进度:${percent}")}})TagUtils.d("小视频时间:${second}")resultFilePath = videoFilePath}TagUtils.d("上传文件:${resultFilePath}")val filetosend = File(resultFilePath)if (!filetosend.exists()) {return@launch}try {var account : String = DataStoreUtils.getAccount()if(fileType == ChatBean.CONTENT_TYPE_IMG){//图片TagUtils.d("图片发送成功。")var chatBeanServer = UploadFileUtils.uploadChatImages(account, toUserId, resultFilePath,0)if(chatBeanServer != null){chatBeanServer.imgPath = chatBeanServer.imgPathchatBean = chatBeanServer}} else if(fileType == ChatBean.CONTENT_TYPE_VOICE){//录音完成,要转码,等待0.2秒再发送delay(100)//语音var chatBeanServer = UploadFileUtils.uploadChatVoice(account, toUserId, resultFilePath, second)if(chatBeanServer != null){chatBeanServer.voiceLocal = resultFilePathchatBean = chatBeanServer}} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){//小视频var chatBeanServer = UploadFileUtils.uploadChatVideo(account, toUserId, resultFilePath, second)if(chatBeanServer != null){chatBeanServer.videoLocal = resultFilePathchatBean = chatBeanServer}}chatBean?.let {ChatRepository.updateChat(it)}var baseSystemBoolean = BaseSystemRepository.getBaseSystemSync(WcApp.getContext().packageName)if(baseSystemBoolean != null && baseSystemBoolean.sync == CommonUtils.Sync.SERVER_SYNC){//同步不需要使用transfer上传文件,因为transfer上传太慢了, 直接上传到服务器,然后发送普通的消息if(fileType == ChatBean.CONTENT_TYPE_VOICE){var content = CommonUtils.Chat.VOICE_MARK + chatBean.voiceChatManagerUtils.getInstance().sendMessage(toUserId, content)} else if(fileType == ChatBean.CONTENT_TYPE_VIDEO){var content = CommonUtils.Chat.VIDEO_MARK + chatBean.videoChatManagerUtils.getInstance().sendMessage(toUserId, content)} else if(fileType == ChatBean.CONTENT_TYPE_IMG){var content = CommonUtils.Chat.IMAGE_MARK + chatBean.imgPathChatManagerUtils.getInstance().sendMessage(toUserId, content)}return@launch}val fileTransferManager = getFileTransferManager()val transfer = fileTransferManager.createOutgoingFileTransfer(toId) // 创建一个输出文件传输对象//对方用户在线才需要上传文件if(XmppConnectionManager.getInstance().isOnLine(toUserId)){TagUtils.d("${toUserId} 在线 开始上传。")transfer.sendFile(filetosend,"recv img")while (!transfer.isDone) {if (transfer.status == FileTransfer.Status.error) {TagUtils.d("聊天文件上传错误 , ERROR!!! " + transfer.error)} else {TagUtils.d("聊天文件上传 " + transfer.status +" , " + transfer.progress)}Thread.sleep(20)}} else {TagUtils.d("toUserId 不在线")}if (transfer.isDone) {//上传完成}} catch (e1: XMPPException) {e1.printStackTrace()}}
}
//接收图片、语音、小视频文件监听
fun addFileListerer() {val manager = getFileTransferManager()manager.addFileTransferListener { request ->CoroutineScope(Dispatchers.IO).launch {//文件接收val transfer = request.accept()OutgoingFileTransfer.getResponseTimeout()//获取文件名字val fileName = transfer.fileNameTagUtils.d("接收图片、语音:${fileName}")//本地创建文件val sdCardDir  = File(FileUtils.CHAT_ALBUM_PATH)if (!sdCardDir.exists()) { //判断文件夹目录是否存在sdCardDir.mkdir() //如果不存在则创建}//val savePath: String = FileUtils.CHAT_ALBUM_PATH + fileNameval savePath: String = FileUtils.getBaseFile(fileName).pathval file = File(savePath)//接收文件try {transfer.recieveFile(file)while (!transfer.isDone) {if (transfer.status == FileTransfer.Status.error) {TagUtils.d("接收、语音文件 ERROR!!! " + transfer.error)} else {TagUtils.d("接收、语音文件" + transfer.status + " , " + transfer.progress)}//接收小视频,要停顿一下,否则小视频不完整打不开。Thread.sleep(20)}if (transfer.isDone) {var account = DataStoreUtils.getAccount()var chatBean : ChatBean? = nullvar userType = ChatBean.USER_TYPE_OTHERvar peer = transfer.peerTagUtils.d("接收文件完成peer:${peer}")var toUserId = BaseUtils.getChatAccountFrom(peer)TagUtils.d("接收文件完成toUserId:${toUserId}, account = ${account}")if(fileName.endsWith(".mp3", true)){//接收语音var second = CommonUtils.Media.getMediaTime(savePath, ChatBean.CONTENT_TYPE_VOICE)chatBean = CommonUtils.Chat.getChatBeanVoice(toUserId, account, userType, ChatBean.CONTENT_TYPE_VOICE, savePath, second)} else if(fileName.endsWith(".mp4", true)){//接收小视频var second = CommonUtils.Media.getMediaTime(file.path, ChatBean.CONTENT_TYPE_VIDEO)TagUtils.d("接收的小视频文件:${savePath}, ${second}秒")chatBean = CommonUtils.Chat.getChatBeanVideo(toUserId, account, userType,  ChatBean.CONTENT_TYPE_VIDEO, savePath, second)} else {//接收图片chatBean = CommonUtils.Chat.getChatBean(toUserId, account, userType, "", ChatBean.CONTENT_TYPE_IMG, savePath,0.0, 0.0)}chatBean?.let {ChatRepository.insertChat(chatBean)EventBus.getDefault().post(chatBean)}}} catch (e: XMPPException) {e.printStackTrace()}}}
}
//web服务器方式下载
override fun chatCreated(chat: Chat, createdLocally: Boolean) {TagUtils.d("消息监听回调:chat = ${chat} , createdLocally = ${createdLocally}")if(!createdLocally){chat.addMessageListener { chat, message ->TagUtils.d("获取好友发来的信息 ${message.from} , ${message.to}, ${message.body}")var content = message.getBody()if(!TextUtils.isEmpty(content) && content.length > 0){var fromUser = BaseUtils.getChatAccountFrom(message.from)var toUser = BaseUtils.getChatAccount(message.to)var userType = ChatBean.USER_TYPE_OTHERif(content.startsWith(CommonUtils.Chat.LOCATION_MARK)){//发送定位//去掉location###标志var remarkContent = CommonUtils.Chat.getLocation(content)//使用逗号分隔符,分别读取经纬度var contents = remarkContent.split(",")var latitude = contents[0].toDouble()var longitude = contents[1].toDouble()var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_LOCATION, "", latitude, longitude)ChatRepository.insertChat(chatBean)chatBean.isReceive = trueEventBus.getDefault().post(chatBean)} else if(content.startsWith(CommonUtils.Chat.REDPACKET_MARK)){//发送红包, 去掉redpacket###写入数据库content = CommonUtils.Chat.getRedpacket(content).toString()var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_REDPACKET, "",0.0, 0.0)ChatRepository.insertChat(chatBean)chatBean.isReceive = trueEventBus.getDefault().post(chatBean)} else if(content.startsWith(CommonUtils.Chat.VOICE_MARK)){//发送语音, 去掉voice###写入数据库content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.VOICE_MARK)var chatBean = CommonUtils.Chat.getChatBeanVoiceServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_VOICE, content,0)chatBean.isReceive = trueprocessDownload(chatBean)} else if(content.startsWith(CommonUtils.Chat.VIDEO_MARK)){//发送小视频, 去掉video###写入数据库content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.VIDEO_MARK)var chatBean = CommonUtils.Chat.getChatBeanVideoServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_VIDEO, content,0)chatBean.isReceive = trueprocessDownload(chatBean)} else if(content.startsWith(CommonUtils.Chat.IMAGE_MARK)){//发送图片, 去掉image###写入数据库content = CommonUtils.Chat.getMedia(content, CommonUtils.Chat.IMAGE_MARK)var chatBean = CommonUtils.Chat.getChatBeanImageServer(fromUser, toUser, userType,ChatBean.CONTENT_TYPE_IMG, content,0)chatBean.isReceive = trueprocessDownload(chatBean)} else if(content.startsWith(CommonUtils.Chat.TRANSFER_MARK)){//发送转账, 去掉transfer###写入数据库content = CommonUtils.Chat.getTransfer(content).toString()var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_TRANSFER, "",0.0, 0.0)ChatRepository.insertChat(chatBean)chatBean.isReceive = trueEventBus.getDefault().post(chatBean)} else if(content.startsWith(CommonUtils.QRCommon.QR_RECEIVE_CODE)){//向个人发送收款var balance = content.substring(CommonUtils.QRCommon.QR_RECEIVE_CODE.length, content.length)TagUtils.d("MyChatManagerListener 向个人发送收款金额: ${fromUser} , ${toUser},  ${balance}")updateBalanceServer(fromUser, toUser, CommonUtils.User.OPERATOR_PLUS, balance.toFloat())} else if(content.startsWith(CommonUtils.QRCommon.QR_PAYMENT_CODE)){//向商家付款var balance = content.substring(CommonUtils.QRCommon.QR_RECEIVE_CODE.length, content.length)TagUtils.d("MyChatManagerListener 向商家付款金额: ${fromUser} , ${toUser}, ${balance}")updateBalanceServer(fromUser, toUser, CommonUtils.User.OPERATOR_MINUS, balance.toFloat())} else {var chatBean = CommonUtils.Chat.getChatBean(fromUser, toUser, userType, content, ChatBean.CONTENT_TYPE_TEXT, "",0.0, 0.0)ChatRepository.insertChat(chatBean)chatBean.isReceive = trueEventBus.getDefault().post(chatBean)}ChatNotificationUtils.sendNotification(fromUser)}}}
}/*** 下载图片、语音、小视频*/
private fun processDownload(chatBean : ChatBean){var fileName = ""if(chatBean.contentType == ChatBean.CONTENT_TYPE_VOICE){fileName = chatBean.voice} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_VIDEO){fileName = chatBean.video} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_IMG){fileName = chatBean.imgPath} else {return}var videoUrl = CommonUtils.Moments.getReallyImageUrl(fileName)var videoFile = FileUtils.getBaseFile(fileName)TagUtils.d("下载多媒体Url :${videoUrl} ")TagUtils.d("下载多媒体File :${videoFile} ")VideoDownloadManager.downloadFast(videoUrl, videoFile, object : VideoDownloadInter {override fun onDone(filePath: String) {TagUtils.d("小视频多媒体完成:${filePath}")if(chatBean.contentType == ChatBean.CONTENT_TYPE_VOICE){var second = CommonUtils.Media.getMediaTime(filePath, chatBean.contentType)chatBean.second = secondchatBean.voiceLocal = filePath} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_VIDEO){chatBean.videoLocal = filePathvar second = CommonUtils.Media.getMediaTime(filePath, chatBean.contentType)chatBean.second = second} else if(chatBean.contentType == ChatBean.CONTENT_TYPE_IMG){chatBean.imgPathLocal = filePath}ChatRepository.insertChat(chatBean)EventBus.getDefault().post(chatBean)}override fun onError() {}override fun onProgress(process: Int) {}})
}

Kotlin高仿微信-第11篇-单聊-语音相关推荐

  1. Flutter高仿微信-第36篇-单聊-语音通话

    Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 目前市 ...

  2. Flutter高仿微信-第32篇-单聊-语音

    Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 详情请 ...

  3. Kotlin高仿微信-第14篇-单聊-视频通话

    Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册.登录.主页.单聊(文本.表情.语音.图片.小视频.视频通话.语音通话.红包.转账).群聊.个人信息.朋友圈.支付服务.扫一扫.搜索 ...

  4. Kotlin高仿微信-第8篇-单聊

    Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册.登录.主页.单聊(文本.表情.语音.图片.小视频.视频通话.语音通话.红包.转账).群聊.个人信息.朋友圈.支付服务.扫一扫.搜索 ...

  5. Flutter高仿微信-第31篇-单聊-表情

    Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 详情请 ...

  6. Flutter高仿微信-第47篇-群聊-语音

     Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 详情 ...

  7. Flutter高仿微信-第46篇-群聊-表情

    Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 详情请 ...

  8. Flutter高仿微信-第48篇-群聊-图片

     Flutter高仿微信系列共59篇,从Flutter客户端.Kotlin客户端.Web服务器.数据库表结构.Xmpp即时通讯服务器.视频通话服务器.腾讯云服务器全面讲解. 详情请查看 效果图: 详情 ...

  9. Kotlin高仿微信-第34篇-支付-向商家付款(二维码)

     Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册.登录.主页.单聊(文本.表情.语音.图片.小视频.视频通话.语音通话.红包.转账).群聊.个人信息.朋友圈.支付服务.扫一扫.搜 ...

最新文章

  1. 【建站系列教程】3.1、cookie介绍
  2. Web纯前端“旭日图”实现元素周期表
  3. GCT之数学公式(代数部分)
  4. 配置 Spring.NET
  5. 14行代码AC_Break the Chocolate HDU-4112(数学推导+解析)
  6. 朴素贝叶斯分类实战:对文档进行分类
  7. Linux 命令之 newgrp -- 登入另一个群组
  8. ajax 请求svg,jQuery append 到AJAX加载的SVG问题
  9. 用hyperledger cello H3C分支创建单机模式区块链系统
  10. php 整行插入mysql_MySQL的多行插入
  11. 分析connection reset by peer, socket write error错误原因
  12. PL/SQL Developer 8.0.2.1505 简体中文汉化版下载,带注册机-永久注册。
  13. [导入]Linq学习笔记(2.2)——深入DLinq查询
  14. WPF自定义Main函数
  15. bzoj3097 Hash Killer I
  16. ajax hack,Ajax Hacks-hack9 深入了解HTTP Respon_jquery
  17. Xilinx FPGA时钟及I/O接口规划(一)
  18. Android 学习网址大全
  19. 有关在html中修改select标签的option selected默认选中属性实现
  20. 2018CCPC吉林赛区 | 部分题解 (HDU6555 HDU6556 HDU6559 HDU6561)

热门文章

  1. pt2 of <confession>
  2. 51单片机Proteus仿真+Keil工程-实验6-单片机扩展RAM6264实验
  3. 学习的Python的第五天
  4. 计算机专业行政工作简历,计算机与行政管理专业的个人简历
  5. 学生用计算机该如何关闭,学生晚上回宿舍时,其在实验室的计算机主机应关闭,显示器一般不用关闭。()...
  6. 二级C语言VC++2010学习版安装
  7. c语言中%-2d是撒意思,C语言中%-2d是撒意思
  8. 论计算机技术与电子商务探讨,计算机技术电子商务应用论文,关于探析计算机技术在电子商务中的应用相关参考文献资料-免费论文范文...
  9. CIO40: 躬身入局者,担当作为,体现CIO价值!
  10. ubuntu 解压文件