最近采用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"/>

       

      《完》