mirror of
https://github.com/oxen-io/session-android.git
synced 2024-12-24 16:57:50 +00:00
group and contact list fixes
1) Updating a group without changing the avatar will keep that avatar 2) Prohibit adding non-push users to an existing push group 3) Add Android contacts to the same database. Takes a small amount more time and memory, but allows queries to not be a hack, and enables us to dedupe numbers in JB and higher devices. // FREEBIE
This commit is contained in:
parent
b715debefc
commit
e2f7c1529a
@ -5,10 +5,6 @@
|
||||
android:orientation="vertical"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<org.thoughtcrime.securesms.components.SingleRecipientPanel android:id="@+id/recipients"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<fragment
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
|
@ -4,6 +4,18 @@
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical">
|
||||
|
||||
<EditText android:id="@+id/filter"
|
||||
android:inputType="textPersonName"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:hint="@string/recipients_panel__to"
|
||||
android:paddingRight="45dip"
|
||||
android:paddingLeft="15dp"
|
||||
android:paddingTop="12dp"
|
||||
android:paddingBottom="12dp"
|
||||
android:textColor="?conversation_editor_text_color"
|
||||
android:background="?conversation_editor_background" />
|
||||
|
||||
<se.emilsjolander.stickylistheaders.StickyListHeadersListView android:id="@android:id/list"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
@ -147,7 +147,6 @@
|
||||
<string name="GroupCreateActivity_actionbar_mms_title">New MMS Group</string>
|
||||
<string name="GroupCreateActivity_contacts_dont_support_push">You have selected a contact that doesn\'t support TextSecure groups, so this group will be MMS.</string>
|
||||
<string name="GroupCreateActivity_you_dont_support_push">You\'re not registered for using the data channel, so TextSecure groups are disabled.</string>
|
||||
<string name="GroupCreateActivity_you_dont_own_this_group">You\'re not the owner of this group, so you cannot edit the title or picture.</string>
|
||||
<string name="GroupCreateActivity_contacts_mms_exception">An unexpected error happened that has made group creation fail.</string>
|
||||
<string name="GroupCreateActivity_contacts_no_members">You need at least one person in your group!</string>
|
||||
<string name="GroupCreateActivity_contacts_invalid_number">One of the members of your group has a number that can\'t be read correctly. Please fix or remove that contact and try again.</string>
|
||||
@ -155,6 +154,7 @@
|
||||
<string name="GroupCreateActivity_avatar_content_description">Group Avatar</string>
|
||||
<string name="GroupCreateActivity_menu_create_title">Create Group</string>
|
||||
<string name="GroupCreateActivity_creating_group">Creating %1$s…</string>
|
||||
<string name="GroupCreateActivity_cannot_add_non_push_to_existing_group">Cannot add non-TextSecure contacts to an existing TextSecure group</string>
|
||||
|
||||
<!-- ImportFragment -->
|
||||
<string name="ImportFragment_import_system_sms_database">Import System SMS Database?</string>
|
||||
@ -765,6 +765,7 @@
|
||||
<string name="contact_selection_list__menu_unselect_all">Unselect All</string>
|
||||
<string name="contact_selection_list__header_textsecure_users">TEXTSECURE USERS</string>
|
||||
<string name="contact_selection_list__header_other">ALL CONTACTS</string>
|
||||
<string name="contact_selection_list__unknown_contact">New message to...</string>
|
||||
|
||||
<!-- contact_selection -->
|
||||
<string name="contact_selection__menu_finished">Finished</string>
|
||||
|
@ -162,9 +162,17 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||
}
|
||||
|
||||
private void addSelectedContact(Recipient contact) {
|
||||
final boolean isPushUser = isActiveInDirectory(this, contact);
|
||||
if (existingContacts != null && !isPushUser) {
|
||||
Toast.makeText(getApplicationContext(),
|
||||
R.string.GroupCreateActivity_cannot_add_non_push_to_existing_group,
|
||||
Toast.LENGTH_LONG).show();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!selectedContacts.contains(contact) && (existingContacts == null || !existingContacts.contains(contact)))
|
||||
selectedContacts.add(contact);
|
||||
if (!isActiveInDirectory(this, contact)) {
|
||||
if (!isPushUser) {
|
||||
disableWhisperGroupUi(R.string.GroupCreateActivity_contacts_dont_support_push);
|
||||
getSupportActionBar().setTitle(R.string.GroupCreateActivity_actionbar_mms_title);
|
||||
}
|
||||
@ -375,6 +383,7 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||
@Override
|
||||
public void onClick(View v) {
|
||||
Intent intent = new Intent(GroupCreateActivity.this, PushContactSelectionActivity.class);
|
||||
if (existingContacts != null) intent.putExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, true);
|
||||
startActivityForResult(intent, PICK_CONTACT);
|
||||
}
|
||||
}
|
||||
@ -533,9 +542,13 @@ public class GroupCreateActivity extends PassphraseRequiredSherlockFragmentActiv
|
||||
@Override
|
||||
protected Pair<Long, Recipients> doInBackground(Void... params) {
|
||||
byte[] avatarBytes = null;
|
||||
if (avatarBmp != null) {
|
||||
final Bitmap bitmap;
|
||||
if (avatarBmp == null) bitmap = existingAvatarBmp;
|
||||
else bitmap = avatarBmp;
|
||||
|
||||
if (bitmap != null) {
|
||||
ByteArrayOutputStream stream = new ByteArrayOutputStream();
|
||||
avatarBmp.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
bitmap.compress(Bitmap.CompressFormat.PNG, 100, stream);
|
||||
avatarBytes = stream.toByteArray();
|
||||
}
|
||||
final String name = (groupName.getText() != null) ? groupName.getText().toString() : null;
|
||||
|
@ -20,6 +20,7 @@ import android.content.Intent;
|
||||
import android.os.Bundle;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.widget.EditText;
|
||||
import android.widget.Toast;
|
||||
|
||||
import com.actionbarsherlock.app.ActionBar;
|
||||
@ -62,7 +63,6 @@ public class NewConversationActivity extends PassphraseRequiredSherlockFragmentA
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
private MasterSecret masterSecret;
|
||||
|
||||
private SingleRecipientPanel recipientsPanel;
|
||||
private PushContactSelectionListFragment contactsFragment;
|
||||
|
||||
@Override
|
||||
@ -106,7 +106,6 @@ public class NewConversationActivity extends PassphraseRequiredSherlockFragmentA
|
||||
}
|
||||
|
||||
private void initializeResources() {
|
||||
recipientsPanel = (SingleRecipientPanel) findViewById(R.id.recipients);
|
||||
contactsFragment = (PushContactSelectionListFragment) getSupportFragmentManager().findFragmentById(R.id.contact_selection_list_fragment);
|
||||
contactsFragment.setOnContactSelectedListener(new PushContactSelectionListFragment.OnContactSelectedListener() {
|
||||
@Override
|
||||
@ -116,15 +115,6 @@ public class NewConversationActivity extends PassphraseRequiredSherlockFragmentA
|
||||
openNewConversation(recipients);
|
||||
}
|
||||
});
|
||||
|
||||
recipientsPanel.setPanelChangeListener(new SingleRecipientPanel.RecipientsPanelChangedListener() {
|
||||
@Override
|
||||
public void onRecipientsPanelUpdate(Recipients recipients) {
|
||||
Log.i(TAG, "Choosing contact from autocompletion.");
|
||||
openNewConversation(recipients);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
private void handleSelectionFinished() {
|
||||
|
@ -57,6 +57,7 @@ import static org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
*/
|
||||
public class PushContactSelectionActivity extends PassphraseRequiredSherlockFragmentActivity {
|
||||
private final static String TAG = "ContactSelectActivity";
|
||||
public final static String PUSH_ONLY_EXTRA = "push_only";
|
||||
|
||||
private final DynamicTheme dynamicTheme = new DynamicTheme();
|
||||
|
||||
|
@ -24,11 +24,14 @@ import android.support.v4.app.Fragment;
|
||||
import android.support.v4.app.LoaderManager;
|
||||
import android.support.v4.content.Loader;
|
||||
import android.support.v4.widget.CursorAdapter;
|
||||
import android.text.Editable;
|
||||
import android.text.TextWatcher;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.EditText;
|
||||
import android.widget.TextView;
|
||||
|
||||
import org.thoughtcrime.securesms.contacts.ContactAccessor;
|
||||
@ -36,6 +39,7 @@ import org.thoughtcrime.securesms.contacts.ContactAccessor.ContactData;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.ViewHolder;
|
||||
import org.thoughtcrime.securesms.contacts.ContactSelectionListAdapter.DataHolder;
|
||||
import org.thoughtcrime.securesms.contacts.ContactsDatabase;
|
||||
|
||||
import java.util.LinkedList;
|
||||
import java.util.List;
|
||||
@ -61,6 +65,8 @@ public class PushContactSelectionListFragment extends Fragment
|
||||
private OnContactSelectedListener onContactSelectedListener;
|
||||
private boolean multi = false;
|
||||
private StickyListHeadersListView listView;
|
||||
private EditText filterEditText;
|
||||
private String cursorFilter;
|
||||
|
||||
|
||||
@Override
|
||||
@ -80,6 +86,12 @@ public class PushContactSelectionListFragment extends Fragment
|
||||
super.onPause();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDestroyView() {
|
||||
super.onDestroyView();
|
||||
ContactsDatabase.destroyInstance();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
return inflater.inflate(R.layout.push_contact_selection_list_activity, container, false);
|
||||
@ -127,9 +139,27 @@ public class PushContactSelectionListFragment extends Fragment
|
||||
listView = (StickyListHeadersListView) getView().findViewById(android.R.id.list);
|
||||
listView.setFocusable(true);
|
||||
listView.setFastScrollEnabled(true);
|
||||
listView.setFastScrollAlwaysVisible(true);
|
||||
listView.setDrawingListUnderStickyHeader(false);
|
||||
listView.setOnItemClickListener(new ListClickListener());
|
||||
filterEditText = (EditText) getView().findViewById(R.id.filter);
|
||||
filterEditText.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) {
|
||||
cursorFilter = charSequence.toString();
|
||||
getLoaderManager().restartLoader(0, null, PushContactSelectionListFragment.this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void afterTextChanged(Editable editable) {
|
||||
|
||||
}
|
||||
});
|
||||
cursorFilter = null;
|
||||
}
|
||||
|
||||
public void update() {
|
||||
@ -138,13 +168,19 @@ public class PushContactSelectionListFragment extends Fragment
|
||||
|
||||
@Override
|
||||
public Loader<Cursor> onCreateLoader(int id, Bundle args) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContacts(getActivity());
|
||||
if (getActivity().getIntent().getBooleanExtra(PushContactSelectionActivity.PUSH_ONLY_EXTRA, false)) {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForPushContacts(getActivity(), cursorFilter);
|
||||
} else {
|
||||
return ContactAccessor.getInstance().getCursorLoaderForContacts(getActivity(), cursorFilter);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
|
||||
((CursorAdapter) listView.getAdapter()).changeCursor(data);
|
||||
emptyText.setText(R.string.contact_selection_group_activity__no_contacts);
|
||||
if (data != null && data.getCount() < 40) listView.setFastScrollAlwaysVisible(false);
|
||||
else listView.setFastScrollAlwaysVisible(true);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -82,8 +82,12 @@ public class ContactAccessor {
|
||||
null, null, null, ContactsContract.Groups.TITLE + " ASC");
|
||||
}
|
||||
|
||||
public Loader<Cursor> getCursorLoaderForContacts(Context context) {
|
||||
return new ContactsCursorLoader(context);
|
||||
public Loader<Cursor> getCursorLoaderForContacts(Context context, String filter) {
|
||||
return new ContactsCursorLoader(context, filter, false);
|
||||
}
|
||||
|
||||
public Loader<Cursor> getCursorLoaderForPushContacts(Context context, String filter) {
|
||||
return new ContactsCursorLoader(context, filter, true);
|
||||
}
|
||||
|
||||
public Cursor getCursorForContactsWithNumbers(Context context) {
|
||||
|
@ -30,6 +30,7 @@ import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.FilterQueryProvider;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.TextView;
|
||||
|
||||
|
@ -28,22 +28,25 @@ import android.support.v4.content.CursorLoader;
|
||||
public class ContactsCursorLoader extends CursorLoader {
|
||||
|
||||
private final Context context;
|
||||
private final String filter;
|
||||
private final boolean pushOnly;
|
||||
private ContactsDatabase db;
|
||||
|
||||
public ContactsCursorLoader(Context context) {
|
||||
public ContactsCursorLoader(Context context, String filter, boolean pushOnly) {
|
||||
super(context);
|
||||
this.context = context;
|
||||
this.filter = filter;
|
||||
this.pushOnly = pushOnly;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cursor loadInBackground() {
|
||||
db = new ContactsDatabase(context);
|
||||
return db.getAllContacts();
|
||||
db = ContactsDatabase.getInstance(context);
|
||||
return db.query(filter, pushOnly);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onReset() {
|
||||
super.onReset();
|
||||
db.close();
|
||||
}
|
||||
}
|
||||
|
@ -20,6 +20,7 @@ import android.content.ContentValues;
|
||||
import android.content.Context;
|
||||
import android.database.Cursor;
|
||||
import android.database.CursorWrapper;
|
||||
import android.database.MatrixCursor;
|
||||
import android.database.MergeCursor;
|
||||
import android.database.sqlite.SQLiteDatabase;
|
||||
import android.database.sqlite.SQLiteOpenHelper;
|
||||
@ -28,10 +29,15 @@ import android.provider.ContactsContract;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
|
||||
import org.thoughtcrime.securesms.R;
|
||||
import org.thoughtcrime.securesms.util.NumberUtil;
|
||||
import org.thoughtcrime.securesms.util.TextSecurePreferences;
|
||||
import org.whispersystems.textsecure.util.Util;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Database to supply all types of contacts that TextSecure needs to know about
|
||||
@ -50,17 +56,36 @@ public class ContactsDatabase {
|
||||
public static final String NUMBER_COLUMN = ContactsContract.CommonDataKinds.Phone.NUMBER;
|
||||
public static final String TYPE_COLUMN = "type";
|
||||
|
||||
private static final String FILTER_SELECTION = NAME_COLUMN + " LIKE ? OR " + NUMBER_COLUMN + " LIKE ?";
|
||||
private static final String CONTACT_LIST_SORT = NAME_COLUMN + " ASC";
|
||||
private static final String[] ANDROID_PROJECTION = new String[]{ID_COLUMN,
|
||||
NAME_COLUMN,
|
||||
NUMBER_TYPE_COLUMN,
|
||||
NUMBER_COLUMN};
|
||||
|
||||
private static final String[] CONTACTS_PROJECTION = new String[]{ID_COLUMN,
|
||||
NAME_COLUMN,
|
||||
NUMBER_TYPE_COLUMN,
|
||||
NUMBER_COLUMN,
|
||||
TYPE_COLUMN};
|
||||
|
||||
public static final int NORMAL_TYPE = 0;
|
||||
public static final int PUSH_TYPE = 1;
|
||||
public static final int GROUP_TYPE = 2;
|
||||
|
||||
public ContactsDatabase(Context context) {
|
||||
private static ContactsDatabase instance = null;
|
||||
|
||||
public synchronized static ContactsDatabase getInstance(Context context) {
|
||||
if (instance == null) instance = new ContactsDatabase(context);
|
||||
return instance;
|
||||
}
|
||||
|
||||
public synchronized static void destroyInstance() {
|
||||
if (instance != null) instance.close();
|
||||
instance = null;
|
||||
}
|
||||
|
||||
private ContactsDatabase(Context context) {
|
||||
this.dbHelper = new DatabaseOpenHelper(context);
|
||||
this.context = context;
|
||||
}
|
||||
@ -69,31 +94,64 @@ public class ContactsDatabase {
|
||||
dbHelper.close();
|
||||
}
|
||||
|
||||
public Cursor getAllContacts() {
|
||||
return query(null, null, null);
|
||||
}
|
||||
|
||||
private Cursor query(String selection, String[] selectionArgs, String[] columns) {
|
||||
final Cursor localCursor = queryLocalDb(selection, selectionArgs, columns);
|
||||
public Cursor query(String filter, boolean pushOnly) {
|
||||
final boolean includeAndroidContacts = !pushOnly && TextSecurePreferences.isSmsNonDataOutEnabled(context);
|
||||
final Cursor localCursor = queryLocalDb(filter);
|
||||
final Cursor androidCursor;
|
||||
final MatrixCursor newNumberCursor;
|
||||
|
||||
if (TextSecurePreferences.isSmsNonDataOutEnabled(context)) {
|
||||
androidCursor = queryAndroidDb();
|
||||
} else{
|
||||
return localCursor;
|
||||
if (includeAndroidContacts) {
|
||||
androidCursor = queryAndroidDb(filter);
|
||||
} else {
|
||||
androidCursor = null;
|
||||
}
|
||||
|
||||
if (localCursor != null && androidCursor != null) return new MergeCursor(new Cursor[]{localCursor,androidCursor});
|
||||
else if (localCursor != null) return localCursor;
|
||||
else if (androidCursor != null) return androidCursor;
|
||||
else return null;
|
||||
if (includeAndroidContacts && !Util.isEmpty(filter) && NumberUtil.isValidSmsOrEmail(filter)) {
|
||||
newNumberCursor = new MatrixCursor(CONTACTS_PROJECTION, 1);
|
||||
newNumberCursor.addRow(new Object[]{-1L, context.getString(R.string.contact_selection_list__unknown_contact),
|
||||
0, filter, NORMAL_TYPE});
|
||||
} else {
|
||||
newNumberCursor = null;
|
||||
}
|
||||
|
||||
private Cursor queryAndroidDb() {
|
||||
Cursor cursor = context.getContentResolver().query(ContactsContract.CommonDataKinds.Phone.CONTENT_URI, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
|
||||
List<Cursor> cursors = new ArrayList<Cursor>();
|
||||
if (localCursor != null) cursors.add(localCursor);
|
||||
if (androidCursor != null) cursors.add(androidCursor);
|
||||
if (newNumberCursor != null) cursors.add(newNumberCursor);
|
||||
|
||||
switch (cursors.size()) {
|
||||
case 0: return null;
|
||||
case 1: return cursors.get(0);
|
||||
default: return new MergeCursor(cursors.toArray(new Cursor[]{}));
|
||||
}
|
||||
}
|
||||
|
||||
private Cursor queryAndroidDb(String filter) {
|
||||
final Uri baseUri;
|
||||
if (filter != null) {
|
||||
baseUri = Uri.withAppendedPath(ContactsContract.CommonDataKinds.Phone.CONTENT_FILTER_URI,
|
||||
Uri.encode(filter));
|
||||
} else {
|
||||
baseUri = ContactsContract.CommonDataKinds.Phone.CONTENT_URI;
|
||||
}
|
||||
Cursor cursor = context.getContentResolver().query(baseUri, ANDROID_PROJECTION, null, null, CONTACT_LIST_SORT);
|
||||
return new TypedCursorWrapper(cursor);
|
||||
}
|
||||
|
||||
private Cursor queryLocalDb(String filter) {
|
||||
final String selection;
|
||||
final String[] selectionArgs;
|
||||
final String fuzzyFilter = "%" + filter + "%";
|
||||
if (!Util.isEmpty(filter)) {
|
||||
selection = FILTER_SELECTION;
|
||||
selectionArgs = new String[]{fuzzyFilter, fuzzyFilter};
|
||||
} else {
|
||||
selection = null;
|
||||
selectionArgs = null;
|
||||
}
|
||||
return queryLocalDb(selection, selectionArgs, null);
|
||||
}
|
||||
|
||||
private Cursor queryLocalDb(String selection, String[] selectionArgs, String[] columns) {
|
||||
SQLiteDatabase localDb = dbHelper.getReadableDatabase();
|
||||
final Cursor localCursor;
|
||||
|
Loading…
x
Reference in New Issue
Block a user