Java tutorial
/* Copyright 2012-2013, Polyvi Inc. (http://polyvi.github.io/openxface) This program is distributed under the terms of the GNU General Public License. This file is part of xFace. xFace is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. xFace is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with xFace. If not, see <http://www.gnu.org/licenses/>. */ package com.polyvi.xface.extension; import java.util.ArrayList; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; import android.app.Service; import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.Cursor; import android.net.ConnectivityManager; import android.net.NetworkInfo; import android.net.Uri; import android.provider.CallLog; import android.telephony.TelephonyManager; import android.webkit.WebView; import com.polyvi.xface.event.XEvent; import com.polyvi.xface.event.XEventType; import com.polyvi.xface.event.XSystemEventCenter; import com.polyvi.xface.extension.XExtensionResult.Status; import com.polyvi.xface.plugin.api.XIWebContext; import com.polyvi.xface.util.XLog; import com.polyvi.xface.util.XUtils; public class XTelephonyExt extends XExtension { private static final String COMMAND_DELETE_ALL_CALL_RECORDS = "deleteAllCallRecords"; private static final String COMMAND_DELETE_CALL_RECORD = "deleteCallRecord"; private static final String COMMAND_GET_CALL_RECORD = "getCallRecord"; private static final String COMMAND_FIND_CALL_RECORDS = "findCallRecords"; private static final String COMMAND_GET_CALL_RECORD_COUNT = "getCallRecordCount"; private static final String COMMAND_INITIATE_VOICE_CALL = "initiateVoiceCall"; private final String CALL_RECORD_TYPE_RECEIVED = "RECEIVED"; /** < ?? */ private final String CALL_RECORD_TYPE_OUTGOING = "OUTGOING"; /** < ?? */ private final String CALL_RECORD_TYPE_MISSED = "MISSED"; /** < ?? */ private ContentResolver mContentResolver; private final String CLASS_NAME = XTelephonyExt.class.getSimpleName(); private BroadcastReceiver mIncomingCallBroadcastReceiver = null; private static final String ACTION_PHONE_STATE_CHANGED = TelephonyManager.ACTION_PHONE_STATE_CHANGED; @Override public void init(XExtensionContext extensionContext, XIWebContext webContext) { super.init(extensionContext, webContext); mContentResolver = getContext().getContentResolver(); genIncomingCallBroadcastReceiver(); getContext().registerReceiver(mIncomingCallBroadcastReceiver, new IntentFilter(ACTION_PHONE_STATE_CHANGED)); } @Override public void sendAsyncResult(String result) { } @Override public boolean isAsync(String action) { if (action.equals(COMMAND_INITIATE_VOICE_CALL)) { return false; } return true; } @Override public XExtensionResult exec(String action, JSONArray args, XCallbackContext callbackCtx) throws JSONException { XExtensionResult.Status status = XExtensionResult.Status.OK; String result = ""; try { if (action.equals(COMMAND_DELETE_ALL_CALL_RECORDS)) { boolean delAllSuccess = deleteAllCallRecords(args.getString(0)); return new XExtensionResult(status, delAllSuccess); } else if (action.equals(COMMAND_DELETE_CALL_RECORD)) { boolean delSuccess = deleteCallRecord(args.getString(0), args.getLong(1)); return new XExtensionResult(status, delSuccess); } else if (action.equals(COMMAND_FIND_CALL_RECORDS)) { JSONArray matchedCallRecord = findCallRecords(args.getJSONObject(0), args.getInt(1), args.getInt(2)); return new XExtensionResult(status, matchedCallRecord); } else if (action.equals(COMMAND_GET_CALL_RECORD)) { JSONObject callRecord = getCallRecord(args.getString(0), args.getInt(1)); return new XExtensionResult(status, callRecord); } else if (action.equals(COMMAND_GET_CALL_RECORD_COUNT)) { int callCount = getCallRecordCount(args.getString(0)); return new XExtensionResult(status, callCount); } else if (action.equals(COMMAND_INITIATE_VOICE_CALL)) { boolean isCallSuccess = false; boolean mobileNetAccessible = isSimCardAvailable(getContext()); isCallSuccess = initiateVoiceCall(args.getString(0)); if (!mobileNetAccessible || !isCallSuccess) { status = Status.ERROR; } return new XExtensionResult(status, result); } return new XExtensionResult(status, result); } catch (JSONException e) { return new XExtensionResult(XExtensionResult.Status.ERROR, result); } } @Override public void destroy() { if (null != mIncomingCallBroadcastReceiver) { getContext().unregisterReceiver(mIncomingCallBroadcastReceiver); mIncomingCallBroadcastReceiver = null; } } /** * ? * * @param[in] callRecordType ? * * @return ??? * */ private boolean deleteAllCallRecords(String callRecordType) { int callType = getCallRecordType(callRecordType); mContentResolver.delete(CallLog.Calls.CONTENT_URI, CallLog.Calls.TYPE + "=" + callType, null); return true; } /** * id?? * * @param[in] callRecordType ? * @param[in] callRecordId ?id * * @return ??? * */ private boolean deleteCallRecord(String callRecordType, long recordId) { int callType = getCallRecordType(callRecordType); mContentResolver.delete(CallLog.Calls.CONTENT_URI, CallLog.Calls._ID + "=" + recordId + " and " + CallLog.Calls.TYPE + "=" + callType, null); return true; } /** * ?id? * * @param[in] callRecordType ? * @param[in] callRecordIndex ? * * @return ?JSON * */ private JSONObject getCallRecord(String callRecordType, int recordIndex) { int callType = getCallRecordType(callRecordType); Cursor c = mContentResolver.query(CallLog.Calls.CONTENT_URI, new String[] { CallLog.Calls.NUMBER, CallLog.Calls._ID, CallLog.Calls.CACHED_NAME, CallLog.Calls.TYPE, CallLog.Calls.DATE, CallLog.Calls.DURATION }, CallLog.Calls.TYPE + "=" + callType, null, CallLog.Calls.DEFAULT_SORT_ORDER); String callRecordAddress = ""; String callRecordId = ""; String callRecordName = ""; long durationSeconds = 0; long startTime = 0; JSONObject callRecord = new JSONObject(); if (null != c) { if (c.moveToPosition(recordIndex)) { callRecordAddress = c.getString(0); callRecordId = c.getString(1); callRecordName = c.getString(2); startTime = c.getLong(4); durationSeconds = c.getLong(5); } c.close(); try { callRecord.put("callRecordAddress", callRecordAddress); callRecord.put("callRecordId", callRecordId); callRecord.put("callRecordName", callRecordName); callRecord.put("durationSeconds", durationSeconds); callRecord.put("startTime", startTime); callRecord.put("callRecordType", callRecordType); } catch (JSONException e) { XLog.e(CLASS_NAME, e.getMessage()); } } return callRecord; } /** * ?? * * @param[in] comparisonCallRecord ?? * @param[in] startIndex ? * @param[in] endIndex ??? * * @return ???JSON * */ private JSONArray findCallRecords(JSONObject comparisonCallRecord, int startIndex, int endIndex) { JSONArray result = new JSONArray(); if (null != comparisonCallRecord) { try { String callRecordId = comparisonCallRecord.getString("callRecordId"); String callRecordAddress = comparisonCallRecord.getString("callRecordAddress"); String callRecordName = comparisonCallRecord.getString("callRecordName"); String callRecordType = comparisonCallRecord.getString("callRecordType"); long startTime = comparisonCallRecord.getLong("startTime"); long durationSeconds = comparisonCallRecord.getLong("durationSeconds"); ArrayList<String> projections = new ArrayList<String>(); projections.add(CallLog.Calls._ID); projections.add(CallLog.Calls.NUMBER); projections.add(CallLog.Calls.CACHED_NAME); ArrayList<String> projectionsValue = new ArrayList<String>(); projectionsValue.add(callRecordId); projectionsValue.add(callRecordAddress); projectionsValue.add(callRecordName); StringBuilder selection = XUtils.constructSelectionStatement(projections, projectionsValue); String selectionStr = buildSelectionStr(selection, startTime, durationSeconds, callRecordType); Cursor cursor = mContentResolver.query(CallLog.Calls.CONTENT_URI, null, selectionStr, null, CallLog.Calls.DEFAULT_SORT_ORDER); if (null == cursor) { return result; } int count = endIndex - startIndex + 1; if (count > 0) { if (cursor.moveToPosition(startIndex)) { do { JSONObject callRecord = getCallRecordFromCursor(cursor); result.put(callRecord); count--; } while (cursor.moveToNext() && count > 0); } } cursor.close(); } catch (JSONException e) { XLog.e(CLASS_NAME, e.toString()); } } return result; } /** * ??? * * @param[in] callRecordType ? * * @return ? * */ private int getCallRecordCount(String callRecordType) { int callType = getCallRecordType(callRecordType); Cursor c = mContentResolver.query(CallLog.Calls.CONTENT_URI, new String[] { CallLog.Calls._ID }, CallLog.Calls.TYPE + "=" + callType, null, CallLog.Calls.DEFAULT_SORT_ORDER); int count = 0; if (null != c) { count = c.getCount(); } c.close(); return count; } /** * ??? * * @param[in] phoneNumber ???? * @return ??? * */ private boolean initiateVoiceCall(String phoneNumber) { try { if (isLegalPhoneNum(phoneNumber)) { Intent intent = new Intent(Intent.ACTION_CALL); intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK); intent.setData(Uri.parse(WebView.SCHEME_TEL + phoneNumber)); getContext().startActivity(intent); return true; } } catch (ActivityNotFoundException e) { XLog.e(CLASS_NAME, e.toString()); } catch (SecurityException e) { XLog.e(CLASS_NAME, e.toString()); } return false; } /** * sim??? * @param context * @return */ public boolean isSimCardAvailable(Context context) { ConnectivityManager cm = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE); NetworkInfo info = cm.getActiveNetworkInfo(); TelephonyManager tm = (TelephonyManager) context.getSystemService(Context.TELEPHONY_SERVICE); return (info != null && info.isConnected() && checkSimCardState(tm)); } /** * sim?? * @param tm * @return */ private boolean checkSimCardState(TelephonyManager tm) { int simState = tm.getSimState(); switch (simState) { case TelephonyManager.SIM_STATE_ABSENT: return false; case TelephonyManager.SIM_STATE_NETWORK_LOCKED: return true; case TelephonyManager.SIM_STATE_PIN_REQUIRED: return true; case TelephonyManager.SIM_STATE_PUK_REQUIRED: return true; case TelephonyManager.SIM_STATE_READY: return true; case TelephonyManager.SIM_STATE_UNKNOWN: return false; default: return false; } } private void genIncomingCallBroadcastReceiver() { if (null == mIncomingCallBroadcastReceiver) { mIncomingCallBroadcastReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { String action = intent.getAction(); if (ACTION_PHONE_STATE_CHANGED.equals(action)) { incomingCallResponse(intent); } } }; } } private void incomingCallResponse(Intent intent) { Context ctx = getContext(); TelephonyManager tm = (TelephonyManager) ctx.getSystemService(Service.TELEPHONY_SERVICE); XEvent evt = XEvent.createEvent(XEventType.CALL_RECEIVED, tm.getCallState()); XSystemEventCenter.getInstance().sendEventAsync(evt); } /** * ?????? * @param phoneNumber ???? * @return ? -true ???? -false ???? * */ private boolean isLegalPhoneNum(String phoneNumber) { String regExpression = "[+*#\\d]+"; return phoneNumber.matches(regExpression); } /** * ?js???native? * * @param[in] callRecordType ? * * @return native? * */ private int getCallRecordType(String callRecordType) { int callType = CallLog.Calls.INCOMING_TYPE; if (CALL_RECORD_TYPE_RECEIVED.equals(callRecordType)) { callType = CallLog.Calls.INCOMING_TYPE; } else if (CALL_RECORD_TYPE_OUTGOING.equals(callRecordType)) { callType = CallLog.Calls.OUTGOING_TYPE; } else if (CALL_RECORD_TYPE_MISSED.equals(callRecordType)) { callType = CallLog.Calls.MISSED_TYPE; } return callType; } /** * ??JS?? * */ private String getCallRecordTypeStr(int type) { String typeStr = CALL_RECORD_TYPE_RECEIVED; switch (type) { case CallLog.Calls.INCOMING_TYPE: typeStr = CALL_RECORD_TYPE_RECEIVED; break; case CallLog.Calls.OUTGOING_TYPE: typeStr = CALL_RECORD_TYPE_OUTGOING; break; case CallLog.Calls.MISSED_TYPE: typeStr = CALL_RECORD_TYPE_MISSED; break; } return typeStr; } /** * Cursor?JSON * */ private JSONObject getCallRecordFromCursor(Cursor cursor) { String callRecordId = cursor.getString(cursor.getColumnIndex(CallLog.Calls._ID)); String callRecordAddress = cursor.getString(cursor.getColumnIndex(CallLog.Calls.NUMBER)); String callRecordName = cursor.getString(cursor.getColumnIndex(CallLog.Calls.CACHED_NAME)); int callRecordTypeInt = cursor.getInt(cursor.getColumnIndex(CallLog.Calls.TYPE)); long startTime = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DATE)); long durationSeconds = cursor.getLong(cursor.getColumnIndex(CallLog.Calls.DURATION)); JSONObject callRecord = new JSONObject(); try { callRecord.put("callRecordId", callRecordId); callRecord.put("callRecordAddress", callRecordAddress); callRecord.put("callRecordName", callRecordName); callRecord.put("callRecordType", getCallRecordTypeStr(callRecordTypeInt)); callRecord.put("startTime", startTime); callRecord.put("durationSeconds", durationSeconds); } catch (JSONException e) { XLog.e(CLASS_NAME, e.toString()); } return callRecord; } /** * ??selection??SQL? * * @param selection * ?? * @param startTime * ? * @param durationSeconds * ?? * * @return SQL?? * */ private String buildSelectionStr(StringBuilder selection, long startTime, long durationSeconds, String type) { if (-1 != startTime) { if (null == selection) { selection = new StringBuilder(); } else { selection.append(" AND "); } selection.append(CallLog.Calls.DATE); selection.append("="); selection.append(startTime); } if (-1 != durationSeconds) { if (null == selection) { selection = new StringBuilder(); } else { selection.append(" AND "); } selection.append(CallLog.Calls.DURATION); selection.append("="); selection.append(durationSeconds); } if (!"".equals(type)) { if (null == selection) { selection = new StringBuilder(); } else { selection.append(" AND "); } selection.append(CallLog.Calls.TYPE); selection.append("="); selection.append(getCallRecordType(type)); } String selectionStr = null; if (null != selection) { selectionStr = selection.toString(); } return selectionStr; } }