From bc4d112060cbe822bef0dda8d5104db725deeb1b Mon Sep 17 00:00:00 2001 From: Christian Schneppe Date: Sat, 3 Feb 2018 23:41:07 +0100 Subject: implemented message search fixes #110 --- .../pixart/messenger/ui/ConversationActivity.java | 12 +- .../pixart/messenger/ui/ConversationFragment.java | 161 +++++++++++++++++++++ .../ic_keyboard_arrow_down_white_24dp.png | Bin 0 -> 168 bytes .../ic_keyboard_arrow_up_white_24dp.png | Bin 0 -> 156 bytes .../ic_keyboard_arrow_down_white_24dp.png | Bin 0 -> 145 bytes .../ic_keyboard_arrow_up_white_24dp.png | Bin 0 -> 129 bytes .../ic_keyboard_arrow_down_white_24dp.png | Bin 0 -> 201 bytes .../ic_keyboard_arrow_up_white_24dp.png | Bin 0 -> 179 bytes .../ic_keyboard_arrow_down_white_24dp.png | Bin 0 -> 268 bytes .../ic_keyboard_arrow_up_white_24dp.png | Bin 0 -> 259 bytes .../ic_keyboard_arrow_down_white_24dp.png | Bin 0 -> 303 bytes .../ic_keyboard_arrow_up_white_24dp.png | Bin 0 -> 284 bytes src/main/res/layout/fragment_conversation.xml | 134 ++++++++++++----- src/main/res/menu/conversations.xml | 6 +- 14 files changed, 272 insertions(+), 41 deletions(-) create mode 100644 src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png create mode 100644 src/main/res/drawable-hdpi/ic_keyboard_arrow_up_white_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png create mode 100644 src/main/res/drawable-mdpi/ic_keyboard_arrow_up_white_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png create mode 100644 src/main/res/drawable-xhdpi/ic_keyboard_arrow_up_white_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png create mode 100644 src/main/res/drawable-xxhdpi/ic_keyboard_arrow_up_white_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png create mode 100644 src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_up_white_24dp.png (limited to 'src') diff --git a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java index 274b51d8a..36940359d 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationActivity.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationActivity.java @@ -59,7 +59,6 @@ import java.util.Iterator; import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; -import de.pixart.messenger.BuildConfig; import de.pixart.messenger.Config; import de.pixart.messenger.R; import de.pixart.messenger.crypto.axolotl.AxolotlService; @@ -168,6 +167,7 @@ public class ConversationActivity extends XmppActivity public void showConversationsOverview() { if (mConversationFragment != null) { mConversationFragment.stopScrolling(); + mConversationFragment.hideSearchField(); } if (mContentView instanceof SlidingPaneLayout) { SlidingPaneLayout mSlidingPaneLayout = (SlidingPaneLayout) mContentView; @@ -291,6 +291,7 @@ public class ConversationActivity extends XmppActivity xmppConnectionService.getNotificationService().setOpenConversation(null); } closeContextMenu(); + mConversationFragment.hideSearchField(); } @Override @@ -475,6 +476,7 @@ public class ConversationActivity extends XmppActivity final MenuItem menuInviteContact = menu.findItem(R.id.action_invite); final MenuItem menuUpdater = menu.findItem(R.id.action_check_updates); final MenuItem menuInviteUser = menu.findItem(R.id.action_invite_user); + final MenuItem menuSearchHistory = menu.findItem(R.id.action_search_history); if (isConversationsOverviewVisable() && isConversationsOverviewHideable()) { menuArchiveChat.setVisible(false); @@ -483,6 +485,7 @@ public class ConversationActivity extends XmppActivity menuInviteContact.setVisible(false); menuAttach.setVisible(false); menuClearHistory.setVisible(false); + menuSearchHistory.setVisible(false); if (xmppConnectionService.installedFromFDroid()) { menuUpdater.setVisible(false); } else { @@ -835,6 +838,9 @@ public class ConversationActivity extends XmppActivity case R.id.action_unblock: BlockContactDialog.show(this, getSelectedConversation()); break; + case R.id.action_search_history: + mConversationFragment.showSearchField(); + break; default: break; } @@ -1067,7 +1073,9 @@ public class ConversationActivity extends XmppActivity @Override public void onBackPressed() { - if (!isConversationsOverviewVisable()) { + if (!isConversationsOverviewVisable() && mConversationFragment.isSearchFieldVisible()) { + mConversationFragment.hideSearchField(); + } else if (!isConversationsOverviewVisable()) { showConversationsOverview(); } else { super.onBackPressed(); diff --git a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java index d62192170..b97dda837 100644 --- a/src/main/java/de/pixart/messenger/ui/ConversationFragment.java +++ b/src/main/java/de/pixart/messenger/ui/ConversationFragment.java @@ -16,6 +16,7 @@ import android.support.v13.view.inputmethod.InputConnectionCompat; import android.support.v13.view.inputmethod.InputContentInfoCompat; import android.text.Editable; import android.text.InputType; +import android.text.TextWatcher; import android.util.Log; import android.util.Pair; import android.view.ContextMenu; @@ -34,6 +35,7 @@ import android.widget.AbsListView; import android.widget.AbsListView.OnScrollListener; import android.widget.AdapterView; import android.widget.AdapterView.AdapterContextMenuInfo; +import android.widget.EditText; import android.widget.ImageButton; import android.widget.ListView; import android.widget.PopupMenu; @@ -93,11 +95,17 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa protected Conversation conversation; protected ListView messagesView; protected MessageAdapter messageListAdapter; + protected Message lastHistoryMessage = null; private EditMessage mEditMessage; private ImageButton mSendButton; private RelativeLayout snackbar; private RelativeLayout messagehint; private TextView messagehint_message; + private RelativeLayout textsend; + private RelativeLayout searchfield; + private EditText searchfield_input; + private ImageButton searchUp; + private ImageButton searchDown; private TextView snackbarMessage; private TextView snackbarAction; private Toast messageLoaderToast; @@ -596,6 +604,13 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa messagehint = view.findViewById(R.id.messagehint); messagehint_message = view.findViewById(R.id.messagehint_message); + textsend = view.findViewById(R.id.textsend); + + searchfield = view.findViewById(R.id.searchfield); + searchfield_input = view.findViewById(R.id.searchfield_input); + searchUp = view.findViewById(R.id.search_up); + searchDown = view.findViewById(R.id.search_down); + messagesView = view.findViewById(R.id.messages_view); messagesView.setOnScrollListener(mOnScrollListener); messagesView.setTranscriptMode(ListView.TRANSCRIPT_MODE_NORMAL); @@ -1531,6 +1546,79 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa messagehint.setVisibility(View.GONE); } + protected void showSearchField() { + textsend.setVisibility(View.GONE); + searchfield.setVisibility(View.VISIBLE); + searchfield_input.addTextChangedListener(mSearchTextWatcher); + searchfield_input.requestFocus(); + final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.showSoftInput(searchfield_input, InputMethodManager.SHOW_IMPLICIT); + } + } + + protected void hideSearchField() { + textsend.setVisibility(View.VISIBLE); + searchfield.setVisibility(View.GONE); + if (activity != null) { + final InputMethodManager imm = (InputMethodManager) activity.getSystemService(Context.INPUT_METHOD_SERVICE); + if (imm != null) { + imm.hideSoftInputFromWindow(searchfield_input.getWindowToken(), InputMethodManager.HIDE_IMPLICIT_ONLY); + } + } + searchfield_input.setText(""); + } + + protected boolean isSearchFieldVisible() { + return searchfield.getVisibility() == View.VISIBLE; + } + + private TextWatcher mSearchTextWatcher = new TextWatcher() { + + @Override + public void afterTextChanged(Editable editable) { + String query = editable.toString().trim(); + + if ((!query.isEmpty() || !query.contains("")) && query.length() >= 3) { + searchUp.setVisibility(View.VISIBLE); + searchDown.setVisibility(View.VISIBLE); + Message found = searchHistory(query); + if (found != null) { + searchUp.setVisibility(View.VISIBLE); + searchDown.setVisibility(View.VISIBLE); + } else { + searchUp.setVisibility(View.GONE); + searchDown.setVisibility(View.GONE); + } + searchUp.setEnabled(found != null); + searchDown.setEnabled(found != null); + View.OnClickListener upDownListener = new View.OnClickListener() { + @Override + public void onClick(View view) { + String searchQuery = searchfield_input.getText().toString().trim(); + if (!searchQuery.isEmpty() || !searchQuery.contains("")) { + searchHistory(searchQuery, view.getId() == R.id.search_up); + } + + } + }; + searchUp.setOnClickListener(upDownListener); + searchDown.setOnClickListener(upDownListener); + } else { + searchUp.setVisibility(View.GONE); + searchDown.setVisibility(View.GONE); + } + } + + @Override + public void beforeTextChanged(CharSequence s, int start, int count, int after) { + } + + @Override + public void onTextChanged(CharSequence s, int start, int before, int count) { + } + }; + protected void sendPlainTextMessage(Message message) { ConversationActivity activity = (ConversationActivity) getActivity(); activity.xmppConnectionService.sendMessage(message); @@ -1788,6 +1876,79 @@ public class ConversationFragment extends Fragment implements EditMessage.Keyboa } } + public Message searchHistory(String query) { + return searchHistory(query, null); + } + + public Message searchHistory(String query, Boolean ascendingSearch) { + return searchHistory(query, lastHistoryMessage, ascendingSearch); + } + + /** + * Search through history from message basis either ascending or descending + * + * @param query search term + * @param basis message to start from. If null, start from last recent message + * @param ascendingSearch do we want to ascend or descend in our search? + * If this is null, ascend to first match and return. + * @return match or null + */ + public Message searchHistory(String query, Message basis, Boolean ascendingSearch) { + int entryIndex; + Message message; + lastHistoryMessage = basis; + if (messageList.size() == 0) { + return null; + } + if (basis == null) { + entryIndex = messageList.size() - 1; + } else { + int in = getIndexOf(basis.getUuid(), messageList); + entryIndex = (in != -1 ? in : messageList.size() - 1); + } + + int firstMatchIndex = entryIndex; + boolean entryIndexWasMatch = true; + do { + message = messageList.get(firstMatchIndex); + if (message.getType() == Message.TYPE_TEXT && messageContainsQuery(message, query)) { + lastHistoryMessage = message; + break; + } + entryIndexWasMatch = false; + firstMatchIndex = (messageList.size() + firstMatchIndex - 1) % messageList.size(); + } while (entryIndex != firstMatchIndex); + + if (!entryIndexWasMatch && entryIndex == firstMatchIndex) { + //No matches + return null; + } + + if (ascendingSearch != null) { + int direction = ascendingSearch ? -1 : 1; + int nextMatchIndex = firstMatchIndex; + do { + nextMatchIndex = (messageList.size() + nextMatchIndex + direction) % messageList.size(); + message = messageList.get(nextMatchIndex); + if (message.getType() == Message.TYPE_TEXT && messageContainsQuery(message, query)) { + lastHistoryMessage = message; + break; + } + } while (nextMatchIndex != entryIndex); + } + + if (lastHistoryMessage != null) { + int pos = getIndexOf(lastHistoryMessage.getUuid(), messageList); + setScrollPosition(new Pair<>(pos, pos)); + messagesView.setSelection(pos); + } + return lastHistoryMessage; + } + + private boolean messageContainsQuery(Message m, String q) { + return m != null && m.getMergedBody().toString().toLowerCase().contains(q.toLowerCase()); + } + enum SendButtonAction { TEXT, TAKE_FROM_CAMERA, SEND_LOCATION, RECORD_VOICE, CANCEL, CHOOSE_PICTURE; diff --git a/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png b/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png new file mode 100644 index 000000000..bbb4fb4dc Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_keyboard_arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-hdpi/ic_keyboard_arrow_up_white_24dp.png b/src/main/res/drawable-hdpi/ic_keyboard_arrow_up_white_24dp.png new file mode 100644 index 000000000..dea898838 Binary files /dev/null and b/src/main/res/drawable-hdpi/ic_keyboard_arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png b/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png new file mode 100644 index 000000000..ef8a4b6a4 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_keyboard_arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-mdpi/ic_keyboard_arrow_up_white_24dp.png b/src/main/res/drawable-mdpi/ic_keyboard_arrow_up_white_24dp.png new file mode 100644 index 000000000..a2e4baad0 Binary files /dev/null and b/src/main/res/drawable-mdpi/ic_keyboard_arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png b/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png new file mode 100644 index 000000000..058cebb7f Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_keyboard_arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-xhdpi/ic_keyboard_arrow_up_white_24dp.png b/src/main/res/drawable-xhdpi/ic_keyboard_arrow_up_white_24dp.png new file mode 100644 index 000000000..ae36d91e1 Binary files /dev/null and b/src/main/res/drawable-xhdpi/ic_keyboard_arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png new file mode 100644 index 000000000..f9622b7be Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_up_white_24dp.png b/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_up_white_24dp.png new file mode 100644 index 000000000..ce4aa5602 Binary files /dev/null and b/src/main/res/drawable-xxhdpi/ic_keyboard_arrow_up_white_24dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png new file mode 100644 index 000000000..30948d983 Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_down_white_24dp.png differ diff --git a/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_up_white_24dp.png b/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_up_white_24dp.png new file mode 100644 index 000000000..42615516b Binary files /dev/null and b/src/main/res/drawable-xxxhdpi/ic_keyboard_arrow_up_white_24dp.png differ diff --git a/src/main/res/layout/fragment_conversation.xml b/src/main/res/layout/fragment_conversation.xml index 2ac131d34..7f1a4197a 100644 --- a/src/main/res/layout/fragment_conversation.xml +++ b/src/main/res/layout/fragment_conversation.xml @@ -11,7 +11,6 @@ android:layout_width="fill_parent" android:layout_height="wrap_content" android:layout_above="@+id/snackbar" - android:layout_below="@+id/messagehint" android:layout_alignParentLeft="true" android:layout_alignParentTop="true" android:background="@color/grey200" @@ -24,55 +23,114 @@ + android:id="@+id/input"> - + + + + + + + + + + + + - - - - + android:clickable="true" + android:paddingBottom="2dp" + android:paddingLeft="2dp" + android:paddingTop="2dp"> + + + + + + + - + \ No newline at end of file -- cgit v1.2.3