Kotlin高仿微信-第15篇-单聊-语音通话

2023-10-23 14:50

本文主要是介绍Kotlin高仿微信-第15篇-单聊-语音通话,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

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

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

效果图:

 

实现代码:

/*** 视频通话、语音通话*/
private fun showVideoPopupWindow(){var popupView = layoutInflater.inflate(R.layout.wc_chat_video_pop_view , moment_root, false)var popupWindow = PopupWindow(popupView, ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT, true)var popupRoot = popupView.findViewById<LinearLayout>(R.id.chat_video_pop_root)popupWindow.showAtLocation(popupRoot, Gravity.BOTTOM, 0, 0)var window = requireActivity().window//popupWindow在弹窗的时候背景半透明val params = window.attributesparams.alpha = 0.5fwindow.attributes = paramspopupWindow.setOnDismissListener {params.alpha = 1.0fwindow.attributes = params}//视频通话popupView.findViewById<AppCompatTextView>(R.id.chat_pop_video_call).setOnClickListener {popupWindow.dismiss()CallSingleActivity.openActivity( requireActivity(),toUserId, true, toUserName, false, false)}//语音通话popupView.findViewById<AppCompatTextView>(R.id.chat_pop_voice_call).setOnClickListener {popupWindow.dismiss()CallSingleActivity.openActivity( requireActivity(),toUserId, true, toUserName, true, false)}//取消popupView.findViewById<AppCompatTextView>(R.id.chat_pop_cancel).setOnClickListener {popupWindow.dismiss()}}

/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/5/15 13:49* Description :*/
class CallSingleActivity : AppCompatActivity(), CallSession.CallSessionCallback {companion object {val EXTRA_TARGET = "targetId"val EXTRA_MO = "isOutGoing"val EXTRA_AUDIO_ONLY = "audioOnly"val EXTRA_USER_NAME = "userName"val EXTRA_FROM_FLOATING_VIEW = "fromFloatingView"fun openActivity(context: Context, targetId: String, isOutgoing: Boolean, inviteUserName: String,isAudioOnly: Boolean, isClearTop: Boolean) {val intent = getCallIntent(context, targetId, isOutgoing, inviteUserName, isAudioOnly, isClearTop)if (context is Activity) {context.startActivity(intent)} else {intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)context.startActivity(intent)}}fun getCallIntent(context: Context, targetId: String, isOutgoing: Boolean, inviteUserName: String,isAudioOnly: Boolean, isClearTop: Boolean): Intent {val voip = Intent(context, CallSingleActivity::class.java)voip.putExtra(EXTRA_MO, isOutgoing)voip.putExtra(EXTRA_TARGET, targetId)voip.putExtra(EXTRA_USER_NAME, inviteUserName)voip.putExtra(EXTRA_AUDIO_ONLY, isAudioOnly)voip.putExtra(EXTRA_FROM_FLOATING_VIEW, false)if (isClearTop) {voip.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)}return voip}}private val TAG = "CallSingleActivity"private val handler = Handler(Looper.getMainLooper())private var isOutgoing = falseprivate var targetId: String = ""private var inviteUserName: String = ""var isAudioOnly = falseprivate var isFromFloatingView = falseprivate var gEngineKit: SkyEngineKit? = nullprivate var currentFragment: SingleCallFragment? = nullprivate var room: String = ""private var activity:CallSingleActivity? = nulloverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setStatusBarOrScreenStatus(this)setContentView(R.layout.activity_single_call)activity = thistry {gEngineKit = SkyEngineKit.Instance()} catch (e: NotInitializedException) {SkyEngineKit.init(VoipEvent()) //重新初始化try {gEngineKit = SkyEngineKit.Instance()} catch (ex: NotInitializedException) {finish()}}val intent = intenttargetId = intent.getStringExtra(EXTRA_TARGET)!!inviteUserName = intent.getStringExtra(EXTRA_USER_NAME)!!isFromFloatingView = intent.getBooleanExtra(EXTRA_FROM_FLOATING_VIEW, false)isOutgoing = intent.getBooleanExtra(EXTRA_MO, false)isAudioOnly = intent.getBooleanExtra(EXTRA_AUDIO_ONLY, false)if (isFromFloatingView) {val serviceIntent = Intent(this, FloatingVoipService::class.java)stopService(serviceIntent)init(targetId, false, isAudioOnly, false)} else {// 权限检测val per: Array<String>per = if (isAudioOnly) {arrayOf(Manifest.permission.RECORD_AUDIO)} else {arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA)}Permissions.request(this, per, object : Consumer<Int> {override fun accept(t: Int) {if (t === 0) {// 权限同意init(targetId, isOutgoing, isAudioOnly, false)} else {Toast.makeText(activity, "权限被拒绝", Toast.LENGTH_SHORT).show()// 权限拒绝finish()}}})}}override fun onBackPressed() {}private fun init(targetId: String, outgoing: Boolean, audioOnly: Boolean, isReplace: Boolean) {val fragment: SingleCallFragmentif (audioOnly) {fragment = FragmentAudio()} else {fragment = FragmentVideo()}val fragmentManager: FragmentManager = getSupportFragmentManager()currentFragment = fragmentif (isReplace) {fragmentManager.beginTransaction().replace(android.R.id.content, fragment).commit()} else {fragmentManager.beginTransaction().add(android.R.id.content, fragment).commit()}if (outgoing && !isReplace) {// 创建会话room = UUID.randomUUID().toString() + System.currentTimeMillis()val b: Boolean = gEngineKit?.startOutCall(applicationContext, room, targetId, audioOnly)!!TagUtils.d("创建房间返回:${room} , ${targetId} , ${b}")if (!b) {finish()return}WcApp.getInstance().roomId = roomWcApp.getInstance().otherUserId = targetIdval session: CallSession? = gEngineKit?.getCurrentSession()if (session == null) {finish()} else {session.setSessionCallback(this)}} else {val session: CallSession? = gEngineKit?.getCurrentSession()if (session == null) {finish()} else {if (session.isAudioOnly() && !audioOnly) { //这种情况是,对方切换成音频的时候,activity还没启动,这里启动后需要切换一下isAudioOnly = session.isAudioOnly()fragment.didChangeMode(true)}session.setSessionCallback(this)}}}fun getEngineKit(): SkyEngineKit? {return gEngineKit}fun isOutgoing(): Boolean {return isOutgoing}fun getInviteUserName(): String {return inviteUserName}fun getToUserId(): String {return targetId}fun isFromFloatingView(): Boolean {return isFromFloatingView}// 显示小窗fun showFloatingView() {if (!checkOverlayPermission()) {return}val intent = Intent(this, FloatingVoipService::class.java)intent.putExtra(EXTRA_TARGET, targetId)intent.putExtra(EXTRA_USER_NAME, inviteUserName)intent.putExtra(EXTRA_AUDIO_ONLY, isAudioOnly)intent.putExtra(EXTRA_MO, isOutgoing)startService(intent)finish()}// 切换到语音通话fun switchAudio() {init(targetId, isOutgoing, true, true)}fun getRoomId(): String {return room}// ======================================界面回调================================override fun didCallEndWithReason(reason: EnumType.CallEndReason) {WcApp.getInstance().otherUserId = "0"//交给fragment去finish
//        finish();handler.post { currentFragment?.didCallEndWithReason(reason) }}override fun didChangeState(callState: EnumType.CallState) {if (callState === EnumType.CallState.Connected) {isOutgoing = false}handler.post { currentFragment?.didChangeState(callState) }}override fun didChangeMode(var1: Boolean) {handler.post { currentFragment?.didChangeMode(var1) }}override fun didCreateLocalVideoTrack() {handler.post { currentFragment?.didCreateLocalVideoTrack() }}override fun didReceiveRemoteVideoTrack(userId: String) {handler.post { currentFragment?.didReceiveRemoteVideoTrack(userId) }}override fun didUserLeave(userId: String) {handler.post { currentFragment?.didUserLeave(userId) }}override fun didError(var1: String) {handler.post { currentFragment?.didError(var1) }
//        finish();}override fun didDisconnected(userId: String) {handler.post { currentFragment?.didDisconnected(userId) }}// ========================================================================================// ========================================================================================private fun checkOverlayPermission(): Boolean {if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {SettingsCompat.setDrawOverlays(this, true)if (!SettingsCompat.canDrawOverlays(this)) {Toast.makeText(this, "需要悬浮窗权限", Toast.LENGTH_LONG).show()SettingsCompat.manageDrawOverlays(this)return false}}return true}@TargetApi(19)private fun getSystemUiVisibility(): Int {var flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION or View.SYSTEM_UI_FLAG_FULLSCREEN orView.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREENif (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {flags = flags or View.SYSTEM_UI_FLAG_IMMERSIVE_STICKY}return flags}/*** 设置状态栏透明*/@TargetApi(19)fun setStatusBarOrScreenStatus(activity: Activity) {val window = activity.window//全屏+锁屏+常亮显示window.addFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN orWindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON orWindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED orWindowManager.LayoutParams.FLAG_TURN_SCREEN_ON)window.decorView.systemUiVisibility = getSystemUiVisibility()if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {val layoutParams = getWindow().attributeslayoutParams.layoutInDisplayCutoutMode =WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGESwindow.attributes = layoutParams}// 5.0以上系统状态栏透明if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {//清除透明状态栏window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)//设置状态栏颜色必须添加window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)window.statusBarColor = Color.TRANSPARENT //设置透明} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { //19window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)}}override fun onDestroy() {super.onDestroy()}
}

/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/5/15 16:20* Description : 语音通话控制界面*/
class FragmentAudio : SingleCallFragment(), View.OnClickListener  {private val TAG = "FragmentAudio"private var muteImageView: ImageView? = nullprivate var speakerImageView: ImageView? = nullprivate var micEnabled = false // 静音private var isSpeakerOn = false // 扬声器override fun getLayout(): Int {return R.layout.fragment_audio}override fun initView(view: View) {super.initView(view)muteImageView = view.findViewById(R.id.muteImageView)speakerImageView = view.findViewById(R.id.speakerImageView)minimizeImageView!!.visibility = View.GONEoutgoingHangupImageView!!.setOnClickListener(this)incomingHangupImageView!!.setOnClickListener(this)minimizeImageView!!.setOnClickListener(this)muteImageView?.setOnClickListener(this)acceptImageView?.setOnClickListener(this)speakerImageView?.setOnClickListener(this)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || OSUtils.isMiui() || OSUtils.isFlyme()) {lytParent!!.post {val params = minimizeImageView!!.layoutParams as RelativeLayout.LayoutParamsparams.topMargin = BarUtils.getStatusBarHeight()minimizeImageView!!.layoutParams = params}}}override fun init() {super.init()val currentSession = gEngineKit!!.getCurrentSession()currentState = currentSession!!.getState()// 如果已经接通if (currentSession != null && currentState === EnumType.CallState.Connected) {descTextView!!.visibility = View.GONE // 提示语outgoingActionContainer!!.visibility = View.VISIBLEdurationTextView!!.visibility = View.VISIBLEminimizeImageView!!.visibility = View.VISIBLEstartRefreshTime()} else {// 如果未接通if (isOutgoing) {descTextView?.setText(R.string.av_waiting)outgoingActionContainer!!.visibility = View.VISIBLEincomingActionContainer!!.visibility = View.GONE} else {descTextView?.setText(R.string.av_audio_invite)outgoingActionContainer!!.visibility = View.GONEincomingActionContainer!!.visibility = View.VISIBLE}}}override fun didChangeState(state: EnumType.CallState) {currentState = staterunOnUiThread {if (state === EnumType.CallState.Connected) {handler!!.removeMessages(WHAT_DELAY_END_CALL)incomingActionContainer!!.visibility = View.GONEoutgoingActionContainer!!.visibility = View.VISIBLEminimizeImageView!!.visibility = View.VISIBLEdescTextView!!.visibility = View.GONEstartRefreshTime()} else {// do nothing now}}}override fun onClick(v: View) {val id = v.id// 接听if (id == R.id.acceptImageView) {val session = gEngineKit!!.getCurrentSession()if (session != null) Log.d(TAG,"session = " + session + "; session.getState() = " + session.getState())if (session != null && session.getState() === EnumType.CallState.Incoming) {session.joinHome(session.getRoomId()!!)} else session?.sendRefuse()}// 挂断电话if (id == R.id.incomingHangupImageView || id == R.id.outgoingHangupImageView) {//App.getInstance().setOtherUserId("0")WcApp.getInstance().otherUserId = "0"val session = gEngineKit!!.getCurrentSession()if (session != null) {TagUtils.d("FragmentAudio 挂电话:endCall ")SkyEngineKit.Instance()?.endCall()}//            activity.finish();//再onEvent中结束,防止ChatActivity结束了,消息发送不了}// 静音if (id == R.id.muteImageView) {val session = gEngineKit!!.getCurrentSession()if (session != null && session.getState() !== EnumType.CallState.Idle) {if (session.toggleMuteAudio(!micEnabled)) {micEnabled = !micEnabled}muteImageView!!.isSelected = micEnabled}}// 扬声器if (id == R.id.speakerImageView) {val session = gEngineKit!!.getCurrentSession()if (session != null && session.getState() !== EnumType.CallState.Idle) {if (session.toggleSpeaker(!isSpeakerOn)) {isSpeakerOn = !isSpeakerOn}speakerImageView!!.isSelected = isSpeakerOn}}// 小窗if (id == R.id.minimizeImageView) {if (callSingleActivity != null) {callSingleActivity!!.showFloatingView()}}}
}
/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/5/15 16:14* Description :*/
abstract class SingleCallFragment : Fragment() {private val TAG = "SingleCallFragment"var minimizeImageView: ImageView? = null// 用户头像var portraitImageView : ImageView? = null// 用户昵称var nameTextView : TextView? = null// 状态提示用语var descTextView : TextView? = null// 通话时长var durationTextView : Chronometer? = nullvar outgoingHangupImageView: ImageView? = nullvar incomingHangupImageView: ImageView? = nullvar acceptImageView: ImageView? = nullvar tvStatus: TextView? = nullvar outgoingActionContainer: View? = nullvar incomingActionContainer: View? = nullvar connectedActionContainer: View? = nullvar lytParent: View? = nullvar isOutgoing = falsevar inviteUserName = ""var toUserId = ""var gEngineKit: SkyEngineKit? = nullvar handler: CallHandler? = nullcompanion object {var callSingleActivity: CallSingleActivity? = nullval WHAT_DELAY_END_CALL = 0x01val WHAT_NO_NET_WORK_END_CALL = 0x02var currentState: EnumType.CallState? = nullvar headsetPlugReceiver: HeadsetPlugReceiver? = nullvar endWithNoAnswerFlag = falsevar isConnectionClosed = false}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)retainInstance = trueif (!EventBus.getDefault().isRegistered(this)) {EventBus.getDefault().register(this)}handler = CallHandler()}override fun onCreateView(inflater: LayoutInflater,container: ViewGroup?,savedInstanceState: Bundle?): View? {val view = inflater.inflate(getLayout(), container, false)initView(view)init()return view}override fun onDestroyView() {if (durationTextView != null) durationTextView!!.stop()refreshMessage(true)super.onDestroyView()}override fun onDestroy() {if (EventBus.getDefault().isRegistered(this)) {EventBus.getDefault().unregister(this)}super.onDestroy()}abstract fun getLayout(): Int@Subscribe(threadMode = ThreadMode.MAIN)fun onEvent(messageEvent: MsgEvent<Any?>) {val code: Int = messageEvent.getCode()Log.d(TAG, "onEvent code = \$code; endWithNoAnswerFlag = \$endWithNoAnswerFlag")if (code == MsgEvent.CODE_ON_CALL_ENDED) {if (endWithNoAnswerFlag) {didCallEndWithReason(EnumType.CallEndReason.Timeout)} else if (isConnectionClosed) {didCallEndWithReason(EnumType.CallEndReason.SignalError)} else {if (callSingleActivity != null) {callSingleActivity!!.finish()}}} else if (code == MsgEvent.CODE_ON_REMOTE_RING) {descTextView!!.text = "对方已响铃"}}override fun onAttach(context: Context) {super.onAttach(context)callSingleActivity = activity as CallSingleActivity?TagUtils.d("SingleCallFragment  callSingleActivity 的对象: ${callSingleActivity}")if (callSingleActivity != null) {callSingleActivity?.let {isOutgoing = it.isOutgoing()gEngineKit = it.getEngineKit()inviteUserName = it.getInviteUserName()toUserId = it.getToUserId()}headsetPlugReceiver = HeadsetPlugReceiver()val filter = IntentFilter()filter.addAction(Intent.ACTION_HEADSET_PLUG)callSingleActivity!!.registerReceiver(headsetPlugReceiver, filter)}}override fun onDetach() {super.onDetach()callSingleActivity!!.unregisterReceiver(headsetPlugReceiver) //注销监听callSingleActivity = null}open fun initView(view: View) {lytParent = view.findViewById(R.id.lytParent)minimizeImageView = view.findViewById(R.id.minimizeImageView)portraitImageView = view.findViewById(R.id.portraitImageView)nameTextView = view.findViewById(R.id.nameTextView)descTextView = view.findViewById(R.id.descTextView)durationTextView = view.findViewById(R.id.durationTextView)outgoingHangupImageView = view.findViewById(R.id.outgoingHangupImageView)incomingHangupImageView = view.findViewById(R.id.incomingHangupImageView)acceptImageView = view.findViewById(R.id.acceptImageView)tvStatus = view.findViewById(R.id.tvStatus)outgoingActionContainer = view.findViewById(R.id.outgoingActionContainer)incomingActionContainer = view.findViewById(R.id.incomingActionContainer)connectedActionContainer = view.findViewById(R.id.connectedActionContainer)durationTextView?.setVisibility(View.GONE)nameTextView?.setText(inviteUserName)//portraitImageView.setImageResource(R.mipmap.icon_default_header);BaseUtils.showAvatar(toUserId, portraitImageView!!)TagUtils.d("邀请名称:" + inviteUserName +" , " + toUserId)if (isOutgoing) {handler!!.sendEmptyMessageDelayed(WHAT_DELAY_END_CALL,(60 * 1000).toLong()) //1分钟之后未接通,则挂断电话}}open fun init() {}// ======================================界面回调================================fun didCallEndWithReason(callEndReason: EnumType.CallEndReason) {when (callEndReason) {EnumType.CallEndReason.Busy -> {tvStatus!!.text = "对方忙线中"}EnumType.CallEndReason.SignalError -> {tvStatus!!.text = "连接断开"}EnumType.CallEndReason.RemoteSignalError -> {tvStatus!!.text = "对方网络断开"}EnumType.CallEndReason.Hangup -> {tvStatus!!.text = "挂断"}EnumType.CallEndReason.MediaError -> {tvStatus!!.text = "媒体错误"}EnumType.CallEndReason.RemoteHangup -> {tvStatus!!.text = "对方挂断"}EnumType.CallEndReason.OpenCameraFailure -> {tvStatus!!.text = "打开摄像头错误"}EnumType.CallEndReason.Timeout -> {tvStatus!!.text = "对方未接听"}EnumType.CallEndReason.AcceptByOtherClient -> {tvStatus!!.text = "在其它设备接听"}}incomingActionContainer!!.visibility = View.GONEoutgoingActionContainer!!.visibility = View.GONEif (connectedActionContainer != null) connectedActionContainer!!.visibility = View.GONErefreshMessage(false)Handler(Looper.getMainLooper()).postDelayed({if (callSingleActivity != null) {callSingleActivity!!.finish()}}, 1500)}open fun didChangeState(state: EnumType.CallState) {}open fun didChangeMode(isAudio: Boolean?) {}open fun didCreateLocalVideoTrack() {}open fun didReceiveRemoteVideoTrack(userId: String?) {}open fun didUserLeave(userId: String?) {}open fun didError(error: String?) {}fun didDisconnected(error: String?) {handler!!.sendEmptyMessage(WHAT_NO_NET_WORK_END_CALL)}private fun refreshMessage(isForCallTime: Boolean) {if (callSingleActivity == null) {return}// 刷新消息; demo中没有消息,不用处理这儿快逻辑}fun startRefreshTime() {val session = SkyEngineKit.Instance()!!.getCurrentSession() ?: returnif (durationTextView != null) {durationTextView!!.visibility = View.VISIBLEdurationTextView!!.base =SystemClock.elapsedRealtime() - (System.currentTimeMillis() - session.getStartTime())durationTextView!!.start()}}fun runOnUiThread(runnable: Runnable?) {if (callSingleActivity != null) {callSingleActivity!!.runOnUiThread(runnable)}}class CallHandler : Handler() {override fun handleMessage(msg: Message) {if (msg.what == WHAT_DELAY_END_CALL) {if (currentState !== EnumType.CallState.Connected) {endWithNoAnswerFlag = trueif (callSingleActivity != null) {TagUtils.d("SingleCallFragment 挂电话:endCall 555 ")SkyEngineKit.Instance()?.endCall()}}} else if (msg.what == WHAT_NO_NET_WORK_END_CALL) {isConnectionClosed = trueif (callSingleActivity != null) {TagUtils.d("SingleCallFragment 挂电话:endCall 666 ")SkyEngineKit.Instance()?.endCall()}}}}class HeadsetPlugReceiver : BroadcastReceiver() {override fun onReceive(context: Context, intent: Intent) {if (intent.hasExtra("state")) {val session = SkyEngineKit.Instance()!!.getCurrentSession() ?: returnif (intent.getIntExtra("state", 0) == 0) { //拔出耳机session.toggleHeadset(false)} else if (intent.getIntExtra("state", 0) == 1) { //插入耳机session.toggleHeadset(true)}}}}}

<?xml version="1.0" encoding="utf-8"?><RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"android:layout_width="match_parent"android:layout_height="match_parent"android:background="@android:color/background_dark"><ImageViewandroid:id="@+id/minimizeImageView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:padding="20dp"android:src="@drawable/av_minimize" /><LinearLayoutandroid:id="@+id/lytParent"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_centerHorizontal="true"android:gravity="center_horizontal"android:orientation="vertical"><ImageViewandroid:id="@+id/portraitImageView"android:layout_width="120dp"android:layout_height="120dp"android:layout_marginTop="80dp"android:src="@drawable/av_default_header" /><TextViewandroid:id="@+id/nameTextView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="20dp"android:text=""android:textColor="@android:color/white" /><TextViewandroid:id="@+id/descTextView"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_marginTop="5dp"android:text="邀请你进行语音通话"android:textColor="@android:color/white" /></LinearLayout><!--拨出按钮显示--><includeandroid:id="@+id/outgoingActionContainer"layout="@layout/av_p2p_audio_outgoing"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:visibility="visible" /><!--接听按钮显示--><includeandroid:id="@+id/incomingActionContainer"layout="@layout/av_p2p_audio_incoming"android:layout_width="match_parent"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:visibility="gone" /><TextViewandroid:id="@+id/tvStatus"android:layout_width="wrap_content"android:layout_height="wrap_content"android:layout_alignParentBottom="true"android:layout_centerHorizontal="true"android:layout_marginBottom="130dp"android:textColor="#FFFFFF"android:textSize="16sp" /></RelativeLayout>

 

这篇关于Kotlin高仿微信-第15篇-单聊-语音通话的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/268544

相关文章

Ilya-AI分享的他在OpenAI学习到的15个提示工程技巧

Ilya(不是本人,claude AI)在社交媒体上分享了他在OpenAI学习到的15个Prompt撰写技巧。 以下是详细的内容: 提示精确化:在编写提示时,力求表达清晰准确。清楚地阐述任务需求和概念定义至关重要。例:不用"分析文本",而用"判断这段话的情感倾向:积极、消极还是中性"。 快速迭代:善于快速连续调整提示。熟练的提示工程师能够灵活地进行多轮优化。例:从"总结文章"到"用

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

W外链微信推广短连接怎么做?

制作微信推广链接的难点分析 一、内容创作难度 制作微信推广链接时,首先需要创作有吸引力的内容。这不仅要求内容本身有趣、有价值,还要能够激起人们的分享欲望。对于许多企业和个人来说,尤其是那些缺乏创意和写作能力的人来说,这是制作微信推广链接的一大难点。 二、精准定位难度 微信用户群体庞大,不同用户的需求和兴趣各异。因此,制作推广链接时需要精准定位目标受众,以便更有效地吸引他们点击并分享链接

阿里开源语音识别SenseVoiceWindows环境部署

SenseVoice介绍 SenseVoice 专注于高精度多语言语音识别、情感辨识和音频事件检测多语言识别: 采用超过 40 万小时数据训练,支持超过 50 种语言,识别效果上优于 Whisper 模型。富文本识别:具备优秀的情感识别,能够在测试数据上达到和超过目前最佳情感识别模型的效果。支持声音事件检测能力,支持音乐、掌声、笑声、哭声、咳嗽、喷嚏等多种常见人机交互事件进行检测。高效推

让树莓派智能语音助手实现定时提醒功能

最初的时候是想直接在rasa 的chatbot上实现,因为rasa本身是带有remindschedule模块的。不过经过一番折腾后,忽然发现,chatbot上实现的定时,语音助手不一定会有响应。因为,我目前语音助手的代码设置了长时间无应答会结束对话,这样一来,chatbot定时提醒的触发就不会被语音助手获悉。那怎么让语音助手也具有定时提醒功能呢? 我最后选择的方法是用threading.Time

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出

AI(文生语音)-TTS 技术线路探索学习:从拼接式参数化方法到Tacotron端到端输出 在数字化时代,文本到语音(Text-to-Speech, TTS)技术已成为人机交互的关键桥梁,无论是为视障人士提供辅助阅读,还是为智能助手注入声音的灵魂,TTS 技术都扮演着至关重要的角色。从最初的拼接式方法到参数化技术,再到现今的深度学习解决方案,TTS 技术经历了一段长足的进步。这篇文章将带您穿越时

uniapp设置微信小程序的交互反馈

链接:uni.showToast(OBJECT) | uni-app官网 (dcloud.net.cn) 设置操作成功的弹窗: title是我们弹窗提示的文字 showToast是我们在加载的时候进入就会弹出的提示。 2.设置失败的提示窗口和标签 icon:'error'是设置我们失败的logo 设置的文字上限是7个文字,如果需要设置的提示文字过长就需要设置icon并给

基于人工智能的智能家居语音控制系统

目录 引言项目背景环境准备 硬件要求软件安装与配置系统设计 系统架构关键技术代码示例 数据预处理模型训练模型预测应用场景结论 1. 引言 随着物联网(IoT)和人工智能技术的发展,智能家居语音控制系统已经成为现代家庭的一部分。通过语音控制设备,用户可以轻松实现对灯光、空调、门锁等家电的控制,提升生活的便捷性和舒适性。本文将介绍如何构建一个基于人工智能的智能家居语音控制系统,包括环境准备

Adblock Plus官方规则Easylist China说明与反馈贴(2015.12.15)

-------------------------------特别说明--------------------------------------- 视频广告问题:因Adblock Plus的局限,存在以下现象,优酷、搜狐、17173黑屏并倒数;乐视、爱奇艺播放广告。因为这些视频网站的Flash播放器被植入了检测代码,而Adblock Plus无法修改播放器。 如需同时使用ads

LLM系列 | 38:解读阿里开源语音多模态模型Qwen2-Audio

引言 模型概述 模型架构 训练方法 性能评估 实战演示 总结 引言 金山挂月窥禅径,沙鸟听经恋法门。 小伙伴们好,我是微信公众号《小窗幽记机器学习》的小编:卖铁观音的小男孩,今天这篇小作文主要是介绍阿里巴巴的语音多模态大模型Qwen2-Audio。近日,阿里巴巴Qwen团队发布了最新的大规模音频-语言模型Qwen2-Audio及其技术报告。该模型在音频理解和多模态交互