Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。
效果图:
实现代码:
/** * 视频通话、语音通话 */ 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(R.id.chat_video_pop_root) popupWindow.showAtLocation(popupRoot, Gravity.BOTTOM, 0, 0) var window = requireActivity().window //popupWindow在弹窗的时候背景半透明 val params = window.attributes params.alpha = 0.5f window.attributes = params popupWindow.setOnDismissListener { params.alpha = 1.0f window.attributes = params } //视频通话 popupView.findViewById (R.id.chat_pop_video_call).setOnClickListener { popupWindow.dismiss() CallSingleActivity.openActivity( requireActivity(),toUserId, true, toUserName, false, false) } //语音通话 popupView.findViewById (R.id.chat_pop_voice_call).setOnClickListener { popupWindow.dismiss() CallSingleActivity.openActivity( requireActivity(),toUserId, true, toUserName, true, false) } //取消 popupView.findViewById (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 = false private var targetId: String = "" private var inviteUserName: String = "" var isAudioOnly = false private var isFromFloatingView = false private var gEngineKit: SkyEngineKit? = null private var currentFragment: SingleCallFragment? = null private var room: String = "" private var activity:CallSingleActivity? = null override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setStatusBarOrScreenStatus(this) setContentView(R.layout.activity_single_call) activity = this try { gEngineKit = SkyEngineKit.Instance() } catch (e: NotInitializedException) { SkyEngineKit.init(VoipEvent()) //重新初始化 try { gEngineKit = SkyEngineKit.Instance() } catch (ex: NotInitializedException) { finish() } } val intent = intent targetId = 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: Arrayper = if (isAudioOnly) { arrayOf(Manifest.permission.RECORD_AUDIO) } else { arrayOf(Manifest.permission.RECORD_AUDIO, Manifest.permission.CAMERA) } Permissions.request(this, per, object : Consumer { 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: SingleCallFragment if (audioOnly) { fragment = FragmentAudio() } else { fragment = FragmentVideo() } val fragmentManager: FragmentManager = getSupportFragmentManager() currentFragment = fragment if (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 = room WcApp.getInstance().otherUserId = targetId val 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 or View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN if (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 or WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON or WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON ) window.decorView.systemUiVisibility = getSystemUiVisibility() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { val layoutParams = getWindow().attributes layoutParams.layoutInDisplayCutoutMode = WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_SHORT_EDGES window.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) { //19 window.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? = null private var audioLayout: LinearLayout? = null private var incomingAudioOnlyImageView: ImageView? = null private var hangupLinearLayout: LinearLayout? = null private var acceptLinearLayout: LinearLayout? = null private var connectedAudioOnlyImageView: ImageView? = null private var connectedHangupImageView: ImageView? = null private var switchCameraImageView: ImageView? = null private var fullscreenRenderer: FrameLayout? = null private var pipRenderer: FrameLayout? = null private var inviteeInfoContainer: LinearLayout? = null private var isFromFloatingView = false private var localSurfaceView: SurfaceViewRenderer? = null private var remoteSurfaceView: SurfaceViewRenderer? = null override 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.LayoutParams params.topMargin = (BarUtils.getStatusBarHeight() * 1.2).toInt() inviteeInfoContainer?.setLayoutParams(params) val params1 = minimizeImageView?.layoutParams as RelativeLayout.LayoutParams params1.topMargin = BarUtils.getStatusBarHeight() minimizeImageView?.layoutParams = params1 } pipRenderer?.post(Runnable { val params2 = pipRenderer?.getLayoutParams() as FrameLayout.LayoutParams params2.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.GONE outgoingActionContainer!!.visibility = View.GONE connectedActionContainer!!.visibility = View.VISIBLE inviteeInfoContainer!!.visibility = View.GONE minimizeImageView!!.visibility = View.VISIBLE startRefreshTime() } else { if (isOutgoing) { incomingActionContainer!!.visibility = View.GONE outgoingActionContainer!!.visibility = View.VISIBLE connectedActionContainer!!.visibility = View.GONE descTextView?.setText(R.string.av_waiting) } else { incomingActionContainer!!.visibility = View.VISIBLE outgoingActionContainer!!.visibility = View.GONE connectedActionContainer!!.visibility = View.GONE descTextView?.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 = state TagUtils.d( "didChangeState, state = $state") runOnUiThread { if (state === EnumType.CallState.Connected) { handler!!.removeMessages(WHAT_DELAY_END_CALL) incomingActionContainer!!.visibility = View.GONE outgoingActionContainer!!.visibility = View.GONE connectedActionContainer!!.visibility = View.VISIBLE inviteeInfoContainer!!.visibility = View.GONE descTextView!!.visibility = View.GONE minimizeImageView!!.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.VISIBLE if (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.VISIBLE remoteSurfaceView = 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) === remoteSurfaceView fullscreenRenderer!!.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 = true session.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? = null var outgoingHangupImageView: ImageView? = null var incomingHangupImageView: ImageView? = null var acceptImageView: ImageView? = null var tvStatus: TextView? = null var outgoingActionContainer: View? = null var incomingActionContainer: View? = null var connectedActionContainer: View? = null var lytParent: View? = null var isOutgoing = false var inviteUserName = "" var toUserId = "" var gEngineKit: SkyEngineKit? = null var handler: CallHandler? = null companion object { var callSingleActivity: CallSingleActivity? = null val WHAT_DELAY_END_CALL = 0x01 val WHAT_NO_NET_WORK_END_CALL = 0x02 var currentState: EnumType.CallState? = null var headsetPlugReceiver: HeadsetPlugReceiver? = null var endWithNoAnswerFlag = false var isConnectionClosed = false } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) retainInstance = true if (!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) { 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.GONE outgoingActionContainer!!.visibility = View.GONE if (connectedActionContainer != null) connectedActionContainer!!.visibility = View.GONE refreshMessage(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() ?: return if (durationTextView != null) { durationTextView!!.visibility = View.VISIBLE durationTextView!!.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 = true if (callSingleActivity != null) { TagUtils.d("SingleCallFragment 挂电话:endCall 555 ") SkyEngineKit.Instance()?.endCall() } } } else if (msg.what == WHAT_NO_NET_WORK_END_CALL) { isConnectionClosed = true if (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() ?: return if (intent.getIntExtra("state", 0) == 0) { //拔出耳机 session.toggleHeadset(false) } else if (intent.getIntExtra("state", 0) == 1) { //插入耳机 session.toggleHeadset(true) } } } } }