Kotlin高仿微信-项目实践58篇详细讲解了各个功能点,包括:注册、登录、主页、单聊(文本、表情、语音、图片、小视频、视频通话、语音通话、红包、转账)、群聊、个人信息、朋友圈、支付服务、扫一扫、搜索好友、添加好友、开通VIP等众多功能。
Kotlin高仿微信-项目实践58篇,点击查看详情
效果图:
实现代码:
/*** Author : wangning* Email : maoning20080809@163.com* Date : 2022/4/19 14:41* Description : 登录页面*/ class LoginFragment : BaseDataBindingFragment() , ServiceConnection {override fun getLayoutRes() = R.layout.wc_main_loginprivate val userViewModel : UserViewModel by viewModels()private var account: String = ""private var password: String = ""private var xmppService: XmppService? = nullprivate var navController : NavController? = null//是否在注册成功页面,注册成功返回private var isRegisterBack:Boolean = falseoverride fun onAttach(context: Context) {super.onAttach(context)}override fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)}override fun onActivityCreated(savedInstanceState: Bundle?) {super.onActivityCreated(savedInstanceState)}override fun onStart() {super.onStart()}override fun onConfigurationChanged(newConfig: Configuration) {super.onConfigurationChanged(newConfig)}override fun onHiddenChanged(hidden: Boolean) {super.onHiddenChanged(hidden)if(!hidden){getFocus();}}override fun onViewCreated(view: View, savedInstanceState: Bundle?) {super.onViewCreated(view, savedInstanceState)initViews()}private fun initViews() {super.builder().setTitleContent(R.string.wc_base_top_login)base_top_back.setOnClickListener {System.exit(1)}navController = Navigation.findNavController(btn_register)navController?.previousBackStackEntry?.savedStateHandle?.getLiveData (CommonUtils.User.IS_REGISTER_BACK)?.observe(viewLifecycleOwner){isRegisterBack = it}var intent = Intent(requireActivity(), XmppService::class.java)requireActivity().bindService(intent, this , BIND_AUTO_CREATE)btn_register.setOnClickListener {Navigation.findNavController(it).navigate(R.id.action_register)}btn_login.setOnClickListener { login() }var accountTemp = DataStoreUtils.get(DataStoreParams.User.DS_ACCOUNT, "") as Stringvar passwordTemp = DataStoreUtils.get(DataStoreParams.User.DS_PASSWORD, "") as StringCoroutineScope(Dispatchers.Main).launch {delay(200)login_account.setText(accountTemp)login_password.setText(passwordTemp)}login_root_layout.setOnClickListener {TagUtils.d("点击根节点 Click ")SoftInputUtils.hideSoftInput(login_account)}userViewModel.insertUserData.observe(viewLifecycleOwner){//如果同步成功,则重新登录if(it){DataStoreUtils.put(DataStoreParams.User.DS_OPENFILE_REGISTER, true)login()} else {ToastUtils.makeText(requireActivity(), BaseUtils.getString(R.string.wc_login_failure))dismissLoadingDialog()}}}private var loadingUtils : BaseDialogUtils? = null//显示加载对话框private fun showLoadingDialog(){loadingUtils = BaseDialogUtils(requireActivity())loadingUtils!!.builder().hideCancel().hideConfirm().setCancelable(true).setOnLoadingClick(object : BaseDialogUtils.OnLoadingClick{override fun onClickCancel() {ToastUtils.makeText(requireActivity(), "对话框取消按钮")}override fun onClickConfirm() {ToastUtils.makeText(requireActivity(), "对话框确定按钮")}})loadingUtils?.show()}//隐藏加载对话框private fun dismissLoadingDialog(){loadingUtils?.dismiss()}//登录private fun login() {var isRegisterSuccess = DataStoreUtils.get(DataStoreParams.User.DS_OPENFILE_REGISTER, false) as BooleanTagUtils.d("login 开始 ${isRegisterSuccess}")account = login_account.text.toString()password = login_password.getText().toString()if (account?.length == 0 || password?.length == 0) {ToastUtils.makeText(requireActivity(), "帐号或密码不能为空")return} else if (!isRegisterSuccess && isRegisterBack){var name = DataStoreUtils.get(DataStoreParams.User.DS_NICKNAME, "") as Stringif(TextUtils.isEmpty(name)){name = account}//如果在openfire注册成功,没有同步到web服务器,点击登录按钮,则重新同步var userBean = UserBean(account = account, name = name, nickName = name, address = "", email = "",phone = "", avatar = "", birthday = "", note = "")userViewModel.insertUser(userBean)} else {SoftInputUtils.hideSoftInput(login_account)showLoadingDialog()try {xmppService?.login(account, password)} catch (e : Exception) {e.printStackTrace()//ToastUtils.makeText(R.string.wc_login_failure)dismissLoadingDialog()}}}/*private fun initCall() {//在前台了,发送广播 调起权限判断弹窗val viop = Intent()val intent: Intent = requireActivity().intentviop.putExtra("room", intent.getStringExtra("room"))viop.putExtra("audioOnly", intent.getBooleanExtra("audioOnly", false))viop.putExtra("inviteId", intent.getStringExtra("inviteId"))viop.putExtra("inviteUserName", intent.getStringExtra("inviteUserName"))// viop.putExtra("msgId", intent.getLongExtra("msgId", 0));viop.putExtra("userList", intent.getStringExtra("userList"))viop.action = Utils.ACTION_VOIP_RECEIVERviop.component = ComponentName(WcApp.getInstance().getPackageName(), VoipReceiver::class.java.getName())WcApp.getInstance().sendBroadcast(viop)}*/private fun processLoginResult(type : Int) {CoroutineScope(Dispatchers.Main).launch {dismissLoadingDialog()if(type == 1){ToastUtils.makeText(R.string.wc_login_success)/*var isFromCall = requireActivity().intent.getBooleanExtra("isFromCall", false)if (isFromCall) { //无权限,来电申请权限会走这里initCall()}*/DataStoreUtils.get(DataStoreParams.Install.DS_FIRST_INSTALL, true)loginSusscess()} else {ToastUtils.makeText(R.string.wc_login_failure)}}}override fun onServiceConnected(p0: ComponentName?, binder: IBinder?) {TagUtils.d("LoginService 连接成功")var xmppBinder = binder as XmppService.XmppBinderxmppService = xmppBinder.servicexmppService?.setCallback(object : XmppService.LoginCallback{override fun onFailure() {//TagUtils.d("LoginFragment登录失败")processLoginResult(2)}override fun onSuccess(account: String, password: String) {TagUtils.d("LoginFragment登录成功")processLoginSuccess()}})}override fun onServiceDisconnected(p0: ComponentName?) {TagUtils.d("LoginService onServiceDisconnected 断开连接")}private fun processLoginSuccess(){CoroutineScope(Dispatchers.IO).launch {DataStoreUtils.put(DataStoreParams.User.DS_ACCOUNT, account!!)DataStoreUtils.put(DataStoreParams.User.DS_PASSWORD, password!!)DataStoreUtils.put(DataStoreParams.User.DS_USER_EXIST, false)//登录成功, 保存本地信息var userBean = UserRepository.getUserByAccount(account!!)TagUtils.d("登录成功:${account} , ${password} , userBean = ${userBean}")// 设置用户名WcApp.getInstance().username = account!!// 添加登录回调SocketManager.getInstance().addUserStateCallback(object : IUserState{override fun userLogin() {TagUtils.d("登录视频服务器成功login")}override fun userLogout() {TagUtils.d("登录视频服务器失败logout")}})// 连接socket:登录SocketManager.getInstance().connect(BaseUtils.WS, account!!, 0)processLoginResult(1)}}//登录成功private fun loginSusscess() {Navigation.findNavController(btn_login).popBackStack(R.id.main_start_dest, true)EventBus.getDefault().post(EventMsgBean(EventMsgBean.TYPE_LOGIN_SUCCESS))}override fun onResume() {super.onResume()getFocus()}private fun getFocus(){view?.isFocusableInTouchMode = trueview?.requestFocus()view?.setOnKeyListener { view, keyCode, event ->if(event.action == KeyEvent.ACTION_UP && keyCode == KeyEvent.KEYCODE_BACK){System.exit(1)}true}}}
fun login(account : String, password : String){CoroutineScope(Dispatchers.IO).launch {try {if(!xmppConnection.isAuthenticated){xmppConnection.connect()// 连接服务器xmppConnection.login(account, password)// 连接服务器成功,更改在线状态val presence = Presence(Presence.Type.available)xmppConnection.sendPacket(presence)TagUtils.d("用户登录状态:${xmppConnection.isAuthenticated} , ${xmppConnection.isConnected}")}if(xmppConnection.isAuthenticated){WcApp.setXmppConnection(xmppConnection)ChatManagerUtils.getInstance().addChatListener()AddFileListener.addFileListerer()AddFriendsListener.addSubscriptionListener()checkConnectionListener = CheckConnectionListener()xmppConnection.addConnectionListener(checkConnectionListener)TagUtils.d("xmpp 管理连接成功 ")var result = UserService.getApi().getUser(account)TagUtils.d("syncUser 用户返回数据:${result}")if(result.isSuccess){//插入本地数据库UserRepository.insertUser(GsonUtils.getUserBean(result.data!!))//处理同步信息} else {//如果在openfire注册成功,没有同步到web服务器,或者web服务器已经不存在该账户,只能重新创建var userBean = UserBean(account = account, name = account, nickName = account, address = "", email = "",phone = "", avatar = "", birthday = "", note = "")var userStr = Gson().toJson(userBean)UserRepository.insertUser(userBean)UserRepository.insertUserServer(userStr)}loginCallback?.onSuccess(account, password)} else {loginCallback?.onFailure()}} catch (e: Exception){//ToastUtils.makeText(R.string.wc_login_failure)TagUtils.d("用户登录状态:${e.message}")e.printStackTrace()loginCallback?.onFailure()}}
}
//登录成功,同步数据
private fun showLoadingDialog(){var isFirstLogin = DataStoreUtils.get(DataStoreParams.User.DS_IS_FIRST_LOGIN, true) as Boolean//首次安装if(!isFirstLogin){return}var account = DataStoreUtils.getAccount()if(TextUtils.isEmpty(account)){return}var view = LayoutInflater.from(this).inflate(R.layout.wc_sync_first_data, null)loadingUtils = BaseDialogUtils(this)loadingUtils!!.builder().showView(view).hideCancel().hideConfirm().setCancelable(false)loadingUtils?.show()SyncRepository.syncFirst(account, packageName, object : ICommonCallback{override fun onFailure(obj: Any) {dismissLoadingDialog()}override fun onSuccess(obj: Any) {dismissLoadingDialog()}})
}