mirror of
https://github.com/oxen-io/session-android.git
synced 2025-03-15 12:30:53 +00:00
Add support for "delivery notifications." Currently SMS-only.
This commit is contained in:
parent
118560cf0d
commit
5cb02445e8
BIN
res/drawable-hdpi/ic_sms_mms_delivered.png
Normal file
BIN
res/drawable-hdpi/ic_sms_mms_delivered.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 927 B |
BIN
res/drawable-mdpi/ic_sms_mms_delivered.png
Normal file
BIN
res/drawable-mdpi/ic_sms_mms_delivered.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 692 B |
BIN
res/drawable-xhdpi/ic_sms_mms_delivered.png
Normal file
BIN
res/drawable-xhdpi/ic_sms_mms_delivered.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 1.1 KiB |
@ -96,6 +96,13 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="left">
|
android:gravity="left">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/delivered_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="3dip"
|
||||||
|
android:src="@drawable/ic_sms_mms_delivered"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView android:id="@+id/group_message_status"
|
<TextView android:id="@+id/group_message_status"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
@ -105,8 +112,7 @@
|
|||||||
android:textColor="#ffcccccc"
|
android:textColor="#ffcccccc"
|
||||||
android:visibility="gone"
|
android:visibility="gone"
|
||||||
android:layout_marginRight="8dip"
|
android:layout_marginRight="8dip"
|
||||||
android:paddingTop="1dip"/>
|
android:paddingTop="1dip"/>
|
||||||
|
|
||||||
|
|
||||||
<TextView android:id="@+id/conversation_item_date"
|
<TextView android:id="@+id/conversation_item_date"
|
||||||
android:autoLink="all"
|
android:autoLink="all"
|
||||||
|
@ -118,6 +118,13 @@
|
|||||||
android:orientation="horizontal"
|
android:orientation="horizontal"
|
||||||
android:gravity="right">
|
android:gravity="right">
|
||||||
|
|
||||||
|
<ImageView android:id="@+id/delivered_indicator"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:paddingRight="3dip"
|
||||||
|
android:src="@drawable/ic_sms_mms_delivered"
|
||||||
|
android:visibility="gone" />
|
||||||
|
|
||||||
<TextView android:id="@+id/group_message_status"
|
<TextView android:id="@+id/group_message_status"
|
||||||
android:layout_width="wrap_content"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
@ -1,22 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<!--
|
|
||||||
/*
|
|
||||||
* Copyright (C) 2007-2008 Esmertec AG.
|
|
||||||
* Copyright (C) 2007-2008 The Android Open Source Project
|
|
||||||
*
|
|
||||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
* you may not use this file except in compliance with the License.
|
|
||||||
* You may obtain a copy of the License at
|
|
||||||
*
|
|
||||||
* http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
*
|
|
||||||
* Unless required by applicable law or agreed to in writing, software
|
|
||||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
* See the License for the specific language governing permissions and
|
|
||||||
* limitations under the License.
|
|
||||||
*/
|
|
||||||
-->
|
|
||||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<PreferenceCategory android:title="@string/preferences__use_settings">
|
<PreferenceCategory android:title="@string/preferences__use_settings">
|
||||||
<CheckBoxPreference android:defaultValue="true"
|
<CheckBoxPreference android:defaultValue="true"
|
||||||
@ -31,6 +14,19 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
|
<PreferenceCategory android:title="Delivery Reports">
|
||||||
|
<CheckBoxPreference android:defaultValue="false"
|
||||||
|
android:key="pref_delivery_report_sms"
|
||||||
|
android:summary="Request a delivery report for each SMS message you send"
|
||||||
|
android:title="SMS delivery reports" />
|
||||||
|
|
||||||
|
<CheckBoxPreference android:defaultValue="false"
|
||||||
|
android:key="pref_delivery_report_mms"
|
||||||
|
android:summary="Request a delivery report for each MMS message you send"
|
||||||
|
android:enabled="false"
|
||||||
|
android:title="MMS delivery reports" />
|
||||||
|
</PreferenceCategory>
|
||||||
|
|
||||||
<PreferenceCategory android:title="@string/preferences__input_settings">
|
<PreferenceCategory android:title="@string/preferences__input_settings">
|
||||||
<CheckBoxPreference android:defaultValue="false"
|
<CheckBoxPreference android:defaultValue="false"
|
||||||
android:key="pref_enter_sends"
|
android:key="pref_enter_sends"
|
||||||
|
@ -80,6 +80,9 @@ public class ApplicationPreferencesActivity extends SherlockPreferenceActivity {
|
|||||||
public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy";
|
public static final String MMSC_PROXY_HOST_PREF = "pref_apn_mms_proxy";
|
||||||
public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port";
|
public static final String MMSC_PROXY_PORT_PREF = "pref_apn_mms_proxy_port";
|
||||||
|
|
||||||
|
public static final String SMS_DELIVERY_REPORT_PREF = "pref_delivery_report_sms";
|
||||||
|
public static final String MMS_DELIVERY_REPORT_PREF = "pref_delivery_report_mms";
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onCreate(Bundle icicle) {
|
protected void onCreate(Bundle icicle) {
|
||||||
super.onCreate(icicle);
|
super.onCreate(icicle);
|
||||||
|
@ -208,6 +208,7 @@ public class ConversationAdapter extends CursorAdapter {
|
|||||||
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
|
long dateReceived = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_RECEIVED));
|
||||||
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_SENT));
|
long dateSent = cursor.getLong(cursor.getColumnIndexOrThrow(MmsSmsDatabase.DATE_SENT));
|
||||||
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
|
long type = cursor.getLong(cursor.getColumnIndexOrThrow(SmsDatabase.TYPE));
|
||||||
|
int status = cursor.getInt(cursor.getColumnIndexOrThrow(SmsDatabase.STATUS));
|
||||||
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
|
String body = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.BODY));
|
||||||
String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
|
String address = cursor.getString(cursor.getColumnIndexOrThrow(SmsDatabase.ADDRESS));
|
||||||
Recipient recipient = getIndividualRecipientFor(address);
|
Recipient recipient = getIndividualRecipientFor(address);
|
||||||
@ -226,7 +227,7 @@ public class ConversationAdapter extends CursorAdapter {
|
|||||||
|
|
||||||
SmsMessageRecord messageRecord = new SmsMessageRecord(context, messageId, recipients,
|
SmsMessageRecord messageRecord = new SmsMessageRecord(context, messageId, recipients,
|
||||||
recipient, dateSent, dateReceived,
|
recipient, dateSent, dateReceived,
|
||||||
type, threadId, groupData);
|
type, threadId, status, groupData);
|
||||||
|
|
||||||
if (body == null) {
|
if (body == null) {
|
||||||
body = "";
|
body = "";
|
||||||
|
@ -84,6 +84,7 @@ public class ConversationItem extends LinearLayout {
|
|||||||
private ImageView failedImage;
|
private ImageView failedImage;
|
||||||
private ImageView keyImage;
|
private ImageView keyImage;
|
||||||
private ImageView contactPhoto;
|
private ImageView contactPhoto;
|
||||||
|
private ImageView deliveredImage;
|
||||||
|
|
||||||
private ImageView mmsThumbnail;
|
private ImageView mmsThumbnail;
|
||||||
private Button mmsDownloadButton;
|
private Button mmsDownloadButton;
|
||||||
@ -118,6 +119,7 @@ public class ConversationItem extends LinearLayout {
|
|||||||
this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button);
|
this.mmsDownloadButton = (Button) findViewById(R.id.mms_download_button);
|
||||||
this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
|
this.mmsDownloadingLabel = (TextView) findViewById(R.id.mms_label_downloading);
|
||||||
this.contactPhoto = (ImageView)findViewById(R.id.contact_photo);
|
this.contactPhoto = (ImageView)findViewById(R.id.contact_photo);
|
||||||
|
this.deliveredImage = (ImageView)findViewById(R.id.delivered_indicator);
|
||||||
|
|
||||||
setOnClickListener(clickListener);
|
setOnClickListener(clickListener);
|
||||||
this.failedImage.setOnClickListener(failedIconClickListener);
|
this.failedImage.setOnClickListener(failedIconClickListener);
|
||||||
@ -182,6 +184,7 @@ public class ConversationItem extends LinearLayout {
|
|||||||
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE);
|
failedImage.setVisibility(messageRecord.isFailed() ? View.VISIBLE : View.GONE);
|
||||||
secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
|
secureImage.setVisibility(messageRecord.isSecure() ? View.VISIBLE : View.GONE);
|
||||||
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
|
keyImage.setVisibility(messageRecord.isKeyExchange() ? View.VISIBLE : View.GONE);
|
||||||
|
deliveredImage.setVisibility(!messageRecord.isKeyExchange() && messageRecord.isDelivered() ? View.VISIBLE : View.GONE);
|
||||||
|
|
||||||
mmsThumbnail.setVisibility(View.GONE);
|
mmsThumbnail.setVisibility(View.GONE);
|
||||||
mmsDownloadButton.setVisibility(View.GONE);
|
mmsDownloadButton.setVisibility(View.GONE);
|
||||||
|
@ -93,7 +93,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
String[] projection = {"_id", "body", "type", "address", "subject",
|
String[] projection = {"_id", "body", "type", "address", "subject",
|
||||||
"normalized_date_sent AS date_sent",
|
"normalized_date_sent AS date_sent",
|
||||||
"normalized_date_received AS date_received",
|
"normalized_date_received AS date_received",
|
||||||
"m_type", "msg_box", "transport_type"};
|
"m_type", "msg_box", "status", "transport_type"};
|
||||||
String order = "normalized_date_received ASC";
|
String order = "normalized_date_received ASC";
|
||||||
String selection = "thread_id = " + threadId;
|
String selection = "thread_id = " + threadId;
|
||||||
|
|
||||||
@ -135,8 +135,8 @@ public class MmsSmsDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private Cursor queryTables(String[] projection, String selection, String order, String groupBy, String limit) {
|
private Cursor queryTables(String[] projection, String selection, String order, String groupBy, String limit) {
|
||||||
String[] mmsProjection = {"date * 1000 AS normalized_date_sent", "date_received * 1000 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
String[] mmsProjection = {"date * 1000 AS normalized_date_sent", "date_received * 1000 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "status", "transport_type"};
|
||||||
String[] smsProjection = {"date_sent * 1 AS normalized_date_sent", "date * 1 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "transport_type"};
|
String[] smsProjection = {"date_sent * 1 AS normalized_date_sent", "date * 1 AS normalized_date_received", "_id", "body", "read", "thread_id", "type", "address", "subject", "date", "m_type", "msg_box", "status", "transport_type"};
|
||||||
|
|
||||||
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder mmsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
SQLiteQueryBuilder smsQueryBuilder = new SQLiteQueryBuilder();
|
||||||
@ -166,6 +166,7 @@ public class MmsSmsDatabase extends Database {
|
|||||||
smsColumnsPresent.add("date");
|
smsColumnsPresent.add("date");
|
||||||
smsColumnsPresent.add("read");
|
smsColumnsPresent.add("read");
|
||||||
smsColumnsPresent.add("thread_id");
|
smsColumnsPresent.add("thread_id");
|
||||||
|
smsColumnsPresent.add("status");
|
||||||
|
|
||||||
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 2, "mms", selection, null, null, null);
|
String mmsSubQuery = mmsQueryBuilder.buildUnionSubQuery("transport_type", mmsProjection, mmsColumnsPresent, 2, "mms", selection, null, null, null);
|
||||||
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 2, "sms", selection, null, null, null);
|
String smsSubQuery = smsQueryBuilder.buildUnionSubQuery("transport_type", smsProjection, smsColumnsPresent, 2, "sms", selection, null, null, null);
|
||||||
|
@ -170,6 +170,16 @@ public class SmsDatabase extends Database {
|
|||||||
updateType(id, Types.SENT_TYPE);
|
updateType(id, Types.SENT_TYPE);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void markStatus(long id, int status) {
|
||||||
|
Log.w("MessageDatabase", "Updating ID: " + id + " to status: " + status);
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(STATUS, status);
|
||||||
|
|
||||||
|
SQLiteDatabase db = databaseHelper.getWritableDatabase();
|
||||||
|
db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {id+""});
|
||||||
|
notifyConversationListeners(getThreadIdForMessage(id));
|
||||||
|
}
|
||||||
|
|
||||||
public void markAsSentFailed(long id) {
|
public void markAsSentFailed(long id) {
|
||||||
updateType(id, Types.FAILED_TYPE);
|
updateType(id, Types.FAILED_TYPE);
|
||||||
}
|
}
|
||||||
@ -317,6 +327,13 @@ public class SmsDatabase extends Database {
|
|||||||
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
" VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static class Status {
|
||||||
|
public static final int STATUS_NONE = -1;
|
||||||
|
public static final int STATUS_COMPLETE = 0;
|
||||||
|
public static final int STATUS_PENDING = 32;
|
||||||
|
public static final int STATUS_FAILED = 64;
|
||||||
|
}
|
||||||
|
|
||||||
public static class Types {
|
public static class Types {
|
||||||
public static final int INBOX_TYPE = 1;
|
public static final int INBOX_TYPE = 1;
|
||||||
public static final int SENT_TYPE = 2;
|
public static final int SENT_TYPE = 2;
|
||||||
|
@ -46,7 +46,8 @@ public class MediaMmsMessageRecord extends MessageRecord {
|
|||||||
long threadId, SlideDeck slideDeck, long mailbox,
|
long threadId, SlideDeck slideDeck, long mailbox,
|
||||||
GroupData groupData)
|
GroupData groupData)
|
||||||
{
|
{
|
||||||
super(id, recipients, individualRecipient, dateSent, dateReceived, threadId, groupData);
|
super(id, recipients, individualRecipient, dateSent, dateReceived,
|
||||||
|
threadId, DELIVERY_STATUS_NONE, groupData);
|
||||||
this.slideDeck = slideDeck;
|
this.slideDeck = slideDeck;
|
||||||
this.mailbox = mailbox;
|
this.mailbox = mailbox;
|
||||||
|
|
||||||
|
@ -29,18 +29,26 @@ import org.thoughtcrime.securesms.recipients.Recipients;
|
|||||||
*/
|
*/
|
||||||
public abstract class MessageRecord extends DisplayRecord {
|
public abstract class MessageRecord extends DisplayRecord {
|
||||||
|
|
||||||
|
public static final int DELIVERY_STATUS_NONE = 0;
|
||||||
|
public static final int DELIVERY_STATUS_RECEIVED = 1;
|
||||||
|
public static final int DELIVERY_STATUS_PENDING = 2;
|
||||||
|
public static final int DELIVERY_STATUS_FAILED = 3;
|
||||||
|
|
||||||
private final Recipient individualRecipient;
|
private final Recipient individualRecipient;
|
||||||
private final long id;
|
private final long id;
|
||||||
|
private final int deliveryStatus;
|
||||||
private final GroupData groupData;
|
private final GroupData groupData;
|
||||||
|
|
||||||
public MessageRecord(long id, Recipients recipients,
|
public MessageRecord(long id, Recipients recipients,
|
||||||
Recipient individualRecipient,
|
Recipient individualRecipient,
|
||||||
long dateSent, long dateReceived,
|
long dateSent, long dateReceived,
|
||||||
long threadId, GroupData groupData)
|
long threadId, int deliveryStatus,
|
||||||
|
GroupData groupData)
|
||||||
{
|
{
|
||||||
super(recipients, dateSent, dateReceived, threadId);
|
super(recipients, dateSent, dateReceived, threadId);
|
||||||
this.id = id;
|
this.id = id;
|
||||||
this.individualRecipient = individualRecipient;
|
this.individualRecipient = individualRecipient;
|
||||||
|
this.deliveryStatus = deliveryStatus;
|
||||||
this.groupData = groupData;
|
this.groupData = groupData;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -58,6 +66,14 @@ public abstract class MessageRecord extends DisplayRecord {
|
|||||||
return id;
|
return id;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public int getDeliveryStatus() {
|
||||||
|
return deliveryStatus;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isDelivered() {
|
||||||
|
return getDeliveryStatus() == DELIVERY_STATUS_RECEIVED;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isStaleKeyExchange() {
|
public boolean isStaleKeyExchange() {
|
||||||
return this.staleKeyExchange;
|
return this.staleKeyExchange;
|
||||||
}
|
}
|
||||||
|
@ -41,7 +41,9 @@ public class NotificationMmsMessageRecord extends MessageRecord {
|
|||||||
byte[] contentLocation, long messageSize, long expiry,
|
byte[] contentLocation, long messageSize, long expiry,
|
||||||
int status, byte[] transactionId)
|
int status, byte[] transactionId)
|
||||||
{
|
{
|
||||||
super(id, recipients, individualRecipient, dateSent, dateReceived, threadId, null);
|
super(id, recipients, individualRecipient, dateSent, dateReceived,
|
||||||
|
threadId, DELIVERY_STATUS_NONE, null);
|
||||||
|
|
||||||
this.contentLocation = contentLocation;
|
this.contentLocation = contentLocation;
|
||||||
this.messageSize = messageSize;
|
this.messageSize = messageSize;
|
||||||
this.expiry = expiry;
|
this.expiry = expiry;
|
||||||
|
@ -36,19 +36,18 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
|
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final long type;
|
private final long type;
|
||||||
private final long dateSent;
|
|
||||||
|
|
||||||
public SmsMessageRecord(Context context, long id,
|
public SmsMessageRecord(Context context, long id,
|
||||||
Recipients recipients,
|
Recipients recipients,
|
||||||
Recipient individualRecipient,
|
Recipient individualRecipient,
|
||||||
long dateSent, long dateReceived,
|
long dateSent, long dateReceived,
|
||||||
long type, long threadId,
|
long type, long threadId,
|
||||||
GroupData groupData)
|
int status, GroupData groupData)
|
||||||
{
|
{
|
||||||
super(id, recipients, individualRecipient, dateSent, dateReceived, threadId, groupData);
|
super(id, recipients, individualRecipient, dateSent, dateReceived,
|
||||||
|
threadId, getGenericDeliveryStatus(status), groupData);
|
||||||
this.context = context.getApplicationContext();
|
this.context = context.getApplicationContext();
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.dateSent = dateSent;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getType() {
|
public long getType() {
|
||||||
@ -76,7 +75,8 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isFailed() {
|
public boolean isFailed() {
|
||||||
return SmsDatabase.Types.isFailedMessageType(getType());
|
return SmsDatabase.Types.isFailedMessageType(getType()) ||
|
||||||
|
getDeliveryStatus() == DELIVERY_STATUS_FAILED;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -99,4 +99,15 @@ public class SmsMessageRecord extends MessageRecord {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static int getGenericDeliveryStatus(int status) {
|
||||||
|
if (status == SmsDatabase.Status.STATUS_NONE) {
|
||||||
|
return MessageRecord.DELIVERY_STATUS_NONE;
|
||||||
|
} else if (status >= SmsDatabase.Status.STATUS_FAILED) {
|
||||||
|
return MessageRecord.DELIVERY_STATUS_FAILED;
|
||||||
|
} else if (status >= SmsDatabase.Status.STATUS_PENDING) {
|
||||||
|
return MessageRecord.DELIVERY_STATUS_PENDING;
|
||||||
|
} else {
|
||||||
|
return MessageRecord.DELIVERY_STATUS_RECEIVED;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,6 +47,7 @@ public class SendReceiveService extends Service {
|
|||||||
|
|
||||||
public static final String SEND_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_SMS_ACTION";
|
public static final String SEND_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_SMS_ACTION";
|
||||||
public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION";
|
public static final String SENT_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SENT_SMS_ACTION";
|
||||||
|
public static final String DELIVERED_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DELIVERED_SMS_ACTION";
|
||||||
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
|
public static final String RECEIVE_SMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_SMS_ACTION";
|
||||||
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
|
public static final String SEND_MMS_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_ACTION";
|
||||||
public static final String SEND_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION";
|
public static final String SEND_MMS_CONNECTIVITY_ACTION = "org.thoughtcrime.securesms.SendReceiveService.SEND_MMS_CONNECTIVITY_ACTION";
|
||||||
@ -93,6 +94,8 @@ public class SendReceiveService extends Service {
|
|||||||
scheduleIntent(RECEIVE_SMS, intent);
|
scheduleIntent(RECEIVE_SMS, intent);
|
||||||
else if (intent.getAction().equals(SENT_SMS_ACTION))
|
else if (intent.getAction().equals(SENT_SMS_ACTION))
|
||||||
scheduleIntent(RECEIVE_SMS, intent);
|
scheduleIntent(RECEIVE_SMS, intent);
|
||||||
|
else if (intent.getAction().equals(DELIVERED_SMS_ACTION))
|
||||||
|
scheduleIntent(RECEIVE_SMS, intent);
|
||||||
else if (intent.getAction().equals(SEND_MMS_ACTION) || intent.getAction().equals(SEND_MMS_CONNECTIVITY_ACTION))
|
else if (intent.getAction().equals(SEND_MMS_ACTION) || intent.getAction().equals(SEND_MMS_CONNECTIVITY_ACTION))
|
||||||
scheduleSecretRequiredIntent(SEND_MMS, intent);
|
scheduleSecretRequiredIntent(SEND_MMS, intent);
|
||||||
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
|
else if (intent.getAction().equals(RECEIVE_MMS_ACTION))
|
||||||
@ -191,8 +194,8 @@ public class SendReceiveService extends Service {
|
|||||||
|
|
||||||
public void run() {
|
public void run() {
|
||||||
switch (what) {
|
switch (what) {
|
||||||
case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
|
case RECEIVE_SMS: smsReceiver.process(masterSecret, intent); return;
|
||||||
case SEND_SMS: smsSender.process(masterSecret, intent); return;
|
case SEND_SMS: smsSender.process(masterSecret, intent); return;
|
||||||
case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
|
case RECEIVE_MMS: mmsReceiver.process(masterSecret, intent); return;
|
||||||
case SEND_MMS: mmsSender.process(masterSecret, intent); return;
|
case SEND_MMS: mmsSender.process(masterSecret, intent); return;
|
||||||
case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
|
case DOWNLOAD_MMS: mmsDownloader.process(masterSecret, intent); return;
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
/**
|
/**
|
||||||
* Copyright (C) 2011 Whisper Systems
|
* Copyright (C) 2011 Whisper Systems
|
||||||
*
|
*
|
||||||
* This program is free software: you can redistribute it and/or modify
|
* This program is free software: you can redistribute it and/or modify
|
||||||
* it under the terms of the GNU General Public License as published by
|
* it under the terms of the GNU General Public License as published by
|
||||||
* the Free Software Foundation, either version 3 of the License, or
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
@ -10,14 +10,12 @@
|
|||||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
* GNU General Public License for more details.
|
* GNU General Public License for more details.
|
||||||
*
|
*
|
||||||
* You should have received a copy of the GNU General Public License
|
* You should have received a copy of the GNU General Public License
|
||||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
*/
|
*/
|
||||||
package org.thoughtcrime.securesms.service;
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
|
||||||
|
|
||||||
import android.content.BroadcastReceiver;
|
import android.content.BroadcastReceiver;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -26,6 +24,8 @@ import android.preference.PreferenceManager;
|
|||||||
import android.telephony.SmsMessage;
|
import android.telephony.SmsMessage;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.protocol.WirePrefix;
|
||||||
|
|
||||||
public class SmsListener extends BroadcastReceiver {
|
public class SmsListener extends BroadcastReceiver {
|
||||||
|
|
||||||
private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
|
private static final String SMS_RECEIVED_ACTION = "android.provider.Telephony.SMS_RECEIVED";
|
||||||
@ -40,58 +40,58 @@ public class SmsListener extends BroadcastReceiver {
|
|||||||
if (messageBody.startsWith("Sparebank1://otp?")) {
|
if (messageBody.startsWith("Sparebank1://otp?")) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Sprint Visual Voicemail
|
// Sprint Visual Voicemail
|
||||||
return
|
return
|
||||||
message.getOriginatingAddress().length() < 7 &&
|
message.getOriginatingAddress().length() < 7 &&
|
||||||
(messageBody.startsWith("//ANDROID:") || messageBody.startsWith("//Android:") ||
|
(messageBody.startsWith("//ANDROID:") || messageBody.startsWith("//Android:") ||
|
||||||
messageBody.startsWith("//android:") || messageBody.startsWith("//BREW:"));
|
messageBody.startsWith("//android:") || messageBody.startsWith("//BREW:"));
|
||||||
}
|
}
|
||||||
|
|
||||||
private SmsMessage getSmsMessageFromIntent(Intent intent) {
|
private SmsMessage getSmsMessageFromIntent(Intent intent) {
|
||||||
Bundle bundle = intent.getExtras();
|
Bundle bundle = intent.getExtras();
|
||||||
Object[] pdus = (Object[])bundle.get("pdus");
|
Object[] pdus = (Object[])bundle.get("pdus");
|
||||||
|
|
||||||
if (pdus == null || pdus.length == 0)
|
if (pdus == null || pdus.length == 0)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
return SmsMessage.createFromPdu((byte[])pdus[0]);
|
return SmsMessage.createFromPdu((byte[])pdus[0]);
|
||||||
}
|
}
|
||||||
|
|
||||||
private String getSmsMessageBodyFromIntent(Intent intent) {
|
private String getSmsMessageBodyFromIntent(Intent intent) {
|
||||||
Bundle bundle = intent.getExtras();
|
Bundle bundle = intent.getExtras();
|
||||||
Object[] pdus = (Object[])bundle.get("pdus");
|
Object[] pdus = (Object[])bundle.get("pdus");
|
||||||
StringBuilder bodyBuilder = new StringBuilder();
|
StringBuilder bodyBuilder = new StringBuilder();
|
||||||
|
|
||||||
if (pdus == null)
|
if (pdus == null)
|
||||||
return null;
|
return null;
|
||||||
|
|
||||||
for (int i=0;i<pdus.length;i++)
|
for (int i=0;i<pdus.length;i++)
|
||||||
bodyBuilder.append(SmsMessage.createFromPdu((byte[])pdus[i]).getDisplayMessageBody());
|
bodyBuilder.append(SmsMessage.createFromPdu((byte[])pdus[i]).getDisplayMessageBody());
|
||||||
|
|
||||||
return bodyBuilder.toString();
|
return bodyBuilder.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean isRelevent(Context context, Intent intent) {
|
private boolean isRelevent(Context context, Intent intent) {
|
||||||
SmsMessage message = getSmsMessageFromIntent(intent);
|
SmsMessage message = getSmsMessageFromIntent(intent);
|
||||||
String messageBody = getSmsMessageBodyFromIntent(intent);
|
String messageBody = getSmsMessageBodyFromIntent(intent);
|
||||||
|
|
||||||
if (message == null && messageBody == null)
|
if (message == null && messageBody == null)
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (isExemption(message, messageBody))
|
if (isExemption(message, messageBody))
|
||||||
return false;
|
return false;
|
||||||
|
|
||||||
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("pref_all_sms", true))
|
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("pref_all_sms", true))
|
||||||
return true;
|
return true;
|
||||||
|
|
||||||
return WirePrefix.isEncryptedMessage(messageBody) || WirePrefix.isKeyExchange(messageBody);
|
return WirePrefix.isEncryptedMessage(messageBody) || WirePrefix.isKeyExchange(messageBody);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void onReceive(Context context, Intent intent) {
|
public void onReceive(Context context, Intent intent) {
|
||||||
Log.w("SMSListener", "Got SMS broadcast...");
|
Log.w("SMSListener", "Got SMS broadcast...");
|
||||||
|
|
||||||
if (intent.getAction().equals(SMS_RECEIVED_ACTION) && isRelevent(context, intent)) {
|
if (intent.getAction().equals(SMS_RECEIVED_ACTION) && isRelevent(context, intent)) {
|
||||||
intent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
|
intent.setAction(SendReceiveService.RECEIVE_SMS_ACTION);
|
||||||
intent.putExtra("ResultCode", this.getResultCode());
|
intent.putExtra("ResultCode", this.getResultCode());
|
||||||
@ -103,6 +103,10 @@ public class SmsListener extends BroadcastReceiver {
|
|||||||
intent.putExtra("ResultCode", this.getResultCode());
|
intent.putExtra("ResultCode", this.getResultCode());
|
||||||
intent.setClass(context, SendReceiveService.class);
|
intent.setClass(context, SendReceiveService.class);
|
||||||
context.startService(intent);
|
context.startService(intent);
|
||||||
|
} else if (intent.getAction().equals(SendReceiveService.DELIVERED_SMS_ACTION)) {
|
||||||
|
intent.putExtra("ResultCode", this.getResultCode());
|
||||||
|
intent.setClass(context, SendReceiveService.class);
|
||||||
|
context.startService(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -176,10 +176,26 @@ public class SmsReceiver {
|
|||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private void handleDeliveredMessage(Intent intent) {
|
||||||
|
long messageId = intent.getLongExtra("message_id", -1);
|
||||||
|
long type = intent.getLongExtra("type", -1);
|
||||||
|
byte[] pdu = intent.getByteArrayExtra("pdu");
|
||||||
|
String format = intent.getStringExtra("format");
|
||||||
|
SmsMessage message = SmsMessage.createFromPdu(pdu);
|
||||||
|
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
DatabaseFactory.getSmsDatabase(context).markStatus(messageId, message.getStatus());
|
||||||
|
}
|
||||||
|
|
||||||
public void process(MasterSecret masterSecret, Intent intent) {
|
public void process(MasterSecret masterSecret, Intent intent) {
|
||||||
if (intent.getAction().equals(SendReceiveService.RECEIVE_SMS_ACTION))
|
if (intent.getAction().equals(SendReceiveService.RECEIVE_SMS_ACTION))
|
||||||
handleReceiveMessage(masterSecret, intent);
|
handleReceiveMessage(masterSecret, intent);
|
||||||
else if (intent.getAction().equals(SendReceiveService.SENT_SMS_ACTION))
|
else if (intent.getAction().equals(SendReceiveService.SENT_SMS_ACTION))
|
||||||
handleSentMessage(intent);
|
handleSentMessage(intent);
|
||||||
|
else if (intent.getAction().equals(SendReceiveService.DELIVERED_SMS_ACTION))
|
||||||
|
handleDeliveredMessage(intent);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,9 +21,11 @@ import android.content.Context;
|
|||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.database.Cursor;
|
import android.database.Cursor;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
|
import android.preference.PreferenceManager;
|
||||||
import android.telephony.SmsManager;
|
import android.telephony.SmsManager;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.ApplicationPreferencesActivity;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
import org.thoughtcrime.securesms.crypto.MasterCipher;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
import org.thoughtcrime.securesms.crypto.SessionCipher;
|
||||||
@ -111,15 +113,36 @@ public class SmsSender {
|
|||||||
return sentIntents;
|
return sentIntents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private ArrayList<PendingIntent> constructDeliveredIntents(long messageId, long type, ArrayList<String> messages) {
|
||||||
|
if (!PreferenceManager.getDefaultSharedPreferences(context)
|
||||||
|
.getBoolean(ApplicationPreferencesActivity.SMS_DELIVERY_REPORT_PREF, false))
|
||||||
|
{
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
ArrayList<PendingIntent> deliveredIntents = new ArrayList<PendingIntent>(messages.size());
|
||||||
|
|
||||||
|
for (int i=0;i<messages.size();i++) {
|
||||||
|
Intent pending = new Intent(SendReceiveService.DELIVERED_SMS_ACTION, Uri.parse("custom://" + messageId + System.currentTimeMillis()), context, SmsListener.class);
|
||||||
|
pending.putExtra("type", type);
|
||||||
|
pending.putExtra("message_id", messageId);
|
||||||
|
deliveredIntents.add(PendingIntent.getBroadcast(context, 0, pending, 0));
|
||||||
|
}
|
||||||
|
|
||||||
|
return deliveredIntents;
|
||||||
|
}
|
||||||
|
|
||||||
private void deliverGSMTransportTextMessage(String recipient, String text, long messageId, long type) {
|
private void deliverGSMTransportTextMessage(String recipient, String text, long messageId, long type) {
|
||||||
ArrayList<String> messages = SmsManager.getDefault().divideMessage(text);
|
ArrayList<String> messages = SmsManager.getDefault().divideMessage(text);
|
||||||
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
|
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
|
||||||
|
ArrayList<PendingIntent> deliveredIntents = constructDeliveredIntents(messageId, type, messages);
|
||||||
|
|
||||||
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
|
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
|
||||||
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
|
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
|
||||||
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
||||||
// the app.
|
// the app.
|
||||||
try {
|
try {
|
||||||
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, null);
|
SmsManager.getDefault().sendMultipartTextMessage(recipient, null, messages, sentIntents, deliveredIntents);
|
||||||
} catch (NullPointerException npe) {
|
} catch (NullPointerException npe) {
|
||||||
Log.w("SmsSender", npe);
|
Log.w("SmsSender", npe);
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||||
@ -142,15 +165,18 @@ public class SmsSender {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
ArrayList<String> messages = multipartMessageHandler.divideMessage(recipient, text, prefix);
|
ArrayList<String> messages = multipartMessageHandler.divideMessage(recipient, text, prefix);
|
||||||
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
|
ArrayList<PendingIntent> sentIntents = constructSentIntents(messageId, type, messages);
|
||||||
|
ArrayList<PendingIntent> deliveredIntents = constructDeliveredIntents(messageId, type, messages);
|
||||||
|
|
||||||
for (int i=0;i<messages.size();i++) {
|
for (int i=0;i<messages.size();i++) {
|
||||||
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
|
// XXX moxie@thoughtcrime.org 1/7/11 -- There's apparently a bug where for some unknown recipients
|
||||||
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
|
// and messages, this will throw an NPE. I have no idea why, so I'm just catching it and marking
|
||||||
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
// the message as a failure. That way at least it doesn't repeatedly crash every time you start
|
||||||
// the app.
|
// the app.
|
||||||
try {
|
try {
|
||||||
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i), null);
|
SmsManager.getDefault().sendTextMessage(recipient, null, messages.get(i), sentIntents.get(i),
|
||||||
|
deliveredIntents == null ? null : deliveredIntents.get(i));
|
||||||
} catch (NullPointerException npe) {
|
} catch (NullPointerException npe) {
|
||||||
Log.w("SmsSender", npe);
|
Log.w("SmsSender", npe);
|
||||||
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
DatabaseFactory.getSmsDatabase(context).markAsSentFailed(messageId);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user