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.attributes
    params.alpha = 0.5f
    window.attributes = params
    popupWindow.setOnDismissListener {
        params.alpha = 1.0f
        window.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 = 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<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: 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:20
 * Description : 语音通话控制界面
 */
class FragmentAudio : SingleCallFragment(), View.OnClickListener  {

    private val TAG = "FragmentAudio"
    private var muteImageView: ImageView? = null
    private var speakerImageView: ImageView? = null
    private 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.GONE
        outgoingHangupImageView!!.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.LayoutParams
                params.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.VISIBLE
            durationTextView!!.visibility = View.VISIBLE
            minimizeImageView!!.visibility = View.VISIBLE
            startRefreshTime()
        } else {
            // 如果未接通
            if (isOutgoing) {
                descTextView?.setText(R.string.av_waiting)
                outgoingActionContainer!!.visibility = View.VISIBLE
                incomingActionContainer!!.visibility = View.GONE
            } else {
                descTextView?.setText(R.string.av_audio_invite)
                outgoingActionContainer!!.visibility = View.GONE
                incomingActionContainer!!.visibility = View.VISIBLE
            }
        }
    }

    override fun didChangeState(state: EnumType.CallState) {
        currentState = state
        runOnUiThread {
            if (state === EnumType.CallState.Connected) {
                handler!!.removeMessages(WHAT_DELAY_END_CALL)
                incomingActionContainer!!.visibility = View.GONE
                outgoingActionContainer!!.visibility = View.VISIBLE
                minimizeImageView!!.visibility = View.VISIBLE
                descTextView!!.visibility = View.GONE
                startRefreshTime()
            } 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? = 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<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.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)
                }
            }
        }
    }

}

<?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">

    <ImageView
        android:id="@+id/minimizeImageView"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:padding="20dp"
        android:src="@drawable/av_minimize" />

    <LinearLayout
        android:id="@+id/lytParent"
        android:layout_width="wrap_content"
        android:layout_height="wrap_content"
        android:layout_centerHorizontal="true"
        android:gravity="center_horizontal"
        android:orientation="vertical">

        <ImageView
            android:id="@+id/portraitImageView"
            android:layout_width="120dp"
            android:layout_height="120dp"
            android:layout_marginTop="80dp"
            android:src="@drawable/av_default_header" />

        <TextView
            android:id="@+id/nameTextView"
            android:layout_width="wrap_content"
            android:layout_height="wrap_content"
            android:layout_marginTop="20dp"
            android:text=""
            android:textColor="@android:color/white" />

        <TextView
            android: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>

    <!--拨出按钮显示-->
    <include
        android: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" />

    <!--接听按钮显示-->
    <include
        android: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" />

    <TextView
        android: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>

 

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

六毛六66

你的鼓励是我创作的最大动力!

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值