Java tutorial
package com.rjfun.cordova.sms; import android.annotation.SuppressLint; import android.app.Activity; import android.app.PendingIntent; import android.content.BroadcastReceiver; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.database.ContentObserver; import android.database.Cursor; import android.net.Uri; import android.os.Bundle; import android.os.Handler; import android.telephony.SmsManager; import android.telephony.SmsMessage; import android.util.Log; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import org.apache.cordova.CallbackContext; import org.apache.cordova.CordovaPlugin; import org.apache.cordova.PluginResult; import org.json.JSONArray; import org.json.JSONException; import org.json.JSONObject; public class SMSPlugin extends CordovaPlugin { private static final String LOGTAG = "SMSPlugin"; public static final String ACTION_SET_OPTIONS = "setOptions"; private static final String ACTION_START_WATCH = "startWatch"; private static final String ACTION_STOP_WATCH = "stopWatch"; private static final String ACTION_ENABLE_INTERCEPT = "enableIntercept"; private static final String ACTION_LIST_SMS = "listSMS"; private static final String ACTION_DELETE_SMS = "deleteSMS"; private static final String ACTION_RESTORE_SMS = "restoreSMS"; private static final String ACTION_SEND_SMS = "sendSMS"; public static final String OPT_LICENSE = "license"; private static final String SEND_SMS_ACTION = "SENT_SMS_ACTION"; private static final String DELIVERED_SMS_ACTION = "DELIVERED_SMS_ACTION"; private static final String SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED"; public static final String SMS_EXTRA_NAME = "pdus"; public static final String SMS_URI_ALL = "content://sms/"; public static final String SMS_URI_INBOX = "content://sms/inbox"; public static final String SMS_URI_SEND = "content://sms/sent"; public static final String SMS_URI_DRAFT = "content://sms/draft"; public static final String SMS_URI_OUTBOX = "content://sms/outbox"; public static final String SMS_URI_FAILED = "content://sms/failed"; public static final String SMS_URI_QUEUED = "content://sms/queued"; public static final String BOX = "box"; public static final String ADDRESS = "address"; public static final String BODY = "body"; public static final String READ = "read"; public static final String SEEN = "seen"; public static final String SUBJECT = "subject"; public static final String SERVICE_CENTER = "service_center"; public static final String DATE = "date"; public static final String DATE_SENT = "date_sent"; public static final String STATUS = "status"; public static final String REPLY_PATH_PRESENT = "reply_path_present"; public static final String TYPE = "type"; public static final String PROTOCOL = "protocol"; public static final int MESSAGE_TYPE_INBOX = 1; public static final int MESSAGE_TYPE_SENT = 2; public static final int MESSAGE_IS_NOT_READ = 0; public static final int MESSAGE_IS_READ = 1; public static final int MESSAGE_IS_NOT_SEEN = 0; public static final int MESSAGE_IS_SEEN = 1; private static final String SMS_GENERAL_ERROR = "SMS_GENERAL_ERROR"; private static final String NO_SMS_SERVICE_AVAILABLE = "NO_SMS_SERVICE_AVAILABLE"; private static final String SMS_FEATURE_NOT_SUPPORTED = "SMS_FEATURE_NOT_SUPPORTED"; private static final String SENDING_SMS_ID = "SENDING_SMS"; private ContentObserver mObserver = null; private BroadcastReceiver mReceiver = null; private boolean mIntercept = false; private String lastFrom = ""; private String lastContent = ""; public boolean execute(String action, JSONArray inputs, CallbackContext callbackContext) throws JSONException { PluginResult result = null; if (ACTION_SET_OPTIONS.equals(action)) { JSONObject options = inputs.optJSONObject(0); this.setOptions(options); result = new PluginResult(PluginResult.Status.OK); } else if (ACTION_START_WATCH.equals(action)) { result = this.startWatch(callbackContext); } else if (ACTION_STOP_WATCH.equals(action)) { result = this.stopWatch(callbackContext); } else if (ACTION_ENABLE_INTERCEPT.equals(action)) { boolean on_off = inputs.optBoolean(0); result = this.enableIntercept(on_off, callbackContext); } else if (ACTION_DELETE_SMS.equals(action)) { JSONObject msg = inputs.optJSONObject(0); result = this.deleteSMS(msg, callbackContext); } else if (ACTION_RESTORE_SMS.equals(action)) { JSONArray smsList = inputs.optJSONArray(0); result = this.restoreSMS(smsList, callbackContext); } else if (ACTION_LIST_SMS.equals(action)) { JSONObject filters = inputs.optJSONObject(0); result = this.listSMS(filters, callbackContext); } else if (ACTION_SEND_SMS.equals(action)) { JSONArray addressList = inputs.optJSONArray(0); String message = inputs.optString(1); result = this.sendSMS(addressList, message, callbackContext); } else { Log.d(LOGTAG, String.format("Invalid action passed: %s", action)); result = new PluginResult(PluginResult.Status.INVALID_ACTION); } if (result != null) { callbackContext.sendPluginResult(result); } return true; } public void onDestroy() { this.stopWatch(null); } public void setOptions(JSONObject options) { Log.d(LOGTAG, ACTION_SET_OPTIONS); } protected String __getProductShortName() { return "SMS"; } public final String md5(String s) { try { MessageDigest digest = MessageDigest.getInstance("MD5"); digest.update(s.getBytes()); byte[] messageDigest = digest.digest(); StringBuffer hexString = new StringBuffer(); for (int i = 0; i < messageDigest.length; ++i) { String h = Integer.toHexString(255 & messageDigest[i]); while (h.length() < 2) { h = "0" + h; } hexString.append(h); } return hexString.toString(); } catch (NoSuchAlgorithmException digest) { return ""; } } private PluginResult startWatch(CallbackContext callbackContext) { Log.d(LOGTAG, ACTION_START_WATCH); if (this.mObserver == null) { this.createContentObserver(); } if (this.mReceiver == null) { this.createIncomingSMSReceiver(); } if (callbackContext != null) { callbackContext.success(); } return null; } private PluginResult stopWatch(CallbackContext callbackContext) { Log.d(LOGTAG, ACTION_STOP_WATCH); Activity ctx = this.cordova.getActivity(); if (this.mReceiver != null) { ctx.unregisterReceiver(this.mReceiver); this.mReceiver = null; Log.d(LOGTAG, "broadcast receiver unregistered"); } if (this.mObserver != null) { ctx.getContentResolver().unregisterContentObserver(this.mObserver); this.mObserver = null; Log.d(LOGTAG, "sms inbox observer unregistered"); } if (callbackContext != null) { callbackContext.success(); } return null; } private PluginResult enableIntercept(boolean on_off, CallbackContext callbackContext) { Log.d(LOGTAG, ACTION_ENABLE_INTERCEPT); this.mIntercept = on_off; if (callbackContext != null) { callbackContext.success(); } return null; } private PluginResult sendSMS(JSONArray addressList, String text, CallbackContext callbackContext) { Log.d(LOGTAG, ACTION_SEND_SMS); if (this.cordova.getActivity().getPackageManager().hasSystemFeature("android.hardware.telephony")) { int n; if ((n = addressList.length()) > 0) { PendingIntent sentIntent = PendingIntent.getBroadcast((Context) this.cordova.getActivity(), (int) 0, (Intent) new Intent("SENDING_SMS"), (int) 0); SmsManager sms = SmsManager.getDefault(); for (int i = 0; i < n; ++i) { String address; if ((address = addressList.optString(i)).length() <= 0) continue; sms.sendTextMessage(address, null, text, sentIntent, (PendingIntent) null); } } else { PendingIntent sentIntent = PendingIntent.getActivity((Context) this.cordova.getActivity(), (int) 0, (Intent) new Intent("android.intent.action.VIEW"), (int) 0); Intent intent = new Intent("android.intent.action.VIEW"); intent.putExtra("sms_body", text); intent.setType("vnd.android-dir/mms-sms"); try { sentIntent.send(this.cordova.getActivity().getApplicationContext(), 0, intent); } catch (PendingIntent.CanceledException e) { e.printStackTrace(); } } callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.OK, "OK")); } else { callbackContext.sendPluginResult(new PluginResult(PluginResult.Status.ERROR, "SMS is not supported")); } return null; } private PluginResult listSMS(JSONObject filter, CallbackContext callbackContext) { Log.i(LOGTAG, ACTION_LIST_SMS); String uri_filter = filter.has(BOX) ? filter.optString(BOX) : "inbox"; int fread = filter.has(READ) ? filter.optInt(READ) : -1; int fid = filter.has("_id") ? filter.optInt("_id") : -1; String faddress = filter.optString(ADDRESS); String fcontent = filter.optString(BODY); int indexFrom = filter.has("indexFrom") ? filter.optInt("indexFrom") : 0; int maxCount = filter.has("maxCount") ? filter.optInt("maxCount") : 10; JSONArray jsons = new JSONArray(); Activity ctx = this.cordova.getActivity(); Uri uri = Uri.parse((SMS_URI_ALL + uri_filter)); Cursor cur = ctx.getContentResolver().query(uri, (String[]) null, "", (String[]) null, null); int i = 0; while (cur.moveToNext()) { JSONObject json; boolean matchFilter = false; if (fid > -1) { matchFilter = (fid == cur.getInt(cur.getColumnIndex("_id"))); } else if (fread > -1) { matchFilter = (fread == cur.getInt(cur.getColumnIndex(READ))); } else if (faddress.length() > 0) { matchFilter = faddress.equals(cur.getString(cur.getColumnIndex(ADDRESS)).trim()); } else if (fcontent.length() > 0) { matchFilter = fcontent.equals(cur.getString(cur.getColumnIndex(BODY)).trim()); } else { matchFilter = true; } if (!matchFilter) continue; if (i < indexFrom) continue; if (i >= indexFrom + maxCount) break; ++i; if ((json = this.getJsonFromCursor(cur)) == null) { callbackContext.error("failed to get json from cursor"); cur.close(); return null; } jsons.put((Object) json); } cur.close(); callbackContext.success(jsons); return null; } private JSONObject getJsonFromCursor(Cursor cur) { JSONObject json = new JSONObject(); int nCol = cur.getColumnCount(); String keys[] = cur.getColumnNames(); try { for (int j = 0; j < nCol; j++) { switch (cur.getType(j)) { case Cursor.FIELD_TYPE_NULL: json.put(keys[j], null); break; case Cursor.FIELD_TYPE_INTEGER: json.put(keys[j], cur.getLong(j)); break; case Cursor.FIELD_TYPE_FLOAT: json.put(keys[j], cur.getFloat(j)); break; case Cursor.FIELD_TYPE_STRING: json.put(keys[j], cur.getString(j)); break; case Cursor.FIELD_TYPE_BLOB: json.put(keys[j], cur.getBlob(j)); break; } } } catch (Exception e) { return null; } return json; } private void fireEvent(final String event, JSONObject json) { final String str = json.toString(); Log.d(LOGTAG, "Event: " + event + ", " + str); cordova.getActivity().runOnUiThread(new Runnable() { @Override public void run() { String js = String.format("javascript:cordova.fireDocumentEvent(\"%s\", {\"data\":%s});", event, str); webView.loadUrl(js); } }); } private void onSMSArrive(JSONObject json) { String from = json.optString(ADDRESS); String content = json.optString(BODY); if (from.equals(this.lastFrom) && content.equals(this.lastContent)) { return; } this.lastFrom = from; this.lastContent = content; this.fireEvent("onSMSArrive", json); } protected void createIncomingSMSReceiver() { Activity ctx = this.cordova.getActivity(); this.mReceiver = new BroadcastReceiver() { public void onReceive(Context context, Intent intent) { String action = intent.getAction(); Log.d(LOGTAG, ("onRecieve: " + action)); if (SMS_RECEIVED.equals(action)) { Bundle bundle; if (SMSPlugin.this.mIntercept) { this.abortBroadcast(); } if ((bundle = intent.getExtras()) != null) { Object[] pdus; if ((pdus = (Object[]) bundle.get("pdus")).length != 0) { for (int i = 0; i < pdus.length; ++i) { SmsMessage sms = SmsMessage.createFromPdu((byte[]) ((byte[]) pdus[i])); JSONObject json = SMSPlugin.this.getJsonFromSmsMessage(sms); SMSPlugin.this.onSMSArrive(json); } } } } } }; String[] filterstr = new String[] { SMS_RECEIVED }; for (int i = 0; i < filterstr.length; ++i) { IntentFilter filter = new IntentFilter(filterstr[i]); filter.setPriority(100); ctx.registerReceiver(this.mReceiver, filter); Log.d(LOGTAG, ("broadcast receiver registered for: " + filterstr[i])); } } protected void createContentObserver() { Activity ctx = this.cordova.getActivity(); this.mObserver = new ContentObserver(new Handler()) { public void onChange(boolean selfChange) { this.onChange(selfChange, null); } public void onChange(boolean selfChange, Uri uri) { ContentResolver resolver = cordova.getActivity().getContentResolver(); Log.d(LOGTAG, ("onChange, selfChange: " + selfChange + ", uri: " + (Object) uri)); int id = -1; String str; if (uri != null && (str = uri.toString()).startsWith(SMS_URI_ALL)) { try { id = Integer.parseInt(str.substring(SMS_URI_ALL.length())); Log.d(LOGTAG, ("sms id: " + id)); } catch (NumberFormatException var6_6) { // empty catch block } } if (id == -1) { uri = Uri.parse(SMS_URI_INBOX); } Cursor cur = resolver.query(uri, null, null, null, "_id desc"); if (cur != null) { int n = cur.getCount(); Log.d(LOGTAG, ("n = " + n)); if (n > 0 && cur.moveToFirst()) { JSONObject json; if ((json = SMSPlugin.this.getJsonFromCursor(cur)) != null) { onSMSArrive(json); } else { Log.d(LOGTAG, "fetch record return null"); } } cur.close(); } } }; ctx.getContentResolver().registerContentObserver(Uri.parse(SMS_URI_INBOX), true, this.mObserver); Log.d(LOGTAG, "sms inbox observer registered"); } private PluginResult deleteSMS(JSONObject filter, CallbackContext callbackContext) { Log.d(LOGTAG, ACTION_DELETE_SMS); String uri_filter = filter.has(BOX) ? filter.optString(BOX) : "inbox"; int fread = filter.has(READ) ? filter.optInt(READ) : -1; int fid = filter.has("_id") ? filter.optInt("_id") : -1; String faddress = filter.optString(ADDRESS); String fcontent = filter.optString(BODY); Activity ctx = this.cordova.getActivity(); int n = 0; try { Uri uri = Uri.parse((SMS_URI_ALL + uri_filter)); Cursor cur = ctx.getContentResolver().query(uri, (String[]) null, "", (String[]) null, null); while (cur.moveToNext()) { int id = cur.getInt(cur.getColumnIndex("_id")); boolean matchId = fid > -1 && fid == id; int read = cur.getInt(cur.getColumnIndex(READ)); boolean matchRead = fread > -1 && fread == read; String address = cur.getString(cur.getColumnIndex(ADDRESS)).trim(); boolean matchAddr = faddress.length() > 0 && address.equals(faddress); String body = cur.getString(cur.getColumnIndex(BODY)).trim(); boolean matchContent = fcontent.length() > 0 && body.equals(fcontent); if (!matchId && !matchRead && !matchAddr && !matchContent) continue; ctx.getContentResolver().delete(uri, "_id=" + id, (String[]) null); ++n; } callbackContext.success(n); } catch (Exception e) { callbackContext.error(e.toString()); } return null; } private JSONObject getJsonFromSmsMessage(SmsMessage sms) { JSONObject json = new JSONObject(); try { json.put(ADDRESS, sms.getOriginatingAddress()); json.put(BODY, sms.getMessageBody()); // May need sms.getMessageBody.toString() json.put(DATE_SENT, sms.getTimestampMillis()); json.put(DATE, System.currentTimeMillis()); json.put(READ, MESSAGE_IS_NOT_READ); json.put(SEEN, MESSAGE_IS_NOT_SEEN); json.put(STATUS, sms.getStatus()); json.put(TYPE, MESSAGE_TYPE_INBOX); json.put(SERVICE_CENTER, sms.getServiceCenterAddress()); } catch (Exception e) { e.printStackTrace(); } return json; } private ContentValues getContentValuesFromJson(JSONObject json) { ContentValues values = new ContentValues(); values.put(ADDRESS, json.optString(ADDRESS)); values.put(BODY, json.optString(BODY)); values.put(DATE_SENT, json.optLong(DATE_SENT)); values.put(READ, json.optInt(READ)); values.put(SEEN, json.optInt(SEEN)); values.put(TYPE, json.optInt(TYPE)); values.put(SERVICE_CENTER, json.optString(SERVICE_CENTER)); return values; } private PluginResult restoreSMS(JSONArray array, CallbackContext callbackContext) { ContentResolver resolver = this.cordova.getActivity().getContentResolver(); Uri uri = Uri.parse(SMS_URI_INBOX); int n = array.length(); int m = 0; for (int i = 0; i < n; ++i) { JSONObject json; if ((json = array.optJSONObject(i)) == null) continue; String str = json.toString(); Log.d(LOGTAG, str); Uri newuri = resolver.insert(uri, this.getContentValuesFromJson(json)); Log.d(LOGTAG, ("inserted: " + newuri.toString())); ++m; } if (callbackContext != null) { callbackContext.success(m); } return null; } }