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: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<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)
}
}
}
}
}













![[附源码]计算机毕业设计springboot教务管理系统](https://img-blog.csdnimg.cn/9a81121aa24a42108cc7f91a75a64698.png)





