mirror of
https://github.com/oxen-io/session-android.git
synced 2025-01-12 12:03:39 +00:00
Fill in group creation actions
This commit is contained in:
parent
41aa53dd66
commit
7c46f3cbf8
@ -6,10 +6,10 @@ import android.content.Intent;
|
|||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.os.Looper;
|
|
||||||
import android.text.Editable;
|
import android.text.Editable;
|
||||||
import android.text.TextWatcher;
|
import android.text.TextWatcher;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
import android.util.Pair;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.widget.EditText;
|
import android.widget.EditText;
|
||||||
import android.widget.ImageView;
|
import android.widget.ImageView;
|
||||||
@ -19,31 +19,43 @@ import android.widget.TextView;
|
|||||||
import com.actionbarsherlock.view.Menu;
|
import com.actionbarsherlock.view.Menu;
|
||||||
import com.actionbarsherlock.view.MenuInflater;
|
import com.actionbarsherlock.view.MenuInflater;
|
||||||
import com.actionbarsherlock.view.MenuItem;
|
import com.actionbarsherlock.view.MenuItem;
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
|
import org.thoughtcrime.securesms.transport.PushTransport;
|
||||||
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
||||||
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
||||||
import org.thoughtcrime.securesms.util.DynamicTheme;
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
||||||
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.textsecure.crypto.MasterSecret;
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
||||||
import org.whispersystems.textsecure.directory.Directory;
|
import org.whispersystems.textsecure.directory.Directory;
|
||||||
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
||||||
|
import org.whispersystems.textsecure.push.PushAttachmentPointer;
|
||||||
import org.whispersystems.textsecure.util.InvalidNumberException;
|
import org.whispersystems.textsecure.util.InvalidNumberException;
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
import java.io.ByteArrayOutputStream;
|
||||||
|
import java.io.IOException;
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
import java.util.Collection;
|
||||||
import java.util.HashSet;
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
|
|
||||||
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.AttachmentPointer;
|
||||||
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
||||||
|
|
||||||
|
|
||||||
public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActivity {
|
public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||||
@ -237,7 +249,15 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
|||||||
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||||
byteArray = stream.toByteArray();
|
byteArray = stream.toByteArray();
|
||||||
}
|
}
|
||||||
|
try {
|
||||||
handleCreatePushGroup(groupName.getText().toString(), byteArray, selectedContacts);
|
handleCreatePushGroup(groupName.getText().toString(), byteArray, selectedContacts);
|
||||||
|
} catch (IOException e) {
|
||||||
|
// TODO Jake's gonna fill this in.
|
||||||
|
Log.w("GroupCreateActivity", e);
|
||||||
|
} catch (InvalidNumberException e) {
|
||||||
|
// TODO jake's gonna fill this in.
|
||||||
|
Log.w("GroupCreateActivity", e);
|
||||||
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -334,11 +354,62 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCreatePushGroup(String groupName, byte[] avatar, Set<Recipient> members) {
|
private Pair<Long, List<Recipient>> handleCreatePushGroup(String groupName,
|
||||||
//todo
|
byte[] avatar,
|
||||||
|
Set<Recipient> members)
|
||||||
|
throws IOException, InvalidNumberException
|
||||||
|
{
|
||||||
|
List<String> memberE164Numbers = getE164Numbers(members);
|
||||||
|
PushTransport transport = new PushTransport(this, masterSecret);
|
||||||
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
||||||
|
ThreadDatabase threadDatabase = DatabaseFactory.getThreadDatabase(this);
|
||||||
|
byte[] groupId = groupDatabase.allocateGroupId();
|
||||||
|
AttachmentPointer avatarPointer = null;
|
||||||
|
|
||||||
|
GroupContext.Builder builder = GroupContext.newBuilder()
|
||||||
|
.setId(ByteString.copyFrom(groupId))
|
||||||
|
.setType(GroupContext.Type.CREATE)
|
||||||
|
.setName(groupName)
|
||||||
|
.addAllMembers(memberE164Numbers);
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
PushAttachmentPointer pointer = transport.createAttachment("image/png", avatar);
|
||||||
|
avatarPointer = AttachmentPointer.newBuilder()
|
||||||
|
.setKey(ByteString.copyFrom(pointer.getKey()))
|
||||||
|
.setContentType(pointer.getContentType())
|
||||||
|
.setId(pointer.getId()).build();
|
||||||
|
builder.setAvatar(avatarPointer);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleCreateMmsGroup(Set<Recipient> members) {
|
List<Recipient> failures = transport.deliver(new LinkedList<Recipient>(members), builder.build());
|
||||||
//todo
|
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
||||||
|
memberE164Numbers, avatarPointer, null);
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
groupDatabase.updateAvatar(groupId, avatar);
|
||||||
|
}
|
||||||
|
|
||||||
|
long threadId = threadDatabase.getThreadIdForGroup(GroupUtil.getEncodedId(groupId));
|
||||||
|
|
||||||
|
return new Pair<Long, List<Recipient>>(threadId, failures);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long handleCreateMmsGroup(Set<Recipient> members) {
|
||||||
|
Recipients recipients = new Recipients(new LinkedList<Recipient>(members));
|
||||||
|
return DatabaseFactory.getThreadDatabase(this)
|
||||||
|
.getThreadIdFor(recipients,
|
||||||
|
ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||||
|
}
|
||||||
|
|
||||||
|
private List<String> getE164Numbers(Set<Recipient> recipients)
|
||||||
|
throws InvalidNumberException
|
||||||
|
{
|
||||||
|
List<String> results = new LinkedList<String>();
|
||||||
|
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
results.add(Util.canonicalizeNumber(this, recipient.getNumber()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -135,8 +135,10 @@ public class KeyExchangeProcessorV2 extends KeyExchangeProcessor {
|
|||||||
DatabaseFactory.getIdentityDatabase(context)
|
DatabaseFactory.getIdentityDatabase(context)
|
||||||
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), message.getIdentityKey());
|
.saveIdentity(masterSecret, recipientDevice.getRecipientId(), message.getIdentityKey());
|
||||||
|
|
||||||
|
if (threadId != -1) {
|
||||||
broadcastSecurityUpdateEvent(context, threadId);
|
broadcastSecurityUpdateEvent(context, threadId);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void processKeyExchangeMessage(KeyExchangeMessage _message, long threadId)
|
public void processKeyExchangeMessage(KeyExchangeMessage _message, long threadId)
|
||||||
|
@ -18,6 +18,8 @@ import org.whispersystems.textsecure.util.Hex;
|
|||||||
import org.whispersystems.textsecure.util.Util;
|
import org.whispersystems.textsecure.util.Util;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.SecureRandom;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
@ -122,14 +124,17 @@ public class GroupDatabase extends Database {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public void updateAvatar(byte[] groupId, Bitmap avatar) {
|
public void updateAvatar(byte[] groupId, Bitmap avatar) {
|
||||||
|
updateAvatar(groupId, BitmapUtil.toByteArray(avatar));
|
||||||
|
}
|
||||||
|
|
||||||
|
public void updateAvatar(byte[] groupId, byte[] avatar) {
|
||||||
ContentValues contentValues = new ContentValues();
|
ContentValues contentValues = new ContentValues();
|
||||||
contentValues.put(AVATAR, BitmapUtil.toByteArray(avatar));
|
contentValues.put(AVATAR, avatar);
|
||||||
|
|
||||||
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
|
databaseHelper.getWritableDatabase().update(TABLE_NAME, contentValues, GROUP_ID + " = ?",
|
||||||
new String[] {GroupUtil.getEncodedId(groupId)});
|
new String[] {GroupUtil.getEncodedId(groupId)});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public void add(byte[] id, String source, List<String> members) {
|
public void add(byte[] id, String source, List<String> members) {
|
||||||
List<String> currentMembers = getCurrentMembers(id);
|
List<String> currentMembers = getCurrentMembers(id);
|
||||||
|
|
||||||
@ -177,6 +182,16 @@ public class GroupDatabase extends Database {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public byte[] allocateGroupId() {
|
||||||
|
try {
|
||||||
|
byte[] groupId = new byte[16];
|
||||||
|
SecureRandom.getInstance("SHA1PRNG").nextBytes(groupId);
|
||||||
|
return groupId;
|
||||||
|
} catch (NoSuchAlgorithmException e) {
|
||||||
|
throw new AssertionError(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
public static class Reader {
|
public static class Reader {
|
||||||
|
|
||||||
|
57
src/org/thoughtcrime/securesms/push/GroupActionRecord.java
Normal file
57
src/org/thoughtcrime/securesms/push/GroupActionRecord.java
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
package org.thoughtcrime.securesms.push;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
public class GroupActionRecord {
|
||||||
|
|
||||||
|
private static final int CREATE_GROUP_TYPE = 1;
|
||||||
|
private static final int ADD_USERS_TYPE = 2;
|
||||||
|
private static final int LEAVE_GROUP_TYPE = 3;
|
||||||
|
|
||||||
|
private final int type;
|
||||||
|
private final Set<Recipient> recipients;
|
||||||
|
private final byte[] groupId;
|
||||||
|
private final String groupName;
|
||||||
|
private final byte[] avatar;
|
||||||
|
|
||||||
|
public GroupActionRecord(int type, byte[] groupId, String groupName,
|
||||||
|
byte[] avatar, Set<Recipient> recipients)
|
||||||
|
{
|
||||||
|
this.type = type;
|
||||||
|
this.groupId = groupId;
|
||||||
|
this.groupName = groupName;
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.recipients = recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isCreateAction() {
|
||||||
|
return type== CREATE_GROUP_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isAddUsersAction() {
|
||||||
|
return type == ADD_USERS_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isLeaveAction() {
|
||||||
|
return type == LEAVE_GROUP_TYPE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
public Set<Recipient> getRecipients() {
|
||||||
|
return recipients;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getGroupId() {
|
||||||
|
return groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getGroupName() {
|
||||||
|
return groupName;
|
||||||
|
}
|
||||||
|
|
||||||
|
public byte[] getAvatar() {
|
||||||
|
return avatar;
|
||||||
|
}
|
||||||
|
}
|
@ -123,6 +123,45 @@ public class PushTransport extends BaseTransport {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public List<Recipient> deliver(List<Recipient> recipients,
|
||||||
|
PushMessageContent.GroupContext groupAction)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
|
byte[] plaintext = PushMessageContent.newBuilder()
|
||||||
|
.setGroup(groupAction)
|
||||||
|
.build().toByteArray();
|
||||||
|
List<Recipient> failures = new LinkedList<Recipient>();
|
||||||
|
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
try {
|
||||||
|
deliver(socket, recipient, -1, plaintext);
|
||||||
|
} catch (UnregisteredUserException e) {
|
||||||
|
Log.w("PushTransport", e);
|
||||||
|
failures.add(recipient);
|
||||||
|
} catch (InvalidNumberException e) {
|
||||||
|
Log.w("PushTransport", e);
|
||||||
|
failures.add(recipient);
|
||||||
|
} catch (IOException e) {
|
||||||
|
Log.w("PushTransport", e);
|
||||||
|
failures.add(recipient);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failures.size() == recipients.size()) {
|
||||||
|
throw new IOException("Total failure.");
|
||||||
|
}
|
||||||
|
|
||||||
|
return failures;
|
||||||
|
}
|
||||||
|
|
||||||
|
public PushAttachmentPointer createAttachment(String contentType, byte[] data)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
PushServiceSocket socket = PushServiceSocketFactory.create(context);
|
||||||
|
return getPushAttachmentPointer(socket, contentType, data);
|
||||||
|
}
|
||||||
|
|
||||||
private void deliver(PushServiceSocket socket, Recipient recipient, long threadId, byte[] plaintext)
|
private void deliver(PushServiceSocket socket, Recipient recipient, long threadId, byte[] plaintext)
|
||||||
throws IOException, InvalidNumberException
|
throws IOException, InvalidNumberException
|
||||||
{
|
{
|
||||||
@ -151,19 +190,26 @@ public class PushTransport extends BaseTransport {
|
|||||||
ContentType.isAudioType(contentType) ||
|
ContentType.isAudioType(contentType) ||
|
||||||
ContentType.isVideoType(contentType))
|
ContentType.isVideoType(contentType))
|
||||||
{
|
{
|
||||||
AttachmentCipher cipher = new AttachmentCipher();
|
attachments.add(getPushAttachmentPointer(socket, contentType, body.getPart(i).getData()));
|
||||||
byte[] key = cipher.getCombinedKeyMaterial();
|
|
||||||
byte[] ciphertextAttachment = cipher.encrypt(body.getPart(i).getData());
|
|
||||||
PushAttachmentData attachmentData = new PushAttachmentData(contentType, ciphertextAttachment);
|
|
||||||
long attachmentId = socket.sendAttachment(attachmentData);
|
|
||||||
|
|
||||||
attachments.add(new PushAttachmentPointer(contentType, attachmentId, key));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return attachments;
|
return attachments;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private PushAttachmentPointer getPushAttachmentPointer(PushServiceSocket socket,
|
||||||
|
String contentType, byte[] data)
|
||||||
|
throws IOException
|
||||||
|
{
|
||||||
|
AttachmentCipher cipher = new AttachmentCipher();
|
||||||
|
byte[] key = cipher.getCombinedKeyMaterial();
|
||||||
|
byte[] ciphertextAttachment = cipher.encrypt(data);
|
||||||
|
PushAttachmentData attachmentData = new PushAttachmentData(contentType, ciphertextAttachment);
|
||||||
|
long attachmentId = socket.sendAttachment(attachmentData);
|
||||||
|
|
||||||
|
return new PushAttachmentPointer(contentType, attachmentId, key);
|
||||||
|
}
|
||||||
|
|
||||||
private void handleMismatchedDevices(PushServiceSocket socket, long threadId,
|
private void handleMismatchedDevices(PushServiceSocket socket, long threadId,
|
||||||
Recipient recipient,
|
Recipient recipient,
|
||||||
MismatchedDevices mismatchedDevices)
|
MismatchedDevices mismatchedDevices)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user