2014-01-19 02:17:08 +00:00
|
|
|
package org.thoughtcrime.securesms;
|
|
|
|
|
|
|
|
import android.app.Activity;
|
2014-02-20 23:41:52 +00:00
|
|
|
import android.app.ProgressDialog;
|
2014-02-13 01:17:33 +00:00
|
|
|
import android.content.Context;
|
2014-01-19 02:17:08 +00:00
|
|
|
import android.content.Intent;
|
|
|
|
import android.graphics.Bitmap;
|
2014-02-18 01:31:48 +00:00
|
|
|
import android.graphics.BitmapFactory;
|
|
|
|
import android.net.Uri;
|
2014-02-13 19:44:07 +00:00
|
|
|
import android.os.AsyncTask;
|
2014-01-19 02:17:08 +00:00
|
|
|
import android.os.Bundle;
|
2014-02-18 01:31:48 +00:00
|
|
|
import android.os.Environment;
|
|
|
|
import android.provider.MediaStore;
|
2014-02-12 20:05:18 +00:00
|
|
|
import android.text.Editable;
|
2014-02-20 23:41:52 +00:00
|
|
|
import android.text.TextUtils;
|
2014-02-12 20:05:18 +00:00
|
|
|
import android.text.TextWatcher;
|
2014-01-19 02:17:08 +00:00
|
|
|
import android.util.Log;
|
2014-02-18 06:04:08 +00:00
|
|
|
import android.util.Pair;
|
2014-01-19 02:17:08 +00:00
|
|
|
import android.view.View;
|
2014-02-12 20:05:18 +00:00
|
|
|
import android.widget.EditText;
|
2014-02-07 02:06:23 +00:00
|
|
|
import android.widget.ImageView;
|
2014-01-19 02:17:08 +00:00
|
|
|
import android.widget.ListView;
|
2014-02-13 19:44:07 +00:00
|
|
|
import android.widget.TextView;
|
2014-02-17 23:23:47 +00:00
|
|
|
import android.widget.Toast;
|
2014-01-19 02:17:08 +00:00
|
|
|
|
|
|
|
import com.actionbarsherlock.view.Menu;
|
|
|
|
import com.actionbarsherlock.view.MenuInflater;
|
|
|
|
import com.actionbarsherlock.view.MenuItem;
|
2014-02-20 05:06:54 +00:00
|
|
|
import com.google.protobuf.ByteString;
|
2014-01-19 02:17:08 +00:00
|
|
|
|
2014-02-07 02:06:23 +00:00
|
|
|
import org.thoughtcrime.securesms.components.PushRecipientsPanel;
|
2014-02-03 19:52:27 +00:00
|
|
|
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
2014-02-20 23:41:52 +00:00
|
|
|
import org.thoughtcrime.securesms.contacts.RecipientsEditor;
|
2014-02-14 01:10:20 +00:00
|
|
|
import org.thoughtcrime.securesms.database.DatabaseFactory;
|
|
|
|
import org.thoughtcrime.securesms.database.GroupDatabase;
|
|
|
|
import org.thoughtcrime.securesms.database.ThreadDatabase;
|
2014-01-19 02:17:08 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipient;
|
2014-02-03 19:52:27 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientFactory;
|
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientFormattingException;
|
2014-02-20 23:41:52 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.RecipientProvider;
|
2014-01-19 02:17:08 +00:00
|
|
|
import org.thoughtcrime.securesms.recipients.Recipients;
|
2014-02-17 19:42:51 +00:00
|
|
|
import org.thoughtcrime.securesms.sms.MessageSender;
|
2014-02-20 05:06:54 +00:00
|
|
|
import org.thoughtcrime.securesms.mms.OutgoingGroupMediaMessage;
|
2014-01-19 02:17:08 +00:00
|
|
|
import org.thoughtcrime.securesms.util.ActionBarUtil;
|
2014-02-19 06:08:24 +00:00
|
|
|
import org.thoughtcrime.securesms.util.BitmapUtil;
|
2014-01-19 02:17:08 +00:00
|
|
|
import org.thoughtcrime.securesms.util.DynamicLanguage;
|
|
|
|
import org.thoughtcrime.securesms.util.DynamicTheme;
|
2014-02-14 01:10:20 +00:00
|
|
|
import org.thoughtcrime.securesms.util.GroupUtil;
|
2014-01-19 02:17:08 +00:00
|
|
|
import org.thoughtcrime.securesms.util.SelectedRecipientsAdapter;
|
2014-02-14 01:10:20 +00:00
|
|
|
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
2014-02-13 01:17:33 +00:00
|
|
|
import org.thoughtcrime.securesms.util.Util;
|
2014-02-13 20:36:31 +00:00
|
|
|
import org.whispersystems.textsecure.crypto.MasterSecret;
|
2014-02-13 01:17:33 +00:00
|
|
|
import org.whispersystems.textsecure.directory.Directory;
|
|
|
|
import org.whispersystems.textsecure.directory.NotInDirectoryException;
|
|
|
|
import org.whispersystems.textsecure.util.InvalidNumberException;
|
2014-02-20 23:41:52 +00:00
|
|
|
import org.whispersystems.textsecure.util.PhoneNumberFormatter;
|
2014-01-19 02:17:08 +00:00
|
|
|
|
2014-02-13 20:15:26 +00:00
|
|
|
import java.io.ByteArrayOutputStream;
|
2014-02-18 01:31:48 +00:00
|
|
|
import java.io.File;
|
|
|
|
import java.io.IOException;
|
2014-01-19 02:17:08 +00:00
|
|
|
import java.util.ArrayList;
|
2014-02-13 01:17:33 +00:00
|
|
|
import java.util.Collection;
|
2014-01-19 02:17:08 +00:00
|
|
|
import java.util.HashSet;
|
2014-02-14 01:10:20 +00:00
|
|
|
import java.util.LinkedList;
|
2014-01-19 02:17:08 +00:00
|
|
|
import java.util.List;
|
|
|
|
import java.util.Set;
|
|
|
|
|
2014-02-17 19:42:51 +00:00
|
|
|
import ws.com.google.android.mms.MmsException;
|
|
|
|
|
2014-02-03 19:52:27 +00:00
|
|
|
import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
2014-02-14 01:10:20 +00:00
|
|
|
import static org.whispersystems.textsecure.push.PushMessageProtos.PushMessageContent.GroupContext;
|
2014-02-03 19:52:27 +00:00
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
|
|
|
|
public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActivity {
|
|
|
|
|
2014-02-19 00:28:54 +00:00
|
|
|
private final static String TAG = GroupCreateActivity.class.getSimpleName();
|
2014-02-07 02:06:23 +00:00
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
public static final String GROUP_RECIPIENT_EXTRA = "group_recipient";
|
|
|
|
public static final String GROUP_THREAD_EXTRA = "group_thread";
|
|
|
|
public static final String MASTER_SECRET_EXTRA = "master_secret";
|
2014-02-13 20:36:31 +00:00
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
|
|
|
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
|
|
|
|
|
2014-02-19 06:08:24 +00:00
|
|
|
private static final String TEMP_PHOTO_FILE = "__tmp_group_create_avatar_photo.tmp";
|
2014-02-18 01:31:48 +00:00
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
private static final int PICK_CONTACT = 1;
|
2014-02-07 02:06:23 +00:00
|
|
|
private static final int PICK_AVATAR = 2;
|
2014-02-20 23:41:52 +00:00
|
|
|
public static final int AVATAR_SIZE = 210;
|
2014-02-07 02:06:23 +00:00
|
|
|
|
2014-02-12 20:05:18 +00:00
|
|
|
private EditText groupName;
|
2014-02-07 02:06:23 +00:00
|
|
|
private ListView lv;
|
|
|
|
private PushRecipientsPanel recipientsPanel;
|
|
|
|
private ImageView avatar;
|
2014-02-19 06:08:24 +00:00
|
|
|
private TextView creatingText;
|
2014-02-20 23:41:52 +00:00
|
|
|
private ProgressDialog pd;
|
|
|
|
|
|
|
|
private Recipients groupRecipient = null;
|
|
|
|
private long groupThread = -1;
|
|
|
|
private byte[] groupId = null;
|
|
|
|
private Set<Recipient> existingContacts = null;
|
|
|
|
private String existingTitle = null;
|
|
|
|
private Bitmap existingAvatarBmp = null;
|
2014-01-19 02:17:08 +00:00
|
|
|
|
2014-02-13 20:36:31 +00:00
|
|
|
private MasterSecret masterSecret;
|
|
|
|
private Bitmap avatarBmp;
|
2014-01-19 02:17:08 +00:00
|
|
|
private Set<Recipient> selectedContacts;
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onCreate(Bundle state) {
|
|
|
|
dynamicTheme.onCreate(this);
|
|
|
|
dynamicLanguage.onCreate(this);
|
|
|
|
super.onCreate(state);
|
|
|
|
|
|
|
|
setContentView(R.layout.group_create_activity);
|
|
|
|
getSupportActionBar().setDisplayHomeAsUpEnabled(true);
|
2014-02-13 01:17:33 +00:00
|
|
|
ActionBarUtil.initializeDefaultActionBar(this, getSupportActionBar(), R.string.GroupCreateActivity_actionbar_title);
|
2014-01-19 02:17:08 +00:00
|
|
|
|
|
|
|
selectedContacts = new HashSet<Recipient>();
|
|
|
|
initializeResources();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public void onResume() {
|
|
|
|
super.onResume();
|
|
|
|
dynamicTheme.onResume(this);
|
2014-02-18 01:31:48 +00:00
|
|
|
dynamicLanguage.onResume(this);
|
|
|
|
if (!TextSecurePreferences.isPushRegistered(this)) {
|
|
|
|
disableWhisperGroupUi(R.string.GroupCreateActivity_you_dont_support_push);
|
|
|
|
}
|
2014-01-19 02:17:08 +00:00
|
|
|
}
|
|
|
|
|
2014-02-13 20:15:26 +00:00
|
|
|
private boolean whisperGroupUiEnabled() {
|
|
|
|
return groupName.isEnabled() && avatar.isEnabled();
|
|
|
|
}
|
|
|
|
|
2014-02-18 01:31:48 +00:00
|
|
|
private void disableWhisperGroupUi(int reasonResId) {
|
2014-02-13 01:17:33 +00:00
|
|
|
View pushDisabled = findViewById(R.id.push_disabled);
|
|
|
|
pushDisabled.setVisibility(View.VISIBLE);
|
2014-02-20 23:41:52 +00:00
|
|
|
((TextView) findViewById(R.id.push_disabled_reason)).setText(reasonResId);
|
2014-02-13 01:17:33 +00:00
|
|
|
avatar.setEnabled(false);
|
|
|
|
groupName.setEnabled(false);
|
|
|
|
}
|
|
|
|
|
2014-02-13 20:15:26 +00:00
|
|
|
private void enableWhisperGroupUi() {
|
2014-02-13 01:17:33 +00:00
|
|
|
findViewById(R.id.push_disabled).setVisibility(View.GONE);
|
|
|
|
avatar.setEnabled(true);
|
|
|
|
groupName.setEnabled(true);
|
|
|
|
final CharSequence groupNameText = groupName.getText();
|
2014-02-20 23:41:52 +00:00
|
|
|
if (groupNameText != null && groupNameText.length() > 0)
|
2014-02-13 01:17:33 +00:00
|
|
|
getSupportActionBar().setTitle(groupNameText);
|
|
|
|
else
|
|
|
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_title);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean isActiveInDirectory(Context context, Recipient recipient) {
|
|
|
|
try {
|
|
|
|
if (!Directory.getInstance(context).isActiveNumber(Util.canonicalizeNumber(context, recipient.getNumber()))) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
} catch (NotInDirectoryException e) {
|
|
|
|
return false;
|
2014-02-13 20:36:31 +00:00
|
|
|
} catch (InvalidNumberException e) {
|
2014-02-13 01:17:33 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
private void addSelectedContact(Recipient contact) {
|
2014-02-20 23:41:52 +00:00
|
|
|
if (!selectedContacts.contains(contact) && (existingContacts == null || !existingContacts.contains(contact)))
|
|
|
|
selectedContacts.add(contact);
|
|
|
|
if (!isActiveInDirectory(this, contact)) {
|
|
|
|
disableWhisperGroupUi(R.string.GroupCreateActivity_contacts_dont_support_push);
|
|
|
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
|
|
|
|
}
|
2014-02-13 01:17:33 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private void addAllSelectedContacts(Collection<Recipient> contacts) {
|
|
|
|
for (Recipient contact : contacts) {
|
|
|
|
addSelectedContact(contact);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void removeSelectedContact(Recipient contact) {
|
|
|
|
selectedContacts.remove(contact);
|
|
|
|
if (!isActiveInDirectory(this, contact)) {
|
|
|
|
for (Recipient recipient : selectedContacts) {
|
|
|
|
if (!isActiveInDirectory(this, recipient))
|
|
|
|
return;
|
|
|
|
}
|
2014-02-13 20:15:26 +00:00
|
|
|
enableWhisperGroupUi();
|
2014-02-13 01:17:33 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
private void initializeResources() {
|
2014-02-20 23:41:52 +00:00
|
|
|
groupRecipient = getIntent().getParcelableExtra(GROUP_RECIPIENT_EXTRA);
|
|
|
|
groupThread = getIntent().getLongExtra(GROUP_THREAD_EXTRA, -1);
|
|
|
|
if (groupRecipient != null) {
|
|
|
|
final String encodedGroupId = groupRecipient.getPrimaryRecipient().getNumber();
|
|
|
|
if (encodedGroupId != null) {
|
|
|
|
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().execute();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-13 20:36:31 +00:00
|
|
|
masterSecret = getIntent().getParcelableExtra(MASTER_SECRET_EXTRA);
|
2014-02-19 06:08:24 +00:00
|
|
|
|
|
|
|
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);
|
|
|
|
|
2014-02-12 20:05:18 +00:00
|
|
|
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) {
|
2014-02-20 23:41:52 +00:00
|
|
|
if (editable.length() > 0) {
|
|
|
|
final int prefixResId = (groupId != null)
|
|
|
|
? R.string.GroupCreateActivity_actionbar_update_title
|
|
|
|
: R.string.GroupCreateActivity_actionbar_title;
|
|
|
|
getSupportActionBar().setTitle(getString(prefixResId) + ": " + editable.toString());
|
|
|
|
} else {
|
2014-02-13 01:17:33 +00:00
|
|
|
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_title);
|
2014-02-20 23:41:52 +00:00
|
|
|
}
|
2014-02-12 20:05:18 +00:00
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
SelectedRecipientsAdapter adapter = new SelectedRecipientsAdapter(this, android.R.id.text1, new ArrayList<SelectedRecipientsAdapter.RecipientWrapper>());
|
2014-02-13 01:17:33 +00:00
|
|
|
adapter.setOnRecipientDeletedListener(new SelectedRecipientsAdapter.OnRecipientDeletedListener() {
|
|
|
|
@Override
|
|
|
|
public void onRecipientDeleted(Recipient recipient) {
|
|
|
|
removeSelectedContact(recipient);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
lv.setAdapter(adapter);
|
2014-02-07 02:06:23 +00:00
|
|
|
|
|
|
|
recipientsPanel.setPanelChangeListener(new PushRecipientsPanel.RecipientsPanelChangedListener() {
|
|
|
|
@Override
|
|
|
|
public void onRecipientsPanelUpdate(Recipients recipients) {
|
|
|
|
Log.i(TAG, "onRecipientsPanelUpdate received.");
|
|
|
|
if (recipients != null) {
|
2014-02-13 01:17:33 +00:00
|
|
|
addAllSelectedContacts(recipients.getRecipientsList());
|
2014-02-07 02:06:23 +00:00
|
|
|
syncAdapterWithSelectedContacts();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
(findViewById(R.id.contacts_button)).setOnClickListener(new AddRecipientButtonListener());
|
|
|
|
|
|
|
|
avatar.setOnClickListener(new View.OnClickListener() {
|
|
|
|
@Override
|
|
|
|
public void onClick(View view) {
|
|
|
|
Intent photoPickerIntent = new Intent(Intent.ACTION_GET_CONTENT, null);
|
|
|
|
photoPickerIntent.setType("image/*");
|
|
|
|
photoPickerIntent.putExtra("crop", "true");
|
|
|
|
photoPickerIntent.putExtra("aspectX", 1);
|
|
|
|
photoPickerIntent.putExtra("aspectY", 1);
|
2014-02-19 06:08:24 +00:00
|
|
|
photoPickerIntent.putExtra("outputX", AVATAR_SIZE);
|
|
|
|
photoPickerIntent.putExtra("outputY", AVATAR_SIZE);
|
2014-02-18 01:31:48 +00:00
|
|
|
photoPickerIntent.putExtra(MediaStore.EXTRA_OUTPUT, getTempUri());
|
|
|
|
photoPickerIntent.putExtra("outputFormat", Bitmap.CompressFormat.PNG.toString());
|
2014-02-07 02:06:23 +00:00
|
|
|
startActivityForResult(photoPickerIntent, PICK_AVATAR);
|
|
|
|
}
|
|
|
|
});
|
2014-02-20 23:41:52 +00:00
|
|
|
|
|
|
|
((RecipientsEditor)findViewById(R.id.recipients_text)).setHint(R.string.recipients_panel__add_member);
|
2014-01-19 02:17:08 +00:00
|
|
|
}
|
|
|
|
|
2014-02-18 01:31:48 +00:00
|
|
|
private Uri getTempUri() {
|
|
|
|
return Uri.fromFile(getTempFile());
|
|
|
|
}
|
|
|
|
|
|
|
|
private File getTempFile() {
|
|
|
|
if (Environment.MEDIA_MOUNTED.equals(Environment.getExternalStorageState())) {
|
|
|
|
|
|
|
|
File f = new File(Environment.getExternalStorageDirectory(), TEMP_PHOTO_FILE);
|
|
|
|
try {
|
|
|
|
f.createNewFile();
|
2014-02-18 23:27:18 +00:00
|
|
|
f.deleteOnExit();
|
2014-02-18 01:31:48 +00:00
|
|
|
} catch (IOException e) {
|
|
|
|
Log.e(TAG, "Error creating new temp file.", e);
|
|
|
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_file_io_exception, Toast.LENGTH_SHORT).show();
|
|
|
|
}
|
|
|
|
return f;
|
|
|
|
} else {
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
@Override
|
|
|
|
public boolean onPrepareOptionsMenu(Menu menu) {
|
|
|
|
MenuInflater inflater = this.getSupportMenuInflater();
|
|
|
|
menu.clear();
|
|
|
|
|
|
|
|
inflater.inflate(R.menu.group_create, menu);
|
|
|
|
super.onPrepareOptionsMenu(menu);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
public boolean onOptionsItemSelected(MenuItem item) {
|
|
|
|
super.onOptionsItemSelected(item);
|
|
|
|
switch (item.getItemId()) {
|
|
|
|
case android.R.id.home:
|
2014-02-13 19:44:07 +00:00
|
|
|
finish();
|
|
|
|
return true;
|
2014-01-19 02:17:08 +00:00
|
|
|
case R.id.menu_create_group:
|
2014-02-20 23:41:52 +00:00
|
|
|
if (groupId == null) handleGroupCreate();
|
|
|
|
else handleGroupUpdate();
|
2014-02-17 23:23:47 +00:00
|
|
|
return true;
|
|
|
|
}
|
2014-02-13 19:44:07 +00:00
|
|
|
|
2014-02-17 23:23:47 +00:00
|
|
|
return false;
|
|
|
|
}
|
2014-02-13 19:44:07 +00:00
|
|
|
|
2014-02-17 23:23:47 +00:00
|
|
|
private void handleGroupCreate() {
|
|
|
|
if (selectedContacts.size() < 1) {
|
2014-02-18 00:36:31 +00:00
|
|
|
Log.i(TAG, getString(R.string.GroupCreateActivity_contacts_no_members));
|
|
|
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_no_members, Toast.LENGTH_SHORT).show();
|
2014-02-17 23:23:47 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
if (whisperGroupUiEnabled()) {
|
2014-02-18 00:36:31 +00:00
|
|
|
enableWhisperGroupCreatingUi();
|
|
|
|
new CreateWhisperGroupAsyncTask().execute();
|
2014-02-17 23:23:47 +00:00
|
|
|
} else {
|
2014-02-18 00:36:31 +00:00
|
|
|
new CreateMmsGroupAsyncTask().execute();
|
|
|
|
}
|
|
|
|
}
|
2014-01-19 02:17:08 +00:00
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
private void handleGroupUpdate() {
|
|
|
|
Log.w("GroupCreateActivity", "Creating...");
|
|
|
|
new UpdateWhisperGroupAsyncTask().execute();
|
|
|
|
}
|
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
private static List<String> recipientsToNormalizedStrings(Collection<Recipient> recipients, String localNumber) {
|
|
|
|
final List<String> e164numbers = new ArrayList<String>(recipients.size());
|
|
|
|
for (Recipient contact : recipients) {
|
|
|
|
try {
|
|
|
|
e164numbers.add(PhoneNumberFormatter.formatNumber(contact.getNumber(), localNumber));
|
|
|
|
} catch (InvalidNumberException ine) {
|
|
|
|
Log.w(TAG, "Failed to format number for added group member.", ine);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return e164numbers;
|
|
|
|
}
|
|
|
|
|
2014-02-18 00:36:31 +00:00
|
|
|
private void enableWhisperGroupCreatingUi() {
|
|
|
|
findViewById(R.id.group_details_layout).setVisibility(View.GONE);
|
|
|
|
findViewById(R.id.creating_group_layout).setVisibility(View.VISIBLE);
|
|
|
|
findViewById(R.id.menu_create_group).setVisibility(View.GONE);
|
2014-02-19 06:08:24 +00:00
|
|
|
if (groupName.getText() != null)
|
|
|
|
creatingText.setText(getString(R.string.GroupCreateActivity_creating_group, groupName.getText().toString()));
|
2014-02-18 00:36:31 +00:00
|
|
|
}
|
2014-02-17 23:23:47 +00:00
|
|
|
|
2014-02-18 00:36:31 +00:00
|
|
|
private void disableWhisperGroupCreatingUi() {
|
|
|
|
findViewById(R.id.group_details_layout).setVisibility(View.VISIBLE);
|
|
|
|
findViewById(R.id.creating_group_layout).setVisibility(View.GONE);
|
|
|
|
findViewById(R.id.menu_create_group).setVisibility(View.VISIBLE);
|
2014-01-19 02:17:08 +00:00
|
|
|
}
|
|
|
|
|
2014-02-07 02:06:23 +00:00
|
|
|
private void syncAdapterWithSelectedContacts() {
|
|
|
|
SelectedRecipientsAdapter adapter = (SelectedRecipientsAdapter)lv.getAdapter();
|
|
|
|
adapter.clear();
|
|
|
|
for (Recipient contact : selectedContacts) {
|
2014-02-20 23:41:52 +00:00
|
|
|
adapter.add(new SelectedRecipientsAdapter.RecipientWrapper(contact, true));
|
|
|
|
}
|
|
|
|
if (existingContacts != null) {
|
|
|
|
for (Recipient contact : existingContacts) {
|
|
|
|
adapter.add(new SelectedRecipientsAdapter.RecipientWrapper(contact, false));
|
|
|
|
}
|
2014-02-07 02:06:23 +00:00
|
|
|
}
|
|
|
|
adapter.notifyDataSetChanged();
|
|
|
|
}
|
|
|
|
|
2014-01-19 02:17:08 +00:00
|
|
|
@Override
|
|
|
|
public void onActivityResult(int reqCode, int resultCode, Intent data) {
|
|
|
|
super.onActivityResult(reqCode, resultCode, data);
|
|
|
|
|
|
|
|
if (data == null || resultCode != Activity.RESULT_OK)
|
|
|
|
return;
|
|
|
|
|
|
|
|
switch (reqCode) {
|
|
|
|
case PICK_CONTACT:
|
2014-02-03 19:52:27 +00:00
|
|
|
List<ContactData> selected = data.getParcelableArrayListExtra("contacts");
|
|
|
|
for (ContactData contact : selected) {
|
|
|
|
for (ContactAccessor.NumberData numberData : contact.numbers) {
|
|
|
|
try {
|
2014-02-07 02:06:23 +00:00
|
|
|
Recipient recipient = RecipientFactory.getRecipientsFromString(this, numberData.number, false)
|
2014-02-03 19:52:27 +00:00
|
|
|
.getPrimaryRecipient();
|
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
if (!selectedContacts.contains(recipient)
|
|
|
|
&& (existingContacts == null || !existingContacts.contains(recipient))) {
|
2014-02-13 01:17:33 +00:00
|
|
|
addSelectedContact(recipient);
|
2014-02-03 19:52:27 +00:00
|
|
|
}
|
|
|
|
} catch (RecipientFormattingException e) {
|
2014-02-19 06:08:24 +00:00
|
|
|
Log.w(TAG, e);
|
2014-02-03 19:52:27 +00:00
|
|
|
}
|
2014-01-19 02:17:08 +00:00
|
|
|
}
|
|
|
|
}
|
2014-02-07 02:06:23 +00:00
|
|
|
syncAdapterWithSelectedContacts();
|
2014-01-19 02:17:08 +00:00
|
|
|
break;
|
2014-02-07 02:06:23 +00:00
|
|
|
case PICK_AVATAR:
|
2014-02-19 06:08:24 +00:00
|
|
|
new DecodeCropAndSetAsyncTask().execute();
|
2014-01-19 02:17:08 +00:00
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private class AddRecipientButtonListener implements View.OnClickListener {
|
|
|
|
@Override
|
|
|
|
public void onClick(View v) {
|
|
|
|
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
|
|
|
startActivityForResult(intent, PICK_CONTACT);
|
|
|
|
}
|
|
|
|
}
|
2014-02-13 20:15:26 +00:00
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
private Pair<Long, Recipients> handleCreatePushGroup(String groupName, byte[] avatar,
|
2014-02-20 05:06:54 +00:00
|
|
|
Set<Recipient> members)
|
2014-02-17 21:19:49 +00:00
|
|
|
throws InvalidNumberException, MmsException
|
2014-02-14 01:10:20 +00:00
|
|
|
{
|
2014-02-22 01:51:25 +00:00
|
|
|
GroupDatabase groupDatabase = DatabaseFactory.getGroupDatabase(this);
|
|
|
|
byte[] groupId = groupDatabase.allocateGroupId();
|
|
|
|
List<String> memberE164Numbers = getE164Numbers(members);
|
2014-02-17 21:19:49 +00:00
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
groupDatabase.create(groupId, TextSecurePreferences.getLocalNumber(this), groupName,
|
|
|
|
memberE164Numbers, null, null);
|
|
|
|
groupDatabase.updateAvatar(groupId, avatar);
|
2014-02-14 01:10:20 +00:00
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
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);
|
|
|
|
List<String> memberE164Numbers = getE164Numbers(members);
|
2014-02-14 01:10:20 +00:00
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
GroupDatabase.GroupRecord record = groupDatabase.getGroup(groupId);
|
|
|
|
Set<String> newMembers = new HashSet<String>(memberE164Numbers);
|
|
|
|
newMembers.removeAll(record.getMembers());
|
|
|
|
|
|
|
|
groupDatabase.add(groupId, TextSecurePreferences.getLocalNumber(this),
|
|
|
|
new LinkedList<String>(newMembers));
|
|
|
|
|
|
|
|
groupDatabase.updateTitle(groupId, groupName);
|
|
|
|
groupDatabase.updateAvatar(groupId, avatar);
|
|
|
|
|
|
|
|
|
|
|
|
return handlePushOperation(groupId, groupName, avatar, memberE164Numbers);
|
|
|
|
}
|
|
|
|
|
|
|
|
private Pair<Long, Recipients> handlePushOperation(byte[] groupId, String groupName, byte[] avatar,
|
|
|
|
List<String> e164numbers)
|
|
|
|
throws MmsException, InvalidNumberException
|
|
|
|
{
|
|
|
|
|
|
|
|
try {
|
|
|
|
String groupRecipientId = GroupUtil.getEncodedId(groupId);
|
|
|
|
Recipients groupRecipient = RecipientFactory.getRecipientsFromString(this, groupRecipientId, false);
|
2014-02-14 23:59:57 +00:00
|
|
|
|
2014-02-20 05:06:54 +00:00
|
|
|
GroupContext context = GroupContext.newBuilder()
|
|
|
|
.setId(ByteString.copyFrom(groupId))
|
2014-02-22 01:51:25 +00:00
|
|
|
.setType(GroupContext.Type.UPDATE)
|
2014-02-20 05:06:54 +00:00
|
|
|
.setName(groupName)
|
2014-02-22 01:51:25 +00:00
|
|
|
.addAllMembers(e164numbers)
|
2014-02-20 05:06:54 +00:00
|
|
|
.build();
|
|
|
|
|
|
|
|
OutgoingGroupMediaMessage outgoingMessage = new OutgoingGroupMediaMessage(this, groupRecipient, context, avatar);
|
|
|
|
long threadId = MessageSender.send(this, masterSecret, outgoingMessage, -1);
|
|
|
|
|
|
|
|
return new Pair<Long, Recipients>(threadId, groupRecipient);
|
2014-02-14 23:59:57 +00:00
|
|
|
} catch (RecipientFormattingException e) {
|
2014-02-17 21:19:49 +00:00
|
|
|
throw new AssertionError(e);
|
2014-02-17 19:42:51 +00:00
|
|
|
} catch (MmsException e) {
|
2014-02-19 06:08:24 +00:00
|
|
|
Log.w(TAG, e);
|
2014-02-17 21:19:49 +00:00
|
|
|
throw new MmsException(e);
|
2014-02-14 23:59:57 +00:00
|
|
|
}
|
2014-02-14 01:10:20 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
private long handleCreateMmsGroup(Set<Recipient> members) {
|
|
|
|
Recipients recipients = new Recipients(new LinkedList<Recipient>(members));
|
|
|
|
return DatabaseFactory.getThreadDatabase(this)
|
|
|
|
.getThreadIdFor(recipients,
|
|
|
|
ThreadDatabase.DistributionTypes.CONVERSATION);
|
2014-02-13 20:15:26 +00:00
|
|
|
}
|
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
|
2014-02-14 01:10:20 +00:00
|
|
|
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;
|
2014-02-13 20:15:26 +00:00
|
|
|
}
|
2014-02-18 00:36:31 +00:00
|
|
|
|
2014-02-19 06:08:24 +00:00
|
|
|
private class DecodeCropAndSetAsyncTask extends AsyncTask<Void,Void,Bitmap> {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Bitmap doInBackground(Void... voids) {
|
|
|
|
File tempFile = getTempFile();
|
|
|
|
avatarBmp = BitmapUtil.getCircleCroppedBitmap(BitmapFactory.decodeFile(tempFile.getAbsolutePath()));
|
|
|
|
tempFile.delete();
|
|
|
|
return avatarBmp;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Bitmap result) {
|
|
|
|
if (avatarBmp != null) avatar.setImageBitmap(avatarBmp);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-18 04:19:54 +00:00
|
|
|
private class CreateMmsGroupAsyncTask extends AsyncTask<Void,Void,Long> {
|
2014-02-18 00:36:31 +00:00
|
|
|
|
|
|
|
@Override
|
2014-02-18 04:19:54 +00:00
|
|
|
protected Long doInBackground(Void... voids) {
|
2014-02-18 00:36:31 +00:00
|
|
|
handleCreateMmsGroup(selectedContacts);
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-02-18 04:19:54 +00:00
|
|
|
protected void onPostExecute(Long resultThread) {
|
|
|
|
if (resultThread > -1) {
|
|
|
|
Intent intent = new Intent(GroupCreateActivity.this, ConversationActivity.class);
|
|
|
|
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
|
|
|
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, resultThread.longValue());
|
|
|
|
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
|
|
|
|
2014-02-20 23:41:52 +00:00
|
|
|
ArrayList<Recipient> selectedContactsList = setToArrayList(selectedContacts);
|
2014-02-18 04:19:54 +00:00
|
|
|
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, new Recipients(selectedContactsList));
|
|
|
|
startActivity(intent);
|
|
|
|
finish();
|
|
|
|
} else {
|
|
|
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
|
|
|
finish();
|
|
|
|
}
|
2014-02-18 00:36:31 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onProgressUpdate(Void... values) {
|
|
|
|
super.onProgressUpdate(values);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-22 01:51:25 +00:00
|
|
|
private class UpdateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
|
|
|
private long RES_BAD_NUMBER = -2;
|
|
|
|
private long RES_MMS_EXCEPTION = -3;
|
|
|
|
@Override
|
|
|
|
protected Pair<Long, Recipients> doInBackground(Void... params) {
|
|
|
|
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 {
|
|
|
|
return handleUpdatePushGroup(groupId, name, avatarBytes, selectedContacts);
|
|
|
|
} catch (MmsException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
return new Pair<Long,Recipients>(RES_MMS_EXCEPTION, null);
|
|
|
|
} catch (InvalidNumberException e) {
|
|
|
|
Log.w(TAG, e);
|
|
|
|
return new Pair<Long,Recipients>(RES_BAD_NUMBER, null);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2014-02-18 06:04:08 +00:00
|
|
|
private class CreateWhisperGroupAsyncTask extends AsyncTask<Void,Void,Pair<Long,Recipients>> {
|
2014-02-18 00:36:31 +00:00
|
|
|
private long RES_BAD_NUMBER = -2;
|
|
|
|
private long RES_MMS_EXCEPTION = -3;
|
|
|
|
|
|
|
|
@Override
|
2014-02-18 06:04:08 +00:00
|
|
|
protected Pair<Long,Recipients> doInBackground(Void... voids) {
|
2014-02-18 04:19:54 +00:00
|
|
|
byte[] avatarBytes = null;
|
2014-02-18 00:36:31 +00:00
|
|
|
if (avatarBmp != null) {
|
|
|
|
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
|
|
|
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
2014-02-18 04:19:54 +00:00
|
|
|
avatarBytes = stream.toByteArray();
|
2014-02-18 00:36:31 +00:00
|
|
|
}
|
2014-02-18 04:19:54 +00:00
|
|
|
final String name = (groupName.getText() != null) ? groupName.getText().toString() : null;
|
2014-02-18 00:36:31 +00:00
|
|
|
try {
|
2014-02-18 04:19:54 +00:00
|
|
|
return handleCreatePushGroup(name, avatarBytes, selectedContacts);
|
2014-02-18 00:36:31 +00:00
|
|
|
} catch (MmsException e) {
|
2014-02-19 06:08:24 +00:00
|
|
|
Log.w(TAG, e);
|
2014-02-18 06:04:08 +00:00
|
|
|
return new Pair<Long,Recipients>(RES_MMS_EXCEPTION, null);
|
2014-02-18 00:36:31 +00:00
|
|
|
} catch (InvalidNumberException e) {
|
2014-02-19 06:08:24 +00:00
|
|
|
Log.w(TAG, e);
|
2014-02-18 06:04:08 +00:00
|
|
|
return new Pair<Long,Recipients>(RES_BAD_NUMBER, null);
|
2014-02-18 00:36:31 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
2014-02-18 06:04:08 +00:00
|
|
|
protected void onPostExecute(Pair<Long,Recipients> groupInfo) {
|
|
|
|
super.onPostExecute(groupInfo);
|
|
|
|
final long threadId = groupInfo.first;
|
|
|
|
final Recipients recipients = groupInfo.second;
|
|
|
|
if (threadId > -1) {
|
2014-02-18 00:36:31 +00:00
|
|
|
Intent intent = new Intent(GroupCreateActivity.this, ConversationActivity.class);
|
|
|
|
intent.putExtra(ConversationActivity.MASTER_SECRET_EXTRA, masterSecret);
|
2014-02-18 06:04:08 +00:00
|
|
|
intent.putExtra(ConversationActivity.THREAD_ID_EXTRA, threadId);
|
2014-02-18 00:36:31 +00:00
|
|
|
intent.putExtra(ConversationActivity.DISTRIBUTION_TYPE_EXTRA, ThreadDatabase.DistributionTypes.DEFAULT);
|
2014-02-18 06:04:08 +00:00
|
|
|
intent.putExtra(ConversationActivity.RECIPIENTS_EXTRA, recipients);
|
2014-02-18 00:36:31 +00:00
|
|
|
startActivity(intent);
|
|
|
|
finish();
|
2014-02-18 06:04:08 +00:00
|
|
|
} else if (threadId == RES_BAD_NUMBER) {
|
2014-02-18 00:36:31 +00:00
|
|
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_invalid_number, Toast.LENGTH_LONG).show();
|
|
|
|
disableWhisperGroupCreatingUi();
|
2014-02-18 06:04:08 +00:00
|
|
|
} else if (threadId == RES_MMS_EXCEPTION) {
|
2014-02-18 00:36:31 +00:00
|
|
|
Toast.makeText(getApplicationContext(), R.string.GroupCreateActivity_contacts_mms_exception, Toast.LENGTH_LONG).show();
|
|
|
|
finish();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onProgressUpdate(Void... values) {
|
|
|
|
super.onProgressUpdate(values);
|
|
|
|
}
|
|
|
|
}
|
2014-02-20 23:41:52 +00:00
|
|
|
|
|
|
|
private class FillExistingGroupInfoAsyncTask extends AsyncTask<Void,Void,Boolean> {
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPreExecute() {
|
|
|
|
pd = new ProgressDialog(GroupCreateActivity.this);
|
|
|
|
pd.setTitle("Loading group details...");
|
|
|
|
pd.setMessage("Please wait.");
|
|
|
|
pd.setCancelable(false);
|
|
|
|
pd.setIndeterminate(true);
|
|
|
|
pd.show();
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected Boolean doInBackground(Void... voids) {
|
|
|
|
final GroupDatabase db = DatabaseFactory.getGroupDatabase(GroupCreateActivity.this);
|
|
|
|
final Recipients recipients = db.getGroupMembers(groupId);
|
|
|
|
if (recipients != null) {
|
|
|
|
final List<Recipient> recipientList = recipients.getRecipientsList();
|
|
|
|
if (recipientList != null) {
|
|
|
|
if (existingContacts == null)
|
|
|
|
existingContacts = new HashSet<Recipient>(recipientList.size());
|
|
|
|
existingContacts.addAll(recipientList);
|
|
|
|
}
|
|
|
|
}
|
2014-02-22 01:51:25 +00:00
|
|
|
GroupDatabase.GroupRecord group = db.getGroup(groupId);
|
2014-02-20 23:41:52 +00:00
|
|
|
if (group != null) {
|
|
|
|
existingTitle = group.getTitle();
|
|
|
|
final byte[] existingAvatar = group.getAvatar();
|
|
|
|
if (existingAvatar != null) {
|
|
|
|
existingAvatarBmp = BitmapUtil.getCircleCroppedBitmap(
|
|
|
|
BitmapFactory.decodeByteArray(existingAvatar, 0, existingAvatar.length));
|
|
|
|
}
|
|
|
|
return (group.getOwner() != null && group.getOwner().equals(TextSecurePreferences.getLocalNumber(GroupCreateActivity.this)));
|
|
|
|
}
|
|
|
|
return null;
|
|
|
|
}
|
|
|
|
|
|
|
|
@Override
|
|
|
|
protected void onPostExecute(Boolean isOwner) {
|
|
|
|
super.onPostExecute(isOwner);
|
|
|
|
|
|
|
|
if (pd != null) pd.dismiss();
|
|
|
|
if (existingTitle != null) groupName.setText(existingTitle);
|
|
|
|
if (existingAvatarBmp != null) avatar.setImageBitmap(existingAvatarBmp);
|
|
|
|
if (existingContacts != null) syncAdapterWithSelectedContacts();
|
|
|
|
if (!isOwner) {
|
|
|
|
disableWhisperGroupUi(R.string.GroupCreateActivity_you_dont_own_this_group);
|
|
|
|
getSupportActionBar().setTitle(getString(R.string.GroupCreateActivity_actionbar_update_title)
|
|
|
|
+ (TextUtils.isEmpty(existingTitle) ? "" : ": " + existingTitle));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2014-01-19 02:17:08 +00:00
|
|
|
}
|