mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-25 01:07:47 +00:00
parent
75483299dc
commit
d05097a6fd
@ -21,16 +21,12 @@ import android.app.Activity;
|
|||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
import android.graphics.BitmapFactory;
|
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.AsyncTask;
|
import android.os.AsyncTask;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.support.annotation.Nullable;
|
import android.support.annotation.Nullable;
|
||||||
import android.text.Editable;
|
|
||||||
import android.text.TextWatcher;
|
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
|
||||||
import android.view.Menu;
|
import android.view.Menu;
|
||||||
import android.view.MenuInflater;
|
import android.view.MenuInflater;
|
||||||
import android.view.MenuItem;
|
import android.view.MenuItem;
|
||||||
@ -44,58 +40,53 @@ import android.widget.Toast;
|
|||||||
import com.bumptech.glide.Glide;
|
import com.bumptech.glide.Glide;
|
||||||
import com.bumptech.glide.request.animation.GlideAnimation;
|
import com.bumptech.glide.request.animation.GlideAnimation;
|
||||||
import com.bumptech.glide.request.target.SimpleTarget;
|
import com.bumptech.glide.request.target.SimpleTarget;
|
||||||
import com.google.protobuf.ByteString;
|
|
||||||
import com.soundcloud.android.crop.Crop;
|
import com.soundcloud.android.crop.Crop;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.attachments.Attachment;
|
|
||||||
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
|
||||||
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
||||||
import org.thoughtcrime.securesms.contacts.ContactsCursorLoader;
|
import org.thoughtcrime.securesms.components.PushRecipientsPanel.RecipientsPanelChangedListener;
|
||||||
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
||||||
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
import org.thoughtcrime.securesms.database.GroupDatabase;
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase.GroupRecord;
|
||||||
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
import org.thoughtcrime.securesms.database.NotInDirectoryException;
|
||||||
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
|
||||||
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
import org.thoughtcrime.securesms.database.TextSecureDirectory;
|
||||||
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
||||||
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
import org.thoughtcrime.securesms.groups.GroupManager;
|
||||||
|
import org.thoughtcrime.securesms.groups.GroupManager.GroupActionResult;
|
||||||
import org.thoughtcrime.securesms.mms.RoundedCorners;
|
import org.thoughtcrime.securesms.mms.RoundedCorners;
|
||||||
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
|
|
||||||
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.Recipients;
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
import org.thoughtcrime.securesms.sms.MessageSender;
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
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.GroupUtil;
|
||||||
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
import org.thoughtcrime.securesms.util.ProgressDialogAsyncTask;
|
||||||
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
||||||
|
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter.OnRecipientDeletedListener;
|
||||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.thoughtcrime.securesms.util.ViewUtil;
|
||||||
|
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||||
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||||
import org.whispersystems.textsecure.internal.push.TextSecureProtos.GroupContext;
|
|
||||||
|
|
||||||
import java.io.ByteArrayOutputStream;
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
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 ws.com.google.android.mms.ContentType;
|
|
||||||
import ws.com.google.android.mms.MmsException;
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Activity to create and update groups
|
* Activity to create and update groups
|
||||||
*
|
*
|
||||||
* @author Jake McGinty
|
* @author Jake McGinty
|
||||||
*/
|
*/
|
||||||
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
public class GroupCreateActivity extends PassphraseRequiredActionBarActivity
|
||||||
|
implements OnRecipientDeletedListener,
|
||||||
|
RecipientsPanelChangedListener
|
||||||
|
{
|
||||||
|
|
||||||
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
||||||
|
|
||||||
@ -106,25 +97,16 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
||||||
|
|
||||||
private static final int PICK_CONTACT = 1;
|
private static final int PICK_CONTACT = 1;
|
||||||
private static final int PICK_AVATAR = 2;
|
|
||||||
public static final int AVATAR_SIZE = 210;
|
public static final int AVATAR_SIZE = 210;
|
||||||
|
|
||||||
private EditText groupName;
|
private EditText groupName;
|
||||||
private ListView lv;
|
private ListView lv;
|
||||||
private PushRecipientsPanel recipientsPanel;
|
private ImageView avatar;
|
||||||
private ImageView avatar;
|
private TextView creatingText;
|
||||||
private TextView creatingText;
|
private MasterSecret masterSecret;
|
||||||
|
private Bitmap avatarBmp;
|
||||||
|
|
||||||
private Recipient groupRecipient = null;
|
@NonNull private Optional<GroupData> groupToUpdate = Optional.absent();
|
||||||
private long groupThread = -1;
|
|
||||||
private byte[] groupId = null;
|
|
||||||
private Set<Recipient> existingContacts = null;
|
|
||||||
private String existingTitle = null;
|
|
||||||
private Bitmap existingAvatarBmp = null;
|
|
||||||
|
|
||||||
private MasterSecret masterSecret;
|
|
||||||
private Bitmap avatarBmp;
|
|
||||||
private Set<Recipient> selectedContacts;
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPreCreate() {
|
protected void onPreCreate() {
|
||||||
@ -137,10 +119,10 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
this.masterSecret = masterSecret;
|
this.masterSecret = masterSecret;
|
||||||
|
|
||||||
setContentView(R.layout.group_create_activity);
|
setContentView(R.layout.group_create_activity);
|
||||||
|
//noinspection ConstantConditions
|
||||||
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
||||||
|
|
||||||
selectedContacts = new HashSet<>();
|
|
||||||
initializeResources();
|
initializeResources();
|
||||||
|
initializeExistingGroup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -148,17 +130,14 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
super.onResume();
|
super.onResume();
|
||||||
dynamicTheme.onResume(this);
|
dynamicTheme.onResume(this);
|
||||||
dynamicLanguage.onResume(this);
|
dynamicLanguage.onResume(this);
|
||||||
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_title);
|
updateViewState();
|
||||||
if (!TextSecurePreferences.isPushRegistered(this)) {
|
|
||||||
disableWhisperGroupUi(R.string.GroupCreateActivity_you_dont_support_push);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private boolean whisperGroupUiEnabled() {
|
private boolean isSignalGroup() {
|
||||||
return groupName.isEnabled() && avatar.isEnabled();
|
return TextSecurePreferences.isPushRegistered(this) && !getAdapter().hasNonPushMembers();
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableWhisperGroupUi(int reasonResId) {
|
private void disableSignalGroupViews(int reasonResId) {
|
||||||
View pushDisabled = findViewById(R.id.push_disabled);
|
View pushDisabled = findViewById(R.id.push_disabled);
|
||||||
pushDisabled.setVisibility(View.VISIBLE);
|
pushDisabled.setVisibility(View.VISIBLE);
|
||||||
((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId);
|
((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId);
|
||||||
@ -166,46 +145,44 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
groupName.setEnabled(false);
|
groupName.setEnabled(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableWhisperGroupUi() {
|
private void enableSignalGroupViews() {
|
||||||
findViewById(R.id.push_disabled).setVisibility(View.GONE);
|
findViewById(R.id.push_disabled).setVisibility(View.GONE);
|
||||||
avatar.setEnabled(true);
|
avatar.setEnabled(true);
|
||||||
groupName.setEnabled(true);
|
groupName.setEnabled(true);
|
||||||
final CharSequence groupNameText = groupName.getText();
|
}
|
||||||
if (groupNameText != null && groupNameText.length() > 0) {
|
|
||||||
getSupportActionBar().setTitle(groupNameText);
|
@SuppressWarnings("ConstantConditions")
|
||||||
|
private void updateViewState() {
|
||||||
|
if (!TextSecurePreferences.isPushRegistered(this)) {
|
||||||
|
disableSignalGroupViews(R.string.GroupCreateActivity_you_dont_support_push);
|
||||||
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
|
||||||
|
} else if (getAdapter().hasNonPushMembers()) {
|
||||||
|
disableSignalGroupViews(R.string.GroupCreateActivity_contacts_dont_support_push);
|
||||||
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
|
||||||
} else {
|
} else {
|
||||||
|
enableSignalGroupViews();
|
||||||
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_title);
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_title);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private static boolean isActiveInDirectory(Context context, Recipient recipient) {
|
private static boolean isActiveInDirectory(Context context, Recipient recipient) {
|
||||||
try {
|
try {
|
||||||
if (!TextSecureDirectory.getInstance(context).isSecureTextSupported(Util.canonicalizeNumber(context, recipient.getNumber()))) {
|
return TextSecureDirectory.getInstance(context)
|
||||||
return false;
|
.isSecureTextSupported(Util.canonicalizeNumber(context, recipient.getNumber()));
|
||||||
}
|
} catch (NotInDirectoryException | InvalidNumberException e) {
|
||||||
} catch (NotInDirectoryException e) {
|
|
||||||
return false;
|
|
||||||
} catch (InvalidNumberException e) {
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addSelectedContact(Recipient contact) {
|
private void addSelectedContact(@NonNull Recipient contact) {
|
||||||
final boolean isPushUser = isActiveInDirectory(this, contact);
|
final boolean isPushUser = isActiveInDirectory(this, contact);
|
||||||
if (existingContacts != null && !isPushUser) {
|
if (groupToUpdate.isPresent() && !isPushUser) {
|
||||||
Toast.makeText(getApplicationContext(),
|
Toast.makeText(this, R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group, Toast.LENGTH_LONG).show();
|
||||||
R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
|
||||||
Toast.LENGTH_LONG).show();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!selectedContacts.contains(contact) && (existingContacts == null || !existingContacts.contains(contact)))
|
getAdapter().add(contact, isPushUser);
|
||||||
selectedContacts.add(contact);
|
updateViewState();
|
||||||
if (!isPushUser) {
|
|
||||||
disableWhisperGroupUi(R.string.GroupCreateActivity_contacts_dont_support_push);
|
|
||||||
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void addAllSelectedContacts(Collection<Recipient> contacts) {
|
private void addAllSelectedContacts(Collection<Recipient> contacts) {
|
||||||
@ -214,88 +191,40 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void removeSelectedContact(Recipient contact) {
|
|
||||||
selectedContacts.remove(contact);
|
|
||||||
if (!isActiveInDirectory(this, contact)) {
|
|
||||||
for (Recipient recipient : selectedContacts) {
|
|
||||||
if (!isActiveInDirectory(this, recipient))
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
enableWhisperGroupUi();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private void initializeResources() {
|
private void initializeResources() {
|
||||||
groupRecipient = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(GROUP_RECIPIENT_EXTRA, -1), true);
|
RecipientsEditor recipientsEditor = ViewUtil.findById(this, R.id.recipients_text);
|
||||||
groupThread = getIntent().getLongExtra(GROUP_THREAD_EXTRA, -1);
|
PushRecipientsPanel recipientsPanel = ViewUtil.findById(this, R.id.recipients);
|
||||||
if (groupRecipient != null) {
|
lv = ViewUtil.findById(this, R.id.selected_contacts_list);
|
||||||
final String encodedGroupId = groupRecipient.getNumber();
|
avatar = ViewUtil.findById(this, R.id.avatar);
|
||||||
if (encodedGroupId != null) {
|
groupName = ViewUtil.findById(this, R.id.group_name);
|
||||||
try {
|
creatingText = ViewUtil.findById(this, R.id.creating_group_text);
|
||||||
groupId = GroupUtil.getDecodedId(encodedGroupId);
|
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this);
|
||||||
} catch (IOException ioe) {
|
adapter.setOnRecipientDeletedListener(this);
|
||||||
Log.w(TAG, "Couldn't decode the encoded groupId passed in via intent", ioe);
|
|
||||||
groupId = null;
|
|
||||||
}
|
|
||||||
if (groupId != null) {
|
|
||||||
new FillExistingGroupInfoAsyncTask().execute();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
lv = (ListView) findViewById(R.id.selected_contacts_list);
|
|
||||||
avatar = (ImageView) findViewById(R.id.avatar);
|
|
||||||
groupName = (EditText) findViewById(R.id.group_name);
|
|
||||||
creatingText = (TextView) findViewById(R.id.creating_group_text);
|
|
||||||
recipientsPanel = (PushRecipientsPanel) findViewById(R.id.recipients);
|
|
||||||
|
|
||||||
groupName.addTextChangedListener(new TextWatcher() {
|
|
||||||
@Override
|
|
||||||
public void beforeTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
|
|
||||||
@Override
|
|
||||||
public void onTextChanged(CharSequence charSequence, int i, int i2, int i3) { }
|
|
||||||
@Override
|
|
||||||
public void afterTextChanged(Editable editable) {
|
|
||||||
final int prefixResId = (groupId != null)
|
|
||||||
? R.string.GroupCreateActivity_actionbar_update_title
|
|
||||||
: R.string.GroupCreateActivity_actionbar_title;
|
|
||||||
if (editable.length() > 0) {
|
|
||||||
getSupportActionBar().setTitle(getString(prefixResId) + ": " + editable.toString());
|
|
||||||
} else {
|
|
||||||
getSupportActionBar().setTitle(prefixResId);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this, android.R.id.text1, new ArrayList<SelectedRecipientsAdapter.RecipientWrapper>());
|
|
||||||
adapter.setOnRecipientDeletedListener(new SelectedRecipientsAdapter.OnRecipientDeletedListener() {
|
|
||||||
@Override
|
|
||||||
public void onRecipientDeleted(Recipient recipient) {
|
|
||||||
removeSelectedContact(recipient);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
lv.setAdapter(adapter);
|
lv.setAdapter(adapter);
|
||||||
|
recipientsEditor.setHint(R.string.recipients_panel__add_member);
|
||||||
recipientsPanel.setPanelChangeListener(new PushRecipientsPanel.RecipientsPanelChangedListener() {
|
recipientsPanel.setPanelChangeListener(this);
|
||||||
@Override
|
findViewById(R.id.contacts_button).setOnClickListener(new AddRecipientButtonListener());
|
||||||
public void onRecipientsPanelUpdate(Recipients recipients) {
|
|
||||||
Log.i(TAG, "onRecipientsPanelUpdate received.");
|
|
||||||
if (recipients != null) {
|
|
||||||
addAllSelectedContacts(recipients.getRecipientsList());
|
|
||||||
syncAdapterWithSelectedContacts();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
(findViewById(R.id.contacts_button)).setOnClickListener(new AddRecipientButtonListener());
|
|
||||||
|
|
||||||
avatar.setOnClickListener(new View.OnClickListener() {
|
avatar.setOnClickListener(new View.OnClickListener() {
|
||||||
@Override
|
@Override
|
||||||
public void onClick(View view) {
|
public void onClick(View view) {
|
||||||
Crop.pickImage(GroupCreateActivity.this);
|
Crop.pickImage(GroupCreateActivity.this);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
((RecipientsEditor)findViewById(R.id.recipients_text)).setHint(R.string.recipients_panel__add_member);
|
private void initializeExistingGroup() {
|
||||||
|
final String encodedGroupId = RecipientFactory.getRecipientForId(this, getIntent().getLongExtra(GROUP_RECIPIENT_EXTRA, -1), true)
|
||||||
|
.getNumber();
|
||||||
|
byte[] groupId;
|
||||||
|
try {
|
||||||
|
groupId = GroupUtil.getDecodedId(encodedGroupId);
|
||||||
|
} catch (IOException ioe) {
|
||||||
|
Log.w(TAG, "Couldn't decode the encoded groupId passed in via intent", ioe);
|
||||||
|
groupId = null;
|
||||||
|
}
|
||||||
|
if (groupId != null) {
|
||||||
|
new FillExistingGroupInfoAsyncTask(this).execute(groupId);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -316,65 +245,58 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
finish();
|
finish();
|
||||||
return true;
|
return true;
|
||||||
case R.id.menu_create_group:
|
case R.id.menu_create_group:
|
||||||
if (groupId == null) handleGroupCreate();
|
if (groupToUpdate.isPresent()) handleGroupUpdate();
|
||||||
else handleGroupUpdate();
|
else handleGroupCreate();
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRecipientDeleted(Recipient recipient) {
|
||||||
|
getAdapter().remove(recipient);
|
||||||
|
updateViewState();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRecipientsPanelUpdate(Recipients recipients) {
|
||||||
|
if (recipients != null) addAllSelectedContacts(recipients.getRecipientsList());
|
||||||
|
}
|
||||||
|
|
||||||
private void handleGroupCreate() {
|
private void handleGroupCreate() {
|
||||||
if (selectedContacts.size() < 1) {
|
if (getAdapter().getCount() < 1) {
|
||||||
Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members));
|
Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members));
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show();
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
if (whisperGroupUiEnabled()) {
|
if (isSignalGroup()) {
|
||||||
enableWhisperGroupProgressUi(false);
|
new CreateSignalGroupTask(this, masterSecret, avatarBmp, getGroupName(), getAdapter().getRecipients()).execute();
|
||||||
new CreateWhisperGroupAsyncTask().execute();
|
|
||||||
} else {
|
} else {
|
||||||
new CreateMmsGroupAsyncTask().execute();
|
new CreateMmsGroupTask(this, getAdapter().getRecipients()).execute();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void handleGroupUpdate() {
|
private void handleGroupUpdate() {
|
||||||
if (whisperGroupUiEnabled()) {
|
new UpdateSignalGroupTask(this, masterSecret, groupToUpdate.get().id, avatarBmp,
|
||||||
enableWhisperGroupProgressUi(true);
|
getGroupName(), getAdapter().getRecipients()).execute();
|
||||||
}
|
|
||||||
new UpdateWhisperGroupAsyncTask().execute();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void enableWhisperGroupProgressUi(boolean isGroupUpdate) {
|
private void handleOpenConversation(long threadId, Recipients recipients) {
|
||||||
findViewById(R.id.group_details_layout).setVisibility(View.GONE);
|
Intent intent = new Intent(this, ConversationActivity.class);
|
||||||
findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
|
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
||||||
findViewById(R.id.menu_create_group).setVisibility(View.GONE);
|
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
||||||
if (groupName.getText() != null) {
|
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
||||||
final int titleResId = isGroupUpdate
|
startActivity(intent);
|
||||||
? R.string.GroupCreateActivity_updating_group
|
finish();
|
||||||
: R.string.GroupCreateActivity_creating_group;
|
|
||||||
creatingText.setText(getString(titleResId, groupName.getText().toString()));
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void disableWhisperGroupProgressUi() {
|
private SelectedRecipientsAdapter getAdapter() {
|
||||||
findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE);
|
return (SelectedRecipientsAdapter)lv.getAdapter();
|
||||||
findViewById(R.id.creating_group_layout).setVisibility(View.GONE);
|
|
||||||
findViewById(R.id.menu_create_group).setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void syncAdapterWithSelectedContacts() {
|
private @Nullable String getGroupName() {
|
||||||
SelectedRecipientsAdapter adapter = (SelectedRecipientsAdapter)lv.getAdapter();
|
return groupName.getText() != null ? groupName.getText().toString() : null;
|
||||||
adapter.clear();
|
|
||||||
for (Recipient contact : selectedContacts) {
|
|
||||||
adapter.add(new SelectedRecipientsAdapter.RecipientWrapper(contact, true));
|
|
||||||
}
|
|
||||||
if (existingContacts != null) {
|
|
||||||
for (Recipient contact : existingContacts) {
|
|
||||||
adapter.add(new SelectedRecipientsAdapter.RecipientWrapper(contact, false));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
adapter.notifyDataSetChanged();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@ -389,15 +311,9 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
case PICK_CONTACT:
|
case PICK_CONTACT:
|
||||||
List<String> selected = data.getStringArrayListExtra("contacts");
|
List<String> selected = data.getStringArrayListExtra("contacts");
|
||||||
for (String contact : selected) {
|
for (String contact : selected) {
|
||||||
Recipient recipient = RecipientFactory.getRecipientsFromString(this, contact, false).getPrimaryRecipient();
|
final Recipient recipient = RecipientFactory.getRecipientsFromString(this, contact, false).getPrimaryRecipient();
|
||||||
|
if (recipient != null) addSelectedContact(recipient);
|
||||||
if (!selectedContacts.contains(recipient) &&
|
|
||||||
(existingContacts == null || !existingContacts.contains(recipient)) &&
|
|
||||||
recipient != null) {
|
|
||||||
addSelectedContact(recipient);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
syncAdapterWithSelectedContacts();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case Crop.REQUEST_PICK:
|
case Crop.REQUEST_PICK:
|
||||||
@ -407,7 +323,8 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
Glide.with(this).load(Crop.getOutput(data)).asBitmap().skipMemoryCache(true)
|
Glide.with(this).load(Crop.getOutput(data)).asBitmap().skipMemoryCache(true)
|
||||||
.centerCrop().override(AVATAR_SIZE, AVATAR_SIZE)
|
.centerCrop().override(AVATAR_SIZE, AVATAR_SIZE)
|
||||||
.into(new SimpleTarget<Bitmap>() {
|
.into(new SimpleTarget<Bitmap>() {
|
||||||
@Override public void onResourceReady(Bitmap resource,
|
@Override
|
||||||
|
public void onResourceReady(Bitmap resource,
|
||||||
GlideAnimation<? super Bitmap> glideAnimation)
|
GlideAnimation<? super Bitmap> glideAnimation)
|
||||||
{
|
{
|
||||||
avatarBmp = resource;
|
avatarBmp = resource;
|
||||||
@ -423,121 +340,36 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
@Override
|
@Override
|
||||||
public void onClick(View v) {
|
public void onClick(View v) {
|
||||||
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
||||||
if (existingContacts != null) intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
if (groupToUpdate.isPresent()) intent.putExtra(ContactSelectionListFragment.DISPLAY_MODE,
|
||||||
ContactSelectionListFragment.DISPLAY_MODE_PUSH_ONLY);
|
ContactSelectionListFragment.DISPLAY_MODE_PUSH_ONLY);
|
||||||
startActivityForResult(intent, PICK_CONTACT);
|
startActivityForResult(intent, PICK_CONTACT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private Pair<Long, Recipients> handleCreatePushGroup(String groupName, byte[] avatar,
|
private static class CreateMmsGroupTask extends AsyncTask<Void,Void,Long> {
|
||||||
Set<Recipient> members)
|
private GroupCreateActivity activity;
|
||||||
throws InvalidNumberException, MmsException
|
private Set<Recipient> members;
|
||||||
{
|
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
|
||||||
byte[] groupId = groupDatabase.allocateGroupId();
|
|
||||||
Set<String> memberE164Numbers = getE164Numbers(members);
|
|
||||||
|
|
||||||
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(this));
|
public CreateMmsGroupTask(GroupCreateActivity activity, Set<Recipient> members) {
|
||||||
|
this.activity = activity;
|
||||||
groupDatabase.create(groupId, groupName, new LinkedList<String>(memberE164Numbers), null, null);
|
this.members = members;
|
||||||
groupDatabase.updateAvatar(groupId, avatar);
|
|
||||||
|
|
||||||
return handlePushOperation(groupId, groupName, avatar, memberE164Numbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pair<Long, Recipients> handleUpdatePushGroup(byte[] groupId, String groupName,
|
|
||||||
byte[] avatar, Set<Recipient> members)
|
|
||||||
throws InvalidNumberException, MmsException
|
|
||||||
{
|
|
||||||
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
|
||||||
Set<String> memberE164Numbers = getE164Numbers(members);
|
|
||||||
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(this));
|
|
||||||
|
|
||||||
for (String number : memberE164Numbers)
|
|
||||||
Log.w(TAG, "Updating: " + number);
|
|
||||||
|
|
||||||
groupDatabase.updateMembers(groupId, new LinkedList<String>(memberE164Numbers));
|
|
||||||
groupDatabase.updateTitle(groupId, groupName);
|
|
||||||
groupDatabase.updateAvatar(groupId, avatar);
|
|
||||||
|
|
||||||
return handlePushOperation(groupId, groupName, avatar, memberE164Numbers);
|
|
||||||
}
|
|
||||||
|
|
||||||
private Pair<Long, Recipients> handlePushOperation(byte[] groupId, String groupName,
|
|
||||||
@Nullable byte[] avatar,
|
|
||||||
Set<String> e164numbers)
|
|
||||||
throws InvalidNumberException
|
|
||||||
{
|
|
||||||
Attachment avatarAttachment = null;
|
|
||||||
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
|
||||||
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
|
|
||||||
|
|
||||||
GroupContext context = GroupContext.newBuilder()
|
|
||||||
.setId(ByteString.copyFrom(groupId))
|
|
||||||
.setType(GroupContext.Type.UPDATE)
|
|
||||||
.setName(groupName)
|
|
||||||
.addAllMembers(e164numbers)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
if (avatar != null) {
|
|
||||||
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
|
|
||||||
avatarAttachment = new UriAttachment(avatarUri, ContentType.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, context, avatarAttachment, System.currentTimeMillis());
|
|
||||||
long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1, false);
|
|
||||||
|
|
||||||
return new Pair<>(threadId, groupRecipient);
|
|
||||||
}
|
|
||||||
|
|
||||||
private long handleCreateMmsGroup(Set<Recipient> members) {
|
|
||||||
Recipients recipients = RecipientFactory.getRecipientsFor(this, new LinkedList<>(members), false);
|
|
||||||
return DatabaseFactory.getThreadDatabase(this)
|
|
||||||
.getThreadIdFor(recipients,
|
|
||||||
ThreadDatabase.DistributionTypes.CONVERSATION);
|
|
||||||
}
|
|
||||||
|
|
||||||
private static <T> ArrayList<T> setToArrayList(Set<T> set) {
|
|
||||||
ArrayList<T> arrayList = new ArrayList<T>(set.size());
|
|
||||||
for (T item : set) {
|
|
||||||
arrayList.add(item);
|
|
||||||
}
|
|
||||||
return arrayList;
|
|
||||||
}
|
|
||||||
|
|
||||||
private Set<String> getE164Numbers(Set<Recipient> recipients)
|
|
||||||
throws InvalidNumberException
|
|
||||||
{
|
|
||||||
Set<String> results = new HashSet<String>();
|
|
||||||
|
|
||||||
for (Recipient recipient : recipients) {
|
|
||||||
results.add(Util.canonicalizeNumber(this, recipient.getNumber()));
|
|
||||||
}
|
|
||||||
|
|
||||||
return results;
|
|
||||||
}
|
|
||||||
|
|
||||||
private class CreateMmsGroupAsyncTask extends AsyncTask<Void,Void,Long> {
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Long doInBackground(Void... voids) {
|
protected Long doInBackground(Void... avoid) {
|
||||||
return handleCreateMmsGroup(selectedContacts);
|
Recipients recipients = RecipientFactory.getRecipientsFor(activity, members, false);
|
||||||
|
return DatabaseFactory.getThreadDatabase(activity)
|
||||||
|
.getThreadIdFor(recipients, ThreadDatabase.DistributionTypes.CONVERSATION);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Long resultThread) {
|
protected void onPostExecute(Long resultThread) {
|
||||||
if (resultThread > -1) {
|
if (resultThread > -1) {
|
||||||
Intent intent = new Intent(GroupCreateActivity.this, ConversationActivity.class);
|
activity.handleOpenConversation(resultThread,
|
||||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, resultThread.longValue());
|
RecipientFactory.getRecipientsFor(activity, members, true));
|
||||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
|
||||||
|
|
||||||
ArrayList<Recipient> selectedContactsList = setToArrayList(selectedContacts);
|
|
||||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, RecipientFactory.getRecipientsFor(GroupCreateActivity.this, selectedContactsList, true).getIds());
|
|
||||||
startActivity(intent);
|
|
||||||
finish();
|
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
Toast.makeText(activity, R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
||||||
finish();
|
activity.finish();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,145 +379,167 @@ public class GroupCreateActivity extends PassphraseRequiredActionBarActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class UpdateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
private abstract static class SignalGroupTask extends AsyncTask<Void,Void,Optional<GroupActionResult>> {
|
||||||
private long RES_BAD_NUMBER = -2;
|
protected GroupCreateActivity activity;
|
||||||
private long RES_MMS_EXCEPTION = -3;
|
protected MasterSecret masterSecret;
|
||||||
@Override
|
protected Bitmap avatar;
|
||||||
protected Pair<Long, Recipients> doInBackground(Void... params) {
|
protected Set<Recipient> members;
|
||||||
byte[] avatarBytes = null;
|
protected String name;
|
||||||
final Bitmap bitmap;
|
|
||||||
if (avatarBmp == null) bitmap = existingAvatarBmp;
|
|
||||||
else bitmap = avatarBmp;
|
|
||||||
|
|
||||||
if (bitmap != null) {
|
public SignalGroupTask(GroupCreateActivity activity,
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
MasterSecret masterSecret,
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
Bitmap avatar,
|
||||||
avatarBytes = stream.toByteArray();
|
String name,
|
||||||
|
Set<Recipient> members)
|
||||||
|
{
|
||||||
|
this.activity = activity;
|
||||||
|
this.masterSecret = masterSecret;
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.name = name;
|
||||||
|
this.members = members;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void onPreExecute() {
|
||||||
|
activity.findViewById(R.id.group_details_layout).setVisibility(View.GONE);
|
||||||
|
activity.findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
|
||||||
|
activity.findViewById(R.id.menu_create_group).setVisibility(View.GONE);
|
||||||
|
final int titleResId = activity.groupToUpdate.isPresent()
|
||||||
|
? R.string.GroupCreateActivity_updating_group
|
||||||
|
: R.string.GroupCreateActivity_creating_group;
|
||||||
|
activity.creatingText.setText(activity.getString(titleResId, activity.getGroupName()));
|
||||||
}
|
}
|
||||||
final String name = (groupName.getText() != null) ? groupName.getText().toString() : null;
|
|
||||||
|
@Override
|
||||||
|
protected void onPostExecute(Optional<GroupActionResult> groupActionResultOptional) {
|
||||||
|
if (activity.isFinishing()) return;
|
||||||
|
activity.findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE);
|
||||||
|
activity.findViewById(R.id.creating_group_layout).setVisibility(View.GONE);
|
||||||
|
activity.findViewById(R.id.menu_create_group).setVisibility(View.VISIBLE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class CreateSignalGroupTask extends SignalGroupTask {
|
||||||
|
public CreateSignalGroupTask(GroupCreateActivity activity, MasterSecret masterSecret, Bitmap avatar, String name, Set<Recipient> members) {
|
||||||
|
super(activity, masterSecret, avatar, name, members);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
|
||||||
try {
|
try {
|
||||||
Set<Recipient> unionContacts = new HashSet<Recipient>(selectedContacts);
|
return Optional.of(GroupManager.createGroup(activity, masterSecret, members, avatar, name));
|
||||||
unionContacts.addAll(existingContacts);
|
|
||||||
return handleUpdatePushGroup(groupId, name, avatarBytes, unionContacts);
|
|
||||||
} catch (MmsException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return new Pair<Long,Recipients>(RES_MMS_EXCEPTION, null);
|
|
||||||
} catch (InvalidNumberException e) {
|
} catch (InvalidNumberException e) {
|
||||||
Log.w(TAG, e);
|
return Optional.absent();
|
||||||
return new Pair<Long,Recipients>(RES_BAD_NUMBER, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Pair<Long, Recipients> groupInfo) {
|
protected void onPostExecute(Optional<GroupActionResult> result) {
|
||||||
final long threadId = groupInfo.first;
|
if (result.isPresent() && result.get().getThreadId() > -1) {
|
||||||
final Recipients recipients = groupInfo.second;
|
if (!activity.isFinishing()) {
|
||||||
if (threadId > -1) {
|
activity.handleOpenConversation(result.get().getThreadId(), result.get().getGroupRecipient());
|
||||||
Intent intent = getIntent();
|
}
|
||||||
intent.putExtra(GROUP_THREAD_EXTRA, threadId);
|
} else {
|
||||||
intent.putExtra(GROUP_RECIPIENT_EXTRA, recipients.getIds());
|
super.onPostExecute(result);
|
||||||
setResult(RESULT_OK, intent);
|
Toast.makeText(activity.getApplicationContext(),
|
||||||
finish();
|
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
|
||||||
} else if (threadId == RES_BAD_NUMBER) {
|
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
|
|
||||||
disableWhisperGroupProgressUi();
|
|
||||||
} else if (threadId == RES_MMS_EXCEPTION) {
|
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
|
||||||
setResult(RESULT_CANCELED);
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CreateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
private static class UpdateSignalGroupTask extends SignalGroupTask {
|
||||||
private long RES_BAD_NUMBER = -2;
|
private byte[] groupId;
|
||||||
private long RES_MMS_EXCEPTION = -3;
|
|
||||||
|
public UpdateSignalGroupTask(GroupCreateActivity activity,
|
||||||
|
MasterSecret masterSecret, byte[] groupId, Bitmap avatar, String name,
|
||||||
|
Set<Recipient> members)
|
||||||
|
{
|
||||||
|
super(activity, masterSecret, avatar, name, members);
|
||||||
|
this.groupId = groupId;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Pair<Long,Recipients> doInBackground(Void... voids) {
|
protected Optional<GroupActionResult> doInBackground(Void... aVoid) {
|
||||||
byte[] avatarBytes = null;
|
|
||||||
if (avatarBmp != null) {
|
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
||||||
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
|
||||||
avatarBytes = stream.toByteArray();
|
|
||||||
}
|
|
||||||
final String name = (groupName.getText() != null) ? groupName.getText().toString() : null;
|
|
||||||
try {
|
try {
|
||||||
return handleCreatePushGroup(name, avatarBytes, selectedContacts);
|
return Optional.of(GroupManager.updateGroup(activity, masterSecret, groupId, members, avatar, name));
|
||||||
} catch (MmsException e) {
|
|
||||||
Log.w(TAG, e);
|
|
||||||
return new Pair<Long,Recipients>(RES_MMS_EXCEPTION, null);
|
|
||||||
} catch (InvalidNumberException e) {
|
} catch (InvalidNumberException e) {
|
||||||
Log.w(TAG, e);
|
return Optional.absent();
|
||||||
return new Pair<Long,Recipients>(RES_BAD_NUMBER, null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Pair<Long,Recipients> groupInfo) {
|
protected void onPostExecute(Optional<GroupActionResult> result) {
|
||||||
super.onPostExecute(groupInfo);
|
if (result.isPresent() && result.get().getThreadId() > -1) {
|
||||||
final long threadId = groupInfo.first;
|
if (!activity.isFinishing()) {
|
||||||
final Recipients recipients = groupInfo.second;
|
Intent intent = activity.getIntent();
|
||||||
if (threadId > -1) {
|
intent.putExtra(GROUP_THREAD_EXTRA, result.get().getThreadId());
|
||||||
Intent intent = new Intent(GroupCreateActivity.this, ConversationActivity.class);
|
intent.putExtra(GROUP_RECIPIENT_EXTRA, result.get().getGroupRecipient().getIds());
|
||||||
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
activity.setResult(RESULT_OK, intent);
|
||||||
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
activity.finish();
|
||||||
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients.getIds());
|
}
|
||||||
startActivity(intent);
|
} else {
|
||||||
finish();
|
super.onPostExecute(result);
|
||||||
} else if (threadId == RES_BAD_NUMBER) {
|
Toast.makeText(activity.getApplicationContext(),
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
|
R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
|
||||||
disableWhisperGroupProgressUi();
|
|
||||||
} else if (threadId == RES_MMS_EXCEPTION) {
|
|
||||||
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
|
||||||
finish();
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
|
||||||
protected void onProgressUpdate(Void... values) {
|
|
||||||
super.onProgressUpdate(values);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<Void,Void,Void> {
|
private static class FillExistingGroupInfoAsyncTask extends ProgressDialogAsyncTask<byte[],Void,Optional<GroupData>> {
|
||||||
|
private GroupCreateActivity activity;
|
||||||
|
|
||||||
public FillExistingGroupInfoAsyncTask() {
|
public FillExistingGroupInfoAsyncTask(GroupCreateActivity activity) {
|
||||||
super(GroupCreateActivity.this,
|
super(activity,
|
||||||
R.string.GroupCreateActivity_loading_group_details,
|
R.string.GroupCreateActivity_loading_group_details,
|
||||||
R.string.please_wait);
|
R.string.please_wait);
|
||||||
|
this.activity = activity;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected Void doInBackground(Void... voids) {
|
protected Optional<GroupData> doInBackground(byte[]... groupIds) {
|
||||||
final GroupDatabase db = DatabaseFactory.getGroupDatabase(GroupCreateActivity.this);
|
final GroupDatabase db = DatabaseFactory.getGroupDatabase(activity);
|
||||||
final Recipients recipients = db.getGroupMembers(groupId, false);
|
final Recipients recipients = db.getGroupMembers(groupIds[0], false);
|
||||||
if (recipients != null) {
|
final GroupRecord group = db.getGroup(groupIds[0]);
|
||||||
final List<Recipient> recipientList = recipients.getRecipientsList();
|
final Set<Recipient> existingContacts = new HashSet<>(recipients.getRecipientsList().size());
|
||||||
if (recipientList != null) {
|
existingContacts.addAll(recipients.getRecipientsList());
|
||||||
if (existingContacts == null)
|
|
||||||
existingContacts = new HashSet<>(recipientList.size());
|
|
||||||
existingContacts.addAll(recipientList);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
GroupDatabase.GroupRecord group = db.getGroup(groupId);
|
|
||||||
if (group != null) {
|
if (group != null) {
|
||||||
existingTitle = group.getTitle();
|
return Optional.of(new GroupData(groupIds[0],
|
||||||
final byte[] existingAvatar = group.getAvatar();
|
existingContacts,
|
||||||
if (existingAvatar != null) {
|
BitmapUtil.fromByteArray(group.getAvatar()),
|
||||||
existingAvatarBmp = BitmapFactory.decodeByteArray(existingAvatar, 0, existingAvatar.length);
|
group.getTitle()));
|
||||||
}
|
} else {
|
||||||
|
return Optional.absent();
|
||||||
}
|
}
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onPostExecute(Void aVoid) {
|
protected void onPostExecute(Optional<GroupData> group) {
|
||||||
super.onPostExecute(aVoid);
|
super.onPostExecute(group);
|
||||||
|
|
||||||
if (existingTitle != null) groupName.setText(existingTitle);
|
if (group.isPresent() && !activity.isFinishing()) {
|
||||||
if (existingAvatarBmp != null) avatar.setImageBitmap(existingAvatarBmp);
|
activity.groupToUpdate = group;
|
||||||
if (existingContacts != null) syncAdapterWithSelectedContacts();
|
|
||||||
|
activity.groupName.setText(group.get().name);
|
||||||
|
if (group.get().avatar != null) activity.avatar.setImageBitmap(group.get().avatar);
|
||||||
|
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(activity, group.get().recipients);
|
||||||
|
adapter.setOnRecipientDeletedListener(activity);
|
||||||
|
activity.lv.setAdapter(adapter);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class GroupData {
|
||||||
|
byte[] id;
|
||||||
|
Set<Recipient> recipients;
|
||||||
|
Bitmap avatar;
|
||||||
|
String name;
|
||||||
|
|
||||||
|
public GroupData(byte[] id, Set<Recipient> recipients, Bitmap avatar, String name) {
|
||||||
|
this.id = id;
|
||||||
|
this.recipients = recipients;
|
||||||
|
this.avatar = avatar;
|
||||||
|
this.name = name;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
package org.thoughtcrime.securesms.database;
|
package org.thoughtcrime.securesms.database;
|
||||||
|
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint;
|
||||||
import android.content.ContentValues;
|
import android.content.ContentValues;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
@ -8,6 +9,8 @@ import android.database.Cursor;
|
|||||||
import android.database.sqlite.SQLiteDatabase;
|
import android.database.sqlite.SQLiteDatabase;
|
||||||
import android.database.sqlite.SQLiteOpenHelper;
|
import android.database.sqlite.SQLiteOpenHelper;
|
||||||
import android.graphics.Bitmap;
|
import android.graphics.Bitmap;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
@ -67,7 +70,8 @@ public class GroupDatabase extends Database {
|
|||||||
super(context, databaseHelper);
|
super(context, databaseHelper);
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupRecord getGroup(byte[] groupId) {
|
public @Nullable GroupRecord getGroup(byte[] groupId) {
|
||||||
|
@SuppressLint("Recycle")
|
||||||
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
|
Cursor cursor = databaseHelper.getReadableDatabase().query(TABLE_NAME, null, GROUP_ID + " = ?",
|
||||||
new String[] {GroupUtil.getEncodedId(groupId)},
|
new String[] {GroupUtil.getEncodedId(groupId)},
|
||||||
null, null, null);
|
null, null, null);
|
||||||
@ -92,7 +96,7 @@ public class GroupDatabase extends Database {
|
|||||||
return new Reader(cursor);
|
return new Reader(cursor);
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
|
public @NonNull Recipients getGroupMembers(byte[] groupId, boolean includeSelf) {
|
||||||
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
String localNumber = TextSecurePreferences.getLocalNumber(context);
|
||||||
List<String> members = getCurrentMembers(groupId);
|
List<String> members = getCurrentMembers(groupId);
|
||||||
List<Recipient> recipients = new LinkedList<>();
|
List<Recipient> recipients = new LinkedList<>();
|
||||||
@ -248,7 +252,7 @@ public class GroupDatabase extends Database {
|
|||||||
this.cursor = cursor;
|
this.cursor = cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
public GroupRecord getNext() {
|
public @Nullable GroupRecord getNext() {
|
||||||
if (cursor == null || !cursor.moveToNext()) {
|
if (cursor == null || !cursor.moveToNext()) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
133
src/org/thoughtcrime/securesms/groups/GroupManager.java
Normal file
133
src/org/thoughtcrime/securesms/groups/GroupManager.java
Normal file
@ -0,0 +1,133 @@
|
|||||||
|
package org.thoughtcrime.securesms.groups;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.graphics.Bitmap;
|
||||||
|
import android.net.Uri;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
|
|
||||||
|
import com.google.protobuf.ByteString;
|
||||||
|
|
||||||
|
import org.thoughtcrime.securesms.attachments.Attachment;
|
||||||
|
import org.thoughtcrime.securesms.attachments.UriAttachment;
|
||||||
|
import org.thoughtcrime.securesms.crypto.MasterSecret;
|
||||||
|
import org.thoughtcrime.securesms.database.AttachmentDatabase;
|
||||||
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
||||||
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
||||||
|
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
||||||
|
import org.thoughtcrime.securesms.providers.SingleUseBlobProvider;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
||||||
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
||||||
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
||||||
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
||||||
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||||
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
|
import org.whispersystems.textsecure.api.util.InvalidNumberException;
|
||||||
|
import org.whispersystems.textsecure.internal.push.TextSecureProtos.GroupContext;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
|
import ws.com.google.android.mms.ContentType;
|
||||||
|
|
||||||
|
public class GroupManager {
|
||||||
|
public static @NonNull GroupActionResult createGroup(@NonNull Context context,
|
||||||
|
@NonNull MasterSecret masterSecret,
|
||||||
|
@NonNull Set<Recipient> members,
|
||||||
|
@Nullable Bitmap avatar,
|
||||||
|
@Nullable String name)
|
||||||
|
throws InvalidNumberException
|
||||||
|
{
|
||||||
|
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
||||||
|
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
final byte[] groupId = groupDatabase.allocateGroupId();
|
||||||
|
final Set<String> memberE164Numbers = getE164Numbers(context, members);
|
||||||
|
|
||||||
|
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context));
|
||||||
|
groupDatabase.create(groupId, name, new LinkedList<>(memberE164Numbers), null, null);
|
||||||
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
||||||
|
return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static Set<String> getE164Numbers(Context context, Collection<Recipient> recipients)
|
||||||
|
throws InvalidNumberException
|
||||||
|
{
|
||||||
|
final Set<String> results = new HashSet<>();
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
results.add(Util.canonicalizeNumber(context, recipient.getNumber()));
|
||||||
|
}
|
||||||
|
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static GroupActionResult updateGroup(@NonNull Context context,
|
||||||
|
@NonNull MasterSecret masterSecret,
|
||||||
|
@NonNull byte[] groupId,
|
||||||
|
@NonNull Set<Recipient> members,
|
||||||
|
@Nullable Bitmap avatar,
|
||||||
|
@Nullable String name)
|
||||||
|
throws InvalidNumberException
|
||||||
|
{
|
||||||
|
final GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(context);
|
||||||
|
final Set<String> memberE164Numbers = getE164Numbers(context, members);
|
||||||
|
final byte[] avatarBytes = BitmapUtil.toByteArray(avatar);
|
||||||
|
|
||||||
|
memberE164Numbers.add(TextSecurePreferences.getLocalNumber(context));
|
||||||
|
groupDatabase.updateMembers(groupId, new LinkedList<>(memberE164Numbers));
|
||||||
|
groupDatabase.updateTitle(groupId, name);
|
||||||
|
groupDatabase.updateAvatar(groupId, avatarBytes);
|
||||||
|
|
||||||
|
return sendGroupUpdate(context, masterSecret, groupId, memberE164Numbers, name, avatarBytes);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static GroupActionResult sendGroupUpdate(@NonNull Context context,
|
||||||
|
@NonNull MasterSecret masterSecret,
|
||||||
|
@NonNull byte[] groupId,
|
||||||
|
@NonNull Set<String> e164numbers,
|
||||||
|
@Nullable String groupName,
|
||||||
|
@Nullable byte[] avatar)
|
||||||
|
{
|
||||||
|
Attachment avatarAttachment = null;
|
||||||
|
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
||||||
|
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(context, groupRecipientId, false);
|
||||||
|
|
||||||
|
GroupContext.Builder groupContextBuilder = GroupContext.newBuilder()
|
||||||
|
.setId(ByteString.copyFrom(groupId))
|
||||||
|
.setType(GroupContext.Type.UPDATE)
|
||||||
|
.addAllMembers(e164numbers);
|
||||||
|
if (groupName != null) groupContextBuilder.setName(groupName);
|
||||||
|
GroupContext groupContext = groupContextBuilder.build();
|
||||||
|
|
||||||
|
if (avatar != null) {
|
||||||
|
Uri avatarUri = SingleUseBlobProvider.getInstance().createUri(avatar);
|
||||||
|
avatarAttachment = new UriAttachment(avatarUri, ContentType.IMAGE_JPEG, AttachmentDatabase.TRANSFER_PROGRESS_DONE, avatar.length);
|
||||||
|
}
|
||||||
|
|
||||||
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(groupRecipient, groupContext, avatarAttachment, System.currentTimeMillis());
|
||||||
|
long threadId = MessageSender.send(context, masterSecret, outgoingMessage, -1, false);
|
||||||
|
|
||||||
|
return new GroupActionResult(groupRecipient, threadId);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static class GroupActionResult {
|
||||||
|
private Recipients groupRecipient;
|
||||||
|
private long threadId;
|
||||||
|
|
||||||
|
public GroupActionResult(Recipients groupRecipient, long threadId) {
|
||||||
|
this.groupRecipient = groupRecipient;
|
||||||
|
this.threadId = threadId;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Recipients getGroupRecipient() {
|
||||||
|
return groupRecipient;
|
||||||
|
}
|
||||||
|
|
||||||
|
public long getThreadId() {
|
||||||
|
return threadId;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -20,11 +20,11 @@ import android.content.Context;
|
|||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
import android.text.TextUtils;
|
import android.text.TextUtils;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.contacts.avatars.ContactPhotoFactory;
|
|
||||||
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
import org.thoughtcrime.securesms.database.CanonicalAddressDatabase;
|
||||||
import org.thoughtcrime.securesms.util.Util;
|
import org.thoughtcrime.securesms.util.Util;
|
||||||
import org.whispersystems.libaxolotl.util.guava.Optional;
|
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||||
|
|
||||||
|
import java.util.Collection;
|
||||||
import java.util.LinkedList;
|
import java.util.LinkedList;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.StringTokenizer;
|
import java.util.StringTokenizer;
|
||||||
@ -40,7 +40,7 @@ public class RecipientFactory {
|
|||||||
return getRecipientsForIds(context, Util.split(recipientIds, " "), asynchronous);
|
return getRecipientsForIds(context, Util.split(recipientIds, " "), asynchronous);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Recipients getRecipientsFor(Context context, List<Recipient> recipients, boolean asynchronous) {
|
public static @NonNull Recipients getRecipientsFor(Context context, Collection<Recipient> recipients, boolean asynchronous) {
|
||||||
long[] ids = new long[recipients.size()];
|
long[] ids = new long[recipients.size()];
|
||||||
int i = 0;
|
int i = 0;
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import android.graphics.YuvImage;
|
|||||||
import android.graphics.drawable.BitmapDrawable;
|
import android.graphics.drawable.BitmapDrawable;
|
||||||
import android.graphics.drawable.Drawable;
|
import android.graphics.drawable.Drawable;
|
||||||
import android.support.annotation.NonNull;
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.util.Log;
|
import android.util.Log;
|
||||||
import android.util.Pair;
|
import android.util.Pair;
|
||||||
|
|
||||||
@ -140,12 +141,18 @@ public class BitmapUtil {
|
|||||||
return new ByteArrayInputStream(thumbnailBytes.toByteArray());
|
return new ByteArrayInputStream(thumbnailBytes.toByteArray());
|
||||||
}
|
}
|
||||||
|
|
||||||
public static byte[] toByteArray(Bitmap bitmap) {
|
public static @Nullable byte[] toByteArray(@Nullable Bitmap bitmap) {
|
||||||
|
if (bitmap == null) return null;
|
||||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||||
return stream.toByteArray();
|
return stream.toByteArray();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static @Nullable Bitmap fromByteArray(@Nullable byte[] bytes) {
|
||||||
|
if (bytes == null) return null;
|
||||||
|
return BitmapFactory.decodeByteArray(bytes, 0, bytes.length);
|
||||||
|
}
|
||||||
|
|
||||||
public static byte[] createFromNV21(@NonNull final byte[] data,
|
public static byte[] createFromNV21(@NonNull final byte[] data,
|
||||||
final int width,
|
final int width,
|
||||||
final int height,
|
final int height,
|
||||||
|
@ -1,107 +1,165 @@
|
|||||||
package org.thoughtcrime.securesms.util;
|
package org.thoughtcrime.securesms.util;
|
||||||
|
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
|
import android.support.annotation.NonNull;
|
||||||
|
import android.support.annotation.Nullable;
|
||||||
import android.view.LayoutInflater;
|
import android.view.LayoutInflater;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
import android.widget.ArrayAdapter;
|
import android.widget.BaseAdapter;
|
||||||
import android.widget.ImageButton;
|
import android.widget.ImageButton;
|
||||||
import android.widget.TextView;
|
import android.widget.TextView;
|
||||||
|
|
||||||
import org.thoughtcrime.securesms.R;
|
import org.thoughtcrime.securesms.R;
|
||||||
import org.thoughtcrime.securesms.recipients.Recipient;
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
||||||
|
import org.whispersystems.libaxolotl.util.guava.Optional;
|
||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.Collection;
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashSet;
|
||||||
|
import java.util.LinkedList;
|
||||||
|
import java.util.List;
|
||||||
|
import java.util.Set;
|
||||||
|
|
||||||
public class SelectedRecipientsAdapter extends ArrayAdapter<SelectedRecipientsAdapter.RecipientWrapper> {
|
public class SelectedRecipientsAdapter extends BaseAdapter {
|
||||||
|
@NonNull private Context context;
|
||||||
|
@Nullable private OnRecipientDeletedListener onRecipientDeletedListener;
|
||||||
|
@NonNull private List<RecipientWrapper> recipients;
|
||||||
|
|
||||||
private ArrayList<RecipientWrapper> recipients;
|
public SelectedRecipientsAdapter(@NonNull Context context) {
|
||||||
private OnRecipientDeletedListener onRecipientDeletedListener;
|
this(context, Collections.<Recipient>emptyList());
|
||||||
|
|
||||||
public SelectedRecipientsAdapter(Context context, int textViewResourceId) {
|
|
||||||
super(context, textViewResourceId);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public SelectedRecipientsAdapter(Context context, int resource, ArrayList<RecipientWrapper> recipients) {
|
public SelectedRecipientsAdapter(@NonNull Context context,
|
||||||
super(context, resource, recipients);
|
@NonNull Collection<Recipient> existingRecipients)
|
||||||
this.recipients = recipients;
|
{
|
||||||
|
this.context = context;
|
||||||
|
this.recipients = wrapExistingMembers(existingRecipients);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(@NonNull Recipient recipient, boolean isPush) {
|
||||||
|
if (!find(recipient).isPresent()) {
|
||||||
|
RecipientWrapper wrapper = new RecipientWrapper(recipient, true, isPush);
|
||||||
|
this.recipients.add(0, wrapper);
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Optional<RecipientWrapper> find(@NonNull Recipient recipient) {
|
||||||
|
RecipientWrapper found = null;
|
||||||
|
for (RecipientWrapper wrapper : recipients) {
|
||||||
|
if (wrapper.getRecipient().equals(recipient)) found = wrapper;
|
||||||
|
}
|
||||||
|
return Optional.fromNullable(found);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(@NonNull Recipient recipient) {
|
||||||
|
Optional<RecipientWrapper> match = find(recipient);
|
||||||
|
if (match.isPresent()) {
|
||||||
|
recipients.remove(match.get());
|
||||||
|
notifyDataSetChanged();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public Set<Recipient> getRecipients() {
|
||||||
|
final Set<Recipient> recipientSet = new HashSet<>(recipients.size());
|
||||||
|
for (RecipientWrapper wrapper : recipients) {
|
||||||
|
recipientSet.add(wrapper.getRecipient());
|
||||||
|
}
|
||||||
|
return recipientSet;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public View getView(final int position, final View convertView, final ViewGroup parent) {
|
public int getCount() {
|
||||||
|
return recipients.size();
|
||||||
|
}
|
||||||
|
|
||||||
View v = convertView;
|
public boolean hasNonPushMembers() {
|
||||||
|
for (RecipientWrapper wrapper : recipients) {
|
||||||
|
if (!wrapper.isPush()) return true;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Object getItem(int position) {
|
||||||
|
return recipients.get(position);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public long getItemId(int position) {
|
||||||
|
return position;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public View getView(final int position, View v, final ViewGroup parent) {
|
||||||
if (v == null) {
|
if (v == null) {
|
||||||
|
v = LayoutInflater.from(context).inflate(R.layout.selected_recipient_list_item, parent, false);
|
||||||
LayoutInflater vi;
|
|
||||||
vi = LayoutInflater.from(getContext());
|
|
||||||
v = vi.inflate(R.layout.selected_recipient_list_item, null);
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
final RecipientWrapper rw = getItem(position);
|
final RecipientWrapper rw = (RecipientWrapper)getItem(position);
|
||||||
final Recipient p = rw.getRecipient();
|
final Recipient p = rw.getRecipient();
|
||||||
final boolean modifiable = rw.isModifiable();
|
final boolean modifiable = rw.isModifiable();
|
||||||
|
|
||||||
if (p != null) {
|
TextView name = (TextView) v.findViewById(R.id.name);
|
||||||
|
TextView phone = (TextView) v.findViewById(R.id.phone);
|
||||||
|
ImageButton delete = (ImageButton) v.findViewById(R.id.delete);
|
||||||
|
|
||||||
TextView name = (TextView) v.findViewById(R.id.name);
|
name.setText(p.getName());
|
||||||
TextView phone = (TextView) v.findViewById(R.id.phone);
|
phone.setText(p.getNumber());
|
||||||
ImageButton delete = (ImageButton) v.findViewById(R.id.delete);
|
delete.setVisibility(modifiable ? View.VISIBLE : View.GONE);
|
||||||
|
delete.setOnClickListener(new View.OnClickListener() {
|
||||||
if (name != null) {
|
@Override
|
||||||
name.setText(p.getName());
|
public void onClick(View view) {
|
||||||
}
|
if (onRecipientDeletedListener != null) {
|
||||||
if (phone != null) {
|
onRecipientDeletedListener.onRecipientDeleted(recipients.get(position).getRecipient());
|
||||||
phone.setText(p.getNumber());
|
|
||||||
}
|
|
||||||
if (delete != null) {
|
|
||||||
if (modifiable) {
|
|
||||||
delete.setVisibility(View.VISIBLE);
|
|
||||||
delete.setOnClickListener(new View.OnClickListener() {
|
|
||||||
@Override
|
|
||||||
public void onClick(View view) {
|
|
||||||
if (onRecipientDeletedListener != null) {
|
|
||||||
onRecipientDeletedListener.onRecipientDeleted(recipients.get(position).getRecipient());
|
|
||||||
}
|
|
||||||
recipients.remove(position);
|
|
||||||
SelectedRecipientsAdapter.this.notifyDataSetChanged();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
delete.setVisibility(View.INVISIBLE);
|
|
||||||
delete.setOnClickListener(null);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
});
|
||||||
|
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setOnRecipientDeletedListener(OnRecipientDeletedListener listener) {
|
private static List<RecipientWrapper> wrapExistingMembers(Collection<Recipient> recipients) {
|
||||||
|
final LinkedList<RecipientWrapper> wrapperList = new LinkedList<>();
|
||||||
|
for (Recipient recipient : recipients) {
|
||||||
|
wrapperList.add(new RecipientWrapper(recipient, false, true));
|
||||||
|
}
|
||||||
|
return wrapperList;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void setOnRecipientDeletedListener(@Nullable OnRecipientDeletedListener listener) {
|
||||||
onRecipientDeletedListener = listener;
|
onRecipientDeletedListener = listener;
|
||||||
}
|
}
|
||||||
|
|
||||||
public interface OnRecipientDeletedListener {
|
public interface OnRecipientDeletedListener {
|
||||||
public void onRecipientDeleted(Recipient recipient);
|
void onRecipientDeleted(Recipient recipient);
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class RecipientWrapper {
|
public static class RecipientWrapper {
|
||||||
private final Recipient recipient;
|
private final Recipient recipient;
|
||||||
private final boolean modifiable;
|
private final boolean modifiable;
|
||||||
|
private final boolean push;
|
||||||
|
|
||||||
public RecipientWrapper(final Recipient recipient, final boolean modifiable) {
|
public RecipientWrapper(final @NonNull Recipient recipient,
|
||||||
this.recipient = recipient;
|
final boolean modifiable,
|
||||||
|
final boolean push)
|
||||||
|
{
|
||||||
|
this.recipient = recipient;
|
||||||
this.modifiable = modifiable;
|
this.modifiable = modifiable;
|
||||||
|
this.push = push;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Recipient getRecipient() {
|
public @NonNull Recipient getRecipient() {
|
||||||
return recipient;
|
return recipient;
|
||||||
}
|
}
|
||||||
|
|
||||||
public boolean isModifiable() {
|
public boolean isModifiable() {
|
||||||
return modifiable;
|
return modifiable;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public boolean isPush() {
|
||||||
|
return push;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user