mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 01:07:47 +00:00
wip
This commit is contained in:
parent
4851a555e7
commit
49daa45dca
@ -7,19 +7,35 @@ message IncomingPushMessageSignal {
|
|||||||
optional uint32 type = 1;
|
optional uint32 type = 1;
|
||||||
optional string source = 2;
|
optional string source = 2;
|
||||||
optional string relay = 3;
|
optional string relay = 3;
|
||||||
repeated string destinations = 4;
|
// repeated string destinations = 4; // No longer supported
|
||||||
optional uint64 timestamp = 5;
|
optional uint64 timestamp = 5;
|
||||||
optional bytes message = 6; // Contains an encrypted PushMessageContent
|
optional bytes message = 6; // Contains an encrypted PushMessageContent
|
||||||
}
|
}
|
||||||
|
|
||||||
message PushMessageContent {
|
message PushMessageContent {
|
||||||
optional string body = 1;
|
|
||||||
|
|
||||||
message AttachmentPointer {
|
message AttachmentPointer {
|
||||||
optional fixed64 id = 1;
|
optional fixed64 id = 1;
|
||||||
optional string contentType = 2;
|
optional string contentType = 2;
|
||||||
optional bytes key = 3;
|
optional bytes key = 3;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
message GroupContext {
|
||||||
|
enum Type {
|
||||||
|
UNKNOWN = 0;
|
||||||
|
CREATE = 1;
|
||||||
|
MODIFY = 2;
|
||||||
|
DELIVER = 3;
|
||||||
|
ADD = 4;
|
||||||
|
QUIT = 5;
|
||||||
|
}
|
||||||
|
optional bytes id = 1;
|
||||||
|
optional Type type = 2;
|
||||||
|
optional string name = 3;
|
||||||
|
repeated string members = 4;
|
||||||
|
optional AttachmentPointer avatar = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
optional string body = 1;
|
||||||
repeated AttachmentPointer attachments = 2;
|
repeated AttachmentPointer attachments = 2;
|
||||||
|
optional GroupContext group = 3;
|
||||||
}
|
}
|
@ -21,9 +21,6 @@ import android.os.Parcelable;
|
|||||||
|
|
||||||
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
import org.whispersystems.textsecure.push.PushMessageProtos.IncomingPushMessageSignal;
|
||||||
|
|
||||||
import java.util.LinkedList;
|
|
||||||
import java.util.List;
|
|
||||||
|
|
||||||
public class IncomingPushMessage implements PushMessage, Parcelable {
|
public class IncomingPushMessage implements PushMessage, Parcelable {
|
||||||
|
|
||||||
public static final Parcelable.Creator<IncomingPushMessage> CREATOR = new Parcelable.Creator<IncomingPushMessage>() {
|
public static final Parcelable.Creator<IncomingPushMessage> CREATOR = new Parcelable.Creator<IncomingPushMessage>() {
|
||||||
@ -40,7 +37,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
|||||||
|
|
||||||
private int type;
|
private int type;
|
||||||
private String source;
|
private String source;
|
||||||
private List<String> destinations;
|
|
||||||
private byte[] message;
|
private byte[] message;
|
||||||
private long timestamp;
|
private long timestamp;
|
||||||
private String relay;
|
private String relay;
|
||||||
@ -51,21 +47,17 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
|||||||
this.timestamp = message.timestamp;
|
this.timestamp = message.timestamp;
|
||||||
this.relay = message.relay;
|
this.relay = message.relay;
|
||||||
this.message = body;
|
this.message = body;
|
||||||
this.destinations = new LinkedList<String>();
|
|
||||||
this.destinations.addAll(message.destinations);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingPushMessage(IncomingPushMessageSignal signal) {
|
public IncomingPushMessage(IncomingPushMessageSignal signal) {
|
||||||
this.type = signal.getType();
|
this.type = signal.getType();
|
||||||
this.source = signal.getSource();
|
this.source = signal.getSource();
|
||||||
this.destinations = signal.getDestinationsList();
|
|
||||||
this.message = signal.getMessage().toByteArray();
|
this.message = signal.getMessage().toByteArray();
|
||||||
this.timestamp = signal.getTimestamp();
|
this.timestamp = signal.getTimestamp();
|
||||||
this.relay = signal.getRelay();
|
this.relay = signal.getRelay();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingPushMessage(Parcel in) {
|
public IncomingPushMessage(Parcel in) {
|
||||||
this.destinations = new LinkedList<String>();
|
|
||||||
this.type = in.readInt();
|
this.type = in.readInt();
|
||||||
this.source = in.readString();
|
this.source = in.readString();
|
||||||
|
|
||||||
@ -73,19 +65,16 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
|||||||
this.relay = in.readString();
|
this.relay = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
in.readStringList(destinations);
|
|
||||||
this.message = new byte[in.readInt()];
|
this.message = new byte[in.readInt()];
|
||||||
in.readByteArray(this.message);
|
in.readByteArray(this.message);
|
||||||
this.timestamp = in.readLong();
|
this.timestamp = in.readLong();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingPushMessage(int type, String source,
|
public IncomingPushMessage(int type, String source,
|
||||||
List<String> destinations,
|
|
||||||
byte[] body, long timestamp)
|
byte[] body, long timestamp)
|
||||||
{
|
{
|
||||||
this.type = type;
|
this.type = type;
|
||||||
this.source = source;
|
this.source = source;
|
||||||
this.destinations = destinations;
|
|
||||||
this.message = body;
|
this.message = body;
|
||||||
this.timestamp = timestamp;
|
this.timestamp = timestamp;
|
||||||
}
|
}
|
||||||
@ -106,10 +95,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
|||||||
return message;
|
return message;
|
||||||
}
|
}
|
||||||
|
|
||||||
public List<String> getDestinations() {
|
|
||||||
return destinations;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@ -123,7 +108,6 @@ public class IncomingPushMessage implements PushMessage, Parcelable {
|
|||||||
if (relay != null) {
|
if (relay != null) {
|
||||||
dest.writeString(relay);
|
dest.writeString(relay);
|
||||||
}
|
}
|
||||||
dest.writeStringList(destinations);
|
|
||||||
dest.writeInt(message.length);
|
dest.writeInt(message.length);
|
||||||
dest.writeByteArray(message);
|
dest.writeByteArray(message);
|
||||||
dest.writeLong(timestamp);
|
dest.writeLong(timestamp);
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -2,6 +2,7 @@ package org.whispersystems.textsecure.util;
|
|||||||
|
|
||||||
import android.app.AlertDialog;
|
import android.app.AlertDialog;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.telephony.TelephonyManager;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
@ -169,6 +170,22 @@ public class Util {
|
|||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static String getDeviceE164Number(Context context) {
|
||||||
|
String localNumber = ((TelephonyManager)context.getSystemService(Context.TELEPHONY_SERVICE))
|
||||||
|
.getLine1Number();
|
||||||
|
|
||||||
|
if (!org.whispersystems.textsecure.util.Util.isEmpty(localNumber) &&
|
||||||
|
!localNumber.startsWith("+"))
|
||||||
|
{
|
||||||
|
if (localNumber.length() == 10) localNumber = "+1" + localNumber;
|
||||||
|
else localNumber = "+" + localNumber;
|
||||||
|
|
||||||
|
return localNumber;
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
public static SecureRandom getSecureRandom() {
|
public static SecureRandom getSecureRandom() {
|
||||||
try {
|
try {
|
||||||
return SecureRandom.getInstance("SHA1PRNG");
|
return SecureRandom.getInstance("SHA1PRNG");
|
||||||
|
@ -52,7 +52,8 @@ public class DatabaseFactory {
|
|||||||
private static final int INTRODUCED_MMS_FROM_VERSION = 8;
|
private static final int INTRODUCED_MMS_FROM_VERSION = 8;
|
||||||
private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9;
|
private static final int INTRODUCED_TOFU_IDENTITY_VERSION = 9;
|
||||||
private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10;
|
private static final int INTRODUCED_PUSH_DATABASE_VERSION = 10;
|
||||||
private static final int DATABASE_VERSION = 10;
|
private static final int INTRODUCED_GROUP_DATABASE_VERSION = 11;
|
||||||
|
private static final int DATABASE_VERSION = 11;
|
||||||
|
|
||||||
private static final String DATABASE_NAME = "messages.db";
|
private static final String DATABASE_NAME = "messages.db";
|
||||||
private static final Object lock = new Object();
|
private static final Object lock = new Object();
|
||||||
@ -73,6 +74,7 @@ public class DatabaseFactory {
|
|||||||
private final IdentityDatabase identityDatabase;
|
private final IdentityDatabase identityDatabase;
|
||||||
private final DraftDatabase draftDatabase;
|
private final DraftDatabase draftDatabase;
|
||||||
private final PushDatabase pushDatabase;
|
private final PushDatabase pushDatabase;
|
||||||
|
private final GroupDatabase groupDatabase;
|
||||||
|
|
||||||
public static DatabaseFactory getInstance(Context context) {
|
public static DatabaseFactory getInstance(Context context) {
|
||||||
synchronized (lock) {
|
synchronized (lock) {
|
||||||
@ -138,6 +140,10 @@ public class DatabaseFactory {
|
|||||||
return getInstance(context).pushDatabase;
|
return getInstance(context).pushDatabase;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static GroupDatabase getGroupDatabase(Context context) {
|
||||||
|
return getInstance(context).groupDatabase;
|
||||||
|
}
|
||||||
|
|
||||||
private DatabaseFactory(Context context) {
|
private DatabaseFactory(Context context) {
|
||||||
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
this.databaseHelper = new DatabaseHelper(context, DATABASE_NAME, null, DATABASE_VERSION);
|
||||||
this.sms = new SmsDatabase(context, databaseHelper);
|
this.sms = new SmsDatabase(context, databaseHelper);
|
||||||
@ -151,6 +157,7 @@ public class DatabaseFactory {
|
|||||||
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
this.identityDatabase = new IdentityDatabase(context, databaseHelper);
|
||||||
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
this.draftDatabase = new DraftDatabase(context, databaseHelper);
|
||||||
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
this.pushDatabase = new PushDatabase(context, databaseHelper);
|
||||||
|
this.groupDatabase = new GroupDatabase(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public void reset(Context context) {
|
public void reset(Context context) {
|
||||||
@ -166,6 +173,8 @@ public class DatabaseFactory {
|
|||||||
this.mmsSmsDatabase.reset(databaseHelper);
|
this.mmsSmsDatabase.reset(databaseHelper);
|
||||||
this.identityDatabase.reset(databaseHelper);
|
this.identityDatabase.reset(databaseHelper);
|
||||||
this.draftDatabase.reset(databaseHelper);
|
this.draftDatabase.reset(databaseHelper);
|
||||||
|
this.pushDatabase.reset(databaseHelper);
|
||||||
|
this.groupDatabase.reset(databaseHelper);
|
||||||
old.close();
|
old.close();
|
||||||
|
|
||||||
this.address.reset(context);
|
this.address.reset(context);
|
||||||
@ -432,6 +441,7 @@ public class DatabaseFactory {
|
|||||||
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
db.execSQL(IdentityDatabase.CREATE_TABLE);
|
||||||
db.execSQL(DraftDatabase.CREATE_TABLE);
|
db.execSQL(DraftDatabase.CREATE_TABLE);
|
||||||
db.execSQL(PushDatabase.CREATE_TABLE);
|
db.execSQL(PushDatabase.CREATE_TABLE);
|
||||||
|
db.execSQL(GroupDatabase.CREATE_TABLE);
|
||||||
|
|
||||||
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
executeStatements(db, SmsDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
executeStatements(db, MmsDatabase.CREATE_INDEXS);
|
||||||
@ -439,6 +449,7 @@ public class DatabaseFactory {
|
|||||||
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
|
executeStatements(db, ThreadDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
|
executeStatements(db, MmsAddressDatabase.CREATE_INDEXS);
|
||||||
executeStatements(db, DraftDatabase.CREATE_INDEXS);
|
executeStatements(db, DraftDatabase.CREATE_INDEXS);
|
||||||
|
executeStatements(db, GroupDatabase.CREATE_INDEXS);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -630,6 +641,11 @@ public class DatabaseFactory {
|
|||||||
db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);");
|
db.execSQL("CREATE INDEX IF NOT EXISTS pending_push_index ON part (pending_push);");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (oldVersion < INTRODUCED_GROUP_DATABASE_VERSION) {
|
||||||
|
db.execSQL("CREATE TABLE groups (_id INTEGER PRIMARY KEY, group_id TEXT, owner TEXT, title TEXT, members TEXT, avatar BLOB, avatar_id INTEGER, avatar_key BLOB, avatar_content_type TEXT, timestamp INTEGER);");
|
||||||
|
db.execSQL("CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON groups (GROUP_ID);");
|
||||||
|
}
|
||||||
|
|
||||||
db.setTransactionSuccessful();
|
db.setTransactionSuccessful();
|
||||||
db.endTransaction();
|
db.endTransaction();
|
||||||
}
|
}
|
||||||
|
241
src/org/thoughtcrime/securesms/database/GroupDatabase.java
Normal file
241
src/org/thoughtcrime/securesms/database/GroupDatabase.java
Normal file
@ -0,0 +1,241 @@
|
|||||||
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
|
||||||
|
import android.content.ContentValues;
|
||||||
|
import android.content.Context;
|
||||||
|
import android.database.Cursor;
|
||||||
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
import org.whispersystems.textsecure.util.Hex;
|
||||||
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer;
|
||||||
|
|
||||||
|
public class GroupDatabase extends Database {
|
||||||
|
|
||||||
|
private static final String TABLE_NAME = "groups";
|
||||||
|
private static final String ID = "_id";
|
||||||
|
private static final String GROUP_ID = "group_id";
|
||||||
|
private static final String OWNER = "owner";
|
||||||
|
private static final String TITLE = "title";
|
||||||
|
private static final String MEMBERS = "members";
|
||||||
|
private static final String AVATAR = "avatar";
|
||||||
|
private static final String AVATAR_ID = "avatar_id";
|
||||||
|
private static final String AVATAR_KEY = "avatar_key";
|
||||||
|
private static final String AVATAR_CONTENT_TYPE = "avatar_content_type";
|
||||||
|
private static final String RELAY = "relay";
|
||||||
|
private static final String TIMESTAMP = "timestamp";
|
||||||
|
|
||||||
|
public static final String CREATE_TABLE =
|
||||||
|
"CREATE TABLE " + TABLE_NAME +
|
||||||
|
" (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
|
GROUP_ID + " TEXT, " +
|
||||||
|
OWNER + " TEXT, " +
|
||||||
|
TITLE + " TEXT, " +
|
||||||
|
MEMBERS + " TEXT, " +
|
||||||
|
AVATAR + " BLOB, " +
|
||||||
|
AVATAR_ID + " INTEGER, " +
|
||||||
|
AVATAR_KEY + " BLOB, " +
|
||||||
|
AVATAR_CONTENT_TYPE + " TEXT, " +
|
||||||
|
TIMESTAMP + " INTEGER);";
|
||||||
|
|
||||||
|
public static final String[] CREATE_INDEXS = {
|
||||||
|
"CREATE UNIQUE INDEX IF NOT EXISTS group_id_index ON " + TABLE_NAME + " (" + GROUP_ID + ");",
|
||||||
|
};
|
||||||
|
|
||||||
|
public GroupDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
|
super(context, databaseHelper);
|
||||||
|
}
|
||||||
|
|
||||||
|
public Reader getGroup(String groupId) {
|
||||||
|
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
|
||||||
|
new String[] {groupId}, null, null, null);
|
||||||
|
|
||||||
|
return new Reader(cursor);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void create(byte[] groupId, String owner, String title,
|
||||||
|
List<String> members, AttachmentPointer avatar,
|
||||||
|
String relay)
|
||||||
|
{
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(GROUP_ID, Hex.toString(groupId));
|
||||||
|
contentValues.put(OWNER, owner);
|
||||||
|
contentValues.put(TITLE, title);
|
||||||
|
contentValues.put(MEMBERS, Util.join(members, ","));
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
contentValues.put(AVATAR_ID, avatar.getId());
|
||||||
|
contentValues.put(AVATAR_KEY, avatar.getKey().toByteArray());
|
||||||
|
contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType());
|
||||||
|
}
|
||||||
|
|
||||||
|
contentValues.put(RELAY, relay);
|
||||||
|
contentValues.put(TIMESTAMP, System.currentTimeMillis());
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, contentValues);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void update(byte[] groupId, String source, String title, AttachmentPointer avatar) {
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
if (title != null) contentValues.put(TITLE, title);
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
contentValues.put(AVATAR_ID, avatar.getId());
|
||||||
|
contentValues.put(AVATAR_CONTENT_TYPE, avatar.getContentType());
|
||||||
|
contentValues.put(AVATAR_KEY, avatar.getKey().toByteArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues,
|
||||||
|
GROUP_ID + " = ? AND " + OWNER + " = ?",
|
||||||
|
new String[] {Hex.toString(groupId), source});
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAvatar(String groupId, Bitmap avatar) {
|
||||||
|
ContentValues contentValues = new ContentValues();
|
||||||
|
contentValues.put(AVATAR, BitmapUtil.toByteArray(avatar));
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?", new String[] {groupId});
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public void add(byte[] id, String source, List<String> members) {
|
||||||
|
List<String> currentMembers = getCurrentMembers(id);
|
||||||
|
|
||||||
|
for (String currentMember : currentMembers) {
|
||||||
|
if (currentMember.equals(source)) {
|
||||||
|
List<String> concatenatedMembers = new LinkedList<String>(currentMembers);
|
||||||
|
concatenatedMembers.addAll(members);
|
||||||
|
|
||||||
|
ContentValues contents = new ContentValues();
|
||||||
|
contents.put(MEMBERS, Util.join(concatenatedMembers, ","));
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
|
||||||
|
new String[] {Hex.toString(id)});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(byte[] id, String source) {
|
||||||
|
List<String> currentMembers = getCurrentMembers(id);
|
||||||
|
currentMembers.remove(source);
|
||||||
|
|
||||||
|
ContentValues contents = new ContentValues();
|
||||||
|
contents.put(MEMBERS, Util.join(currentMembers, ","));
|
||||||
|
|
||||||
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contents, GROUP_ID + " = ?",
|
||||||
|
new String[]{Hex.toString(id)});
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getCurrentMembers(byte[] id) {
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, new String[] {MEMBERS},
|
||||||
|
GROUP_ID + " = ?", new String[] {Hex.toString(id)},
|
||||||
|
null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
return Util.split(cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)), ",");
|
||||||
|
}
|
||||||
|
|
||||||
|
return new LinkedList<String>();
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public static class Reader {
|
||||||
|
|
||||||
|
private final Cursor cursor;
|
||||||
|
|
||||||
|
public Reader(Cursor cursor) {
|
||||||
|
this.cursor = cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
public GroupRecord getNext() {
|
||||||
|
if (cursor == null || !cursor.moveToNext()) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new GroupRecord(cursor.getString(cursor.getColumnIndexOrThrow(GROUP_ID)),
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(TITLE)),
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(MEMBERS)),
|
||||||
|
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR)),
|
||||||
|
cursor.getLong(cursor.getColumnIndexOrThrow(AVATAR_ID)),
|
||||||
|
cursor.getBlob(cursor.getColumnIndexOrThrow(AVATAR_KEY)),
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(AVATAR_CONTENT_TYPE)),
|
||||||
|
cursor.getString(cursor.getColumnIndexOrThrow(RELAY)));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void close() {
|
||||||
|
if (this.cursor != null)
|
||||||
|
this.cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GroupRecord {
|
||||||
|
|
||||||
|
private final String id;
|
||||||
|
private final String title;
|
||||||
|
private final List<String> members;
|
||||||
|
private final byte[] avatar;
|
||||||
|
private final long avatarId;
|
||||||
|
private final byte[] avatarKey;
|
||||||
|
private final String avatarContentType;
|
||||||
|
private final String relay;
|
||||||
|
|
||||||
|
public GroupRecord(String id, String title, String members, byte[] avatar,
|
||||||
|
long avatarId, byte[] avatarKey, String avatarContentType,
|
||||||
|
String relay)
|
||||||
|
{
|
||||||
|
this.id = id;
|
||||||
|
this.title = title;
|
||||||
|
this.members = Util.split(members, ",");
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.avatarId = avatarId;
|
||||||
|
this.avatarKey = avatarKey;
|
||||||
|
this.avatarContentType = avatarContentType;
|
||||||
|
this.relay = relay;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getId() {
|
||||||
|
return id;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getTitle() {
|
||||||
|
return title;
|
||||||
|
}
|
||||||
|
|
||||||
|
public List<String> getMembers() {
|
||||||
|
return members;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getAvatar() {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getAvatarId() {
|
||||||
|
return avatarId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getAvatarKey() {
|
||||||
|
return avatarKey;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getAvatarContentType() {
|
||||||
|
return avatarContentType;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getRelay() {
|
||||||
|
return relay;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -179,20 +179,38 @@ public class MmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException {
|
private long getThreadIdFor(IncomingMediaMessage retrieved) throws RecipientFormattingException {
|
||||||
|
if (retrieved.getGroupId() != null) {
|
||||||
|
return DatabaseFactory.getThreadDatabase(context).getThreadIdForGroup(retrieved.getGroupId());
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
PduHeaders headers = retrieved.getPduHeaders();
|
PduHeaders headers = retrieved.getPduHeaders();
|
||||||
Set<String> group = new HashSet<String>();
|
Set<String> group = new HashSet<String>();
|
||||||
|
|
||||||
EncodedStringValue encodedFrom = headers.getEncodedStringValue(PduHeaders.FROM);
|
EncodedStringValue encodedFrom = headers.getEncodedStringValue(PduHeaders.FROM);
|
||||||
|
EncodedStringValue[] encodedCcList = headers.getEncodedStringValues(PduHeaders.CC);
|
||||||
|
EncodedStringValue[] encodedToList = headers.getEncodedStringValues(PduHeaders.TO);
|
||||||
|
|
||||||
group.add(new String(encodedFrom.getTextString(), CharacterSets.MIMENAME_ISO_8859_1));
|
group.add(new String(encodedFrom.getTextString(), CharacterSets.MIMENAME_ISO_8859_1));
|
||||||
|
|
||||||
EncodedStringValue[] encodedCcList = headers.getEncodedStringValues(PduHeaders.CC);
|
|
||||||
if (encodedCcList != null) {
|
if (encodedCcList != null) {
|
||||||
for (EncodedStringValue encodedCc : encodedCcList) {
|
for (EncodedStringValue encodedCc : encodedCcList) {
|
||||||
group.add(new String(encodedCc.getTextString(), CharacterSets.MIMENAME_ISO_8859_1));
|
group.add(new String(encodedCc.getTextString(), CharacterSets.MIMENAME_ISO_8859_1));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (encodedToList != null) {
|
||||||
|
String localNumber = Util.getDeviceE164Number(context);
|
||||||
|
|
||||||
|
for (EncodedStringValue encodedTo : encodedToList) {
|
||||||
|
String to = new String(encodedTo.getTextString(), CharacterSets.MIMENAME_ISO_8859_1);
|
||||||
|
|
||||||
|
if (!localNumber.equals(to)) {
|
||||||
|
group.add(to);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
String recipientsList = Util.join(group, ",");
|
String recipientsList = Util.join(group, ",");
|
||||||
Recipients recipients = RecipientFactory.getRecipientsFromString(context, recipientsList, false);
|
Recipients recipients = RecipientFactory.getRecipientsFromString(context, recipientsList, false);
|
||||||
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
return DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
|
@ -18,12 +18,11 @@ public class PushDatabase extends Database {
|
|||||||
public static final String ID = "_id";
|
public static final String ID = "_id";
|
||||||
public static final String TYPE = "type";
|
public static final String TYPE = "type";
|
||||||
public static final String SOURCE = "source";
|
public static final String SOURCE = "source";
|
||||||
public static final String DESTINATIONS = "destinations";
|
|
||||||
public static final String BODY = "body";
|
public static final String BODY = "body";
|
||||||
public static final String TIMESTAMP = "timestamp";
|
public static final String TIMESTAMP = "timestamp";
|
||||||
|
|
||||||
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + ID + " INTEGER PRIMARY KEY, " +
|
||||||
TYPE + " INTEGER, " + SOURCE + " TEXT, " + DESTINATIONS + " TEXT, " + BODY + " TEXT, " + TIMESTAMP + " INTEGER);";
|
TYPE + " INTEGER, " + SOURCE + " TEXT, " + BODY + " TEXT, " + TIMESTAMP + " INTEGER);";
|
||||||
|
|
||||||
public PushDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
public PushDatabase(Context context, SQLiteOpenHelper databaseHelper) {
|
||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
@ -33,7 +32,6 @@ public class PushDatabase extends Database {
|
|||||||
ContentValues values = new ContentValues();
|
ContentValues values = new ContentValues();
|
||||||
values.put(TYPE, message.getType());
|
values.put(TYPE, message.getType());
|
||||||
values.put(SOURCE, message.getSource());
|
values.put(SOURCE, message.getSource());
|
||||||
values.put(DESTINATIONS, Util.join(message.getDestinations(), ","));
|
|
||||||
values.put(BODY, Base64.encodeBytes(message.getBody()));
|
values.put(BODY, Base64.encodeBytes(message.getBody()));
|
||||||
values.put(TIMESTAMP, message.getTimestampMillis());
|
values.put(TIMESTAMP, message.getTimestampMillis());
|
||||||
|
|
||||||
@ -66,11 +64,10 @@ public class PushDatabase extends Database {
|
|||||||
|
|
||||||
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
int type = cursor.getInt(cursor.getColumnIndexOrThrow(TYPE));
|
||||||
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
|
String source = cursor.getString(cursor.getColumnIndexOrThrow(SOURCE));
|
||||||
List<String> destinations = Util.split(cursor.getString(cursor.getColumnIndexOrThrow(DESTINATIONS)), ",");
|
|
||||||
byte[] body = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY)));
|
byte[] body = Base64.decode(cursor.getString(cursor.getColumnIndexOrThrow(BODY)));
|
||||||
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
long timestamp = cursor.getLong(cursor.getColumnIndexOrThrow(TIMESTAMP));
|
||||||
|
|
||||||
return new IncomingPushMessage(type, source, destinations, body, timestamp);
|
return new IncomingPushMessage(type, source, body, timestamp);
|
||||||
} catch (IOException e) {
|
} catch (IOException e) {
|
||||||
throw new AssertionError(e);
|
throw new AssertionError(e);
|
||||||
}
|
}
|
||||||
|
@ -259,10 +259,15 @@ public class SmsDatabase extends Database implements MmsSmsColumns {
|
|||||||
|
|
||||||
Recipient recipient = new Recipient(null, message.getSender(), null, null);
|
Recipient recipient = new Recipient(null, message.getSender(), null, null);
|
||||||
Recipients recipients = new Recipients(recipient);
|
Recipients recipients = new Recipients(recipient);
|
||||||
long threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
String groupId = message.getGroupId();
|
||||||
boolean unread = org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
|
boolean unread = org.thoughtcrime.securesms.util.Util.isDefaultSmsProvider(context) ||
|
||||||
message.isSecureMessage() || message.isKeyExchange();
|
message.isSecureMessage() || message.isKeyExchange();
|
||||||
|
|
||||||
|
long threadId;
|
||||||
|
|
||||||
|
if (groupId == null) threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdFor(recipients);
|
||||||
|
else threadId = DatabaseFactory.getThreadDatabase(context).getThreadIdForGroup(groupId);
|
||||||
|
|
||||||
ContentValues values = new ContentValues(6);
|
ContentValues values = new ContentValues(6);
|
||||||
values.put(ADDRESS, message.getSender());
|
values.put(ADDRESS, message.getSender());
|
||||||
values.put(DATE_RECEIVED, System.currentTimeMillis());
|
values.put(DATE_RECEIVED, System.currentTimeMillis());
|
||||||
|
@ -101,6 +101,17 @@ public class ThreadDatabase extends Database {
|
|||||||
return sb.toString();
|
return sb.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private long createThreadForGroup(String group) {
|
||||||
|
long date = System.currentTimeMillis();
|
||||||
|
|
||||||
|
ContentValues values = new ContentValues();
|
||||||
|
values.put(DATE, date - date % 1000);
|
||||||
|
values.put(RECIPIENT_IDS, group);
|
||||||
|
values.put(MESSAGE_COUNT, 0);
|
||||||
|
|
||||||
|
return databaseHelper.getWritableDatabase().insert(TABLE_NAME, null, values);
|
||||||
|
}
|
||||||
|
|
||||||
private long createThreadForRecipients(String recipients, int recipientCount, int distributionType) {
|
private long createThreadForRecipients(String recipients, int recipientCount, int distributionType) {
|
||||||
ContentValues contentValues = new ContentValues(4);
|
ContentValues contentValues = new ContentValues(4);
|
||||||
long date = System.currentTimeMillis();
|
long date = System.currentTimeMillis();
|
||||||
@ -325,7 +336,7 @@ public class ThreadDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public long getThreadIdFor(Recipients recipients) {
|
public long getThreadIdFor(Recipients recipients) {
|
||||||
return getThreadIdFor(recipients, 0);
|
return getThreadIdFor(recipients, DistributionTypes.DEFAULT);
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getThreadIdFor(Recipients recipients, int distributionType) {
|
public long getThreadIdFor(Recipients recipients, int distributionType) {
|
||||||
@ -349,6 +360,26 @@ public class ThreadDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public long getThreadIdForGroup(String groupId) {
|
||||||
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
|
String where = RECIPIENT_IDS + " = ?";
|
||||||
|
String[] recipientsArg = new String[] {groupId};
|
||||||
|
Cursor cursor = null;
|
||||||
|
|
||||||
|
try {
|
||||||
|
cursor = db.query(TABLE_NAME, new String[]{ID}, where, recipientsArg, null, null, null);
|
||||||
|
|
||||||
|
if (cursor != null && cursor.moveToFirst()) {
|
||||||
|
return cursor.getLong(cursor.getColumnIndexOrThrow(ID));
|
||||||
|
} else {
|
||||||
|
return createThreadForGroup(groupId);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (cursor != null)
|
||||||
|
cursor.close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public Recipients getRecipientsForThreadId(long threadId) {
|
public Recipients getRecipientsForThreadId(long threadId) {
|
||||||
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
SQLiteDatabase db = databaseHelper.getReadableDatabase();
|
||||||
Cursor cursor = null;
|
Cursor cursor = null;
|
||||||
|
@ -7,9 +7,6 @@ import org.whispersystems.textsecure.push.IncomingPushMessage;
|
|||||||
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
||||||
import org.whispersystems.textsecure.util.Base64;
|
import org.whispersystems.textsecure.util.Base64;
|
||||||
|
|
||||||
import java.io.UnsupportedEncodingException;
|
|
||||||
|
|
||||||
import ws.com.google.android.mms.pdu.CharacterSets;
|
|
||||||
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
import ws.com.google.android.mms.pdu.EncodedStringValue;
|
||||||
import ws.com.google.android.mms.pdu.PduBody;
|
import ws.com.google.android.mms.pdu.PduBody;
|
||||||
import ws.com.google.android.mms.pdu.PduHeaders;
|
import ws.com.google.android.mms.pdu.PduHeaders;
|
||||||
@ -20,29 +17,28 @@ public class IncomingMediaMessage {
|
|||||||
|
|
||||||
private final PduHeaders headers;
|
private final PduHeaders headers;
|
||||||
private final PduBody body;
|
private final PduBody body;
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
public IncomingMediaMessage(RetrieveConf retreived) {
|
public IncomingMediaMessage(RetrieveConf retreived) {
|
||||||
this.headers = retreived.getPduHeaders();
|
this.headers = retreived.getPduHeaders();
|
||||||
this.body = retreived.getBody();
|
this.body = retreived.getBody();
|
||||||
|
this.groupId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingMediaMessage(MasterSecret masterSecret, String localNumber,
|
public IncomingMediaMessage(MasterSecret masterSecret, String localNumber,
|
||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
PushMessageContent messageContent)
|
PushMessageContent messageContent,
|
||||||
|
String groupId)
|
||||||
{
|
{
|
||||||
this.headers = new PduHeaders();
|
this.headers = new PduHeaders();
|
||||||
this.body = new PduBody();
|
this.body = new PduBody();
|
||||||
|
this.groupId = groupId;
|
||||||
|
|
||||||
this.headers.setEncodedStringValue(new EncodedStringValue(message.getSource()), PduHeaders.FROM);
|
this.headers.setEncodedStringValue(new EncodedStringValue(message.getSource()), PduHeaders.FROM);
|
||||||
this.headers.appendEncodedStringValue(new EncodedStringValue(localNumber), PduHeaders.TO);
|
this.headers.appendEncodedStringValue(new EncodedStringValue(localNumber), PduHeaders.TO);
|
||||||
|
|
||||||
for (String destination : message.getDestinations()) {
|
|
||||||
this.headers.appendEncodedStringValue(new EncodedStringValue(destination), PduHeaders.CC);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.headers.setLongInteger(message.getTimestampMillis() / 1000, PduHeaders.DATE);
|
this.headers.setLongInteger(message.getTimestampMillis() / 1000, PduHeaders.DATE);
|
||||||
|
|
||||||
if (messageContent.getBody() != null && messageContent.getBody().length() > 0) {
|
if (!org.whispersystems.textsecure.util.Util.isEmpty(messageContent.getBody())) {
|
||||||
PduPart text = new PduPart();
|
PduPart text = new PduPart();
|
||||||
text.setData(Util.toIsoBytes(messageContent.getBody()));
|
text.setData(Util.toIsoBytes(messageContent.getBody()));
|
||||||
text.setContentType(Util.toIsoBytes("text/plain"));
|
text.setContentType(Util.toIsoBytes("text/plain"));
|
||||||
@ -77,8 +73,15 @@ public class IncomingMediaMessage {
|
|||||||
return body;
|
return body;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
public boolean isGroupMessage() {
|
public boolean isGroupMessage() {
|
||||||
return !Util.isEmpty(headers.getEncodedStringValues(PduHeaders.CC));
|
return groupId != null ||
|
||||||
|
!Util.isEmpty(headers.getEncodedStringValues(PduHeaders.CC)) ||
|
||||||
|
(headers.getEncodedStringValues(PduHeaders.TO) != null &&
|
||||||
|
headers.getEncodedStringValues(PduHeaders.TO).length > 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -25,10 +25,8 @@ import org.thoughtcrime.securesms.util.NumberUtil;
|
|||||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||||
import org.whispersystems.textsecure.util.Util;
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
import java.util.HashSet;
|
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
|
|
||||||
public class RecipientFactory {
|
public class RecipientFactory {
|
||||||
@ -36,7 +34,7 @@ public class RecipientFactory {
|
|||||||
private static final RecipientProvider provider = new RecipientProvider();
|
private static final RecipientProvider provider = new RecipientProvider();
|
||||||
|
|
||||||
public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) {
|
public static Recipients getRecipientsForIds(Context context, String recipientIds, boolean asynchronous) {
|
||||||
if (recipientIds == null || recipientIds.trim().length() == 0)
|
if (Util.isEmpty(recipientIds))
|
||||||
return new Recipients(new LinkedList<Recipient>());
|
return new Recipients(new LinkedList<Recipient>());
|
||||||
|
|
||||||
List<Recipient> results = new LinkedList<Recipient>();
|
List<Recipient> results = new LinkedList<Recipient>();
|
||||||
@ -79,12 +77,8 @@ public class RecipientFactory {
|
|||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
boolean asynchronous)
|
boolean asynchronous)
|
||||||
{
|
{
|
||||||
Set<String> recipients = new HashSet<String>();
|
|
||||||
recipients.add(message.getSource());
|
|
||||||
recipients.addAll(message.getDestinations());
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
return getRecipientsFromString(context, Util.join(recipients, ","), asynchronous);
|
return getRecipientsFromString(context, message.getSource(), asynchronous);
|
||||||
} catch (RecipientFormattingException e) {
|
} catch (RecipientFormattingException e) {
|
||||||
Log.w("RecipientFactory", e);
|
Log.w("RecipientFactory", e);
|
||||||
return new Recipients(new Recipient("Unknown", "Unknown", null,
|
return new Recipients(new Recipient("Unknown", "Unknown", null,
|
||||||
@ -93,8 +87,12 @@ public class RecipientFactory {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) {
|
private static Recipient getRecipientFromProviderId(Context context, String recipientId, boolean asynchronous) {
|
||||||
String number = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId);
|
if (recipientId.startsWith("g_")) {
|
||||||
return getRecipientForNumber(context, number, asynchronous);
|
return provider.getGroupRecipient(context, recipientId, asynchronous);
|
||||||
|
} else {
|
||||||
|
String number = DatabaseFactory.getAddressDatabase(context).getAddressFromId(recipientId);
|
||||||
|
return getRecipientForNumber(context, number, asynchronous);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean hasBracketedNumber(String recipient) {
|
private static boolean hasBracketedNumber(String recipient) {
|
||||||
|
@ -27,6 +27,8 @@ import android.provider.ContactsContract.PhoneLookup;
|
|||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
import org.thoughtcrime.securesms.contacts.ContactPhotoFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.util.LRUCache;
|
import org.thoughtcrime.securesms.util.LRUCache;
|
||||||
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
import org.whispersystems.textsecure.util.ListenableFutureTask;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
@ -40,7 +42,6 @@ import java.util.concurrent.ExecutorService;
|
|||||||
public class RecipientProvider {
|
public class RecipientProvider {
|
||||||
|
|
||||||
private static final Map<String,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<String,Recipient>(1000));
|
private static final Map<String,Recipient> recipientCache = Collections.synchronizedMap(new LRUCache<String,Recipient>(1000));
|
||||||
// private static final ExecutorService asyncRecipientResolver = Executors.newSingleThreadExecutor();
|
|
||||||
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
|
private static final ExecutorService asyncRecipientResolver = Util.newSingleThreadedLifoExecutor();
|
||||||
|
|
||||||
private static final String[] CALLER_ID_PROJECTION = new String[] {
|
private static final String[] CALLER_ID_PROJECTION = new String[] {
|
||||||
@ -57,6 +58,14 @@ public class RecipientProvider {
|
|||||||
else return getSynchronousRecipient(context, number);
|
else return getSynchronousRecipient(context, number);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Recipient getGroupRecipient(Context context, String groupId, boolean asynchronous) {
|
||||||
|
Recipient cachedRecipient = recipientCache.get(groupId);
|
||||||
|
|
||||||
|
if (cachedRecipient != null) return cachedRecipient;
|
||||||
|
else if (asynchronous) return getAsynchronousGroupRecipient(context, groupId);
|
||||||
|
else return getSynchronousGroupRecipient(context, groupId);
|
||||||
|
}
|
||||||
|
|
||||||
private Recipient getSynchronousRecipient(Context context, String number) {
|
private Recipient getSynchronousRecipient(Context context, String number) {
|
||||||
Log.w("RecipientProvider", "Cache miss [SYNC]!");
|
Log.w("RecipientProvider", "Cache miss [SYNC]!");
|
||||||
RecipientDetails details = getRecipientDetails(context, number);
|
RecipientDetails details = getRecipientDetails(context, number);
|
||||||
@ -72,34 +81,26 @@ public class RecipientProvider {
|
|||||||
return recipient;
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Recipient getSynchronousGroupRecipient(Context context, String groupId) {
|
||||||
|
RecipientDetails details = getGroupRecipientDetails(context, groupId);
|
||||||
|
Recipient recipient;
|
||||||
|
|
||||||
|
if (details != null) {
|
||||||
|
recipient = new Recipient(details.name, groupId, details.contactUri, details.avatar);
|
||||||
|
} else {
|
||||||
|
recipient = new Recipient(null, groupId, null, ContactPhotoFactory.getDefaultContactPhoto(context));
|
||||||
|
}
|
||||||
|
|
||||||
|
recipientCache.put(groupId, recipient);
|
||||||
|
return recipient;
|
||||||
|
}
|
||||||
|
|
||||||
private Recipient getAsynchronousRecipient(final Context context, final String number) {
|
private Recipient getAsynchronousRecipient(final Context context, final String number) {
|
||||||
Log.w("RecipientProvider", "Cache miss [ASYNC]!");
|
Log.w("RecipientProvider", "Cache miss [ASYNC]!");
|
||||||
|
|
||||||
// Recipient recipient = new Recipient(null, number, null, ContactPhotoFactory.getDefaultContactPhoto(context));
|
|
||||||
// recipientCache.put(number, recipient);
|
|
||||||
//
|
|
||||||
// new AsyncTask<Recipient, Void, RecipientDetails>() {
|
|
||||||
// private Recipient recipient;
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// protected RecipientDetails doInBackground(Recipient... recipient) {
|
|
||||||
// this.recipient = recipient[0];
|
|
||||||
// return getRecipientDetails(context, number);
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
// @Override
|
|
||||||
// protected void onPostExecute(RecipientDetails result) {
|
|
||||||
// recipient.updateAsynchronousContent(result);
|
|
||||||
// }
|
|
||||||
// }.execute(recipient);
|
|
||||||
//
|
|
||||||
// return recipient;
|
|
||||||
|
|
||||||
// ListenableFutureTask<RecipientDetails> future = new ListenableFutureTask<RecipientDetails>(new Callable<RecipientDetails>() {
|
|
||||||
Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
|
Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
|
||||||
@Override
|
@Override
|
||||||
public RecipientDetails call() throws Exception {
|
public RecipientDetails call() throws Exception {
|
||||||
// Process.setThreadPriority(Process.THREAD_PRIORITY_BACKGROUND);
|
|
||||||
return getRecipientDetails(context, number);
|
return getRecipientDetails(context, number);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@ -112,7 +113,24 @@ public class RecipientProvider {
|
|||||||
recipientCache.put(number, recipient);
|
recipientCache.put(number, recipient);
|
||||||
|
|
||||||
return recipient;
|
return recipient;
|
||||||
//// return new Recipient(null, number, ContactPhotoFactory.getDefaultContactPhoto(context));
|
}
|
||||||
|
|
||||||
|
private Recipient getAsynchronousGroupRecipient(final Context context, final String groupId) {
|
||||||
|
Callable<RecipientDetails> task = new Callable<RecipientDetails>() {
|
||||||
|
@Override
|
||||||
|
public RecipientDetails call() throws Exception {
|
||||||
|
return getGroupRecipientDetails(context, groupId);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
ListenableFutureTask<RecipientDetails> future = new ListenableFutureTask<RecipientDetails>(task, null);
|
||||||
|
|
||||||
|
asyncRecipientResolver.submit(future);
|
||||||
|
|
||||||
|
Recipient recipient = new Recipient(groupId, ContactPhotoFactory.getDefaultContactPhoto(context), future);
|
||||||
|
recipientCache.put(groupId, recipient);
|
||||||
|
|
||||||
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void clearCache() {
|
public void clearCache() {
|
||||||
@ -140,6 +158,27 @@ public class RecipientProvider {
|
|||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private RecipientDetails getGroupRecipientDetails(Context context, String groupId) {
|
||||||
|
GroupDatabase.Reader reader = DatabaseFactory.getGroupDatabase(context).getGroup(groupId.substring(2));
|
||||||
|
GroupDatabase.GroupRecord record;
|
||||||
|
|
||||||
|
try {
|
||||||
|
if ((record = reader.getNext()) != null) {
|
||||||
|
byte[] avatarBytes = record.getAvatar();
|
||||||
|
Bitmap avatar;
|
||||||
|
|
||||||
|
if (avatarBytes == null) avatar = ContactPhotoFactory.getDefaultContactPhoto(context);
|
||||||
|
else avatar = BitmapFactory.decodeByteArray(avatarBytes, 0, avatarBytes.length);
|
||||||
|
|
||||||
|
return new RecipientDetails(record.getTitle(), null, avatar);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
reader.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
private Bitmap getContactPhoto(Context context, Uri uri) {
|
private Bitmap getContactPhoto(Context context, Uri uri) {
|
||||||
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
InputStream inputStream = ContactsContract.Contacts.openContactPhotoInputStream(context.getContentResolver(), uri);
|
||||||
|
|
||||||
|
75
src/org/thoughtcrime/securesms/service/AvatarDownloader.java
Normal file
75
src/org/thoughtcrime/securesms/service/AvatarDownloader.java
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
package org.thoughtcrime.securesms.service;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.content.Intent;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.util.Log;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.PartDatabase;
|
||||||
|
import org.thoughtcrime.securesms.push.PushServiceSocketFactory;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapDecodingException;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
import org.whispersystems.textsecure.crypto.AttachmentCipherInputStream;
|
||||||
|
import org.whispersystems.textsecure.crypto.InvalidMessageException;
|
||||||
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
|
import org.whispersystems.textsecure.push.PushServiceSocket;
|
||||||
|
|
||||||
|
import java.io.File;
|
||||||
|
import java.io.IOException;
|
||||||
|
import java.io.InputStream;
|
||||||
|
|
||||||
|
public class AvatarDownloader {
|
||||||
|
|
||||||
|
private final Context context;
|
||||||
|
|
||||||
|
public AvatarDownloader(Context context) {
|
||||||
|
this.context = context.getApplicationContext();
|
||||||
|
}
|
||||||
|
|
||||||
|
public void process(MasterSecret masterSecret, Intent intent) {
|
||||||
|
try {
|
||||||
|
if (!SendReceiveService.DOWNLOAD_AVATAR_ACTION.equals(intent.getAction()))
|
||||||
|
return;
|
||||||
|
|
||||||
|
String groupId = intent.getStringExtra("group_id");
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
GroupDatabase.Reader reader = database.getGroup(groupId);
|
||||||
|
|
||||||
|
GroupDatabase.GroupRecord record;
|
||||||
|
|
||||||
|
while ((record = reader.getNext()) != null) {
|
||||||
|
long avatarId = record.getAvatarId();
|
||||||
|
byte[] key = record.getAvatarKey();
|
||||||
|
String relay = record.getRelay();
|
||||||
|
|
||||||
|
if (avatarId == -1 || key == null) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
File attachment = downloadAttachment(relay, avatarId);
|
||||||
|
InputStream scaleInputStream = new AttachmentCipherInputStream(attachment, key);
|
||||||
|
InputStream measureInputStream = new AttachmentCipherInputStream(attachment, key);
|
||||||
|
Bitmap avatar = BitmapUtil.createScaledBitmap(measureInputStream, scaleInputStream, 500, 500);
|
||||||
|
|
||||||
|
database.updateAvatar(groupId, avatar);
|
||||||
|
|
||||||
|
avatar.recycle();
|
||||||
|
attachment.delete();
|
||||||
|
}
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("AvatarDownloader", e);
|
||||||
|
} catch (InvalidMessageException e) {
|
||||||
|
Log.w("AvatarDownloader", e);
|
||||||
|
} catch (BitmapDecodingException e) {
|
||||||
|
Log.w("AvatarDownloader", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private File downloadAttachment(String relay, long contentLocation) throws IOException {
|
||||||
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
|
return socket.retrieveAttachment(relay, contentLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
@ -11,6 +11,7 @@ import org.thoughtcrime.securesms.crypto.DecryptingQueue;
|
|||||||
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
|
import org.thoughtcrime.securesms.crypto.KeyExchangeProcessorV2;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
import org.thoughtcrime.securesms.database.EncryptingSmsDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
import org.thoughtcrime.securesms.database.MmsDatabase;
|
import org.thoughtcrime.securesms.database.MmsDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
import org.thoughtcrime.securesms.mms.IncomingMediaMessage;
|
||||||
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
import org.thoughtcrime.securesms.notifications.MessageNotifier;
|
||||||
@ -31,9 +32,13 @@ import org.whispersystems.textsecure.crypto.protocol.PreKeyWhisperMessage;
|
|||||||
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
import org.whispersystems.textsecure.push.IncomingPushMessage;
|
||||||
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
import org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent;
|
||||||
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
import org.whispersystems.textsecure.storage.InvalidKeyIdException;
|
||||||
|
import org.whispersystems.textsecure.util.Hex;
|
||||||
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
import ws.com.google.android.mms.MmsException;
|
||||||
|
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext.Type;
|
||||||
|
|
||||||
public class PushReceiver {
|
public class PushReceiver {
|
||||||
|
|
||||||
public static final int RESULT_OK = 0;
|
public static final int RESULT_OK = 0;
|
||||||
@ -67,8 +72,16 @@ public class PushReceiver {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void handleMessage(MasterSecret masterSecret, Intent intent) {
|
private void handleMessage(MasterSecret masterSecret, Intent intent) {
|
||||||
|
if (intent.getExtras() == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
IncomingPushMessage message = intent.getExtras().getParcelable("message");
|
IncomingPushMessage message = intent.getExtras().getParcelable("message");
|
||||||
|
|
||||||
|
if (message == null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
if (message.isSecureMessage()) handleReceivedSecureMessage(masterSecret, message);
|
if (message.isSecureMessage()) handleReceivedSecureMessage(masterSecret, message);
|
||||||
else if (message.isPreKeyBundle()) handleReceivedPreKeyBundle(masterSecret, message);
|
else if (message.isPreKeyBundle()) handleReceivedPreKeyBundle(masterSecret, message);
|
||||||
else handleReceivedMessage(masterSecret, message, false);
|
else handleReceivedMessage(masterSecret, message, false);
|
||||||
@ -105,7 +118,7 @@ public class PushReceiver {
|
|||||||
} else {
|
} else {
|
||||||
SmsTransportDetails transportDetails = new SmsTransportDetails();
|
SmsTransportDetails transportDetails = new SmsTransportDetails();
|
||||||
String encoded = new String(transportDetails.getEncodedMessage(message.getBody()));
|
String encoded = new String(transportDetails.getEncodedMessage(message.getBody()));
|
||||||
IncomingTextMessage textMessage = new IncomingTextMessage(message, "");
|
IncomingTextMessage textMessage = new IncomingTextMessage(message, "", null);
|
||||||
|
|
||||||
textMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
|
textMessage = new IncomingPreKeyBundleMessage(textMessage, encoded);
|
||||||
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, textMessage);
|
DatabaseFactory.getEncryptingSmsDatabase(context).insertMessageInbox(masterSecret, textMessage);
|
||||||
@ -133,12 +146,15 @@ public class PushReceiver {
|
|||||||
Log.w("PushReceiver", "Processing: " + new String(message.getBody()));
|
Log.w("PushReceiver", "Processing: " + new String(message.getBody()));
|
||||||
PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody());
|
PushMessageContent messageContent = PushMessageContent.parseFrom(message.getBody());
|
||||||
|
|
||||||
if (messageContent.getAttachmentsCount() > 0 || message.getDestinations().size() > 0) {
|
if (messageContent.hasGroup()) {
|
||||||
|
Log.w("PushReceiver", "Received push group message...");
|
||||||
|
handleReceivedGroupMessage(masterSecret, message, messageContent, secure);
|
||||||
|
} else if (messageContent.getAttachmentsCount() > 0) {
|
||||||
Log.w("PushReceiver", "Received push media message...");
|
Log.w("PushReceiver", "Received push media message...");
|
||||||
handleReceivedMediaMessage(masterSecret, message, messageContent, secure);
|
handleReceivedMediaMessage(masterSecret, message, messageContent, secure, null);
|
||||||
} else {
|
} else {
|
||||||
Log.w("PushReceiver", "Received push text message...");
|
Log.w("PushReceiver", "Received push text message...");
|
||||||
handleReceivedTextMessage(masterSecret, message, messageContent, secure);
|
handleReceivedTextMessage(masterSecret, message, messageContent, secure, null);
|
||||||
}
|
}
|
||||||
} catch (InvalidProtocolBufferException e) {
|
} catch (InvalidProtocolBufferException e) {
|
||||||
Log.w("PushReceiver", e);
|
Log.w("PushReceiver", e);
|
||||||
@ -146,17 +162,75 @@ public class PushReceiver {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleReceivedMediaMessage(MasterSecret masterSecret,
|
private void handleReceivedGroupMessage(MasterSecret masterSecret,
|
||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
PushMessageContent messageContent,
|
PushMessageContent messageContent,
|
||||||
boolean secure)
|
boolean secure)
|
||||||
{
|
{
|
||||||
|
if (messageContent.getGroup().getType().equals(Type.UNKNOWN)) {
|
||||||
|
Log.w("PushReceiver", "Received group message of unknown type: " +
|
||||||
|
messageContent.getGroup().getType().getNumber());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!messageContent.getGroup().hasId()) {
|
||||||
|
Log.w("PushReceiver", "Received group message with no id!");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
GroupDatabase database = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
GroupContext group = messageContent.getGroup();
|
||||||
|
byte[] id = group.getId().toByteArray();
|
||||||
|
int type = group.getType().getNumber();
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case Type.CREATE_VALUE:
|
||||||
|
database.create(id, message.getSource(), group.getName(), group.getMembersList(), group.getAvatar(), message.getRelay());
|
||||||
|
break;
|
||||||
|
case Type.ADD_VALUE:
|
||||||
|
database.add(id, message.getSource(), group.getMembersList());
|
||||||
|
break;
|
||||||
|
case Type.QUIT_VALUE:
|
||||||
|
database.remove(id, message.getSource());
|
||||||
|
break;
|
||||||
|
case Type.MODIFY_VALUE:
|
||||||
|
database.update(id, message.getSource(), group.getName(), group.getAvatar());
|
||||||
|
break;
|
||||||
|
case Type.DELIVER_VALUE:
|
||||||
|
break;
|
||||||
|
case Type.UNKNOWN_VALUE:
|
||||||
|
default:
|
||||||
|
Log.w("PushReceiver", "Received group message of unknown type: " + type);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (group.hasAvatar()) {
|
||||||
|
Intent intent = new Intent(context, SendReceiveService.class);
|
||||||
|
intent.setAction(SendReceiveService.DOWNLOAD_AVATAR_ACTION);
|
||||||
|
context.startService(intent);
|
||||||
|
}
|
||||||
|
|
||||||
|
String groupId = "g_" + Hex.toString(group.getId().toByteArray());
|
||||||
|
|
||||||
|
if (messageContent.getAttachmentsCount() > 0) {
|
||||||
|
handleReceivedMediaMessage(masterSecret, message, messageContent, secure, groupId);
|
||||||
|
} else if (messageContent.hasBody()) {
|
||||||
|
handleReceivedTextMessage(masterSecret, message, messageContent, secure, groupId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void handleReceivedMediaMessage(MasterSecret masterSecret,
|
||||||
|
IncomingPushMessage message,
|
||||||
|
PushMessageContent messageContent,
|
||||||
|
boolean secure, String groupId)
|
||||||
|
{
|
||||||
|
|
||||||
try {
|
try {
|
||||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||||
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
MmsDatabase database = DatabaseFactory.getMmsDatabase(context);
|
||||||
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, localNumber,
|
IncomingMediaMessage mediaMessage = new IncomingMediaMessage(masterSecret, localNumber,
|
||||||
message, messageContent);
|
message, messageContent,
|
||||||
|
groupId);
|
||||||
|
|
||||||
Pair<Long, Long> messageAndThreadId;
|
Pair<Long, Long> messageAndThreadId;
|
||||||
|
|
||||||
@ -181,10 +255,10 @@ public class PushReceiver {
|
|||||||
private void handleReceivedTextMessage(MasterSecret masterSecret,
|
private void handleReceivedTextMessage(MasterSecret masterSecret,
|
||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
PushMessageContent messageContent,
|
PushMessageContent messageContent,
|
||||||
boolean secure)
|
boolean secure, String groupId)
|
||||||
{
|
{
|
||||||
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
EncryptingSmsDatabase database = DatabaseFactory.getEncryptingSmsDatabase(context);
|
||||||
IncomingTextMessage textMessage = new IncomingTextMessage(message, "");
|
IncomingTextMessage textMessage = new IncomingTextMessage(message, "", groupId);
|
||||||
|
|
||||||
if (secure) {
|
if (secure) {
|
||||||
textMessage = new IncomingEncryptedMessage(textMessage, "");
|
textMessage = new IncomingEncryptedMessage(textMessage, "");
|
||||||
@ -210,7 +284,7 @@ public class PushReceiver {
|
|||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
boolean invalidVersion)
|
boolean invalidVersion)
|
||||||
{
|
{
|
||||||
IncomingTextMessage corruptedMessage = new IncomingTextMessage(message, "");
|
IncomingTextMessage corruptedMessage = new IncomingTextMessage(message, "", null);
|
||||||
IncomingKeyExchangeMessage corruptedKeyMessage = new IncomingKeyExchangeMessage(corruptedMessage, "");
|
IncomingKeyExchangeMessage corruptedKeyMessage = new IncomingKeyExchangeMessage(corruptedMessage, "");
|
||||||
|
|
||||||
if (!invalidVersion) corruptedKeyMessage.setCorrupted(true);
|
if (!invalidVersion) corruptedKeyMessage.setCorrupted(true);
|
||||||
@ -235,7 +309,7 @@ public class PushReceiver {
|
|||||||
IncomingPushMessage message,
|
IncomingPushMessage message,
|
||||||
boolean secure)
|
boolean secure)
|
||||||
{
|
{
|
||||||
IncomingTextMessage placeholder = new IncomingTextMessage(message, "");
|
IncomingTextMessage placeholder = new IncomingTextMessage(message, "", null);
|
||||||
|
|
||||||
if (secure) {
|
if (secure) {
|
||||||
placeholder = new IncomingEncryptedMessage(placeholder, "");
|
placeholder = new IncomingEncryptedMessage(placeholder, "");
|
||||||
|
@ -57,6 +57,7 @@ public class SendReceiveService extends Service {
|
|||||||
public static final String RECEIVE_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_PUSH_ACTION";
|
public static final String RECEIVE_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.RECEIVE_PUSH_ACTION";
|
||||||
public static final String DECRYPTED_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DECRYPTED_PUSH_ACTION";
|
public static final String DECRYPTED_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DECRYPTED_PUSH_ACTION";
|
||||||
public static final String DOWNLOAD_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_PUSH_ACTION";
|
public static final String DOWNLOAD_PUSH_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_PUSH_ACTION";
|
||||||
|
public static final String DOWNLOAD_AVATAR_ACTION = "org.thoughtcrime.securesms.SendReceiveService.DOWNLOAD_AVATAR_ACTION";
|
||||||
|
|
||||||
private static final int SEND_SMS = 0;
|
private static final int SEND_SMS = 0;
|
||||||
private static final int RECEIVE_SMS = 1;
|
private static final int RECEIVE_SMS = 1;
|
||||||
@ -66,16 +67,18 @@ public class SendReceiveService extends Service {
|
|||||||
private static final int DOWNLOAD_MMS_PENDING = 5;
|
private static final int DOWNLOAD_MMS_PENDING = 5;
|
||||||
private static final int RECEIVE_PUSH = 6;
|
private static final int RECEIVE_PUSH = 6;
|
||||||
private static final int DOWNLOAD_PUSH = 7;
|
private static final int DOWNLOAD_PUSH = 7;
|
||||||
|
private static final int DOWNLOAD_AVATAR = 8;
|
||||||
|
|
||||||
private ToastHandler toastHandler;
|
private ToastHandler toastHandler;
|
||||||
|
|
||||||
private SmsReceiver smsReceiver;
|
private SmsReceiver smsReceiver;
|
||||||
private SmsSender smsSender;
|
private SmsSender smsSender;
|
||||||
private MmsReceiver mmsReceiver;
|
private MmsReceiver mmsReceiver;
|
||||||
private MmsSender mmsSender;
|
private MmsSender mmsSender;
|
||||||
private MmsDownloader mmsDownloader;
|
private MmsDownloader mmsDownloader;
|
||||||
private PushReceiver pushReceiver;
|
private PushReceiver pushReceiver;
|
||||||
private PushDownloader pushDownloader;
|
private PushDownloader pushDownloader;
|
||||||
|
private AvatarDownloader avatarDownloader;
|
||||||
|
|
||||||
private MasterSecret masterSecret;
|
private MasterSecret masterSecret;
|
||||||
private boolean hasSecret;
|
private boolean hasSecret;
|
||||||
@ -122,6 +125,8 @@ public class SendReceiveService extends Service {
|
|||||||
scheduleSecretRequiredIntent(RECEIVE_PUSH, intent);
|
scheduleSecretRequiredIntent(RECEIVE_PUSH, intent);
|
||||||
else if (action.equals(DOWNLOAD_PUSH_ACTION))
|
else if (action.equals(DOWNLOAD_PUSH_ACTION))
|
||||||
scheduleSecretRequiredIntent(DOWNLOAD_PUSH, intent);
|
scheduleSecretRequiredIntent(DOWNLOAD_PUSH, intent);
|
||||||
|
else if (action.equals(DOWNLOAD_AVATAR_ACTION))
|
||||||
|
scheduleIntent(DOWNLOAD_AVATAR, intent);
|
||||||
else
|
else
|
||||||
Log.w("SendReceiveService", "Received intent with unknown action: " + intent.getAction());
|
Log.w("SendReceiveService", "Received intent with unknown action: " + intent.getAction());
|
||||||
}
|
}
|
||||||
@ -148,13 +153,14 @@ public class SendReceiveService extends Service {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void initializeProcessors() {
|
private void initializeProcessors() {
|
||||||
smsReceiver = new SmsReceiver(this);
|
smsReceiver = new SmsReceiver(this);
|
||||||
smsSender = new SmsSender(this, toastHandler);
|
smsSender = new SmsSender(this, toastHandler);
|
||||||
mmsReceiver = new MmsReceiver(this);
|
mmsReceiver = new MmsReceiver(this);
|
||||||
mmsSender = new MmsSender(this, toastHandler);
|
mmsSender = new MmsSender(this, toastHandler);
|
||||||
mmsDownloader = new MmsDownloader(this, toastHandler);
|
mmsDownloader = new MmsDownloader(this, toastHandler);
|
||||||
pushReceiver = new PushReceiver(this);
|
pushReceiver = new PushReceiver(this);
|
||||||
pushDownloader = new PushDownloader(this);
|
pushDownloader = new PushDownloader(this);
|
||||||
|
avatarDownloader = new AvatarDownloader(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeWorkQueue() {
|
private void initializeWorkQueue() {
|
||||||
@ -235,14 +241,15 @@ public class SendReceiveService extends Service {
|
|||||||
@Override
|
@Override
|
||||||
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;
|
||||||
case DOWNLOAD_MMS_PENDING: mmsDownloader.process(masterSecret, intent); return;
|
case DOWNLOAD_MMS_PENDING: mmsDownloader.process(masterSecret, intent); return;
|
||||||
case RECEIVE_PUSH: pushReceiver.process(masterSecret, intent); return;
|
case RECEIVE_PUSH: pushReceiver.process(masterSecret, intent); return;
|
||||||
case DOWNLOAD_PUSH: pushDownloader.process(masterSecret, intent); return;
|
case DOWNLOAD_PUSH: pushDownloader.process(masterSecret, intent); return;
|
||||||
|
case DOWNLOAD_AVATAR: avatarDownloader.process(masterSecret, intent); return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,6 +29,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
private final boolean replyPathPresent;
|
private final boolean replyPathPresent;
|
||||||
private final String pseudoSubject;
|
private final String pseudoSubject;
|
||||||
private final long sentTimestampMillis;
|
private final long sentTimestampMillis;
|
||||||
|
private final String groupId;
|
||||||
|
|
||||||
public IncomingTextMessage(SmsMessage message) {
|
public IncomingTextMessage(SmsMessage message) {
|
||||||
this.message = message.getDisplayMessageBody();
|
this.message = message.getDisplayMessageBody();
|
||||||
@ -38,9 +39,10 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.replyPathPresent = message.isReplyPathPresent();
|
this.replyPathPresent = message.isReplyPathPresent();
|
||||||
this.pseudoSubject = message.getPseudoSubject();
|
this.pseudoSubject = message.getPseudoSubject();
|
||||||
this.sentTimestampMillis = message.getTimestampMillis();
|
this.sentTimestampMillis = message.getTimestampMillis();
|
||||||
|
this.groupId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingTextMessage(IncomingPushMessage message, String encodedBody) {
|
public IncomingTextMessage(IncomingPushMessage message, String encodedBody, String groupId) {
|
||||||
this.message = encodedBody;
|
this.message = encodedBody;
|
||||||
this.sender = message.getSource();
|
this.sender = message.getSource();
|
||||||
this.protocol = 31337;
|
this.protocol = 31337;
|
||||||
@ -48,6 +50,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.replyPathPresent = true;
|
this.replyPathPresent = true;
|
||||||
this.pseudoSubject = "";
|
this.pseudoSubject = "";
|
||||||
this.sentTimestampMillis = message.getTimestampMillis();
|
this.sentTimestampMillis = message.getTimestampMillis();
|
||||||
|
this.groupId = groupId;
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingTextMessage(Parcel in) {
|
public IncomingTextMessage(Parcel in) {
|
||||||
@ -58,6 +61,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.replyPathPresent = (in.readInt() == 1);
|
this.replyPathPresent = (in.readInt() == 1);
|
||||||
this.pseudoSubject = in.readString();
|
this.pseudoSubject = in.readString();
|
||||||
this.sentTimestampMillis = in.readLong();
|
this.sentTimestampMillis = in.readLong();
|
||||||
|
this.groupId = in.readString();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
|
public IncomingTextMessage(IncomingTextMessage base, String newBody) {
|
||||||
@ -68,6 +72,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.replyPathPresent = base.isReplyPathPresent();
|
this.replyPathPresent = base.isReplyPathPresent();
|
||||||
this.pseudoSubject = base.getPseudoSubject();
|
this.pseudoSubject = base.getPseudoSubject();
|
||||||
this.sentTimestampMillis = base.getSentTimestampMillis();
|
this.sentTimestampMillis = base.getSentTimestampMillis();
|
||||||
|
this.groupId = base.getGroupId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
|
public IncomingTextMessage(List<IncomingTextMessage> fragments) {
|
||||||
@ -84,6 +89,7 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
this.replyPathPresent = fragments.get(0).isReplyPathPresent();
|
this.replyPathPresent = fragments.get(0).isReplyPathPresent();
|
||||||
this.pseudoSubject = fragments.get(0).getPseudoSubject();
|
this.pseudoSubject = fragments.get(0).getPseudoSubject();
|
||||||
this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis();
|
this.sentTimestampMillis = fragments.get(0).getSentTimestampMillis();
|
||||||
|
this.groupId = fragments.get(0).getGroupId();
|
||||||
}
|
}
|
||||||
|
|
||||||
public long getSentTimestampMillis() {
|
public long getSentTimestampMillis() {
|
||||||
@ -130,6 +136,10 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int describeContents() {
|
public int describeContents() {
|
||||||
return 0;
|
return 0;
|
||||||
@ -144,5 +154,6 @@ public class IncomingTextMessage implements Parcelable {
|
|||||||
out.writeInt(replyPathPresent ? 1 : 0);
|
out.writeInt(replyPathPresent ? 1 : 0);
|
||||||
out.writeString(pseudoSubject);
|
out.writeString(pseudoSubject);
|
||||||
out.writeLong(sentTimestampMillis);
|
out.writeLong(sentTimestampMillis);
|
||||||
|
out.writeString(groupId);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -116,15 +116,20 @@ public class MessageSender {
|
|||||||
SendReq sendRequest, long threadId, int distributionType, boolean secure)
|
SendReq sendRequest, long threadId, int distributionType, boolean secure)
|
||||||
throws MmsException
|
throws MmsException
|
||||||
{
|
{
|
||||||
|
Log.w("MessageSender", "Distribution type: " + distributionType);
|
||||||
|
|
||||||
String[] recipientsArray = recipients.toNumberStringArray(true);
|
String[] recipientsArray = recipients.toNumberStringArray(true);
|
||||||
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
|
EncodedStringValue[] encodedNumbers = EncodedStringValue.encodeStrings(recipientsArray);
|
||||||
|
|
||||||
if (recipients.isSingleRecipient()) {
|
if (recipients.isSingleRecipient()) {
|
||||||
|
Log.w("MessageSender", "Single recipient!?");
|
||||||
sendRequest.setTo(encodedNumbers);
|
sendRequest.setTo(encodedNumbers);
|
||||||
} else if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
|
} else if (distributionType == ThreadDatabase.DistributionTypes.BROADCAST) {
|
||||||
|
Log.w("MessageSender", "Broadcast...");
|
||||||
sendRequest.setBcc(encodedNumbers);
|
sendRequest.setBcc(encodedNumbers);
|
||||||
} else if (distributionType == ThreadDatabase.DistributionTypes.CONVERSATION) {
|
} else if (distributionType == ThreadDatabase.DistributionTypes.CONVERSATION || distributionType == 0) {
|
||||||
sendRequest.setCc(encodedNumbers);
|
Log.w("MessageSender", "Conversation...");
|
||||||
|
sendRequest.setTo(encodedNumbers);
|
||||||
}
|
}
|
||||||
|
|
||||||
long messageId = DatabaseFactory.getMmsDatabase(context)
|
long messageId = DatabaseFactory.getMmsDatabase(context)
|
||||||
|
@ -122,4 +122,9 @@ public class BitmapUtil {
|
|||||||
return output;
|
return output;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static byte[] toByteArray(Bitmap bitmap) {
|
||||||
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||||
|
return stream.toByteArray();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user