自己动手开发phonegap插件之《phonegap拨打电话插件》
最近采用phonegap做一个APP,需要在应用内实现拨打电话,通话结束后能反馈通话相关状态,查看了一番phonegap官方文档,似乎没有跟电话相关的api,在互联网上也大概搜了一下,好像没找到相关的内容。其实html5是支持直接在网页上拨打电话的,只要加入如下html代码即可:
package com.juhuibao.PhoneGapPlugin; import java.util.Date; import org.apache.cordova.api.CallbackContext; import org.apache.cordova.api.CordovaPlugin; import org.apache.cordova.api.PluginResult; import org.json.JSONArray; import org.json.JSONException; import android.app.Activity; import android.content.Intent; import android.database.Cursor; import android.net.Uri; import android.provider.CallLog.Calls; import android.telephony.PhoneNumberUtils; public class Phone extends CordovaPlugin { private static final int PHONE_CALL = 0; // 拨打电话 private static final int PHONE_ABORT = 1; // 挂断电话 private Date start_time; private CallbackContext callbackContext; private String phonenumber; @Override public boolean execute(String action, JSONArray args, CallbackContext callbackContext) throws JSONException { try{ this.callbackContext = callbackContext; if ("call".equals(action)) { this.phonenumber=args.getString(0); this.call(args.getString(0),callbackContext); return true; } if ("abort".equals(action)) { this.abort(callbackContext); return true; } return false; } catch(Exception e){ callbackContext.error(e.getMessage()); } return false; } //拨打电话 private void call(String phonenumber, CallbackContext callbackContext) { if (phonenumber != null && phonenumber.length() > 0) { if(PhoneNumberUtils.isGlobalPhoneNumber(phonenumber)){ Intent i = new Intent(); i.setAction(Intent.ACTION_CALL); i.setData(Uri.parse("tel:"+phonenumber)); start_time=new Date(); this.cordova.startActivityForResult(this, i,PHONE_CALL); } else{ callbackContext.error(phonenumber+"不是有效的电话号码。"); } } else { callbackContext.error("电话号码不能为空."); } } //中断电话 private void abort(CallbackContext callbackContext) { } public void onActivityResult(int requestCode, int resultCode, Intent intent) { Date end_time=new Date(); if (resultCode == Activity.RESULT_OK) { if (requestCode == PHONE_CALL) { this.callbackContext.error("未知状态"); } } else if (resultCode == Activity.RESULT_CANCELED) { try{ if (requestCode == PHONE_CALL) { long duration=GetLastCallDuration(); PhoneResult result=new PhoneResult(); result.setNumber(Phone.this.phonenumber); result.setStartTime(Phone.this.start_time); result.setEndTime(end_time); result.setDuration(duration); this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject())); } } catch(Exception e){ this.callbackContext.error(e.getMessage()); } } else { this.callbackContext.error("其他错误!"); } } long delayTime=0; long timeOut=2000; long GetLastCallDuration() throws InterruptedException{ Activity activity = this.cordova.getActivity(); Cursor cursor = activity.getContentResolver().query(Calls.CONTENT_URI, new String[] {Calls.NUMBER,Calls.DATE, Calls.DURATION, Calls.TYPE, Calls.DATE }, "number=?and type=?", new String[]{this.phonenumber,"2"}, Calls.DEFAULT_SORT_ORDER); activity.startManagingCursor(cursor); boolean hasRecord = cursor.moveToFirst(); if (hasRecord) { long endTime=cursor.getLong(cursor.getColumnIndex(Calls.DATE)); Date date = new Date(endTime); long duration = cursor.getLong(cursor.getColumnIndex(Calls.DURATION)); long dif=this.start_time.getTime()-date.getTime(); long second=dif/1000; if(second<2&&second>-2){ return duration; }else{ if(delayTime>=timeOut){ return 0; } Thread.sleep(200); delayTime+=200; return GetLastCallDuration(); } }else{ if(delayTime>=timeOut){ return 0; } Thread.sleep(200); delayTime+=200; return GetLastCallDuration(); } } }
然后调用具体实现拨打电话功能的方法call方法,在call方法内我们首先判断电话号码合法性,不合法直接调用callbackContext.error("不是有效的电话号码。"),这个方法将触发JS端的error回调函数执行,并传入一个字符串参数,比如以上的failureCallback
如果电话号码合法则弹出呼叫界面,如下代码
if(PhoneNumberUtils.isGlobalPhoneNumber(phonenumber)){ Intent i = new Intent(); i.setAction(Intent.ACTION_CALL); i.setData(Uri.parse("tel:"+phonenumber)); start_time=new Date(); this.cordova.startActivityForResult(this, i,PHONE_CALL); } else{ callbackContext.error(phonenumber+"不是有效的电话号码。"); }
在代码中我们通过变量start_time记录开始时间,然后使用this.cordova.startActivityForResult(this, i,PHONE_CALL);启动Activity。
在终止拨号或挂断电话时将执行onActivityResult方法,我们将在此方法内实现通话状态信息的反馈。主要看一下这段代码
else if (resultCode == Activity.RESULT_CANCELED) { try{ if (requestCode == PHONE_CALL) { long duration=GetLastCallDuration(); PhoneResult result=new PhoneResult(); result.setNumber(Phone.this.phonenumber); result.setStartTime(Phone.this.start_time); result.setEndTime(end_time); result.setDuration(duration); this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject())); } } catch(Exception e){ this.callbackContext.error(e.getMessage()); } }
不管电话有没有接通,resultCode总是等于Activity.RESULT_CANCELED。
我们通过GetLastCallDuration()方法获得通话时长,原理就是读取通话记录里边的duration字段,由于通话刚结束时通话记录可能还没出现记录,所以我们要反复调用该方法,直到有记录或到超时时间为止。
取得了时长我们通过this.callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, result.toJSONObject()));把结果反馈给JS端。
附 PhoneResult.java
package com.juhuibao.PhoneGapPlugin; import java.text.SimpleDateFormat; import java.util.Date; import org.json.JSONException; import org.json.JSONObject; public class PhoneResult { private String number = ""; private Date startTime; private Date endTime;; private long duration = 0; public JSONObject toJSONObject() throws JSONException { SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss:S"); return new JSONObject( "{number:" + JSONObject.quote(number) + ",startTime:" + JSONObject.quote(sdf.format(startTime)) + ",endTime:" + JSONObject.quote(sdf.format(endTime)) + ",duration:" + duration + "}"); } public String getNumber() { return number; } public void setNumber(String number) { this.number = number; } public Date getStartTime() { return startTime; } public void setStartTime(Date startTime) { this.startTime = startTime; } public Date getEndTime() { return endTime; } public void setEndTime(Date endTime) { this.endTime = endTime; } public long getDuration() { return duration; } public void setDuration(long duration) { this.duration = duration; } }
这样native端的代码完成。
三、客户端调用
- window.Phone.call(function (obj) { alert(JSON.stringify(obj));
- }, function (err) { alert(err); }, "13401100000");
四、配置插件
在config.xml文件中注册插件
- <plugin name="Phone" value="com.juhuibao.PhoneGapPlugin.Phone"/>
在AndroidManifest.xml文件中增加权限
- <uses-permission android:name="android.permission.CALL_PHONE" />
- <uses-permission android:name="android.permission.READ_CALL_LOG" />
- <uses-permission android:name="android.permission.PROCESS_OUTGOING_CALLS" />
- <uses-permission android:name="android.permission.READ_PHONE_STATE"/>
《完》