tags navigation and self contact
This commit is contained in:
parent
18c41eb05e
commit
a2a943c36a
|
@ -99,7 +99,7 @@ android {
|
|||
compileSdkVersion 33
|
||||
|
||||
defaultConfig {
|
||||
minSdkVersion 21
|
||||
minSdkVersion 24
|
||||
targetSdkVersion 33
|
||||
versionCode 42058
|
||||
versionName "2.12.5"
|
||||
|
|
|
@ -109,6 +109,11 @@ public class Contact implements ListItem, Blockable {
|
|||
this.keys = new JSONObject();
|
||||
}
|
||||
|
||||
public Contact(Contact other) {
|
||||
this(null, other.systemName, other.serverName, other.presenceName, other.jid, other.subscription, other.photoUri, other.systemAccount, other.keys == null ? null : other.keys.toString(), other.getAvatar() == null ? null : other.getAvatar().sha1sum, other.mLastseen, other.mLastPresence, other.groups == null ? null : other.groups.toString(), other.rtpCapability);
|
||||
setAccount(other.getAccount());
|
||||
}
|
||||
|
||||
public static Contact fromCursor(final Cursor cursor) {
|
||||
final Jid jid;
|
||||
try {
|
||||
|
|
|
@ -27,9 +27,10 @@ import eu.siacs.conversations.ui.adapter.ListItemAdapter;
|
|||
public abstract class AbstractSearchableListItemActivity extends XmppActivity implements TextView.OnEditorActionListener {
|
||||
protected ActivityChooseContactBinding binding;
|
||||
private final List<ListItem> listItems = new ArrayList<>();
|
||||
private ArrayAdapter<ListItem> mListItemsAdapter;
|
||||
private ListItemAdapter mListItemsAdapter;
|
||||
|
||||
private EditText mSearchEditText;
|
||||
protected MenuItem mMenuSearchView;
|
||||
protected EditText mSearchEditText;
|
||||
|
||||
private final MenuItem.OnActionExpandListener mOnActionExpandListener = new MenuItem.OnActionExpandListener() {
|
||||
|
||||
|
@ -63,12 +64,12 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
|
|||
|
||||
@Override
|
||||
public void beforeTextChanged(final CharSequence s, final int start, final int count,
|
||||
final int after) {
|
||||
final int after) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onTextChanged(final CharSequence s, final int start, final int before,
|
||||
final int count) {
|
||||
final int count) {
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -84,7 +85,7 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
|
|||
return mSearchEditText;
|
||||
}
|
||||
|
||||
public ArrayAdapter<ListItem> getListItemAdapter() {
|
||||
public ListItemAdapter getListItemAdapter() {
|
||||
return mListItemsAdapter;
|
||||
}
|
||||
|
||||
|
@ -102,13 +103,13 @@ public abstract class AbstractSearchableListItemActivity extends XmppActivity im
|
|||
@Override
|
||||
public boolean onCreateOptionsMenu(final Menu menu) {
|
||||
getMenuInflater().inflate(R.menu.choose_contact, menu);
|
||||
final MenuItem menuSearchView = menu.findItem(R.id.action_search);
|
||||
final View mSearchView = menuSearchView.getActionView();
|
||||
mMenuSearchView = menu.findItem(R.id.action_search);
|
||||
final View mSearchView = mMenuSearchView.getActionView();
|
||||
mSearchEditText = mSearchView.findViewById(R.id.search_field);
|
||||
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
|
||||
mSearchEditText.setHint(R.string.search_contacts);
|
||||
mSearchEditText.setOnEditorActionListener(this);
|
||||
menuSearchView.setOnActionExpandListener(mOnActionExpandListener);
|
||||
mMenuSearchView.setOnActionExpandListener(mOnActionExpandListener);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -120,7 +120,7 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
|
|||
|
||||
multiple = intent.getBooleanExtra(EXTRA_SELECT_MULTIPLE, false);
|
||||
if (multiple) {
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
|
||||
getListView().setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
|
||||
getListView().setMultiChoiceModeListener(this);
|
||||
}
|
||||
|
||||
|
@ -136,6 +136,15 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
|
|||
final SharedPreferences preferences = getPreferences();
|
||||
this.startSearching = intent.getBooleanExtra("direct_search", false) && preferences.getBoolean("start_searching", getResources().getBoolean(R.bool.start_searching));
|
||||
|
||||
getListItemAdapter().refreshSettings();
|
||||
getListItemAdapter().setOnTagClickedListener((tag) -> {
|
||||
if (mMenuSearchView != null) {
|
||||
mMenuSearchView.expandActionView();
|
||||
mSearchEditText.setText("");
|
||||
mSearchEditText.append(tag);
|
||||
filterContacts(tag);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void onFabClicked(View v) {
|
||||
|
@ -278,10 +287,19 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
|
|||
getListItems().add(contact);
|
||||
}
|
||||
}
|
||||
|
||||
final Contact self = new Contact(account.getSelfContact());
|
||||
self.setSystemName("Note to Self");
|
||||
if (self.match(this, needle)) {
|
||||
getListItems().add(self);
|
||||
}
|
||||
}
|
||||
}
|
||||
Collections.sort(getListItems());
|
||||
getListItemAdapter().notifyDataSetChanged();
|
||||
for (int i = 0; i < getListItemAdapter().getCount(); i++) {
|
||||
getListView().setItemChecked(i, selected.contains(getListItemAdapter().getItem(i).getJid().toString()));
|
||||
}
|
||||
}
|
||||
|
||||
private String[] getSelectedContactJids() {
|
||||
|
@ -390,8 +408,24 @@ public class ChooseContactActivity extends AbstractSearchableListItemActivity im
|
|||
@Override
|
||||
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
|
||||
if (multiple) {
|
||||
startActionMode(this);
|
||||
getListView().setItemChecked(position, true);
|
||||
if (getListView().isItemChecked(position)) {
|
||||
selected.add(getListItemAdapter().getItem(position).getJid().toString());
|
||||
} else {
|
||||
selected.remove(getListItemAdapter().getItem(position).getJid().toString());
|
||||
}
|
||||
|
||||
if (selected.isEmpty()) {
|
||||
this.binding.fab.setImageResource(R.drawable.ic_person_add_white_24dp);
|
||||
if (this.showEnterJid) {
|
||||
this.binding.fab.show();
|
||||
} else {
|
||||
this.binding.fab.hide();
|
||||
}
|
||||
} else {
|
||||
binding.fab.setImageResource(R.drawable.ic_forward_white_24dp);
|
||||
binding.fab.show();
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
final InputMethodManager imm = (InputMethodManager) getSystemService(Context.INPUT_METHOD_SERVICE);
|
||||
|
|
|
@ -12,6 +12,7 @@ import android.content.pm.PackageManager;
|
|||
import android.net.Uri;
|
||||
import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.preference.PreferenceManager;
|
||||
import android.text.Editable;
|
||||
import android.text.Html;
|
||||
import android.text.TextWatcher;
|
||||
|
@ -21,6 +22,7 @@ import android.util.Pair;
|
|||
import android.view.ContextMenu;
|
||||
import android.view.ContextMenu.ContextMenuInfo;
|
||||
import android.view.KeyEvent;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.Menu;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
|
@ -49,6 +51,8 @@ import androidx.databinding.DataBindingUtil;
|
|||
import androidx.fragment.app.Fragment;
|
||||
import androidx.fragment.app.FragmentManager;
|
||||
import androidx.fragment.app.FragmentTransaction;
|
||||
import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
|
||||
import androidx.viewpager.widget.PagerAdapter;
|
||||
import androidx.viewpager.widget.ViewPager;
|
||||
|
@ -58,9 +62,15 @@ import com.leinardi.android.speeddial.SpeedDialActionItem;
|
|||
import com.leinardi.android.speeddial.SpeedDialView;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Comparator;
|
||||
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.stream.Collectors;
|
||||
|
||||
import eu.siacs.conversations.Config;
|
||||
import eu.siacs.conversations.R;
|
||||
|
@ -83,6 +93,7 @@ import eu.siacs.conversations.ui.util.PendingItem;
|
|||
import eu.siacs.conversations.ui.util.SoftKeyboardUtils;
|
||||
import eu.siacs.conversations.ui.widget.SwipeRefreshListFragment;
|
||||
import eu.siacs.conversations.utils.AccountUtils;
|
||||
import eu.siacs.conversations.utils.UIHelper;
|
||||
import eu.siacs.conversations.utils.XmppUri;
|
||||
import eu.siacs.conversations.xmpp.Jid;
|
||||
import eu.siacs.conversations.xmpp.OnUpdateBlocklist;
|
||||
|
@ -102,6 +113,7 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
private ListPagerAdapter mListPagerAdapter;
|
||||
private final List<ListItem> contacts = new ArrayList<>();
|
||||
private ListItemAdapter mContactsAdapter;
|
||||
private TagsAdapter mTagsAdapter = new TagsAdapter();
|
||||
private final List<ListItem> conferences = new ArrayList<>();
|
||||
private ListItemAdapter mConferenceAdapter;
|
||||
private final List<String> mActivatedAccounts = new ArrayList<>();
|
||||
|
@ -703,6 +715,15 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
mSearchEditText = mSearchView.findViewById(R.id.search_field);
|
||||
mSearchEditText.addTextChangedListener(mSearchTextWatcher);
|
||||
mSearchEditText.setOnEditorActionListener(mSearchDone);
|
||||
|
||||
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(this);
|
||||
boolean showDynamicTags = preferences.getBoolean(SettingsActivity.SHOW_DYNAMIC_TAGS, getResources().getBoolean(R.bool.show_dynamic_tags));
|
||||
if (showDynamicTags) {
|
||||
RecyclerView tags = mSearchView.findViewById(R.id.tags);
|
||||
tags.setLayoutManager(new LinearLayoutManager(this, LinearLayoutManager.HORIZONTAL, false));
|
||||
tags.setAdapter(mTagsAdapter);
|
||||
}
|
||||
|
||||
String initialSearchValue = mInitialSearchValue.pop();
|
||||
if (initialSearchValue != null) {
|
||||
mMenuSearchView.expandActionView();
|
||||
|
@ -996,7 +1017,9 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
|
||||
protected void filterContacts(String needle) {
|
||||
this.contacts.clear();
|
||||
ArrayList<ListItem.Tag> tags = new ArrayList<>();
|
||||
final List<Account> accounts = xmppConnectionService.getAccounts();
|
||||
boolean foundSopranica = false;
|
||||
for (Account account : accounts) {
|
||||
if (account.getStatus() != Account.State.DISABLED) {
|
||||
for (Contact contact : account.getRoster().getContacts()) {
|
||||
|
@ -1006,11 +1029,52 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
|| (needle != null && !needle.trim().isEmpty())
|
||||
|| s.compareTo(Presence.Status.OFFLINE) < 0)) {
|
||||
this.contacts.add(contact);
|
||||
tags.addAll(contact.getTags(this));
|
||||
}
|
||||
}
|
||||
|
||||
final Contact self = new Contact(account.getSelfContact());
|
||||
self.setSystemName("Note to Self");
|
||||
if (self.match(this, needle)) {
|
||||
this.contacts.add(self);
|
||||
}
|
||||
|
||||
for (Bookmark bookmark : account.getBookmarks()) {
|
||||
if (bookmark.match(this, needle)) {
|
||||
if (bookmark.getJid().toString().equals("discuss@conference.soprani.ca")) {
|
||||
foundSopranica = true;
|
||||
}
|
||||
this.contacts.add(bookmark);
|
||||
tags.addAll(bookmark.getTags(this));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Comparator<Map.Entry<ListItem.Tag,Integer>> sortTagsBy = Map.Entry.comparingByValue(Comparator.reverseOrder());
|
||||
sortTagsBy = sortTagsBy.thenComparing(entry -> entry.getKey().getName());
|
||||
|
||||
mTagsAdapter.setTags(
|
||||
tags.stream()
|
||||
.collect(Collectors.toMap((x) -> x, (t) -> 1, (c1, c2) -> c1 + c2))
|
||||
.entrySet().stream()
|
||||
.sorted(sortTagsBy)
|
||||
.map(e -> e.getKey()).collect(Collectors.toList())
|
||||
);
|
||||
Collections.sort(this.contacts);
|
||||
|
||||
final boolean sopranicaDeleted = getPreferences().getBoolean("cheogram_sopranica_bookmark_deleted", false);
|
||||
|
||||
if (!sopranicaDeleted && !foundSopranica && (needle == null || needle.equals("")) && xmppConnectionService.getAccounts().size() > 0) {
|
||||
Bookmark bookmark = new Bookmark(
|
||||
xmppConnectionService.getAccounts().get(0),
|
||||
Jid.of("discuss@conference.soprani.ca")
|
||||
);
|
||||
bookmark.setBookmarkName("Soprani.ca / Cheogram Discussion");
|
||||
bookmark.addChild("group").setContent("support");
|
||||
this.contacts.add(0, bookmark);
|
||||
}
|
||||
|
||||
mContactsAdapter.notifyDataSetChanged();
|
||||
}
|
||||
|
||||
|
@ -1393,4 +1457,65 @@ public class StartConversationActivity extends XmppActivity implements XmppConne
|
|||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class TagsAdapter extends RecyclerView.Adapter<TagsAdapter.ViewHolder> {
|
||||
class ViewHolder extends RecyclerView.ViewHolder {
|
||||
protected TextView tv;
|
||||
|
||||
public ViewHolder(View v) {
|
||||
super(v);
|
||||
tv = (TextView) v;
|
||||
tv.setOnClickListener(view -> {
|
||||
String needle = mSearchEditText.getText().toString();
|
||||
String tag = tv.getText().toString();
|
||||
String[] parts = needle.split("[,\\s]+");
|
||||
if(needle.isEmpty()) {
|
||||
needle = tag;
|
||||
} else if (tag.toLowerCase(Locale.US).contains(parts[parts.length-1])) {
|
||||
needle = needle.replace(parts[parts.length-1], tag);
|
||||
} else {
|
||||
needle += ", " + tag;
|
||||
}
|
||||
mSearchEditText.setText("");
|
||||
mSearchEditText.append(needle);
|
||||
filter(needle);
|
||||
});
|
||||
}
|
||||
|
||||
public void setTag(ListItem.Tag tag) {
|
||||
tv.setText(tag.getName());
|
||||
tv.setBackgroundColor(tag.getColor());
|
||||
}
|
||||
}
|
||||
|
||||
protected List<ListItem.Tag> tags = new ArrayList<>();
|
||||
|
||||
@Override
|
||||
public ViewHolder onCreateViewHolder(ViewGroup viewGroup, int i) {
|
||||
View view = LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.list_item_tag, null);
|
||||
return new ViewHolder(view);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindViewHolder(ViewHolder viewHolder, int i) {
|
||||
viewHolder.setTag(tags.get(i));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getItemCount() {
|
||||
return tags.size();
|
||||
}
|
||||
|
||||
public void setTags(final List<ListItem.Tag> tags) {
|
||||
ListItem.Tag channelTag = new ListItem.Tag("Channel", UIHelper.getColorForName("Channel", true));
|
||||
String needle = mSearchEditText == null ? "" : mSearchEditText.getText().toString().toLowerCase(Locale.US).trim();
|
||||
HashSet<String> parts = new HashSet<>(Arrays.asList(needle.split("[,\\s]+")));
|
||||
this.tags = tags.stream().filter(
|
||||
tag -> !tag.equals(channelTag) && !parts.contains(tag.getName().toLowerCase(Locale.US))
|
||||
).collect(Collectors.toList());
|
||||
if (!parts.contains("channel") && tags.contains(channelTag)) this.tags.add(0, channelTag);
|
||||
notifyDataSetChanged();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,4 +15,10 @@
|
|||
android:imeOptions="flagNoExtractUi|actionSearch"
|
||||
android:inputType="textEmailAddress|textNoSuggestions"/>
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/tags"
|
||||
android:layout_below="@+id/search_field"
|
||||
android:layout_width="fill_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
</RelativeLayout>
|
Loading…
Reference in a new issue