group contacts by tag
This commit is contained in:
parent
d1d23e4627
commit
43870114d9
|
@ -68,6 +68,7 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
|
|||
public static final String SHOW_DYNAMIC_TAGS = "show_dynamic_tags";
|
||||
public static final String OMEMO_SETTING = "omemo";
|
||||
public static final String PREVENT_SCREENSHOTS = "prevent_screenshots";
|
||||
public static final String GROUP_BY_TAGS = "groupByTags";
|
||||
|
||||
public static final int REQUEST_CREATE_BACKUP = 0xbf8701;
|
||||
|
||||
|
@ -536,6 +537,25 @@ public class SettingsActivity extends XmppActivity implements OnSharedPreference
|
|||
if (xmppConnectionService.reconfigurePushDistributor()) {
|
||||
xmppConnectionService.renewUnifiedPushEndpoints();
|
||||
}
|
||||
} else if (name.equals(SHOW_DYNAMIC_TAGS) || name.equals(GROUP_BY_TAGS)) {
|
||||
boolean dynamicTagsEnabled = preferences.getBoolean(SHOW_DYNAMIC_TAGS, false);
|
||||
boolean groupByTags = preferences.getBoolean(GROUP_BY_TAGS, false);
|
||||
|
||||
if (name.equals(SHOW_DYNAMIC_TAGS) && !dynamicTagsEnabled && groupByTags) {
|
||||
preferences.edit().putBoolean(GROUP_BY_TAGS, false).apply();
|
||||
Preference preference = mSettingsFragment.findPreference(GROUP_BY_TAGS);
|
||||
if (preference instanceof CheckBoxPreference) {
|
||||
((CheckBoxPreference) preference).setChecked(false);
|
||||
}
|
||||
}
|
||||
|
||||
if (name.equals(GROUP_BY_TAGS) && !dynamicTagsEnabled && groupByTags) {
|
||||
preferences.edit().putBoolean(SHOW_DYNAMIC_TAGS, true).apply();
|
||||
Preference preference = mSettingsFragment.findPreference(SHOW_DYNAMIC_TAGS);
|
||||
if (preference instanceof CheckBoxPreference) {
|
||||
((CheckBoxPreference) preference).setChecked(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,10 @@ import android.content.Context;
|
|||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.content.res.Resources;
|
||||
import android.database.DataSetObserver;
|
||||
import android.graphics.Color;
|
||||
import android.graphics.drawable.ColorDrawable;
|
||||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
|
@ -17,6 +21,7 @@ import android.text.Editable;
|
|||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.Pair;
|
||||
import android.view.ContextMenu;
|
||||
|
@ -27,6 +32,7 @@ import android.view.Menu;
|
|||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.ViewParent;
|
||||
import android.view.inputmethod.InputMethodManager;
|
||||
import android.widget.AdapterView;
|
||||
import android.widget.AdapterView.AdapterContextMenuInfo;
|
||||
|
@ -34,7 +40,12 @@ import android.widget.ArrayAdapter;
|
|||
import android.widget.AutoCompleteTextView;
|
||||
import android.widget.CheckBox;
|
||||
import android.widget.EditText;
|
||||
import android.widget.ExpandableListAdapter;
|
||||
import android.widget.ExpandableListView;
|
||||
import android.widget.FrameLayout;
|
||||
import android.widget.ListAdapter;
|
||||
import android.widget.ListView;
|
||||
import android.widget.Space;
|
||||
import android.widget.Spinner;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
@ -65,11 +76,13 @@ import java.util.ArrayList;
|
|||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Locale;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
|
@ -91,6 +104,7 @@ import eu.siacs.conversations.ui.util.JidDialog;
|
|||
import eu.siacs.conversations.ui.util.MenuDoubleTabUtil;
|
||||
import eu.siacs.conversations.ui.util.PendingItem;
|
||||
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
|
||||
import eu.siacs.conversations.ui.util.StyledAttributes;
|
||||
import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment;
|
||||
import eu.siacs.conversations.utils.AccountUtils;
|
||||
import eu.siacs.conversations.utils.StringUtils;
|
||||
|
@ -113,16 +127,19 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
public int contact_context_id;
|
||||
private ListPagerAdapter mListPagerAdapter;
|
||||
private final List<ListItem> contacts = new ArrayList<>();
|
||||
private ListItemAdapter mContactsAdapter;
|
||||
private ExpandableListItemAdapter mContactsAdapter;
|
||||
private TagsAdapter mTagsAdapter = new TagsAdapter();
|
||||
private final List<ListItem> conferences = new ArrayList<>();
|
||||
private ListItemAdapter mConferenceAdapter;
|
||||
private ExpandableListItemAdapter mConferenceAdapter;
|
||||
private final List<String> mActivatedAccounts = new ArrayList<>();
|
||||
private EditText mSearchEditText;
|
||||
private final AtomicBoolean mRequestedContactsPermission = new AtomicBoolean(false);
|
||||
private final AtomicBoolean mOpenedFab = new AtomicBoolean(false);
|
||||
private boolean mHideOfflineContacts = false;
|
||||
private boolean createdByViewIntent = false;
|
||||
|
||||
boolean groupingEnabled = false;
|
||||
|
||||
private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||
|
||||
@Override
|
||||
|
@ -295,8 +312,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
mListPagerAdapter = new ListPagerAdapter(getSupportFragmentManager());
|
||||
binding.startConversationViewPager.setAdapter(mListPagerAdapter);
|
||||
|
||||
mConferenceAdapter = new ListItemAdapter(this, conferences);
|
||||
mContactsAdapter = new ListItemAdapter(this, contacts);
|
||||
mConferenceAdapter = new ExpandableListItemAdapter(this, conferences);
|
||||
mContactsAdapter = new ExpandableListItemAdapter(this, contacts);
|
||||
mContactsAdapter.setOnTagClickedListener(this.mOnTagClickedListener);
|
||||
|
||||
final SharedPreferences preferences = getPreferences();
|
||||
|
@ -317,6 +334,12 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
intent = savedInstanceState.getParcelable("intent");
|
||||
}
|
||||
|
||||
if (savedInstanceState == null) {
|
||||
groupingEnabled = preferences.getBoolean(SettingsActivity.GROUP_BY_TAGS, getResources().getBoolean(R.bool.group_by_tags));
|
||||
} else {
|
||||
groupingEnabled = savedInstanceState.getBoolean("groupingEnabled");
|
||||
}
|
||||
|
||||
if (isViewIntent(intent)) {
|
||||
pendingViewIntent.push(intent);
|
||||
createdByViewIntent = true;
|
||||
|
@ -386,6 +409,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
savedInstanceState.putBoolean("requested_contacts_permission", mRequestedContactsPermission.get());
|
||||
savedInstanceState.putBoolean("opened_fab", mOpenedFab.get());
|
||||
savedInstanceState.putBoolean("created_by_view_intent", createdByViewIntent);
|
||||
savedInstanceState.putBoolean("groupingEnabled", groupingEnabled);
|
||||
if (mMenuSearchView != null && mMenuSearchView.isActionViewExpanded()) {
|
||||
savedInstanceState.putString("search", mSearchEditText != null ? mSearchEditText.getText().toString() : null);
|
||||
}
|
||||
|
@ -1255,6 +1279,25 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
this.mOnItemClickListener = l;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
|
||||
View view = super.onCreateView(inflater, container, savedInstanceState);
|
||||
|
||||
if (getActivity() instanceof StartConversationActivity && ((StartConversationActivity) getActivity()).groupingEnabled) {
|
||||
FixedExpandableListView lv = new FixedExpandableListView(view.getContext());
|
||||
lv.setId(android.R.id.list);
|
||||
lv.setDrawSelectorOnTop(false);
|
||||
|
||||
ListView oldList = view.findViewById(android.R.id.list);
|
||||
ViewGroup oldListParent = (ViewGroup) oldList.getParent();
|
||||
oldListParent.removeView(oldList);
|
||||
oldListParent.addView(lv, new FrameLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT));
|
||||
}
|
||||
|
||||
return view;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onViewCreated(@NonNull final View view, final Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
|
@ -1506,4 +1549,172 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
|
||||
static class FixedExpandableListView extends ExpandableListView {
|
||||
public FixedExpandableListView(Context context) {
|
||||
super(context);
|
||||
}
|
||||
|
||||
public FixedExpandableListView(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
}
|
||||
|
||||
public FixedExpandableListView(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
}
|
||||
|
||||
public FixedExpandableListView(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
super(context, attrs, defStyleAttr, defStyleRes);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAdapter(ListAdapter adapter) {
|
||||
if (adapter instanceof ExpandableListAdapter) {
|
||||
setAdapter((ExpandableListAdapter) adapter);
|
||||
} else {
|
||||
super.setAdapter(adapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ExpandableListItemAdapter extends ListItemAdapter implements ExpandableListAdapter {
|
||||
|
||||
private List<ListItem.Tag> tags = new ArrayList<>();
|
||||
private String generalTagName = activity.getString(R.string.contact_tag_general);
|
||||
private ListItem.Tag generalTag = new ListItem.Tag(generalTagName, UIHelper.getColorForName(generalTagName,true));
|
||||
|
||||
private Map<ListItem.Tag, List<ListItem>> groupedItems = new HashMap<>();
|
||||
|
||||
public ExpandableListItemAdapter(XmppActivity activity, List<ListItem> objects) {
|
||||
super(activity, objects);
|
||||
|
||||
registerDataSetObserver(new DataSetObserver() {
|
||||
@Override
|
||||
public void onChanged() {
|
||||
if (activity instanceof StartConversationActivity && ((StartConversationActivity) activity).groupingEnabled) {
|
||||
tags.clear();
|
||||
tags.addAll(mTagsAdapter.tags);
|
||||
|
||||
for (int i=tags.size()-1;i>=0;i--) {
|
||||
if (UIHelper.isStatusTag(activity, tags.get(i))) {
|
||||
tags.remove(tags.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
groupedItems.clear();
|
||||
|
||||
boolean generalTagAdded = false;
|
||||
|
||||
for (int i=0;i<ExpandableListItemAdapter.super.getCount();i++) {
|
||||
ListItem item = getItem(i);
|
||||
List<ListItem.Tag> itemTags = item.getTags(activity);
|
||||
|
||||
if (itemTags.size() == 0 || (itemTags.size() == 1 && UIHelper.isStatusTag(activity, itemTags.get(0)))) {
|
||||
if (!generalTagAdded) {
|
||||
tags.add(0, generalTag);
|
||||
generalTagAdded = true;
|
||||
}
|
||||
|
||||
List<ListItem> group = groupedItems.computeIfAbsent(generalTag, tag -> new ArrayList<>());
|
||||
group.add(item);
|
||||
} else {
|
||||
for (ListItem.Tag itemTag : itemTags) {
|
||||
if (UIHelper.isStatusTag(activity, itemTag)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
List<ListItem> group = groupedItems.computeIfAbsent(itemTag, tag -> new ArrayList<>());
|
||||
group.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (int i=tags.size()-1;i>=0;i--) {
|
||||
if (groupedItems.get(tags.get(i)) == null) {
|
||||
tags.remove(tags.get(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getGroupCount() {
|
||||
return tags.size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getChildrenCount(int groupPosition) {
|
||||
return groupedItems.get(tags.get(groupPosition)).size();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getGroup(int groupPosition) {
|
||||
return tags.get(groupPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object getChild(int groupPosition, int childPosition) {
|
||||
return groupedItems.get(tags.get(groupPosition)).get(childPosition);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getGroupId(int groupPosition) {
|
||||
return tags.get(groupPosition).hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getChildId(int groupPosition, int childPosition) {
|
||||
return groupedItems.get(tags.get(groupPosition)).get(childPosition).getJid().hashCode();
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getGroupView(int groupPosition, boolean isExpanded, View convertView, ViewGroup parent) {
|
||||
ListItem.Tag tag = tags.get(groupPosition);
|
||||
|
||||
View v = activity.getLayoutInflater().inflate(R.layout.contact_group, parent, false);
|
||||
|
||||
TextView tv = v.findViewById(R.id.text);
|
||||
tv.setText(activity.getString(R.string.contact_tag_with_total, tag.getName(), getChildrenCount(groupPosition)));
|
||||
tv.setBackgroundColor(tag.getColor());
|
||||
|
||||
return v;
|
||||
}
|
||||
|
||||
@Override
|
||||
public View getChildView(int groupPosition, int childPosition, boolean isLastChild, View convertView, ViewGroup parent) {
|
||||
ListItem item = groupedItems.get(tags.get(groupPosition)).get(childPosition);
|
||||
return super.getView(super.getPosition(item), convertView, parent);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isChildSelectable(int groupPosition, int childPosition) {
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupExpanded(int groupPosition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onGroupCollapsed(int groupPosition) {
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCombinedChildId(long groupId, long childId) {
|
||||
return 0x8000000000000000L | ((groupId & 0x7FFFFFFF) << 32) | (childId & 0xFFFFFFFF);
|
||||
}
|
||||
|
||||
@Override
|
||||
public long getCombinedGroupId(long groupId) {
|
||||
return (groupId & 0x7FFFFFFF) << 32;
|
||||
}
|
||||
|
||||
private int dpToPx(int dp) {
|
||||
return (int) (dp * Resources.getSystem().getDisplayMetrics().density);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -602,6 +602,15 @@ public class UIHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static boolean isStatusTag(Context context, ListItem.Tag tag) {
|
||||
String name = tag.getName();
|
||||
return name.equals(context.getString(R.string.presence_chat)) ||
|
||||
name.equals(context.getString(R.string.presence_away)) ||
|
||||
name.equals(context.getString(R.string.presence_xa)) ||
|
||||
name.equals(context.getString(R.string.presence_dnd)) ||
|
||||
name.equals(context.getString(R.string.presence_online));
|
||||
}
|
||||
|
||||
public static String filesizeToString(long size) {
|
||||
if (size > (1.5 * 1024 * 1024)) {
|
||||
return Math.round(size * 1f / (1024 * 1024)) + " MiB";
|
||||
|
|
23
src/main/res/layout/contact_group.xml
Normal file
23
src/main/res/layout/contact_group.xml
Normal file
|
@ -0,0 +1,23 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/text"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="8dp"
|
||||
android:textAllCaps="true"
|
||||
android:layout_marginTop="20dp"
|
||||
android:layout_marginBottom="20dp"
|
||||
android:layout_marginStart="40dp"
|
||||
android:layout_marginEnd="16dp" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1px"
|
||||
android:background="?color_background_tertiary" />
|
||||
|
||||
</FrameLayout>
|
|
@ -21,6 +21,7 @@
|
|||
<bool name="use_green_background">true</bool>
|
||||
<string name="quick_action">recent</string>
|
||||
<bool name="show_dynamic_tags">false</bool>
|
||||
<bool name="group_by_tags">false</bool>
|
||||
<bool name="btbv">true</bool>
|
||||
<integer name="automatic_message_deletion">0</integer>
|
||||
<bool name="dont_trust_system_cas">false</bool>
|
||||
|
|
|
@ -356,6 +356,8 @@
|
|||
<string name="no_application_found_to_view_contact">No app found to view contact</string>
|
||||
<string name="pref_show_dynamic_tags">Dynamic Tags</string>
|
||||
<string name="pref_show_dynamic_tags_summary">Allow organizing with tags</string>
|
||||
<string name="pref_group_by_tags">Group by Dynamic Tags</string>
|
||||
<string name="pref_group_by_tags_summary">Allow to grouping contacts by their tags</string>
|
||||
<string name="enable_notifications">Enable notifications</string>
|
||||
<string name="no_conference_server_found">No group chat server found</string>
|
||||
<string name="conference_creation_failed">Could not create group chat</string>
|
||||
|
@ -1049,4 +1051,6 @@
|
|||
<string name="could_not_create_file">could_not_create_file</string>
|
||||
<string name="muc_private_conversation_title">%1$s (%2$s)</string>
|
||||
<string name="message_selection_title">%1$d selected</string>
|
||||
<string name="contact_tag_general">General</string>
|
||||
<string name="contact_tag_with_total">%1$s (%2$d)</string>
|
||||
</resources>
|
||||
|
|
|
@ -176,6 +176,11 @@
|
|||
android:key="show_dynamic_tags"
|
||||
android:summary="@string/pref_show_dynamic_tags_summary"
|
||||
android:title="@string/pref_show_dynamic_tags" />
|
||||
<CheckBoxPreference
|
||||
android:defaultValue="@bool/group_by_tags"
|
||||
android:key="groupByTags"
|
||||
android:summary="@string/pref_group_by_tags_summary"
|
||||
android:title="@string/pref_group_by_tags" />
|
||||
<ListPreference
|
||||
android:defaultValue="@string/theme"
|
||||
android:entries="@array/themes"
|
||||
|
|
Loading…
Reference in a new issue