云之讯官方测试Demo音频版源码阅读(编辑)
2015-12-02 15:20
429 查看
* 由于最近项目中貌似需要做这一块,于是就去读了一下云之讯官方测试Demo的音频版的源码[Android音频版](http://www.ucpaas.com/product_service/download)。 * 其实这个Demo并不是什么高大上的代码,也没有很多生涩难懂的代码在里面,相反,读起来还是很轻松的。 * 不过,虽然代码结构清晰,但是里面的广播实在太多。如果不去理一理,确实不太好明白到底流程是怎样的。不过,好在Demo中还提供了一个Activity的结构图,也是辅助大家去做进一步的理解。 * 基于这些辅助,以及代码中的关键注释,差不多将其中的操作流程理清楚了。这里就贴出来欣赏欣赏这样一个Demo的逻辑流程。不过由于其中的代码太多,我只读了Activity结构图的第一条线的代码。(网络电话)这条线相对简单,其他的线代码量更大,就没有去读了。 * application logic 1. 当application被打开的时候,就开启了一个服务ConnectionService. { ConnectionService服务的逻辑: * 1.在服务创建的时候,就添加了一些监听器,注册了广播接收器 * 1.1.添加若干监听器: 连接的监听,消息的监听,通话的监听 * 1.2.开启了一个广播接收器,接收4条广播。 接收广播: UIDfineAction.ACTION_LOGIN && UIDfineAction.ACTION_DIAL && UCSService.ACTION_INIT_SUCCESS && android.intent.action.ACTION_SHUTDOWN // 系统关机广播 * 2.在该广播中,处理逻辑为: * 1. action== UIDfineAction.ACTION_LOGIN; * 1. 如果当前有通话,则挂断; * 2.从收到的意图对象intent中获取传递的cliend_id,cliend_pwd,sid,sid_pwd的值,并赋值给当前类定义的成员变量! * 3. 根据这4个值,分情况进行不同方式的(非token方式/token方式)“通接云之讯通信平台”。 1. 4个值都有,就进行非token的方式进行连接-> 开子线程,调用sdk提供的(非token的方式)connect函数,进行连接云之讯通信平台。 2. 4个值,cliend_id与cliend_pwd没有,进行token方式的连接-> 开子线程,调用sdk提高的(token方式的)connect函数,进行连接云之讯通信平台。 ============ACTION_LOGIN的逻辑处理完毕。 2. action== UIDfineAction.ACTION_DIAL 1. 从收到的意图对象intent中获取传递的type,call_uid,call_phone的值。 2. 根据type的值,做不同的逻辑操作。 // type: 0:直拨 1:免费 2:回拨 3:视频点对点 4:会议 5:智能拨打 case 0://0:直拨 UCSCall.dial(ConnectionService.this, CallType.DIRECT, phone); case 1://1:免费 UCSCall.dial(ConnectionService.this, CallType.VOIP, uid, "欢迎加入云之讯"); case 2: //2:回拨 UCSCall.callBack(ConnectionService.this, phone, fromSerNum, toSerNum); case 3: 无逻辑 case 4: 1. 根据传递的意图对象intent获取callType的值。 2. 根据callType值的不同,分别开启不同的UCSCall.startChatRoom(,callType,); case 5: UCSCall.dial(ConnectionService.this, CallType.CALL_AUTO, phone); ==================ACTION_DIAL的逻辑处理完毕。 3. action== android.intent.action.ACTION_SHUTDOWN 1.停止响铃 2.断开通话 ==================ACTION_SHUTDOWN的逻辑处理完毕。 3.onConnectionFailed(UcsReason reason)连接失败或断线的回调方法的逻辑处理: 1. 取消定时器,将定时器置空(// TODO ------------->) 2. 发送广播意图动作action==UIDfineAction.ACTION_TCP_LOGIN_CLIENT_RESPONSE, 并携带数据(UIDfineAction.RESULT_KEY, 1),以及(UIDfineAction.REASON_KEY, reason.getReason()); 3. 根据该回调方法的参数reason的getReason()获取的值,去判断,如果getReason()==300505||300207,就发送广播, 发送的广播意图动作action==UIDfineAction.ACTION_LOGOUT,并携带数据(UIDfineAction.REASON_KEY, reason.getReason()); 并创建意图进行Activity跳转,跳转到TerminalLoginDialogActivity,同时携带数据("reason",reason.getReason()); 4.onConnectionSuccessful()连接成功回调方法的处理逻辑 1. 将IMMessageActivity.msgList 中的数据清空 2. 发送广播,action== UIDfineAction.ACTION_TCP_LOGIN_CLIENT_RESPONSE, 并携带数据(UIDfineAction.RESULT_KEY, 0); 3. 判断当前成员变量cliend_id有没有被赋值,如果有,就保存到sp文件中(文件名:yunzhixun_demo); 5.onAlerting(String arg0)对方正在响铃的回调方法的逻辑处理 1. 发送广播,action== UIDfineAction.ACTION_DIAL_STATE,并携带数据("state", UCSCall.CALL_VOIP_RINGING_180); 6.onAnswer(String arg0)对方接通的回调方法的逻辑处理 1. 发送广播,action== UIDfineAction.ACTION_ANSWER 2. 开启定时器 记录通话时长,每隔1秒发送一次广播action== UIDfineAction.ACTION_CALL_TIME, 并携带数据("callduration",hour * 3600 + minute * 60 + second),以及("timer", timer.toString()); 7.onDialFailed(String arg0, UcsReason reason)拨打失败的回调方法的逻辑处理 1. 根据reason.getReason()的值分别发送不同的广播: case 300210: sendBroadcast(new Intent(UIDfineAction.ACTION_DIAL_STATE).putExtra( "state", UCSCall.CALL_VOIP_ERROR)); break; case 300211: sendBroadcast(new Intent(UIDfineAction.ACTION_DIAL_STATE).putExtra( "state", UCSCall.CALL_VOIP_NOT_ENOUGH_BALANCE)); break; case 300212: sendBroadcast(new Intent(UIDfineAction.ACTION_DIAL_STATE).putExtra( "state", UCSCall.CALL_VOIP_BUSY)); break; case 300213: xxxx,后面还有很多case 不一一列举 ======反正都是发广播说明拨打失败的原因 8. onHangUp(String arg0, final UcsReason reason) 回调方法的逻辑处理 1. 根据arg0的值是否为空,并且是否与成员变量lastIncomingCallId相等,并且当前系统时间毫秒值与lastIncomingCallTime相差不到1000毫秒 Y 如果以上成立,就过1000-(系统当前时间毫秒值与lastIncomingCallTime的差值)之后,停止响铃,并发送广播, 发送广播的action== UIDfineAction.ACTION_DIAL_HANGUP,并携带数据("state", reason.getReason()); N 如果以上不成了,就直接发送广播,action== UIDfineAction.ACTION_DIAL_HANGUP,并携带数据("state", reason.getReason()); 2. 走onDialFailed(String arg0, UcsReason reason)拨打失败的回调中相同的逻辑(发送广播说明失败的原因) 3. 取消定时器(onAnswer(String arg0)对方接通的回调方法中创建的定时器) 9. onIncomingCall(String callId, String callType,String callerNumber, String nickName, String userdata) 回调方法的逻辑处理: 1. 休眠1秒 2. lastIncomingCallId = callId;将callId赋值给成员变量lastIncomingCallId 3. lastIncomingCallTime = System.currentTimeMillis();将系统当前时间的毫秒值赋值给成员变量lastIncomingCallTime 4. 根据传入的callType判断, 如果callType.equals("0"),就开启Activity-->AudioConverseActivity 如果callType.equals("2"),就开启Activity-->ConferenceConverseActivity//会议 无论开启那个Activity,都携带数据("phoneNumber", callerNumber),以及("inCall", true),以及("nickName", nickName); 10. onReceiveUcsMessage(UcsReason reason, UcsMessage message) 接收新消息回调方法的逻辑处理: 1. 如果reason.getReason() == 0,就:将信息存放到IMMessageActivity.msgList集合中 否则:log输出下载文件失败... 11.xxx后面还有3,4个回调方法,不一一解释了 } --------------------- 2. 当application打开的时候,不仅开启了上面的服务,还打开的欢迎界面,在欢迎界面停留了两秒钟之后,自动跳转到了登录界面LoginActivity 3. LoginActivity的逻辑 { 1.LoginActivity.onCreate(bundle)方法 1. 注册广播接收器,接收如下广播: 接收广播: UIDfineAction.ACTION_TCP_LOGIN_RESPONSE &&UIDfineAction.ACTION_TCP_LOGIN_CLIENT_RESPONSE 2. 实例化用户名和密码的输入框EditText 3. 从sp文件中获取存放过的用户名和密码。(首次登录就获取不到) Y 如果获取到了: 就判断开启当前Activity的意图有没有携带数据("AutoLogin", false),并且值为true,同时,成员变量mLoginDialog==null, 那么,就 1. 判断一下当前的用户是个人开发者还是企业开发者。 2. 开启一个定时器,30秒后发送一个what=0的message给handler。 3. 弹出对话框显示"正在获取测试账号,请稍等...",并立即关闭对话框。 4. 开启一个匿名子线程,在子线程中做如下逻辑: 1. 判断当前开发者是个人开发者还是企业开发者 2. 将用户名和密码以及时间戳等参数以post的方式,发送http请求给指定的URL,获取一个response对象,通过 response对象,获取其中的json字符串。 3. 对获取的json字符串进行操作 0. 设立局部变量result=1; 0.5 判断该json是否含有节点resp,如果有进行后续1,如果没有,不走1。 1. 判断该json中是否有节点respCode,并且节点值为equals("000000"), 如果成立,就将json串中的sid,token,appId,client,client_number, client_pwd,mobile等信息写进文件 xxx/config.properties中。并将局部变量result赋值为0; 如果不成立,就判断当前json是否包含节点respCode,并将节点值转车int赋值给result 2. 最后,无论有没有走1,都会做一件事:发送广播 发送广播的action== UIDfineAction.ACTION_TCP_LOGIN_RESPONSE, 并携带数据(UIDfineAction.RESULT_KEY, result); N 如果没有获取到: 就不做任何逻辑操作。 4. 绑定登录按钮的点击事件: 1.登录按钮的点击事件的逻辑: 1.检查网络,如果没有联网,吐司提醒网络异常; 如果有联网,就走上面获取到sp中【用户名和密码,并且("AutoLogin", false),的值为true,mLoginDialog==null,】的相同逻辑 上面的1,2,3,4(4.1,4.2,4.3)全部的逻辑。(只是这里的用户名和密码是用户输入的,不是sp文件中取出来的) 5. 绑定注册按钮的点击事件: 1.注册按钮的点击事件的逻辑:直接跳转到RegisterActivity界面,无他。 6. 实例化显示当前版本号的TextView,并赋值。 2. onDestory 方法: 0.关闭显示正在登录的对话框 1.反注册广播接收器 2.停止登陆超时计时器 3. handlermessage 方法的处理逻辑: 1. 收到msg.what==0时 1.关闭显示正在登录的对话框 2.吐司显示登录失败 4. 广播接收器里面的处理逻辑: 1.action== UIDfineAction.ACTION_TCP_LOGIN_RESPONSE:{在登录按钮点击或者当前Activity创建的之后,会发送出去} 1. 收到该广播之后,先判断(null==mLoginDialog || !mLoginDialog.isShowing())是否成立, Y 如果成立,就 1.获取该广播携带的数据(UIDfineAction.RESULT_KEY, 1),赋值给局部变量result, 如果result==0,那么 1. 保存"用户名=密码"到sp文件 2. 保存用户名到sp文件 3. 将.properties文件的信息赋值给一个properties对象,给Config类的静态成员变量赋值 4. 如果当前Activity有LoginDialog对象,先置空,然后创建一个,并显示出来。 1、 在LoginDialog的构造函数中: 1. 加载UI 1. 实例化View 2. 给ListView设置数据适配器(===数据源来自Config类中保存的Properties文件中的client_id的值) 1. 在getView 中,在Item尾部复选框的点击事件中,判断当前item的位置是否在clients.size()的范围内, 如果在,就将设置当前Item为选中状态反置,并且根据当前Item是否被选中来确定是否给currentSelectClient赋值。 如果当前Item是选中状态就将当前position+"",赋值给currentSelectClient,否则赋值""给它。 3. 绑定按钮的点击事件"对话框底部的--OK,立即体验"的点击事件 点击事件逻辑: 1. 判断listview的item是否有被选中过, Y 如果有: 1. 将当前系统时间毫秒值赋值给成员变量mTime 2. 将sp中sp_CLIENT_ID字段赋值为"" 3. 开启一个定时器,30秒后发送Handler消息,携带what==0; 4. 弹出对话框显示"正在登入账号,请稍等..." 5. 发送广播,action= UIDfineAction.ACTION_LOGIN, 并携带数据("cliend_id", Config.getClient_id().split(",")[Integer.parseInt(currentSelectClient)]), 以及("cliend_pwd", Config.getClient_token().split(",")[Integer.parseInt(currentSelectClient)]), 以及("sid", Config.getMain_account()),以及("sid_pwd", Config.getMain_token()); N 如果没有选中过Item: 1. 将sp中sp_CLIENT_ID字段赋值为"" 2. 土司显示"选择一个测试用户" 2. 否则(result!=0),根据result值的不同->土司显示具体的失败原因。 N 如果不成立,就不做任何处理. 2. action== UIDfineAction.ACTION_TCP_LOGIN_CLIENT_RESPONSE:{在application打开时就创建的Service中的连接失败时的回调,以及连接成功时的回调会发送该action的广播} 1. 判断mLoginDialog是否还在显示 Y 在显示,判断意图对象携带的数据(UIDfineAction.RESULT_KEY, 1)==0? Y ==0 1. 土司提醒登录成功 2. 过一秒钟,关闭对话框,跳转到AbilityShowActivity界面,关闭当前Activity。 N !=0 1. 土司提醒登录失败+携带的数据值。 } 5. AudioActivity {AudioActivity的逻辑: 1. onCreate方法的逻辑处理 1. 实例化View控件 2. 给ListView设置数据,数据来源-->Config类中的Properties中保存的。 1. ListView的Item的点击事件: 1.通过意图开启新的Activity-->AudioCallActivity, 并携带数据("call_client", call_client), 以及("call_phone", phone),以及("call_position", phone_position); } 6. AudioCallActivity {AudioCallActivity的逻辑: 1. onCreate方法的逻辑处理 1. 实例化View控件 2. 给View绑定点击事件 1.免费电话的点击事件 1. 检测网络连接,如果没有网络连接,就结束该方法 2. 如果走到这一步,通过意图启动新的界面-->AudioConverseActivity 并携带数据("call_client",getIntent().getStringExtra("call_client")), 以及("call_type", 1); } 7.AudioConverseActivity {AudioConverseActivity的逻辑: 1.onCreate方法的逻辑处理 1. 初始化Views 1. 实例化xml中定义的view 2. 给相应的View设置开启当前Activity的intent对象携带的数据 3. 给相应的View绑定点击事件 1. 静音按钮的点击事件的逻辑处理: 1. UI做相应的背景切换 2. 通过SDK提供的UCSCall.setMicMute(boolean)来反置当前是否静音的状态 2. 扬声器按钮的点击事件的逻辑处理: 1. UI做相应的背景切换 2. 通过SDK提供的UCSCall.setSpeakerphone(boolean)来反置当前是否打开扬声器的状态 3. 接通按钮的点击事件的逻辑处理: 1. 停止响铃(SDK提供的方法)UCSCall.stopRinging(); 2. 接听UCSCall.answer(""); 3. 关闭扬声器 4. 挂掉按钮的点击事件的逻辑处理 1. 停止响铃 2. 挂断 3. 1.5秒之后,关闭当前界面 5. 结束通话按钮的点击事件的逻辑处理 1. 停止响铃 2. 关闭扬声器 3. 挂掉 4. 1.5秒之后,关闭当前界面 6. 结束通话(键盘界面中的按钮)的点击逻辑处理:如同5的逻辑 7. 打开键盘按钮的点击事件逻辑处理: 1.key_layout 显示 2.converse_main 隐藏 8. 关闭键盘按钮的点击事件的逻辑处理:7取反逻辑 9. 各数字键点击的逻辑处理 1. 调用SDK提供的UCSCall.sendDTMF(context,str,view)去处理 2. 获取系统服务AudioManager 3. 获取系统音量最大值 4. 获取当前系统音量 5. 关闭屏幕触摸音 6. 注册广播,接收7个广播: 接收的广播的action: UIDfineAction.ACTION_DIAL_STATE && UIDfineAction.ACTION_CALL_BACK && UIDfineAction.ACTION_ANSWER && UIDfineAction.ACTION_CALL_TIME && UIDfineAction.ACTION_DIAL_HANGUP && UIDfineAction.ACTION_NETWORK_STATE && android.intent.action.HEADSET_PLUG // 插拔耳麦广播 7. 获取当前Activity开启时,传递的意图携带的数据,并赋值给成员变量 8. 根据意图携带的("inCall", false)的值,来判断当前是来电还是去电,分别做不同的逻辑处理 Y,如果为true,说明是来电: 1. 如果传递的意图中携带了昵称信息,就在UI中显示昵称,否则显示电话号码 2. 打开扬声器 3. 开始响铃 4. 接听,挂断的View隐藏,结束通话的View显示 N,不是true,说明是去电: 1. 接听,结束通话View隐藏,挂断View显示 2. 进行拨号 1. 关闭扬声器 2. 打开屏幕触摸音 3. 创建一个新的意图对象 1. action==UIDfineAction.ACTION_DIAL 2. 根据当前Activity的打开意图是否携带UIDfineAction.FROM_NUM_KEY对应的Str 如果有携带就将该数据封装到刚创建的意图中 3. 根据当前Activity的打开意图是否携带UIDfineAction.TO_NUM_KEY对应的Str 如果有携带就将该数据封装到刚创建的意图中 4. 根据callType(当前Activity打开意图携带过来的)的不同,再封装两组数据进刚才创建的意图中, 并发送该action对应的广播,[action== UIDfineAction.ACTION_DIAL] case 0: // 直拨传入电话号码 sendBroadcast(intent .putExtra(UIDfineAction.CALL_PHONE, calledPhone).putExtra( "type", calltype)); break; case 1: // 免费传入clientid sendBroadcast(intent.putExtra(UIDfineAction.CALL_UID, calledUid) .putExtra("type", calltype)); break; case 2: // 回拨传入电话号码 sendBroadcast(intent .putExtra(UIDfineAction.CALL_PHONE, calledPhone).putExtra( "type", calltype)); break; case 3: // 视频点对点 传入clientid sendBroadcast(intent.putExtra(UIDfineAction.CALL_UID, calledPhone) .putExtra("type", calltype)); break; case 5: // 智能拨打 clientid和电话号码同时传入,先选择clientid进行免费拨打,如果对方不在线再选择手机号码进行直拨 sendBroadcast(intent.putExtra(UIDfineAction.CALL_UID, calledUid) .putExtra(UIDfineAction.CALL_PHONE, calledPhone) .putExtra("type", calltype)); ---->该广播在app打开时创建的广播中有处理!!! 3. 根据intent传入的callType的不同,分别在UI上显示"免费电话呼叫中","直拨电话呼叫中"等不同文字 2. 广播接收器中的逻辑处理 1. action== UIDfineAction.ACTION_DIAL_STATE 1. 根据意图传递的state的值给TextView.converse_information显示不同的文字(多半是错误信息)。 2. action== UIDfineAction.ACTION_CALL_BACK 1. converse_information.setText("回拨成功"); 2. 停止响铃(来去电的响铃都停止) 3. 1.5秒之后关闭当前界面 3. action== UIDfineAction.ACTION_ANSWER 1. UI做相应的改变 2. 停止响铃 3. 关闭扬声器 4. 变量open_headset = true; 4. action== UIDfineAction.ACTION_DIAL_HANGUP 1. 1.5秒之后,关闭当前界面 5. action== UIDfineAction.ACTION_CALL_TIME 1. 获取intent携带的数据String timer = intent.getStringExtra("timer"); 2. 将获取的数据赋值给converse_information.setText(timer); 6. action== UIDfineAction.ACTION_NETWORK_STATE 1. 获取Intent携带的数据("state", -1),并根据该数值在TextView中显示网络状态极好,还是一般还是差等信息。 7. action== "android.intent.action.HEADSET_PLUG" //插拔耳麦广播 1. 根据当前是否有耳麦插入,如果耳麦插入就关闭扬声器,否则打开。 3. onDestory的逻辑处理 1.反注册广播 2.停止响铃 3.如果之前有关闭过屏幕触摸音,现在打开 } ******* > 第一条线,也就是语音通话的整个流程差不多就这样子了。不过,由于水平有限,以及无意的疏漏,还是有可能对源码的理解有些偏差与遗漏。不过,大体流程,也就是这样子了。 > > 简单总结一下语音通话这条线的逻辑,差不多就是 > 1. 将必要的数据保存到文件中 2. 在需要通知其他界面或者后台做对应操作的时候,通过这边发送广播,那边接收广播的方式进行数据的传递。 > >这条线的一个重要的类就是`Config`类,这个`Config`类其实算是`java.util.Properties`的一个包装类,所有的核心逻辑就是将数据存入`/config.properties`文件中,以及,从该`.properties`文件中取出对应的数据,在各个需要的UI进行数据展示。 >首先,在首次登录的时候,会发一个post请求将登录信息发送到云之讯的服务器端,返回一个json串。然后就调用了`Config.parseConfig(json,context)`的方法,将json中返回的登录信息,帐号信息等存放到了`.properties`文件中,然后后面的操作都是基于这个配置文件的中的数据来的。 2. 给ListView设置数据适配器(<===数据源来自Config类中保存的Properties文件中的client_id的值) 1. 在getView 中,在Item尾部复选框的点击事件中,判断当前item的位置是否在clients.size()的范围内, 如果在,就将设置当前Item为选中状态反置,并且根据当前Item是否被选中来确定是否给currentSelectClient赋值。 如果当前Item是选中状态就将当前position+"",赋值给currentSelectClient,否则赋值""给它。 3. 绑定按钮的点击事件"对话框底部的--OK,立即体验"的点击事件 点击事件逻辑: 1. 判断listview的item是否有被选中过, Y 如果有: 1. 将当前系统时间毫秒值赋值给成员变量mTime 2. 将sp中sp_CLIENT_ID字段赋值为"" 3. 开启一个定时器,30秒后发送Handler消息,携带what==0; 4. 弹出对话框显示"正在登入账号,请稍等..." 5. 发送广播,action= UIDfineAction.ACTION_LOGIN, 并携带数据("cliend_id", Config.getClient_id().split(",")[Integer.parseInt(currentSelectClient)]), 以及("cliend_pwd", Config.getClient_token().split(",")[Integer.parseInt(currentSelectClient)]), 以及("sid", Config.getMain_account()),以及("sid_pwd", Config.getMain_token()); N 如果没有选中过Item: 1. 将sp中sp_CLIENT_ID字段赋值为"" 2. 土司显示"选择一个测试用户" 2. 否则(result!=0),根据result值的不同->土司显示具体的失败原因。 N 如果不成立,就不做任何处理. 2. action== UIDfineAction.ACTION_TCP_LOGIN_CLIENT_RESPONSE:{在application打开时就创建的Service中的连接失败时的回调,以及连接成功时的回调会发送该action的广播} 1. 判断mLoginDialog是否还在显示 Y 在显示,判断意图对象携带的数据(UIDfineAction.RESULT_KEY, 1)==0? Y ==0 1. 土司提醒登录成功 2. 过一秒钟,关闭对话框,跳转到AbilityShowActivity界面,关闭当前Activity。 N !=0 1. 土司提醒登录失败+携带的数据值。 }
相关文章推荐
- Redis与Memcached的区别
- 151202iOS使用NSMutableAttributedString 实现富文本(不同颜色字体、下划线等)
- vnc for mac
- POJ 1721 CARDS(置换)
- webpack练手项目之easySlide(三):commonChunks
- Bundle savedInstanceState的作用
- 一个关于多线程池任务配合的项目的总结
- VMware 11安装Mac OS X 10.10
- C++的动态库和静态库
- Android动画_ScaleAnimation
- 1036. Boys vs Girls (25)
- android输入法ImeOptions
- 最值钱互联网公司
- codeforces 603B (数学)
- kvm libvirt qemu 磁盘 NIC 热添加和删除
- WM_CLOSE、WM_DESTROY、WM_QUIT区别
- 破除依赖
- 支付宝支付开发的防钓鱼的时代已经到来
- Android Studio 创建项目的项目R文件不更新问题
- MYSQL 修改语句