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


    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(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: Array
                per = 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)
                    }
                }
            }
        }
    
    }

     

  • 相关阅读:
    uniapp微信小程序蓝牙连接与设备数据对接
    云栖大会,未来万物皆是计算机?
    linux基础--磁盘管理
    MybatisPlus核心功能——实现CRUD增删改查操作 (包含条件构造器)
    JavaScript入门⑥-WEB浏览器API
    万字长文!对比分析了多款存储方案,KeeWiDB最终选择自己来
    Making a Difference to Differential Evolution
    华为李鹏:到 2025 年智能算力需求将达到目前水平的 100 倍
    了解什么是架构基本概念和架构本质
    大语言模型推理与部署工具介绍
  • 原文地址:https://blog.csdn.net/maoning20080808/article/details/128111413