您的位置:首页 > 其它

云之讯官方测试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. 土司提醒登录失败+携带的数据值。
}
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: