最近采用phonegap做一个APP,需要在应用内实现拨打电话,通话结束后能反馈通话相关状态,查看了一番phonegap官方文档,似乎没有跟电话相关的api,在互联网上也大概搜了一下,好像没找到相关的内容。其实html5是支持直接在网页上拨打电话的,只要加入如下html代码即可:
1. 打电话<a href="tel:10086" >移动客服</a>
2. 发短信<a href="sms:10086" >发短信</a>
但这中方式只能实现拨打电话和发送短信,不能反馈结果,比如我想在应用能实现拨打电话,通话结束后自动保存通话的电话号码、通话时间、时长等,默认的html功能就不能满足了。
phonegap官方不提供的功能,我们只有自己通过开发插件了实现了,phonegap官网上有一个phonegap插件开发指导http://docs.phonegap.com/en/2.5.0/guide_plugin-development_index.md.html#Plugin%20Development%20Guide,
下面我们通过代码来简单介绍下phonegap插件开发步骤:
一、定义JS插件API
1. var Phone = function() {
2.
3. };
4. Phone.prototype.call = function (successCallback, failureCallback,number) {
5. "Phone", "call", [number]);
6. };
7.
8. window.Phone = new Phone();
上面的代码我们定义了一个Phone插件,插件有一个call API,我们传入
1. successCallback
2. failureCallback
分别做为电话拨打成功和失败的回调函数
传入
1. number
做为电话号码
在方法里边调用
phonegap的cordova.exec()
1. cordova.exec(successCallback, failureCallback, "Phone", "call", [number]);
就是在这个方法里将执行到native端的功能调用
最后我们new一个Phone对象,把他附件到window对象上,方便以后调用。
这样,插件的JS代码完成。
二、native端代码(以android平台为例)
先直接上代码
1. package com.juhuibao.PhoneGapPlugin;
2.
3. import java.util.Date;
4.
5. import org.apache.cordova.api.CallbackContext;
6. import org.apache.cordova.api.CordovaPlugin;
7. import org.apache.cordova.api.PluginResult;
8. import org.json.JSONArray;
9. import org.json.JSONException;
10. import android.app.Activity;
11. import android.content.Intent;
12. import android.database.Cursor;
13. import android.net.Uri;
14. import android.provider.CallLog.Calls;
15. import android.telephony.PhoneNumberUtils;
16.
17. public class Phone extends CordovaPlugin {
18.
19. private static final int PHONE_CALL = 0; // 拨打电话
20. private static final int PHONE_ABORT = 1; // 挂断电话
21. private Date start_time;
22. private CallbackContext callbackContext;
23. private String phonenumber;
24.
25. @Override
26. public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException {
27. try{
28.
29. this.callbackContext = callbackContext;
30.
31. if ("call".equals(action)) {
32. this.phonenumber=args.getString(0);
33. this.call(args.getString(0),callbackContext);
34. return true;
35. }
36. if ("abort".equals(action)) {
37. this.abort(callbackContext);
38. return true;
39. }
40. return false;
41. }
42. catch(Exception e){
43. callbackContext.error(e.getMessage());
44. }
45. return false;
46. }
47. //拨打电话
48. private void call(String phonenumber, CallbackContext callbackContext) {
49.
50. if (phonenumber != null && phonenumber.length() > 0) {
51.
52. if(PhoneNumberUtils.isGlobalPhoneNumber(phonenumber)){
53. new Intent();
54. i.setAction(Intent.ACTION_CALL);
55. "tel:"+phonenumber));
56. new Date();
57. this.cordova.startActivityForResult(this, i,PHONE_CALL);
58.
59. }
60. else{
61. "不是有效的电话号码。");
62. }
63. else {
64. "电话号码不能为空.");
65. }
66. }
67. //中断电话
68. private void abort(CallbackContext callbackContext) {
69.
70. }
71. public void onActivityResult(int requestCode, int resultCode, Intent intent) {
72. new Date();
73. if (resultCode == Activity.RESULT_OK) {
74.
75. if (requestCode == PHONE_CALL) {
76.
77. this.callbackContext.error("未知状态");
78. }
79. }
80. else if (resultCode == Activity.RESULT_CANCELED) {
81. try{
82. if (requestCode == PHONE_CALL) {
83. long duration=GetLastCallDuration();
84.
85. new PhoneResult();
86.
87. this.phonenumber);
88. this.start_time);
89. result.setEndTime(end_time);
90. result.setDuration(duration);
91. this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
92.
93. }
94. }
95. catch(Exception e){
96. this.callbackContext.error(e.getMessage());
97. }
98. }
99.
100. else {
101. this.callbackContext.error("其他错误!");
102.
103. }
104. }
105. long delayTime=0;
106. long timeOut=2000;
107. long GetLastCallDuration() throws InterruptedException{
108. this.cordova.getActivity();
109. Cursor cursor = activity.getContentResolver().query(Calls.CONTENT_URI,
110. new String[] {Calls.NUMBER,Calls.DATE, Calls.DURATION, Calls.TYPE, Calls.DATE },
111. "number=?and type=?",
112. new String[]{this.phonenumber,"2"},
113. Calls.DEFAULT_SORT_ORDER);
114. activity.startManagingCursor(cursor);
115. boolean hasRecord = cursor.moveToFirst();
116. if (hasRecord) {
117. long endTime=cursor.getLong(cursor.getColumnIndex(Calls.DATE));
118. new Date(endTime);
119. long duration = cursor.getLong(cursor.getColumnIndex(Calls.DURATION));
120. long dif=this.start_time.getTime()-date.getTime();
121. long second=dif/1000;
122. if(second<2&&second>-2){
123. return duration;
124. else{
125. if(delayTime>=timeOut){
126. return 0;
127. }
128. 200);
129. 200;
130. return GetLastCallDuration();
131. }
132. else{
133. if(delayTime>=timeOut){
134. return 0;
135. }
136. 200);
137. 200;
138. return GetLastCallDuration();
139. }
140. }
141.
142. }
然后调用具体实现拨打电话功能的方法call方法,在call方法内我们首先判断电话号码合法性,不合法直接调用callbackContext.error("不是有效的电话号码。"),这个方法将触发JS端的error回调函数执行,并传入一个字符串参数,比如以上的failureCallback
如果电话号码合法则弹出呼叫界面,如下代码
1. if(PhoneNumberUtils.isGlobalPhoneNumber(phonenumber)){
2. new Intent();
3. i.setAction(Intent.ACTION_CALL);
4. "tel:"+phonenumber));
5. new Date();
6. this.cordova.startActivityForResult(this, i,PHONE_CALL);
7.
8. }
9. else{
10. "不是有效的电话号码。");
11. }
在代码中我们通过变量start_time记录开始时间,然后使用this.cordova.startActivityForResult(this, i,PHONE_CALL);启动Activity。
在终止拨号或挂断电话时将执行onActivityResult方法,我们将在此方法内实现通话状态信息的反馈。主要看一下这段代码
1. else if (resultCode == Activity.RESULT_CANCELED) {
2. try{
3. if (requestCode == PHONE_CALL) {
4. long duration=GetLastCallDuration();
5.
6. new PhoneResult();
7.
8. this.phonenumber);
9. this.start_time);
10. result.setEndTime(end_time);
11. result.setDuration(duration);
12. this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));
13.
14. }
15. }
16. catch(Exception e){
17. this.callbackContext.error(e.getMessage());
18. }
19. }
不管电话有没有接通,resultCode总是等于Activity.RESULT_CANCELED
。
我们通过GetLastCallDuration()方法获得通话时长,原理就是读取通话记录里边的duration字段,由于通话刚结束时通话记录可能还没出现记录,所以我们要反复调用该方法,直到有记录或到超时时间为止。
取得了时长我们通过this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));把结果反馈给JS端。
附 PhoneResult.java
1. package com.juhuibao.PhoneGapPlugin;
2. import java.text.SimpleDateFormat;
3. import java.util.Date;
4.
5. import org.json.JSONException;
6. import org.json.JSONObject;
7. public class PhoneResult {
8. private String number = "";
9. private Date startTime;
10. private Date endTime;;
11. private long duration = 0;
12.
13.
14.
15. public JSONObject toJSONObject() throws JSONException {
16.
17. new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:S");
18.
19. return new JSONObject(
20. "{number:" + JSONObject.quote(number) +
21. ",startTime:" + JSONObject.quote(sdf.format(startTime)) +
22. ",endTime:" + JSONObject.quote(sdf.format(endTime)) +
23. ",duration:" + duration + "}");
24. }
25.
26.
27.
28. public String getNumber() {
29. return number;
30. }
31.
32.
33.
34. public void setNumber(String number) {
35. this.number = number;
36. }
37.
38.
39.
40. public Date getStartTime() {
41. return startTime;
42. }
43.
44.
45.
46. public void setStartTime(Date startTime) {
47. this.startTime = startTime;
48. }
49.
50.
51.
52. public Date getEndTime() {
53. return endTime;
54. }
55.
56.
57.
58. public void setEndTime(Date endTime) {
59. this.endTime = endTime;
60. }
61.
62.
63.
64. public long getDuration() {
65. return duration;
66. }
67.
68.
69.
70. public void setDuration(long duration) {
71. this.duration = duration;
72. }
73. }
这样native端的代码完成。
三、客户端调用
1. window.Phone.call(function (obj) { alert(JSON.stringify(obj));
2.
3. function (err) { alert(err); }, "13401100000");
四、配置插件
在config.xml文件中注册插件
1. <plugin name="Phone" value="com.juhuibao.PhoneGapPlugin.Phone"/>
在AndroidManifest.xml文件中增加权限
1. <uses-permission android:name="android.permission.CALL_PHONE" />
2. <uses-permission android:name="android.permission.READ_CALL_LOG" />
3. <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
4. <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
《完》