mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 1212679 - Handle KitKat default messaging app intents. r=fabrice r=snorp
This commit is contained in:
parent
f864c25f7f
commit
079e40fcb3
@ -24,14 +24,16 @@ namespace mozilla {
|
||||
|
||||
/*static*/
|
||||
void
|
||||
SmsManager::NotifySmsReceived(jni::String::Param aSender,
|
||||
SmsManager::NotifySmsReceived(int32_t aId,
|
||||
jni::String::Param aSender,
|
||||
jni::String::Param aBody,
|
||||
int32_t aMessageClass,
|
||||
int64_t aSentTimestamp,
|
||||
int64_t aTimestamp)
|
||||
{
|
||||
// TODO Need to correct the message `threadId` parameter value. Bug 859098
|
||||
SmsMessageData message;
|
||||
message.id() = 0;
|
||||
message.id() = aId;
|
||||
message.threadId() = 0;
|
||||
message.iccId() = EmptyString();
|
||||
message.delivery() = eDeliveryState_Received;
|
||||
@ -41,7 +43,7 @@ SmsManager::NotifySmsReceived(jni::String::Param aSender,
|
||||
message.body() = aBody ? nsString(aBody) : EmptyString();
|
||||
message.messageClass() = static_cast<MessageClass>(aMessageClass);
|
||||
message.timestamp() = aTimestamp;
|
||||
message.sentTimestamp() = aTimestamp;
|
||||
message.sentTimestamp() = aSentTimestamp;
|
||||
message.deliveryTimestamp() = aTimestamp;
|
||||
message.read() = false;
|
||||
|
||||
|
@ -16,9 +16,11 @@ private:
|
||||
SmsManager();
|
||||
|
||||
public:
|
||||
static void NotifySmsReceived(jni::String::Param aSender,
|
||||
static void NotifySmsReceived(int32_t aId,
|
||||
jni::String::Param aSender,
|
||||
jni::String::Param aBody,
|
||||
int32_t aMessageClass,
|
||||
int64_t aSentTimestamp,
|
||||
int64_t aTimestamp);
|
||||
static void NotifySmsSent(int32_t aId,
|
||||
jni::String::Param aReceiver,
|
||||
|
@ -6,7 +6,12 @@ ANDROID_MANIFEST_FILE := src/main/AndroidManifest.xml
|
||||
|
||||
JAVAFILES := \
|
||||
src/main/java/org/mozilla/b2gdroid/Apps.java \
|
||||
src/main/java/org/mozilla/b2gdroid/GeckoEventReceiver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/HeadlessSmsSendService.java \
|
||||
src/main/java/org/mozilla/b2gdroid/Launcher.java \
|
||||
src/main/java/org/mozilla/b2gdroid/MmsService.java \
|
||||
src/main/java/org/mozilla/b2gdroid/NotificationObserver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/RemoteGeckoEventProxy.java \
|
||||
src/main/java/org/mozilla/b2gdroid/ScreenStateObserver.java \
|
||||
src/main/java/org/mozilla/b2gdroid/SettingsMapper.java \
|
||||
src/main/java/org/mozilla/b2gdroid/GeckoEventReceiver.java \
|
||||
|
@ -33,6 +33,9 @@
|
||||
<uses-permission android:name="android.permission.RECEIVE_SMS"/>
|
||||
<uses-permission android:name="android.permission.WRITE_SMS"/>
|
||||
<uses-permission android:name="android.permission.READ_SMS"/>
|
||||
<uses-permission android:name="android.permission.BROADCAST_SMS"/>
|
||||
<uses-permission android:name="android.permission.WAP_PUSH_DELIVER"/>
|
||||
<uses-permission android:name="android.permission.SEND_RESPOND_VIA_MESSAGE"/>
|
||||
|
||||
<uses-feature android:name="android.hardware.telephony"/>
|
||||
|
||||
@ -69,6 +72,37 @@
|
||||
|
||||
<meta-data android:name="com.sec.android.support.multiwindow" android:value="true"/>
|
||||
|
||||
<!-- Listen for incoming SMS messages -->
|
||||
<receiver android:name="org.mozilla.gecko.GeckoSmsManager"
|
||||
android:permission="android.permission.BROADCAST_SMS">
|
||||
<intent-filter>
|
||||
<action android:name="android.provider.Telephony.SMS_DELIVER" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Listen for incoming MMS messages -->
|
||||
<receiver android:name=".MmsService"
|
||||
android:permission="android.permission.BROADCAST_WAP_PUSH">
|
||||
<intent-filter>
|
||||
<action android:name="android.provider.Telephony.WAP_PUSH_DELIVER" />
|
||||
<data android:mimeType="application/vnd.wap.mms-message" />
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<!-- Service that delivers messages from the phone "quick response" -->
|
||||
<service android:name=".HeadlessSmsSendService"
|
||||
android:permission="android.permission.SEND_RESPOND_VIA_MESSAGE"
|
||||
android:exported="true" >
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.RESPOND_VIA_MESSAGE" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<data android:scheme="sms" />
|
||||
<data android:scheme="smsto" />
|
||||
<data android:scheme="mms" />
|
||||
<data android:scheme="mmsto" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<activity android:name="org.mozilla.b2gdroid.Launcher"
|
||||
android:theme="@android:style/Theme.NoTitleBar.Fullscreen"
|
||||
android:icon="@drawable/b2g"
|
||||
@ -84,6 +118,18 @@
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Handle SMS intents -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.SEND" />
|
||||
<action android:name="android.intent.action.SENDTO" />
|
||||
<category android:name="android.intent.category.DEFAULT" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="sms" />
|
||||
<data android:scheme="smsto" />
|
||||
<data android:scheme="mms" />
|
||||
<data android:scheme="mmsto" />
|
||||
</intent-filter>
|
||||
|
||||
<!-- Default browser intents -->
|
||||
<intent-filter>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
|
@ -0,0 +1,40 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.b2gdroid;
|
||||
|
||||
import android.app.IntentService;
|
||||
import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
|
||||
public class HeadlessSmsSendService extends IntentService {
|
||||
private final static String ACTION_RESPOND_VIA_MESSAGE = "android.intent.action.RESPOND_VIA_MESSAGE";
|
||||
|
||||
public HeadlessSmsSendService() {
|
||||
super("HeadlessSmsSendService");
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onHandleIntent(Intent intent) {
|
||||
if (!ACTION_RESPOND_VIA_MESSAGE.equals(intent.getAction())) {
|
||||
return;
|
||||
}
|
||||
|
||||
Bundle extras = intent.getExtras();
|
||||
if (extras == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
String recipient = intent.getData().getPath();
|
||||
String message = extras.getString(Intent.EXTRA_TEXT);
|
||||
|
||||
if (recipient.length() == 0 || message.length() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
GeckoAppShell.sendMessage(recipient, message, 0, /* shouldNotify */ false);
|
||||
}
|
||||
}
|
@ -235,7 +235,7 @@ public class Launcher extends FragmentActivity
|
||||
try {
|
||||
obj.put("action", "view");
|
||||
obj.put("url", intent.getDataString());
|
||||
} catch(Exception ex) {
|
||||
} catch(JSONException ex) {
|
||||
Log.wtf(LOGTAG, "Error building Android:Launcher view message", ex);
|
||||
}
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
|
||||
@ -269,6 +269,21 @@ public class Launcher extends FragmentActivity
|
||||
}
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
} else if (Intent.ACTION_SENDTO.equals(action) ||
|
||||
Intent.ACTION_SEND.equals(action)) {
|
||||
Log.d(LOGTAG, "Sending new SMS intent to Gecko");
|
||||
JSONObject obj = new JSONObject();
|
||||
try {
|
||||
obj.put("action", "send_sms");
|
||||
if (Intent.ACTION_SENDTO.equals(action)) {
|
||||
obj.put("number", intent.getData().getPath());
|
||||
}
|
||||
obj.put("body", intent.getStringExtra("sms_body"));
|
||||
} catch (JSONException ex) {
|
||||
Log.wtf(LOGTAG, "Error building Android:Launcher message", ex);
|
||||
}
|
||||
GeckoEvent e = GeckoEvent.createBroadcastEvent("Android:Launcher", obj.toString());
|
||||
GeckoAppShell.sendEventToGecko(e);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -0,0 +1,202 @@
|
||||
/* This Source Code Form is subject to the terms of the Mozilla Public
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
|
||||
* You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
package org.mozilla.b2gdroid;
|
||||
|
||||
import android.content.BroadcastReceiver;
|
||||
import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.database.Cursor;
|
||||
import android.database.DatabaseUtils;
|
||||
import android.database.sqlite.SQLiteException;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.provider.Telephony;
|
||||
import android.telephony.SmsManager;
|
||||
import android.util.Log;
|
||||
|
||||
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_DELIVERY_IND;
|
||||
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND;
|
||||
import static com.google.android.mms.pdu.PduHeaders.MESSAGE_TYPE_READ_ORIG_IND;
|
||||
|
||||
import com.google.android.mms.MmsException;
|
||||
import com.google.android.mms.pdu.DeliveryInd;
|
||||
import com.google.android.mms.pdu.GenericPdu;
|
||||
import com.google.android.mms.pdu.NotificationInd;
|
||||
import com.google.android.mms.pdu.PduHeaders;
|
||||
import com.google.android.mms.pdu.PduParser;
|
||||
import com.google.android.mms.pdu.PduPersister;
|
||||
import com.google.android.mms.pdu.ReadOrigInd;
|
||||
import com.google.android.mms.util.SqliteWrapper;
|
||||
|
||||
import org.mozilla.gecko.GeckoAppShell;
|
||||
import org.mozilla.gecko.GeckoSmsManager;
|
||||
|
||||
public class MmsService extends BroadcastReceiver {
|
||||
private final static String LOGTAG = "MmsService";
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(Telephony.Sms.Intents.WAP_PUSH_DELIVER_ACTION)) {
|
||||
final byte[] data = intent.getByteArrayExtra("data");
|
||||
writeInboxMessage(data);
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean shouldParseContentDisposition() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
|
||||
return SmsManager.getDefault().getCarrierConfigValues().getBoolean(SmsManager.MMS_CONFIG_SUPPORT_MMS_CONTENT_DISPOSITION, true);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
private void writeInboxMessage(byte[] pushData) {
|
||||
final Context context = GeckoAppShell.getContext();
|
||||
final GenericPdu pdu = new PduParser(pushData, shouldParseContentDisposition()).parse();
|
||||
if (pdu == null) {
|
||||
Log.e(LOGTAG, "Invalid PUSH PDU");
|
||||
}
|
||||
final PduPersister persister = PduPersister.getPduPersister(context);
|
||||
final int type = pdu.getMessageType();
|
||||
try {
|
||||
switch (type) {
|
||||
case MESSAGE_TYPE_DELIVERY_IND:
|
||||
case MESSAGE_TYPE_READ_ORIG_IND: {
|
||||
final long threadId = getDeliveryOrReadReportThreadId(context, pdu);
|
||||
if (threadId == -1) {
|
||||
// The associated SendReq isn't found, therefore skip
|
||||
// processing this PDU.
|
||||
Log.e(LOGTAG, "Failed to find delivery or read report's thread id");
|
||||
break;
|
||||
}
|
||||
final Uri uri = persister.persist(
|
||||
pdu,
|
||||
Telephony.Mms.Inbox.CONTENT_URI,
|
||||
true/*createThreadId*/,
|
||||
true/*groupMmsEnabled*/,
|
||||
null/*preOpenedFiles*/);
|
||||
if (uri == null) {
|
||||
Log.e(LOGTAG, "Failed to persist delivery or read report");
|
||||
break;
|
||||
}
|
||||
// Update thread ID for ReadOrigInd & DeliveryInd.
|
||||
final ContentValues values = new ContentValues(1);
|
||||
values.put(Telephony.Mms.THREAD_ID, threadId);
|
||||
if (SqliteWrapper.update(
|
||||
context,
|
||||
context.getContentResolver(),
|
||||
uri,
|
||||
values,
|
||||
null/*where*/,
|
||||
null/*selectionArgs*/) != 1) {
|
||||
Log.e(LOGTAG, "Failed to update delivery or read report thread id");
|
||||
}
|
||||
break;
|
||||
}
|
||||
case MESSAGE_TYPE_NOTIFICATION_IND: {
|
||||
final NotificationInd nInd = (NotificationInd) pdu;
|
||||
if (!isDuplicateNotification(context, nInd)) {
|
||||
final Uri uri = persister.persist(
|
||||
pdu,
|
||||
Telephony.Mms.Inbox.CONTENT_URI,
|
||||
true/*createThreadId*/,
|
||||
true/*groupMmsEnabled*/,
|
||||
null/*preOpenedFiles*/);
|
||||
if (uri == null) {
|
||||
Log.e(LOGTAG, "Failed to save MMS WAP push notification ind");
|
||||
}
|
||||
} else {
|
||||
Log.d(LOGTAG, "Skip storing duplicate MMS WAP push notification ind: "
|
||||
+ new String(nInd.getContentLocation()));
|
||||
}
|
||||
break;
|
||||
}
|
||||
default:
|
||||
Log.e(LOGTAG, "Received unrecognized WAP Push PDU.");
|
||||
}
|
||||
} catch (MmsException e) {
|
||||
Log.e(LOGTAG, "Failed to save MMS WAP push data: type=" + type, e);
|
||||
} catch (RuntimeException e) {
|
||||
Log.e(LOGTAG, "Unexpected RuntimeException in persisting MMS WAP push data", e);
|
||||
}
|
||||
}
|
||||
|
||||
private static final String THREAD_ID_SELECTION =
|
||||
Telephony.Mms.MESSAGE_ID + "=? AND " + Telephony.Mms.MESSAGE_TYPE + "=?";
|
||||
|
||||
private static long getDeliveryOrReadReportThreadId(Context context, GenericPdu pdu) {
|
||||
String messageId;
|
||||
if (pdu instanceof DeliveryInd) {
|
||||
messageId = new String(((DeliveryInd) pdu).getMessageId());
|
||||
} else if (pdu instanceof ReadOrigInd) {
|
||||
messageId = new String(((ReadOrigInd) pdu).getMessageId());
|
||||
} else {
|
||||
Log.e(LOGTAG, "WAP Push data is neither delivery or read report type: "
|
||||
+ pdu.getClass().getCanonicalName());
|
||||
return -1L;
|
||||
}
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = SqliteWrapper.query(
|
||||
context,
|
||||
context.getContentResolver(),
|
||||
Telephony.Mms.CONTENT_URI,
|
||||
new String[]{ Telephony.Mms.THREAD_ID },
|
||||
THREAD_ID_SELECTION,
|
||||
new String[]{
|
||||
DatabaseUtils.sqlEscapeString(messageId),
|
||||
Integer.toString(PduHeaders.MESSAGE_TYPE_SEND_REQ)
|
||||
},
|
||||
null/*sortOrder*/);
|
||||
if (cursor != null && cursor.moveToFirst()) {
|
||||
return cursor.getLong(0);
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(LOGTAG, "Failed to query delivery or read report thread id", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
return -1L;
|
||||
}
|
||||
|
||||
private static final String LOCATION_SELECTION =
|
||||
Telephony.Mms.MESSAGE_TYPE + "=? AND " + Telephony.Mms.CONTENT_LOCATION + " =?";
|
||||
|
||||
private static boolean isDuplicateNotification(Context context, NotificationInd nInd) {
|
||||
final byte[] rawLocation = nInd.getContentLocation();
|
||||
if (rawLocation != null) {
|
||||
String location = new String(rawLocation);
|
||||
String[] selectionArgs = new String[] { location };
|
||||
Cursor cursor = null;
|
||||
try {
|
||||
cursor = SqliteWrapper.query(
|
||||
context,
|
||||
context.getContentResolver(),
|
||||
Telephony.Mms.CONTENT_URI,
|
||||
new String[]{Telephony.Mms._ID},
|
||||
LOCATION_SELECTION,
|
||||
new String[]{
|
||||
Integer.toString(PduHeaders.MESSAGE_TYPE_NOTIFICATION_IND),
|
||||
new String(rawLocation)
|
||||
},
|
||||
null/*sortOrder*/);
|
||||
if (cursor != null && cursor.getCount() > 0) {
|
||||
// We already received the same notification before.
|
||||
return true;
|
||||
}
|
||||
} catch (SQLiteException e) {
|
||||
Log.e(LOGTAG, "failed to query existing notification ind", e);
|
||||
} finally {
|
||||
if (cursor != null) {
|
||||
cursor.close();
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
@ -105,6 +105,16 @@ this.MessagesBridge = {
|
||||
data: { type: "url",
|
||||
url: data.url } });
|
||||
break;
|
||||
case "send_sms":
|
||||
new window.MozActivity({
|
||||
name: "new",
|
||||
data: {
|
||||
type: "websms/sms",
|
||||
number: data.number,
|
||||
body: data.body
|
||||
}
|
||||
});
|
||||
break;
|
||||
case "home-key":
|
||||
window.dispatchEvent(new window.KeyboardEvent("keydown", { key: "Home" }));
|
||||
window.dispatchEvent(new window.KeyboardEvent("keyup", { key: "Home" }));
|
||||
|
@ -2319,13 +2319,17 @@ public class GeckoAppShell
|
||||
/*
|
||||
* WebSMS related methods.
|
||||
*/
|
||||
@WrapForJNI(stubName = "SendMessageWrapper")
|
||||
public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
|
||||
public static void sendMessage(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify) {
|
||||
if (!SmsManager.isEnabled()) {
|
||||
return;
|
||||
}
|
||||
|
||||
SmsManager.getInstance().send(aNumber, aMessage, aRequestId);
|
||||
SmsManager.getInstance().send(aNumber, aMessage, aRequestId, aShouldNotify);
|
||||
}
|
||||
|
||||
@WrapForJNI(stubName = "SendMessageWrapper")
|
||||
public static void sendMessage(String aNumber, String aMessage, int aRequestId) {
|
||||
sendMessage(aNumber, aMessage, aRequestId, /* shouldNotify */ true);
|
||||
}
|
||||
|
||||
@WrapForJNI(stubName = "GetMessageWrapper")
|
||||
|
@ -21,6 +21,7 @@ import android.net.Uri;
|
||||
import android.os.Bundle;
|
||||
import android.os.Handler;
|
||||
import android.os.HandlerThread;
|
||||
import android.provider.Telephony;
|
||||
import android.telephony.SmsManager;
|
||||
import android.telephony.SmsMessage;
|
||||
import android.util.Log;
|
||||
@ -267,7 +268,6 @@ public class GeckoSmsManager
|
||||
extends BroadcastReceiver
|
||||
implements ISmsManager
|
||||
{
|
||||
public final static String ACTION_SMS_RECEIVED = "android.provider.Telephony.SMS_RECEIVED";
|
||||
public final static String ACTION_SMS_SENT = "org.mozilla.gecko.SMS_SENT";
|
||||
public final static String ACTION_SMS_DELIVERED = "org.mozilla.gecko.SMS_DELIVERED";
|
||||
|
||||
@ -297,12 +297,12 @@ public class GeckoSmsManager
|
||||
|
||||
private final static int kMaxMessageSize = 160;
|
||||
|
||||
private final static Uri kSmsContentUri = Uri.parse("content://sms");
|
||||
private final static Uri kSmsSentContentUri = Uri.parse("content://sms/sent");
|
||||
private final static Uri kSmsThreadsContentUri = Uri.parse("content://sms/conversations");
|
||||
private final static Uri kSmsContentUri = Telephony.Sms.Inbox.CONTENT_URI;
|
||||
private final static Uri kSmsSentContentUri = Telephony.Sms.Sent.CONTENT_URI;
|
||||
private final static Uri kSmsThreadsContentUri = Telephony.Sms.Conversations.CONTENT_URI;
|
||||
|
||||
private final static int kSmsTypeInbox = 1;
|
||||
private final static int kSmsTypeSentbox = 2;
|
||||
private final static int kSmsTypeInbox = Telephony.Sms.MESSAGE_TYPE_INBOX;
|
||||
private final static int kSmsTypeSentbox = Telephony.Sms.MESSAGE_TYPE_SENT;
|
||||
|
||||
/*
|
||||
* Keep the following state codes in syng with |DeliveryState| in:
|
||||
@ -325,15 +325,6 @@ public class GeckoSmsManager
|
||||
private final static int kDeliveryStatusPending = 2;
|
||||
private final static int kDeliveryStatusError = 3;
|
||||
|
||||
/*
|
||||
* android.provider.Telephony.Sms.STATUS_*. Duplicated because they're not
|
||||
* part of Android public API.
|
||||
*/
|
||||
private final static int kInternalDeliveryStatusNone = -1;
|
||||
private final static int kInternalDeliveryStatusComplete = 0;
|
||||
private final static int kInternalDeliveryStatusPending = 32;
|
||||
private final static int kInternalDeliveryStatusFailed = 64;
|
||||
|
||||
/*
|
||||
* Keep the following values in sync with |MessageClass| in:
|
||||
* dom/mobilemessage/Types.h
|
||||
@ -355,45 +346,91 @@ public class GeckoSmsManager
|
||||
private static final long UNSIGNED_INTEGER_MAX_VALUE = Integer.MAX_VALUE * 2L + 1L;
|
||||
|
||||
public GeckoSmsManager() {
|
||||
SmsIOThread.getInstance().start();
|
||||
if (SmsIOThread.getInstance().getState() == Thread.State.NEW) {
|
||||
SmsIOThread.getInstance().start();
|
||||
}
|
||||
}
|
||||
|
||||
private boolean isDefaultSmsApp(Context context) {
|
||||
String myPackageName = context.getPackageName();
|
||||
return Telephony.Sms.getDefaultSmsPackage(context).equals(myPackageName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
IntentFilter smsFilter = new IntentFilter();
|
||||
smsFilter.addAction(ACTION_SMS_RECEIVED);
|
||||
smsFilter.addAction(Telephony.Sms.Intents.SMS_RECEIVED_ACTION);
|
||||
smsFilter.addAction(ACTION_SMS_SENT);
|
||||
smsFilter.addAction(ACTION_SMS_DELIVERED);
|
||||
|
||||
GeckoAppShell.getContext().registerReceiver(this, smsFilter);
|
||||
}
|
||||
|
||||
/**
|
||||
* Build up the SMS message body from the SmsMessage array of received SMS
|
||||
*
|
||||
* @param msgs The SmsMessage array of the received SMS
|
||||
* @return The text message body
|
||||
*/
|
||||
private static String buildMessageBodyFromPdus(SmsMessage[] msgs) {
|
||||
if (msgs.length == 1) {
|
||||
// There is only one part, so grab the body directly.
|
||||
return replaceFormFeeds(msgs[0].getDisplayMessageBody());
|
||||
} else {
|
||||
// Build up the body from the parts.
|
||||
StringBuilder body = new StringBuilder();
|
||||
for (SmsMessage msg : msgs) {
|
||||
// getDisplayMessageBody() can NPE if mWrappedMessage inside is null.
|
||||
body.append(msg.getDisplayMessageBody());
|
||||
}
|
||||
return replaceFormFeeds(body.toString());
|
||||
}
|
||||
}
|
||||
|
||||
// Some providers send formfeeds in their messages. Convert those formfeeds to newlines.
|
||||
private static String replaceFormFeeds(String s) {
|
||||
return s == null ? "" : s.replace('\f', '\n');
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReceive(Context context, Intent intent) {
|
||||
if (intent.getAction().equals(ACTION_SMS_RECEIVED)) {
|
||||
// TODO: Try to find the receiver number to be able to populate
|
||||
// SmsMessage.receiver.
|
||||
// TODO: Get the id and the date from the stock app saved message.
|
||||
// Using the stock app saved message require us to wait for it to
|
||||
// be saved which can lead to race conditions.
|
||||
|
||||
Bundle bundle = intent.getExtras();
|
||||
|
||||
if (bundle == null) {
|
||||
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_DELIVER_ACTION) ||
|
||||
intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
|
||||
// If we're the default SMS, ignore SMS_RECEIVED intents since we'll handle
|
||||
// the SMS_DELIVER intent instead.
|
||||
if (isDefaultSmsApp(GeckoAppShell.getContext()) && intent.getAction().equals(Telephony.Sms.Intents.SMS_RECEIVED_ACTION)) {
|
||||
return;
|
||||
}
|
||||
|
||||
Object[] pdus = (Object[]) bundle.get("pdus");
|
||||
|
||||
for (int i=0; i<pdus.length; ++i) {
|
||||
SmsMessage msg = SmsMessage.createFromPdu((byte[])pdus[i]);
|
||||
|
||||
notifySmsReceived(msg.getDisplayOriginatingAddress(),
|
||||
msg.getDisplayMessageBody(),
|
||||
getGeckoMessageClass(msg.getMessageClass()),
|
||||
System.currentTimeMillis());
|
||||
// TODO: Try to find the receiver number to be able to populate
|
||||
// SmsMessage.receiver.
|
||||
SmsMessage[] messages = Telephony.Sms.Intents.getMessagesFromIntent(intent);
|
||||
if (messages == null || messages.length == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
SmsMessage sms = messages[0];
|
||||
String body = buildMessageBodyFromPdus(messages);
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
int id = 0;
|
||||
// We only need to save the message if we're the default SMS app
|
||||
if (intent.getAction().equals(Telephony.Sms.Intents.SMS_DELIVER_ACTION)) {
|
||||
id = saveReceivedMessage(context,
|
||||
sms.getDisplayOriginatingAddress(),
|
||||
body,
|
||||
sms.getTimestampMillis(),
|
||||
timestamp,
|
||||
sms.getPseudoSubject());
|
||||
}
|
||||
|
||||
notifySmsReceived(id,
|
||||
sms.getDisplayOriginatingAddress(),
|
||||
body,
|
||||
getGeckoMessageClass(sms.getMessageClass()),
|
||||
sms.getTimestampMillis(),
|
||||
timestamp);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
@ -447,7 +484,9 @@ public class GeckoSmsManager
|
||||
|
||||
if (envelope.isFailing(part)) {
|
||||
if (part == Envelope.SubParts.SENT_PART) {
|
||||
notifySmsSendFailed(envelope.getError(), bundle.getInt("requestId"));
|
||||
if (bundle.getBoolean("shouldNotify")) {
|
||||
notifySmsSendFailed(envelope.getError(), bundle.getInt("requestId"));
|
||||
}
|
||||
Log.i("GeckoSmsManager", "SMS sending failed!");
|
||||
} else {
|
||||
notifySmsDelivery(envelope.getMessageId(),
|
||||
@ -463,16 +502,24 @@ public class GeckoSmsManager
|
||||
String message = bundle.getString("message");
|
||||
long timestamp = System.currentTimeMillis();
|
||||
|
||||
int id = saveSentMessage(number, message, timestamp);
|
||||
// save message only if we're default SMS app, otherwise sendTextMessage does it for us
|
||||
int id = 0;
|
||||
if (isDefaultSmsApp(GeckoAppShell.getContext())) {
|
||||
id = saveSentMessage(number, message, timestamp);
|
||||
}
|
||||
|
||||
notifySmsSent(id, number, message, timestamp,
|
||||
bundle.getInt("requestId"));
|
||||
if (bundle.getBoolean("shouldNotify")) {
|
||||
notifySmsSent(id, number, message, timestamp, bundle.getInt("requestId"));
|
||||
}
|
||||
|
||||
envelope.setMessageId(id);
|
||||
envelope.setMessageTimestamp(timestamp);
|
||||
|
||||
Log.i("GeckoSmsManager", "SMS sending was successful!");
|
||||
} else {
|
||||
Uri id = ContentUris.withAppendedId(kSmsContentUri, envelope.getMessageId());
|
||||
updateMessageStatus(id, Telephony.Sms.STATUS_COMPLETE);
|
||||
|
||||
notifySmsDelivery(envelope.getMessageId(),
|
||||
kDeliveryStatusSuccess,
|
||||
bundle.getString("number"),
|
||||
@ -491,19 +538,22 @@ public class GeckoSmsManager
|
||||
}
|
||||
|
||||
@Override
|
||||
public void send(String aNumber, String aMessage, int aRequestId) {
|
||||
public void send(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify) {
|
||||
int envelopeId = Postman.kUnknownEnvelopeId;
|
||||
|
||||
try {
|
||||
SmsManager sm = SmsManager.getDefault();
|
||||
|
||||
Intent sentIntent = new Intent(ACTION_SMS_SENT);
|
||||
Intent deliveredIntent = new Intent(ACTION_SMS_DELIVERED);
|
||||
Intent sentIntent = new Intent(GeckoAppShell.getContext(), GeckoSmsManager.class);
|
||||
sentIntent.setAction(ACTION_SMS_SENT);
|
||||
Intent deliveredIntent = new Intent(GeckoAppShell.getContext(), GeckoSmsManager.class);
|
||||
deliveredIntent.setAction(ACTION_SMS_DELIVERED);
|
||||
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString("number", aNumber);
|
||||
bundle.putString("message", aMessage);
|
||||
bundle.putInt("requestId", aRequestId);
|
||||
bundle.putBoolean("shouldNotify", aShouldNotify);
|
||||
|
||||
if (aMessage.length() <= kMaxMessageSize) {
|
||||
envelopeId = Postman.getInstance().createEnvelope(1);
|
||||
@ -533,7 +583,7 @@ public class GeckoSmsManager
|
||||
pendingIntentGuid.incrementAndGet(), deliveredIntent,
|
||||
PendingIntent.FLAG_CANCEL_CURRENT);
|
||||
|
||||
sm.sendTextMessage(aNumber, "", aMessage,
|
||||
sm.sendTextMessage(aNumber, null, aMessage,
|
||||
sentPendingIntent, deliveredPendingIntent);
|
||||
} else {
|
||||
ArrayList<String> parts = sm.divideMessage(aMessage);
|
||||
@ -562,7 +612,7 @@ public class GeckoSmsManager
|
||||
);
|
||||
}
|
||||
|
||||
sm.sendMultipartTextMessage(aNumber, "", parts, sentPendingIntents,
|
||||
sm.sendMultipartTextMessage(aNumber, null, parts, sentPendingIntents,
|
||||
deliveredPendingIntents);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
@ -579,11 +629,13 @@ public class GeckoSmsManager
|
||||
public int saveSentMessage(String aRecipient, String aBody, long aDate) {
|
||||
try {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put("address", aRecipient);
|
||||
values.put("body", aBody);
|
||||
values.put("date", aDate);
|
||||
values.put(Telephony.Sms.ADDRESS, aRecipient);
|
||||
values.put(Telephony.Sms.BODY, aBody);
|
||||
values.put(Telephony.Sms.DATE, aDate);
|
||||
// Always 'PENDING' because we always request status report.
|
||||
values.put("status", kInternalDeliveryStatusPending);
|
||||
values.put(Telephony.Sms.STATUS, Telephony.Sms.STATUS_PENDING);
|
||||
values.put(Telephony.Sms.SEEN, 0);
|
||||
values.put(Telephony.Sms.READ, 0);
|
||||
|
||||
ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
|
||||
Uri uri = cr.insert(kSmsSentContentUri, values);
|
||||
@ -606,6 +658,41 @@ public class GeckoSmsManager
|
||||
}
|
||||
}
|
||||
|
||||
public void updateMessageStatus(Uri aId, int aStatus) {
|
||||
ContentValues values = new ContentValues(1);
|
||||
values.put(Telephony.Sms.STATUS, aStatus);
|
||||
|
||||
ContentResolver cr = GeckoAppShell.getContext().getContentResolver();
|
||||
if (cr.update(aId, values, null, null) != 1) {
|
||||
Log.i("GeckoSmsManager", "Failed to update message status!");
|
||||
}
|
||||
}
|
||||
|
||||
public int saveReceivedMessage(Context aContext, String aSender, String aBody, long aDateSent, long aDate, String aSubject) {
|
||||
ContentValues values = new ContentValues();
|
||||
values.put(Telephony.Sms.Inbox.ADDRESS, aSender);
|
||||
values.put(Telephony.Sms.Inbox.BODY, aBody);
|
||||
values.put(Telephony.Sms.Inbox.DATE_SENT, aDateSent);
|
||||
values.put(Telephony.Sms.Inbox.DATE, aDate);
|
||||
values.put(Telephony.Sms.Inbox.STATUS, Telephony.Sms.STATUS_COMPLETE);
|
||||
values.put(Telephony.Sms.Inbox.READ, 0);
|
||||
values.put(Telephony.Sms.Inbox.SEEN, 0);
|
||||
|
||||
ContentResolver cr = aContext.getContentResolver();
|
||||
Uri uri = cr.insert(kSmsContentUri, values);
|
||||
|
||||
long id = ContentUris.parseId(uri);
|
||||
|
||||
// The DOM API takes a 32bits unsigned int for the id. It's unlikely that
|
||||
// we happen to need more than that but it doesn't cost to check.
|
||||
if (id > UNSIGNED_INTEGER_MAX_VALUE) {
|
||||
Log.i("GeckoSmsManager", "The id we received is higher than the higher allowed value.");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return (int)id;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void getMessage(int aMessageId, int aRequestId) {
|
||||
class GetMessageRunnable implements Runnable {
|
||||
@ -1079,13 +1166,13 @@ public class GeckoSmsManager
|
||||
}
|
||||
|
||||
private int getGeckoDeliveryStatus(int aDeliveryStatus) {
|
||||
if (aDeliveryStatus == kInternalDeliveryStatusNone) {
|
||||
if (aDeliveryStatus == Telephony.Sms.STATUS_NONE) {
|
||||
return kDeliveryStatusNotApplicable;
|
||||
}
|
||||
if (aDeliveryStatus >= kInternalDeliveryStatusFailed) {
|
||||
if (aDeliveryStatus >= Telephony.Sms.STATUS_FAILED) {
|
||||
return kDeliveryStatusError;
|
||||
}
|
||||
if (aDeliveryStatus >= kInternalDeliveryStatusPending) {
|
||||
if (aDeliveryStatus >= Telephony.Sms.STATUS_PENDING) {
|
||||
return kDeliveryStatusPending;
|
||||
}
|
||||
return kDeliveryStatusSuccess;
|
||||
@ -1127,7 +1214,7 @@ public class GeckoSmsManager
|
||||
}
|
||||
|
||||
@WrapForJNI
|
||||
private static native void notifySmsReceived(String aSender, String aBody, int aMessageClass, long aTimestamp);
|
||||
public static native void notifySmsReceived(int aId, String aSender, String aBody, int aMessageClass, long aSentTimestamp, long aTimestamp);
|
||||
@WrapForJNI
|
||||
private static native void notifySmsSent(int aId, String aReceiver, String aBody, long aTimestamp, int aRequestId);
|
||||
@WrapForJNI
|
||||
|
@ -28,7 +28,7 @@ public class SmsManager {
|
||||
void stop();
|
||||
void shutdown();
|
||||
|
||||
void send(String aNumber, String aMessage, int aRequestId);
|
||||
void send(String aNumber, String aMessage, int aRequestId, boolean aShouldNotify);
|
||||
void getMessage(int aMessageId, int aRequestId);
|
||||
void deleteMessage(int aMessageId, int aRequestId);
|
||||
void markMessageRead(int aMessageId, boolean aValue, boolean aSendReadReport, int aRequestId);
|
||||
|
@ -362,16 +362,16 @@ Java_org_mozilla_gecko_GeckoJavaSampler_getProfilerTime(JNIEnv * arg0, jclass ar
|
||||
|
||||
#ifdef JNI_STUBS
|
||||
|
||||
typedef void (*Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t)(JNIEnv *, jclass, jstring, jstring, jint, jlong);
|
||||
typedef void (*Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t)(JNIEnv *, jclass, jint, jstring, jstring, jint, jlong, jlong);
|
||||
static Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived_t f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived;
|
||||
extern "C" NS_EXPORT void MOZ_JNICALL
|
||||
Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv * arg0, jclass arg1, jstring arg2, jstring arg3, jint arg4, jlong arg5) {
|
||||
Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(JNIEnv * arg0, jclass arg1, jint arg2, jstring arg3, jstring arg4, jint arg5, jlong arg6, jlong arg7) {
|
||||
if (!f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived) {
|
||||
arg0->ThrowNew(arg0->FindClass("java/lang/UnsupportedOperationException"),
|
||||
"JNI Function called before it was loaded");
|
||||
return ;
|
||||
}
|
||||
f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(arg0, arg1, arg2, arg3, arg4, arg5);
|
||||
f_Java_org_mozilla_gecko_GeckoSmsManager_notifySmsReceived(arg0, arg1, arg2, arg3, arg4, arg5, arg6, arg7);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
@ -2391,13 +2391,15 @@ public:
|
||||
typedef void ReturnType;
|
||||
typedef void SetterType;
|
||||
typedef mozilla::jni::Args<
|
||||
int32_t,
|
||||
mozilla::jni::String::Param,
|
||||
mozilla::jni::String::Param,
|
||||
int32_t,
|
||||
int64_t,
|
||||
int64_t> Args;
|
||||
static constexpr char name[] = "notifySmsReceived";
|
||||
static constexpr char signature[] =
|
||||
"(Ljava/lang/String;Ljava/lang/String;IJ)V";
|
||||
"(ILjava/lang/String;Ljava/lang/String;IJJ)V";
|
||||
static const bool isStatic = true;
|
||||
static const bool isMultithreaded = false;
|
||||
static const mozilla::jni::ExceptionMode exceptionMode =
|
||||
|
Loading…
Reference in New Issue
Block a user