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:58* Description : 视频通话控制界面*/
class FragmentVideo : SingleCallFragment(), View.OnClickListener {//private val TAG = "FragmentVideo"private var outgoingAudioOnlyImageView: ImageView? = nullprivate var audioLayout: LinearLayout? = nullprivate var incomingAudioOnlyImageView: ImageView? = nullprivate var hangupLinearLayout: LinearLayout? = nullprivate var acceptLinearLayout: LinearLayout? = nullprivate var connectedAudioOnlyImageView: ImageView? = nullprivate var connectedHangupImageView: ImageView? = nullprivate var switchCameraImageView: ImageView? = nullprivate var fullscreenRenderer: FrameLayout? = nullprivate var pipRenderer: FrameLayout? = nullprivate var inviteeInfoContainer: LinearLayout? = nullprivate var isFromFloatingView = falseprivate var localSurfaceView: SurfaceViewRenderer? = nullprivate var remoteSurfaceView: SurfaceViewRenderer? = nulloverride fun onAttach(context: Context) {super.onAttach(context)if (callSingleActivity != null) {isFromFloatingView = callSingleActivity!!.isFromFloatingView()}}override fun getLayout(): Int {return R.layout.fragment_video}override fun initView(view: View) {super.initView(view)fullscreenRenderer = view.findViewById(R.id.fullscreen_video_view)pipRenderer = view.findViewById(R.id.pip_video_view)inviteeInfoContainer = view.findViewById(R.id.inviteeInfoContainer)outgoingAudioOnlyImageView = view.findViewById(R.id.outgoingAudioOnlyImageView)audioLayout = view.findViewById(R.id.audioLayout)incomingAudioOnlyImageView = view.findViewById(R.id.incomingAudioOnlyImageView)hangupLinearLayout = view.findViewById(R.id.hangupLinearLayout)acceptLinearLayout = view.findViewById(R.id.acceptLinearLayout)connectedAudioOnlyImageView = view.findViewById(R.id.connectedAudioOnlyImageView)connectedHangupImageView = view.findViewById(R.id.connectedHangupImageView)switchCameraImageView = view.findViewById(R.id.switchCameraImageView)outgoingHangupImageView?.setOnClickListener(this)incomingHangupImageView?.setOnClickListener(this)minimizeImageView?.setOnClickListener(this)connectedHangupImageView?.setOnClickListener(this)acceptImageView?.setOnClickListener(this)switchCameraImageView?.setOnClickListener(this)pipRenderer?.setOnClickListener(this)outgoingAudioOnlyImageView?.setOnClickListener(this)incomingAudioOnlyImageView?.setOnClickListener(this)connectedAudioOnlyImageView?.setOnClickListener(this)if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M || OSUtils.isMiui() || OSUtils.isFlyme()) {lytParent!!.post {val params = inviteeInfoContainer?.getLayoutParams() as RelativeLayout.LayoutParamsparams.topMargin = (BarUtils.getStatusBarHeight() * 1.2).toInt()inviteeInfoContainer?.setLayoutParams(params)val params1 = minimizeImageView?.layoutParams as RelativeLayout.LayoutParamsparams1.topMargin = BarUtils.getStatusBarHeight()minimizeImageView?.layoutParams = params1}pipRenderer?.post(Runnable {val params2 = pipRenderer?.getLayoutParams() as FrameLayout.LayoutParamsparams2.topMargin = (BarUtils.getStatusBarHeight() * 1.2).toInt()pipRenderer?.setLayoutParams(params2)})}}override fun init() {super.init()val session = gEngineKit!!.getCurrentSession()if (session != null) {currentState = session.getState()}if (session == null || EnumType.CallState.Idle === session.getState()) {if (callSingleActivity != null) {callSingleActivity!!.finish()}} else if (EnumType.CallState.Connected === session.getState()) {incomingActionContainer!!.visibility = View.GONEoutgoingActionContainer!!.visibility = View.GONEconnectedActionContainer!!.visibility = View.VISIBLEinviteeInfoContainer!!.visibility = View.GONEminimizeImageView!!.visibility = View.VISIBLEstartRefreshTime()} else {if (isOutgoing) {incomingActionContainer!!.visibility = View.GONEoutgoingActionContainer!!.visibility = View.VISIBLEconnectedActionContainer!!.visibility = View.GONEdescTextView?.setText(R.string.av_waiting)} else {incomingActionContainer!!.visibility = View.VISIBLEoutgoingActionContainer!!.visibility = View.GONEconnectedActionContainer!!.visibility = View.GONEdescTextView?.setText(R.string.av_video_invite)if (currentState === EnumType.CallState.Incoming) {val surfaceView = gEngineKit!!.getCurrentSession()!!.setupLocalVideo(false)TagUtils.d("init surfaceView != null is " + (surfaceView != null) + "; isOutgoing = " + isOutgoing + "; currentState = " + currentState)if (surfaceView != null) {localSurfaceView = surfaceView as SurfaceViewRenderer?localSurfaceView!!.setZOrderMediaOverlay(false)fullscreenRenderer!!.addView(localSurfaceView)}}}}if (isFromFloatingView) {didCreateLocalVideoTrack()if (session != null) {didReceiveRemoteVideoTrack(session.mTargetId)}}}override fun didChangeState(state: EnumType.CallState) {currentState = stateTagUtils.d( "didChangeState, state = $state")runOnUiThread {if (state === EnumType.CallState.Connected) {handler!!.removeMessages(WHAT_DELAY_END_CALL)incomingActionContainer!!.visibility = View.GONEoutgoingActionContainer!!.visibility = View.GONEconnectedActionContainer!!.visibility = View.VISIBLEinviteeInfoContainer!!.visibility = View.GONEdescTextView!!.visibility = View.GONEminimizeImageView!!.visibility = View.VISIBLE// 开启计时器startRefreshTime()} else {// do nothing now}}}override fun didChangeMode(isAudio: Boolean?) {runOnUiThread { callSingleActivity!!.switchAudio() }}override fun didCreateLocalVideoTrack() {if (localSurfaceView == null) {val surfaceView = gEngineKit!!.getCurrentSession()!!.setupLocalVideo(true)localSurfaceView = if (surfaceView != null) {surfaceView as SurfaceViewRenderer?} else {if (callSingleActivity != null) callSingleActivity!!.finish()return}} else {localSurfaceView!!.setZOrderMediaOverlay(true)}TagUtils.d("didCreateLocalVideoTrack localSurfaceView != null is " + (localSurfaceView != null) + "; remoteSurfaceView == null = " + (remoteSurfaceView == null))if (localSurfaceView!!.parent != null) {(localSurfaceView!!.parent as ViewGroup).removeView(localSurfaceView)}if (isOutgoing && remoteSurfaceView == null) {if (fullscreenRenderer != null && fullscreenRenderer!!.childCount != 0) fullscreenRenderer!!.removeAllViews()fullscreenRenderer!!.addView(localSurfaceView)} else {if (pipRenderer!!.childCount != 0) pipRenderer!!.removeAllViews()pipRenderer!!.addView(localSurfaceView)}}override fun didReceiveRemoteVideoTrack(userId: String?) {pipRenderer!!.visibility = View.VISIBLEif (localSurfaceView != null) {localSurfaceView!!.setZOrderMediaOverlay(true)if (isOutgoing) {if (localSurfaceView!!.parent != null) {(localSurfaceView!!.parent as ViewGroup).removeView(localSurfaceView)}pipRenderer!!.addView(localSurfaceView)}}val surfaceView = gEngineKit!!.getCurrentSession()!!.setupRemoteVideo(userId!!, false)TagUtils.d( "didReceiveRemoteVideoTrack,surfaceView = $surfaceView")if (surfaceView != null) {fullscreenRenderer!!.visibility = View.VISIBLEremoteSurfaceView = surfaceView as SurfaceViewRenderer?fullscreenRenderer!!.removeAllViews()if (remoteSurfaceView!!.parent != null) {(remoteSurfaceView!!.parent as ViewGroup).removeView(remoteSurfaceView)}fullscreenRenderer!!.addView(remoteSurfaceView)}}override fun didUserLeave(userId: String?) {}override fun didError(error: String?) {}override fun onClick(v: View) {val id = v.id// 接听val session = gEngineKit!!.getCurrentSession()if (id == R.id.acceptImageView) {TagUtils.d("接听 ")if (session != null && session.getState() === EnumType.CallState.Incoming) {TagUtils.d("接听 if  = >  ${session.getRoomId()}")session.joinHome(session.getRoomId()!!)} else if (session != null) {TagUtils.d("接听 else callSingleActivity =  ${callSingleActivity}")if (callSingleActivity != null) {session.sendRefuse()callSingleActivity!!.finish()}}}// 挂断电话if (id == R.id.incomingHangupImageView || id == R.id.outgoingHangupImageView || id == R.id.connectedHangupImageView) {if (session != null) {TagUtils.d("FragmentVideo 挂电话:endCall ")SkyEngineKit.Instance()?.endCall()}if (callSingleActivity != null) callSingleActivity?.finish()}// 切换摄像头if (id == R.id.switchCameraImageView) {session!!.switchCamera()}if (id == R.id.pip_video_view) {val isFullScreenRemote = fullscreenRenderer!!.getChildAt(0) === remoteSurfaceViewfullscreenRenderer!!.removeAllViews()pipRenderer!!.removeAllViews()if (isFullScreenRemote) {remoteSurfaceView!!.setZOrderMediaOverlay(true)pipRenderer!!.addView(remoteSurfaceView)localSurfaceView!!.setZOrderMediaOverlay(false)fullscreenRenderer!!.addView(localSurfaceView)} else {localSurfaceView!!.setZOrderMediaOverlay(true)pipRenderer!!.addView(localSurfaceView)remoteSurfaceView!!.setZOrderMediaOverlay(false)fullscreenRenderer!!.addView(remoteSurfaceView)}}// 切换到语音拨打if (id == R.id.outgoingAudioOnlyImageView || id == R.id.incomingAudioOnlyImageView || id == R.id.connectedAudioOnlyImageView) {if (session != null) {if (callSingleActivity != null) callSingleActivity!!.isAudioOnly = truesession.switchToAudio()}}// 小窗if (id == R.id.minimizeImageView) {if (callSingleActivity != null) callSingleActivity!!.showFloatingView()}}override fun onDestroyView() {super.onDestroyView()fullscreenRenderer!!.removeAllViews()pipRenderer!!.removeAllViews()}
}
/*** 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)}}}}}

Kotlin高仿微信-第14篇-单聊-视频通话相关推荐

  1. Kotlin高仿微信-第11篇-单聊-语音

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

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

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

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

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

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

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

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

    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. postgresql存图片字段类型_PostgreSQL 入门 | Linux 中国
  2. 面试前临时抱佛脚——常见的Shell脚本面试题
  3. 数据挖掘学到最后全是数学
  4. 【Linux】时间同步设置+防火墙设置+SELinux设置
  5. HQL查询(分页查询,动态sql查询,参数查询)
  6. html需要顺序阅读吗,前端知识:HTML5所有的标签-按顺序排列 | 前端面试题
  7. c++在调用类的时候不一定非得实例化对象哦,有时候你不写系统会为你默认生成一个临时实例对象哦~
  8. 递归删除父节点及所有子节点(转)
  9. 对matlab中colormap的解释及fill、imshow的用法说明
  10. freemark 时间比较
  11. request.getRealPath不推荐使用
  12. mysql学习笔记03 mysql数据类型
  13. “机智号”成功试飞火星,但它使用的开源软件安全吗?
  14. FocusPoint.js实现图片响应
  15. Sublime Text3插件管理
  16. kubernetes视频教程笔记 (38)-高可用的K8S构建-系统初始化
  17. BS和CS 架构的介绍以及优缺点
  18. 怎么编写微博爬虫 图片爬虫
  19. Oracle JDK | 迅雷内置浏览器解决 Java 下载需要登录 不能直接复制链接 直接下载慢
  20. 在北京工作5年的程序员,通过人才引进入职县城事业编,月薪曝光

热门文章

  1. 电脑同步控制android设备,Total Control 免费使用电脑同时控制多台手机的教程及使用方法...
  2. label_studio自动预标注功能
  3. 拯救脆弱的智慧城市:不但要“智商” 还得有“生气”
  4. 宋星:误读数据等于自杀
  5. 中国软件业的机会——抓住机遇、挑战未来
  6. 好用的浏览器,与Google Chrome同内核
  7. Reporting verbs
  8. java.lang.ClassNotFoundException: org.eclipse.jdt.internal.compiler.env.INameEnvironment
  9. Zabbix系列:设置动态监控告警时间范围
  10. 乘风破浪程序猿,拒绝原地踏步!