group conversation by tags

This commit is contained in:
kosyak 2024-01-01 23:36:07 +01:00
parent c4c5aaa6d6
commit 5f16051cf7
14 changed files with 435 additions and 72 deletions

View file

@ -57,16 +57,23 @@ import com.google.common.base.Optional;
import com.google.common.collect.Collections2;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.atomic.AtomicReference;
import java.util.stream.Collectors;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.FragmentConversationsOverviewBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Bookmark;
import eu.siacs.conversations.entities.Contact;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Presence;
import eu.siacs.conversations.services.XmppConnectionService;
import eu.siacs.conversations.ui.adapter.ConversationAdapter;
import eu.siacs.conversations.ui.interfaces.OnConversationArchived;
@ -90,6 +97,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
private static final String STATE_SCROLL_POSITION = ConversationsOverviewFragment.class.getName()+".scroll_state";
private final List<Conversation> conversations = new ArrayList<>();
private final List<ListItem.Tag> tags = new ArrayList<>();
private final PendingItem<Conversation> swipedConversation = new PendingItem<>();
private final PendingItem<ScrollState> pendingScrollState = new PendingItem<>();
private FragmentConversationsOverviewBinding binding;
@ -296,7 +305,7 @@ public class ConversationsOverviewFragment extends XmppFragment {
this.binding = DataBindingUtil.inflate(inflater, R.layout.fragment_conversations_overview, container, false);
this.binding.fab.setOnClickListener((view) -> StartConversationActivity.launch(getActivity()));
this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations);
this.conversationsAdapter = new ConversationAdapter(this.activity, this.conversations, this.tags);
this.conversationsAdapter.setConversationClickListener((view, conversation) -> {
if (activity instanceof OnConversationSelected) {
((OnConversationSelected) activity).onConversationSelected(conversation);
@ -487,6 +496,8 @@ public class ConversationsOverviewFragment extends XmppFragment {
pendingActionHelper.execute();
}
}
refreshTags();
this.conversationsAdapter.notifyDataSetChanged();
ScrollState scrollState = pendingScrollState.pop();
if (scrollState != null) {
@ -494,6 +505,56 @@ public class ConversationsOverviewFragment extends XmppFragment {
}
}
private void refreshTags() {
this.conversationsAdapter.setGroupingEnabled(activity.xmppConnectionService.getPreferences().getBoolean("conversationsGroupByTags", false));
if (conversationsAdapter.isGroupingEnabled()) {
List<ListItem.Tag> tags = new ArrayList<>();
final List<Account> accounts = activity.xmppConnectionService.getAccounts();
for (final Account account : accounts) {
if (account.isEnabled()) {
for (Contact contact : account.getRoster().getContacts()) {
if (contact.showInContactList()) {
tags.addAll(contact.getTags(activity));
}
}
for (Bookmark bookmark : account.getBookmarks()) {
tags.addAll(bookmark.getTags(activity));
}
}
}
Comparator<Map.Entry<ListItem.Tag, Integer>> sortTagsBy = Map.Entry.comparingByValue(Comparator.reverseOrder());
sortTagsBy = sortTagsBy.thenComparing(entry -> entry.getKey().getName());
this.tags.clear();
this.tags.addAll(
tags.stream()
.collect(Collectors.toMap((x) -> x, (t) -> 1, (c1, c2) -> c1 + c2))
.entrySet().stream()
.sorted(sortTagsBy)
.map(e -> e.getKey()).collect(Collectors.toList())
);
ListItem.Tag channelTag = null;
int channelTagIndex = 0;
for (ListItem.Tag tag : this.tags) {
if (tag.getName().equals("Channel")) {
channelTag = tag;
break;
}
channelTagIndex++;
}
if (channelTag != null) {
this.tags.remove(channelTagIndex);
this.tags.add(0, channelTag);
}
}
}
private void setScrollPosition(ScrollState scrollPosition) {
if (scrollPosition != null) {
LinearLayoutManager layoutManager = (LinearLayoutManager) binding.list.getLayoutManager();

View file

@ -95,7 +95,7 @@ public class ShareWithActivity extends XmppActivity implements XmppConnectionSer
setTitle(getString(R.string.title_activity_sharewith));
RecyclerView mListView = findViewById(R.id.choose_conversation_list);
mAdapter = new ConversationAdapter(this, this.mConversations);
mAdapter = new ConversationAdapter(this, this.mConversations, new ArrayList<>());
mListView.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false));
mListView.setAdapter(mAdapter);
mAdapter.setConversationClickListener((view, conversation) -> share(conversation));

View file

@ -13,6 +13,7 @@ import android.content.res.Resources;
import android.database.DataSetObserver;
import android.graphics.Color;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
@ -24,6 +25,7 @@ import android.text.method.LinkMovementMethod;
import android.util.AttributeSet;
import android.util.Log;
import android.util.Pair;
import android.util.TypedValue;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.KeyEvent;
@ -1308,6 +1310,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
FixedExpandableListView lv = new FixedExpandableListView(view.getContext());
lv.setId(android.R.id.list);
lv.setDrawSelectorOnTop(false);
lv.setGroupIndicator(null);
ListView oldList = view.findViewById(android.R.id.list);
ViewGroup oldListParent = (ViewGroup) oldList.getParent();
@ -1682,8 +1685,6 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
List<ListItem> group = groupedItems.computeIfAbsent(itemTag, tag -> new ArrayList<>());
group.add(item);
android.util.Log.e("27fd add", group.size() + " " + itemTag.getName());
}
}
}
@ -1743,6 +1744,8 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
v = activity.getLayoutInflater().inflate(R.layout.contact_group, parent, false);
v.findViewById(R.id.arrow).setRotation(isExpanded ? 180 : 0);
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());
@ -1751,8 +1754,10 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
v = activity.getLayoutInflater().inflate(R.layout.contact_account, parent, false);
v.findViewById(R.id.arrow).setRotation(isExpanded ? 180 : 0);
TextView tv = v.findViewById(R.id.text);
tv.setText(acc.getJid().toString());
tv.setText(acc.getJid().asBareJid().toString());
if (acc.isOnlineAndConnected()) {
tv.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline));

View file

@ -1,28 +1,44 @@
package eu.siacs.conversations.ui.adapter;
import android.content.Context;
import android.content.SharedPreferences;
import android.content.res.TypedArray;
import android.database.DataSetObserver;
import android.graphics.Typeface;
import android.preference.PreferenceManager;
import android.util.Pair;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.recyclerview.widget.RecyclerView;
import com.google.common.base.Optional;
import com.google.common.base.Strings;
import org.checkerframework.checker.units.qual.C;
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import eu.siacs.conversations.R;
import eu.siacs.conversations.databinding.ConversationListRowBinding;
import eu.siacs.conversations.entities.Account;
import eu.siacs.conversations.entities.Conversation;
import eu.siacs.conversations.entities.Conversational;
import eu.siacs.conversations.entities.ListItem;
import eu.siacs.conversations.entities.Message;
import eu.siacs.conversations.ui.ConversationFragment;
import eu.siacs.conversations.ui.StartConversationActivity;
import eu.siacs.conversations.ui.XmppActivity;
import eu.siacs.conversations.ui.util.AvatarWorkerTask;
import eu.siacs.conversations.ui.util.StyledAttributes;
@ -33,36 +49,251 @@ import eu.siacs.conversations.xmpp.Jid;
import eu.siacs.conversations.xmpp.jingle.OngoingRtpSession;
public class ConversationAdapter
extends RecyclerView.Adapter<ConversationAdapter.ConversationViewHolder> {
extends RecyclerView.Adapter<RecyclerView.ViewHolder> {
private static final int VIEW_TYPE_ACCOUNT = 0;
private static final int VIEW_TYPE_TAG = 1;
private static final int VIEW_TYPE_CONVERSATION = 2;
private final XmppActivity activity;
private final List<Conversation> conversations;
private OnConversationClickListener listener;
private boolean allowRelativeTimestamps = true;
private boolean allowRelativeTimestamps;
public ConversationAdapter(XmppActivity activity, List<Conversation> conversations) {
private ListItem.Tag generalTag;
private List<Object> items = new ArrayList<>();
private Map<Account, Set<ListItem.Tag>> expandedItems = new HashMap<>();
private Map<Account, Map<ListItem.Tag, Set<Conversation>>> groupedItems = new HashMap<>();
private boolean groupingEnabled = false;
SharedPreferences prefs;
public ConversationAdapter(XmppActivity activity, List<Conversation> conversations, List<ListItem.Tag> tags) {
this.activity = activity;
this.conversations = conversations;
/*prefs = activity.getSharedPreferences("expansionPrefs", Context.MODE_PRIVATE);
Set<String> expandedAccounts = prefs.getStringSet("expandedAccounts", Collections.emptySet());
for (String id : expandedAccounts) {
Set<String> tags = prefs.getStringSet("expandedTags" + id, Collections.emptySet());
Account account = activity.xmppConnectionService.findAccountByUuid(id);
}*/
final SharedPreferences p = PreferenceManager.getDefaultSharedPreferences(activity);
allowRelativeTimestamps = !p.getBoolean("always_full_timestamps", activity.getResources().getBoolean(R.bool.always_full_timestamps));
String generalTagName = activity.getString(R.string.contact_tag_general);
generalTag = new ListItem.Tag(generalTagName, UIHelper.getColorForName(generalTagName, true));
registerAdapterDataObserver(new RecyclerView.AdapterDataObserver() {
@Override
public void onChanged() {
if (groupingEnabled) {
items.clear();
groupedItems.clear();
List<Account> accounts = activity.xmppConnectionService.getAccounts();
for (Account account : accounts) {
if (accounts.size() > 1) {
items.add(account);
}
boolean accountExpanded = accounts.size() == 1 || expandedItems.containsKey(account);
boolean generalTagAdded = false;
int initialPosition = items.size();
Set<ListItem.Tag> expandedTags = expandedItems.getOrDefault(account, Collections.emptySet());
Map<ListItem.Tag, Set<Conversation>> groupedItems = new HashMap<>();
Map<Conversation, List<ListItem.Tag>> tagsToConversationCache = new HashMap<>();
for (int i = 0; i < conversations.size(); i++) {
Conversation item = conversations.get(i);
if (item.getAccount() != account) continue;
List<ListItem.Tag> itemTags = item.getContact().getTags(activity);
if (item.getBookmark() != null) {
itemTags.addAll(item.getBookmark().getTags(activity));
}
tagsToConversationCache.put(item, itemTags);
if (itemTags.size() == 0 || (itemTags.size() == 1 && UIHelper.isStatusTag(activity, itemTags.get(0)))) {
if (accountExpanded && !generalTagAdded) {
items.add(initialPosition, generalTag);
generalTagAdded = true;
}
if (accountExpanded && expandedTags.contains(generalTag)) {
items.add(item);
}
Set<Conversation> group = groupedItems.computeIfAbsent(generalTag, t -> new HashSet<>());
group.add(item);
}
}
for (ListItem.Tag tag : tags) {
if (UIHelper.isStatusTag(activity, tag)) {
continue;
}
if (accountExpanded) {
items.add(tag);
}
for (int i = 0; i < conversations.size(); i++) {
Conversation item = conversations.get(i);
if (item.getAccount() != account) continue;
List<ListItem.Tag> itemTags = tagsToConversationCache.get(item);
if (itemTags.contains(tag)) {
if (accountExpanded && expandedTags.contains(tag)) {
items.add(item);
}
Set<Conversation> group = groupedItems.computeIfAbsent(tag, t -> new HashSet<>());
group.add(item);
}
}
if (accountExpanded && groupedItems.get(tag) == null) {
items.remove(items.size() - 1);
}
}
ConversationAdapter.this.groupedItems.put(account, groupedItems);
}
}
}
});
}
public boolean isGroupingEnabled() {
return groupingEnabled;
}
public void setGroupingEnabled(boolean groupingEnabled) {
if (groupingEnabled != this.groupingEnabled) {
this.groupingEnabled = groupingEnabled;
notifyDataSetChanged();
}
}
@Override
public int getItemViewType(int position) {
if (!groupingEnabled) {
return VIEW_TYPE_CONVERSATION;
} else {
Object item = items.get(position);
if (item instanceof Account) {
return VIEW_TYPE_ACCOUNT;
} else if (item instanceof ListItem.Tag) {
return VIEW_TYPE_TAG;
} else {
return VIEW_TYPE_CONVERSATION;
}
}
}
@NonNull
@Override
public ConversationViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
return new ConversationViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.conversation_list_row,
parent,
false));
public RecyclerView.ViewHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
if (viewType == VIEW_TYPE_ACCOUNT) {
return new AccountViewHolder(parent);
} else if (viewType == VIEW_TYPE_TAG) {
return new TagViewHolder(parent);
} else {
return new ConversationViewHolder(
DataBindingUtil.inflate(
LayoutInflater.from(parent.getContext()),
R.layout.conversation_list_row,
parent,
false));
}
}
@Override
public void onBindViewHolder(@NonNull ConversationViewHolder viewHolder, int position) {
Conversation conversation = conversations.get(position);
public void onBindViewHolder(@NonNull RecyclerView.ViewHolder viewHolder, int position) {
if (groupingEnabled) {
if (viewHolder instanceof ConversationViewHolder) {
bindConversation((ConversationViewHolder) viewHolder, (Conversation) items.get(position));
} else if (viewHolder instanceof TagViewHolder) {
bindTag((TagViewHolder) viewHolder, (ListItem.Tag) items.get(position), position);
} else {
bindAccount((AccountViewHolder) viewHolder, (Account) items.get(position));
}
} else {
bindConversation((ConversationViewHolder) viewHolder, (Conversation) conversations.get(position));
}
}
@Override
public int getItemCount() {
if (groupingEnabled) {
return items.size();
} else {
return conversations.size();
}
}
private void bindAccount(AccountViewHolder viewHolder, Account account) {
viewHolder.text.setText(activity.getString(R.string.contact_tag_with_total, account.getJid().asBareJid().toString(), getChildCount(account, null)));
if (account.isOnlineAndConnected()) {
viewHolder.itemView.setBackgroundColor(StyledAttributes.getColor(activity, R.attr.TextColorOnline));
} else {
viewHolder.itemView.setBackgroundColor(StyledAttributes.getColor(activity, android.R.attr.textColorSecondary));
}
viewHolder.arrow.setRotation(expandedItems.containsKey(account) ? 180 : 0);
viewHolder.itemView.setOnClickListener(v -> {
if (expandedItems.containsKey(account)) {
expandedItems.remove(account);
} else {
expandedItems.put(account, new HashSet<>());
}
notifyDataSetChanged();
});
}
private void bindTag(TagViewHolder viewHolder, ListItem.Tag tag, int position) {
Account account = findAccountForTag(position);
viewHolder.text.setText(activity.getString(R.string.contact_tag_with_total, tag.getName(), getChildCount(account, tag)));
viewHolder.text.setBackgroundColor(tag.getColor());
viewHolder.arrow.setRotation(expandedItems.computeIfAbsent(account, a -> new HashSet<>()).contains(tag) ? 180 : 0);
viewHolder.itemView.setOnClickListener(v -> {
Set<ListItem.Tag> expandedTags = expandedItems.computeIfAbsent(account, a -> new HashSet<>());
if (expandedTags.contains(tag)) {
expandedTags.remove(tag);
} else {
expandedTags.add(tag);
}
notifyDataSetChanged();
});
}
private void bindConversation(ConversationViewHolder viewHolder, Conversation conversation) {
if (conversation == null) {
return;
}
@ -120,8 +351,8 @@ public class ConversationAdapter
final boolean showPreviewText;
if (fileAvailable
&& (message.isFileOrImage()
|| message.treatAsDownloadable()
|| message.isGeoUri())) {
|| message.treatAsDownloadable()
|| message.isGeoUri())) {
final int imageResource;
if (message.isGeoUri()) {
imageResource =
@ -299,11 +530,6 @@ public class ConversationAdapter
viewHolder.itemView.setOnClickListener(v -> listener.onConversationClick(v, conversation));
}
@Override
public int getItemCount() {
return conversations.size();
}
public void setConversationClickListener(OnConversationClickListener listener) {
this.listener = listener;
}
@ -318,6 +544,55 @@ public class ConversationAdapter
notifyItemRemoved(position);
}
@Nullable
private Account findAccountForTag(int position) {
Account account = null;
if (activity.xmppConnectionService.getAccounts().size() == 1) {
return activity.xmppConnectionService.getAccounts().get(0);
}
for (int i = position; i >= 0; i--) {
Object prev = items.get(i);
if (prev instanceof Account) {
account = (Account) prev;
break;
}
}
return account;
}
private int getChildCount(Account account, @Nullable ListItem.Tag tag) {
if (tag == null) {
int res = 0;
for (Conversation c : conversations) {
if (c.getAccount() == account) {
res++;
}
}
return res;
} else {
if (account == null) {
return 0;
}
Map<ListItem.Tag, Set<Conversation>> childTags = groupedItems.get(account);
if (childTags == null) {
return 0;
}
Set<Conversation> childConversations = childTags.get(tag);
if (childConversations == null) {
return 0;
}
return childConversations.size();
}
}
public interface OnConversationClickListener {
void onConversationClick(View view, Conversation conversation);
}
@ -331,4 +606,26 @@ public class ConversationAdapter
binding.getRoot().setLongClickable(true);
}
}
static class AccountViewHolder extends RecyclerView.ViewHolder {
private TextView text;
private View arrow;
private AccountViewHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_account, parent, false));
text = itemView.findViewById(R.id.text);
arrow = itemView.findViewById(R.id.arrow);
}
}
static class TagViewHolder extends RecyclerView.ViewHolder {
private TextView text;
private View arrow;
private TagViewHolder(ViewGroup parent) {
super(LayoutInflater.from(parent.getContext()).inflate(R.layout.contact_group, parent, false));
text = itemView.findViewById(R.id.text);
arrow = itemView.findViewById(R.id.arrow);
}
}
}

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/black"
android:pathData="M480,615L240,375L296,319L480,503L664,319L720,375L480,615Z"/>
</vector>

View file

@ -0,0 +1,9 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="960"
android:viewportHeight="960">
<path
android:fillColor="@android:color/white"
android:pathData="M480,615L240,375L296,319L480,503L664,319L720,375L480,615Z"/>
</vector>

View file

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string name="app_name">Simple Gallery</string>
<string name="app_launcher_name">Gallery</string>
<string name="ok">OK</string>
<string name="cancel">Cancel</string>
<string name="custom">Custom</string>
<string name="resize_and_save">Resize selection and save</string>
<string name="width">Width</string>
<string name="height">Height</string>
<string name="keep_aspect_ratio">Keep aspect ratio</string>
<string name="invalid_values">Please enter a valid resolution</string>
<string name="editor">Editor</string>
<string name="basic_editor">Basic Editor</string>
<string name="rotate">Rotate</string>
<string name="invalid_image_path">Invalid image path</string>
<string name="image_editing_failed">Image editing failed</string>
<string name="unknown_file_location">Unknown file location</string>
<string name="transform">Transform</string>
<string name="crop">Crop</string>
<string name="draw">Draw</string>
<string name="flip_horizontally">Flip horizontally</string>
<string name="flip_vertically">Flip vertically</string>
<string name="free_aspect_ratio">Free</string>
<string name="other_aspect_ratio">Other</string>
<string name="thumbnails">Thumbnails</string>
<string name="saving">saving</string>
<string name="out_of_memory_error">out_of_memory_error</string>
<string name="none">none</string>
<string name="file_saved">file_saved</string>
<string name="error">error</string>
<string name="simple_commons">simple_commons</string>
<string name="value_copied_to_clipboard_show">value_copied_to_clipboard_show</string>
<string name="default_color">default_color</string>
<string name="unknown_error_occurred">unknown_error_occurred</string>
<string name="undo">undo</string>
<string name="change_color">change_color</string>
<string name="resize">resize</string>
<string name="filter">filter</string>
<string name="could_not_create_file">could_not_create_file</string>
</resources>

View file

@ -3,14 +3,22 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/arrow"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="?attr/icon_expand_more"
android:layout_marginStart="8dp"
android:layout_gravity="center_vertical" />
<TextView
android:id="@+id/text"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="8dp"
android:fontFamily="sans-serif-medium"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_marginTop="12dp"
android:layout_marginBottom="12dp"
android:layout_marginStart="40dp"
android:layout_marginEnd="16dp" />

View file

@ -3,12 +3,21 @@
android:layout_width="match_parent"
android:layout_height="wrap_content">
<ImageView
android:id="@+id/arrow"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="?attr/icon_expand_more"
android:layout_marginStart="8dp"
android:layout_gravity="center_vertical" />
<TextView
android:id="@+id/text"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:padding="8dp"
android:textAllCaps="true"
android:fontFamily="sans-serif-medium"
android:layout_marginTop="20dp"
android:layout_marginBottom="20dp"
android:layout_marginStart="40dp"

View file

@ -80,6 +80,7 @@
<attr name="media_preview_unknown" format="reference" />
<attr name="icon_expand_more" format="reference" />
<attr name="icon_add_group" format="reference" />
<attr name="icon_add_person" format="reference" />
<attr name="icon_cancel" format="reference" />

View file

@ -22,6 +22,7 @@
<string name="quick_action">recent</string>
<bool name="show_dynamic_tags">false</bool>
<bool name="group_by_tags">false</bool>
<bool name="conversations_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>

View file

@ -361,6 +361,8 @@
<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="pref_group_conversations_by_tags">Group Conversations by Dynamic Tags</string>
<string name="pref_group_conversations_by_tags_summary">Allow to grouping conversations list 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>

View file

@ -104,6 +104,7 @@
<item name="media_preview_backup" type="reference">@drawable/ic_backup_black_48dp</item>
<item name="media_preview_unknown" type="reference">@drawable/ic_help_black_48dp</item>
<item name="icon_expand_more" type="reference">@drawable/ic_expand_more</item>
<item name="icon_add_group" type="reference">@drawable/ic_group_add_white_24dp</item>
<item name="icon_add_person" type="reference">@drawable/ic_person_add_white_24dp</item>
<item name="icon_cancel" type="reference">@drawable/ic_cancel_black_24dp</item>
@ -230,6 +231,7 @@
<item name="ic_attach_location" type="reference">@drawable/ic_attach_location_white</item>
<item name="ic_attach_photo" type="reference">@drawable/ic_attach_photo_white</item>
<item name="ic_attach_record" type="reference">@drawable/ic_attach_record_white</item>
<item name="icon_expand_more" type="reference">@drawable/ic_expand_more_white</item>
<item name="message_bubble_received_monochrome" type="reference">
@drawable/message_bubble_received_grey

View file

@ -181,6 +181,11 @@
android:key="groupByTags"
android:summary="@string/pref_group_by_tags_summary"
android:title="@string/pref_group_by_tags" />
<CheckBoxPreference
android:defaultValue="@bool/conversations_group_by_tags"
android:key="conversationsGroupByTags"
android:summary="@string/pref_group_conversations_by_tags_summary"
android:title="@string/pref_group_conversations_by_tags" />
<ListPreference
android:defaultValue="@string/theme"
android:entries="@array/themes"