1 // Copyright (c) 2013 Intel Corporation. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 package org.xwalk.core.internal.extension.api.messaging;
7 import android.app.Activity;
8 import android.app.PendingIntent;
9 import android.content.BroadcastReceiver;
10 import android.content.ContentResolver;
11 import android.content.ContentValues;
12 import android.content.Context;
13 import android.content.Intent;
14 import android.content.IntentFilter;
15 import android.os.Bundle;
16 import android.telephony.SmsManager;
17 import android.telephony.SmsMessage;
18 import android.telephony.TelephonyManager;
19 import android.net.Uri;
20 import android.util.Log;
22 import java.lang.ref.WeakReference;
23 import java.text.SimpleDateFormat;
24 import java.util.ArrayList;
25 import java.util.UUID;
27 import org.json.JSONArray;
28 import org.json.JSONException;
29 import org.json.JSONObject;
30 import org.xwalk.core.internal.extension.api.messaging.Messaging;
32 public class MessagingSmsManager {
33 private final static String TAG = "MessagingSmsManager";
34 private final static String EXTRA_MSGID = "asyncCallId";
35 private final static String EXTRA_MSGTEXT = "message";
36 private final static String EXTRA_MSGTO = "to";
37 private final static String EXTRA_MSGINSTANCEID = "instanceid";
38 private final static String EXTRA_UUID= "UUID";
39 private final static String DEFAULT_SERVICE_ID = "sim0";
40 private final WeakReference<Activity> mActivity;
41 private final Messaging mMessagingHandler;
42 private BroadcastReceiver mSmsSentReceiver, mSmsDeliveredReceiver,
43 mSmsReceiveReceiver, mSmsServiceReceiver;
46 private abstract class MessagingReceiver extends BroadcastReceiver {
47 protected Messaging mMessaging;
49 public MessagingReceiver(Messaging messaging) {
50 mMessaging = messaging;
54 MessagingSmsManager(Activity activity, Messaging messaging) {
55 mActivity = new WeakReference<Activity>(activity);
56 mMessagingHandler = messaging;
57 mUUID = UUID.randomUUID().toString();
60 private boolean checkService(String serviceID) {
61 Activity activity = mActivity.get();
62 if (activity == null) return false;
64 (TelephonyManager) activity.getSystemService(Context.TELEPHONY_SERVICE);
65 return (TelephonyManager.SIM_STATE_READY == tm.getSimState());
68 public void onSmsSend(int instanceID, JSONObject jsonMsg) {
69 if (!checkService(DEFAULT_SERVICE_ID)) {
70 Log.e(TAG, "No Sim Card");
72 Activity activity = mActivity.get();
73 if (activity == null) return;
75 String asyncCallId = null;
76 JSONObject eventBody = null;
78 String smsMessage = null;
80 asyncCallId = jsonMsg.getString("asyncCallId");
81 eventBody = jsonMsg.getJSONObject("data");
82 phone = eventBody.getString("phone");
83 smsMessage = eventBody.getString("message");
84 } catch (JSONException e) {
89 SmsManager sms = SmsManager.getDefault();
90 Intent intentSmsSent = new Intent("SMS_SENT");
91 intentSmsSent.putExtra(EXTRA_MSGID, asyncCallId);
92 intentSmsSent.putExtra(EXTRA_MSGTEXT, smsMessage);
93 intentSmsSent.putExtra(EXTRA_MSGTO, phone);
94 String instanceIDString = Integer.toString(instanceID);
95 intentSmsSent.putExtra(EXTRA_MSGINSTANCEID, instanceIDString);
96 intentSmsSent.putExtra(EXTRA_UUID, mUUID);
97 int promiseIdInt = Integer.valueOf(asyncCallId);
98 PendingIntent piSent = PendingIntent.getBroadcast(activity,
101 PendingIntent.FLAG_ONE_SHOT);
102 Intent intentSmsDelivered = new Intent("SMS_DELIVERED");
103 intentSmsDelivered.putExtra(EXTRA_MSGID, asyncCallId);
104 intentSmsDelivered.putExtra(EXTRA_MSGTEXT, smsMessage);
105 intentSmsDelivered.putExtra(EXTRA_MSGINSTANCEID, instanceIDString);
106 intentSmsDelivered.putExtra(EXTRA_UUID, mUUID);
107 PendingIntent piDelivered = PendingIntent.getBroadcast(activity,
110 PendingIntent.FLAG_ONE_SHOT);
112 sms.sendTextMessage(phone, null, smsMessage, piSent, piDelivered);
113 } catch (Exception e) {
114 Log.e(TAG, "Failed to send SMS message.", e);
118 public void onSmsClear(int instanceID, JSONObject jsonMsg) {
119 Activity activity = mActivity.get();
120 if (activity == null) return;
122 String asyncCallId = null, cmd = null;
123 JSONObject eventBody = null;
124 String serviceID = null;
126 asyncCallId = jsonMsg.getString("asyncCallId");
127 cmd = jsonMsg.getString("cmd");
128 eventBody = jsonMsg.getJSONObject("data");
129 serviceID = eventBody.getString("serviceID");
130 } catch (JSONException e) {
135 ContentResolver cr = activity.getContentResolver();
136 cr.delete(Uri.parse("content://sms"), null, null);
138 JSONObject jsonMsgRet = null;
140 jsonMsgRet = new JSONObject();
141 jsonMsgRet.put("asyncCallId", asyncCallId);
142 jsonMsgRet.put("cmd", cmd + "_ret");
143 JSONObject jsData = new JSONObject();
144 jsonMsgRet.put("data", jsData);
145 jsData.put("error", false);
146 JSONObject jsBody = new JSONObject();
147 jsData.put("body", jsBody);
148 jsBody.put("value", serviceID);
149 } catch (JSONException e) {
154 mMessagingHandler.postMessage(instanceID, jsonMsgRet.toString());
157 public void onSmsSegmentInfo(int instanceID, JSONObject jsonMsg) {
158 String asyncCallId = null;
159 JSONObject eventBody = null;
162 asyncCallId = jsonMsg.getString("asyncCallId");
163 eventBody = jsonMsg.getJSONObject("data");
164 text = eventBody.getString("text");
167 Log.e(TAG, "No \"text\" attribute.");
170 } catch (JSONException e) {
175 SmsManager sms = SmsManager.getDefault();
176 ArrayList<String> segs = sms.divideMessage(text);
178 JSONObject jsonMsgRet = new JSONObject();
179 jsonMsgRet.put("cmd", "msg_smsSegmentInfo_ret");
180 jsonMsgRet.put("asyncCallId", asyncCallId);
181 JSONObject jsData = new JSONObject();
182 jsonMsgRet.put("data", jsData);
183 jsData.put("error", false);
184 JSONObject jsBody = new JSONObject();
185 jsData.put("body", jsBody);
186 jsBody.put("segments", segs.size());
187 jsBody.put("charsPerSegment", segs.get(0).length());
188 jsBody.put("charsAvailableInLastSegment", segs.get(segs.size() - 1).length());
190 mMessagingHandler.postMessage(instanceID, jsonMsgRet.toString());
191 } catch (JSONException e) {
197 public void registerIntentFilters() {
198 Activity activity = mActivity.get();
199 if (activity == null) return;
201 mSmsReceiveReceiver = new MessagingReceiver(mMessagingHandler) {
203 public void onReceive(Context context, Intent intent) {
204 Bundle bundle = intent.getExtras();
210 Object[] pdus = (Object[]) bundle.get("pdus");
211 for (int i=0; i < pdus.length; ++i) {
213 JSONObject jsonMsg = new JSONObject();
214 jsonMsg.put("cmd", "received");
216 SmsMessage msgs = SmsMessage.createFromPdu((byte[])pdus[i]);
218 JSONObject jsData = new JSONObject();
219 jsonMsg.put("data", jsData);
220 JSONObject jsMsg = new JSONObject();
221 jsData.put("message", jsMsg);
222 jsMsg.put("messageID", "");
223 jsMsg.put("type", "sms");
224 jsMsg.put("serviceID", DEFAULT_SERVICE_ID);
225 jsMsg.put("from", msgs.getOriginatingAddress());
226 SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
227 jsMsg.put("timestamp", sDateFormat.format(new java.util.Date()));
228 jsMsg.put("body", msgs.getMessageBody().toString());
230 mMessaging.broadcastMessage(jsonMsg.toString());
231 } catch (JSONException e) {
239 mSmsSentReceiver = new MessagingReceiver(mMessagingHandler) {
241 public void onReceive(Context content, Intent intent) {
242 Activity activity = mActivity.get();
243 if (activity == null) return;
244 String uuid = intent.getStringExtra(EXTRA_UUID);
245 if (null == uuid || !uuid.equals(mUUID)) return;
247 boolean error = getResultCode() != Activity.RESULT_OK;
248 String asyncCallId = intent.getStringExtra(EXTRA_MSGID);
249 String smsMessage = intent.getStringExtra(EXTRA_MSGTEXT);
250 String to = intent.getStringExtra(EXTRA_MSGTO);
251 int instanceID = Integer.valueOf(intent.getStringExtra(EXTRA_MSGINSTANCEID));
254 JSONObject jsSentMsg = new JSONObject();
255 jsSentMsg.put("type", "sms");
256 jsSentMsg.put("from", "");
257 jsSentMsg.put("read", true);
258 jsSentMsg.put("to", to);
259 jsSentMsg.put("body", smsMessage);
260 jsSentMsg.put("messageClass", "class1");
261 jsSentMsg.put("state", error ? "failed" : "sending");
262 jsSentMsg.put("deliveryStatus", error ? "error" : "pending");
264 JSONObject jsonMsgPromise = new JSONObject();
265 jsonMsgPromise.put("asyncCallId", asyncCallId);
266 jsonMsgPromise.put("cmd", "msg_smsSend_ret");
267 JSONObject jsData = new JSONObject();
268 jsonMsgPromise.put("data", jsData);
269 jsData.put("error", error);
270 jsData.put("body", jsSentMsg);
272 mMessaging.postMessage(instanceID, jsonMsgPromise.toString());
274 JSONObject jsonMsgEvent = new JSONObject();
275 jsonMsgEvent.put("cmd", "sent");
276 jsonMsgEvent.put("data", jsSentMsg);
278 mMessaging.broadcastMessage(jsonMsgEvent.toString());
279 } catch (JSONException e) {
284 ContentValues values = new ContentValues();
285 values.put("address", to);
286 values.put("body", smsMessage);
287 activity.getContentResolver().insert(Uri.parse("content://sms/sent"), values);
291 mSmsDeliveredReceiver = new MessagingReceiver(mMessagingHandler) {
293 public void onReceive(Context content, Intent intent) {
294 String uuid = intent.getStringExtra(EXTRA_UUID);
295 if (null == uuid || !uuid.equals(mUUID)) return;
297 boolean error = getResultCode() != Activity.RESULT_OK;
298 String asyncCallId = intent.getStringExtra(EXTRA_MSGID);
299 int instanceID = Integer.valueOf(intent.getStringExtra(EXTRA_MSGINSTANCEID));
302 JSONObject jsonMsg = new JSONObject();
303 jsonMsg.put("asyncCallId", asyncCallId);
304 jsonMsg.put("cmd", error ? "deliveryerror": "deliverysuccess");
305 JSONObject jsData = new JSONObject();
306 jsonMsg.put("data", jsData);
307 JSONObject jsEvent = new JSONObject();
308 jsData.put("event", jsEvent);
309 jsEvent.put("serviceID", DEFAULT_SERVICE_ID);
310 jsEvent.put("messageID", "");
311 jsEvent.put("recipients", "");
312 SimpleDateFormat sDateFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
313 jsEvent.put("deliveryTimestamps", sDateFormat.format(new java.util.Date()));
314 jsData.put("error", error);
315 mMessaging.postMessage(instanceID, jsonMsg.toString());
316 } catch (JSONException e) {
323 mSmsServiceReceiver = new MessagingReceiver(mMessagingHandler) {
325 public void onReceive(Context content, Intent intent) {
327 JSONObject jsonMsg = new JSONObject();
329 checkService(DEFAULT_SERVICE_ID) ? "serviceadded": "serviceremoved");
330 JSONObject jsData = new JSONObject();
331 jsonMsg.put("data", jsData);
332 JSONObject jsEvent = new JSONObject();
333 jsData.put("event", jsEvent);
334 jsEvent.put("serviceID", DEFAULT_SERVICE_ID);
335 mMessaging.broadcastMessage(jsonMsg.toString());
336 } catch (JSONException e) {
343 activity.registerReceiver(
344 mSmsReceiveReceiver, new IntentFilter("android.provider.Telephony.SMS_RECEIVED"));
345 activity.registerReceiver(
346 mSmsSentReceiver, new IntentFilter("SMS_SENT"));
347 activity.registerReceiver(
348 mSmsDeliveredReceiver,new IntentFilter("SMS_DELIVERED"));
349 activity.registerReceiver(
350 mSmsServiceReceiver,new IntentFilter("android.intent.action.SIM_STATE_CHANGED"));
353 public void unregisterIntentFilters() {
354 Activity activity = mActivity.get();
355 if (activity == null) return;
357 activity.unregisterReceiver(mSmsReceiveReceiver);
358 activity.unregisterReceiver(mSmsSentReceiver);
359 activity.unregisterReceiver(mSmsDeliveredReceiver);
360 activity.unregisterReceiver(mSmsServiceReceiver);
363 public String getServiceIds() {
364 JSONArray serviceIds = new JSONArray();
366 // FIXME:(shawn) Android doesn't has the concept of service ID, which means messages are
367 // bundled to given SIM card. And official SDK does not support multiple SIM cards.
368 // So mock the default one to satisfy the spec.
369 serviceIds.put(DEFAULT_SERVICE_ID);
370 return serviceIds.toString();