diff --git a/res/drawable/search_toolbar_shadow.xml b/res/drawable/search_toolbar_shadow.xml
new file mode 100644
index 0000000000..5afdc2a2df
--- /dev/null
+++ b/res/drawable/search_toolbar_shadow.xml
@@ -0,0 +1,7 @@
+
+
+
\ No newline at end of file
diff --git a/res/layout/conversation_list_activity.xml b/res/layout/conversation_list_activity.xml
index 6b9c319930..dcd8957b3d 100644
--- a/res/layout/conversation_list_activity.xml
+++ b/res/layout/conversation_list_activity.xml
@@ -1,13 +1,14 @@
-
+
-
+
+
-
-
-
-
-
+
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/res/layout/search_toolbar.xml b/res/layout/search_toolbar.xml
new file mode 100644
index 0000000000..5b5a8508e9
--- /dev/null
+++ b/res/layout/search_toolbar.xml
@@ -0,0 +1,19 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/menu/conversation_list.xml b/res/menu/conversation_list.xml
deleted file mode 100644
index b6804e62d9..0000000000
--- a/res/menu/conversation_list.xml
+++ /dev/null
@@ -1,11 +0,0 @@
-
-
diff --git a/res/menu/conversation_list_search.xml b/res/menu/conversation_list_search.xml
new file mode 100644
index 0000000000..79f229416c
--- /dev/null
+++ b/res/menu/conversation_list_search.xml
@@ -0,0 +1,11 @@
+
+
\ No newline at end of file
diff --git a/res/values/attrs.xml b/res/values/attrs.xml
index fb7a552396..689384eb8f 100644
--- a/res/values/attrs.xml
+++ b/res/values/attrs.xml
@@ -136,6 +136,8 @@
+
+
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 80a839e077..dfd33f171b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -1494,6 +1494,7 @@
Inbox zeeerrro
Zip. Zilch. Zero. Nada. You\'re all caught up!
No results found for \'%s\'
+ Search
diff --git a/res/values/themes.xml b/res/values/themes.xml
index 1371d9c967..24b1c886ea 100644
--- a/res/values/themes.xml
+++ b/res/values/themes.xml
@@ -224,6 +224,8 @@
- @drawable/ic_group_grey600_24dp
- @style/PreferenceThemeOverlay.Fix
+
+ - @color/white
diff --git a/src/org/thoughtcrime/securesms/ConversationListActivity.java b/src/org/thoughtcrime/securesms/ConversationListActivity.java
index 8f5c97b5fb..b2dff8044e 100644
--- a/src/org/thoughtcrime/securesms/ConversationListActivity.java
+++ b/src/org/thoughtcrime/securesms/ConversationListActivity.java
@@ -26,21 +26,19 @@ import android.os.AsyncTask;
import android.os.Bundle;
import android.provider.ContactsContract;
import android.support.annotation.NonNull;
-import android.support.v4.view.MenuItemCompat;
-import android.support.v7.app.ActionBar;
-import android.support.v7.widget.SearchView;
import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
-import android.widget.LinearLayout;
+import android.view.View;
+import android.widget.ImageView;
import android.widget.Toast;
import org.thoughtcrime.securesms.components.RatingManager;
+import org.thoughtcrime.securesms.components.SearchToolbar;
import org.thoughtcrime.securesms.crypto.MasterSecret;
import org.thoughtcrime.securesms.database.DatabaseFactory;
-import org.thoughtcrime.securesms.database.MessagingDatabase;
import org.thoughtcrime.securesms.database.MessagingDatabase.MarkedMessageInfo;
import org.thoughtcrime.securesms.notifications.MarkReadReceiver;
import org.thoughtcrime.securesms.notifications.MessageNotifier;
@@ -62,8 +60,10 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
private final DynamicLanguage dynamicLanguage = new DynamicLanguage();
private ConversationListFragment fragment;
- private ContentObserver observer;
- private MasterSecret masterSecret;
+ private ContentObserver observer;
+ private MasterSecret masterSecret;
+ private SearchToolbar searchToolbar;
+ private ImageView searchAction;
@Override
protected void onPreCreate() {
@@ -80,9 +80,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
Toolbar toolbar = findViewById(R.id.toolbar);
setSupportActionBar(toolbar);
- fragment = initFragment(R.id.fragment_container, new ConversationListFragment(), masterSecret, dynamicLanguage.getCurrentLocale());
+ searchToolbar = findViewById(R.id.search_toolbar);
+ searchAction = findViewById(R.id.search_action);
+ fragment = initFragment(R.id.fragment_container, new ConversationListFragment(), masterSecret, dynamicLanguage.getCurrentLocale());
initializeContactUpdatesReceiver();
+ initializeSearchListener();
RatingManager.showRatingDialogIfNecessary(this);
}
@@ -109,47 +112,28 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
menu.findItem(R.id.menu_clear_passphrase).setVisible(!TextSecurePreferences.isPasswordDisabled(this));
- inflater.inflate(R.menu.conversation_list, menu);
- MenuItem menuItem = menu.findItem(R.id.menu_search);
- initializeSearch(menuItem);
-
super.onPrepareOptionsMenu(menu);
return true;
}
- private void initializeSearch(MenuItem searchViewItem) {
- SearchView searchView = (SearchView)MenuItemCompat.getActionView(searchViewItem);
- searchView.setQueryHint(getString(R.string.ConversationListActivity_search));
- searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
- @Override
- public boolean onQueryTextSubmit(String query) {
- if (fragment != null) {
- fragment.setQueryFilter(query);
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean onQueryTextChange(String newText) {
- return onQueryTextSubmit(newText);
- }
+ private void initializeSearchListener() {
+ searchAction.setOnClickListener(v -> {
+ searchToolbar.display(searchAction.getX() + (searchAction.getWidth() / 2), searchAction.getY() + (searchAction.getHeight() / 2));
});
- MenuItemCompat.setOnActionExpandListener(searchViewItem, new MenuItemCompat.OnActionExpandListener() {
+ searchToolbar.setListener(new SearchToolbar.SearchListener() {
@Override
- public boolean onMenuItemActionExpand(MenuItem menuItem) {
- return true;
+ public void onSearchTextChange(String text) {
+ if (fragment != null) {
+ fragment.setQueryFilter(text);
+ }
}
@Override
- public boolean onMenuItemActionCollapse(MenuItem menuItem) {
+ public void onSearchReset() {
if (fragment != null) {
fragment.resetQueryFilter();
}
-
- return true;
}
});
}
@@ -190,6 +174,12 @@ public class ConversationListActivity extends PassphraseRequiredActionBarActivit
startActivity(intent);
}
+ @Override
+ public void onBackPressed() {
+ if (searchToolbar.isVisible()) searchToolbar.collapse();
+ else super.onBackPressed();
+ }
+
private void createGroup() {
Intent intent = new Intent(this, GroupCreateActivity.class);
startActivity(intent);
diff --git a/src/org/thoughtcrime/securesms/components/SearchToolbar.java b/src/org/thoughtcrime/securesms/components/SearchToolbar.java
new file mode 100644
index 0000000000..a1ef72dc55
--- /dev/null
+++ b/src/org/thoughtcrime/securesms/components/SearchToolbar.java
@@ -0,0 +1,152 @@
+package org.thoughtcrime.securesms.components;
+
+
+import android.animation.Animator;
+import android.content.Context;
+import android.graphics.PorterDuff;
+import android.os.Build;
+import android.support.annotation.MainThread;
+import android.support.annotation.Nullable;
+import android.support.v7.widget.SearchView;
+import android.support.v7.widget.Toolbar;
+import android.util.AttributeSet;
+import android.view.MenuItem;
+import android.view.View;
+import android.view.ViewAnimationUtils;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+
+import org.thoughtcrime.securesms.R;
+import org.thoughtcrime.securesms.animation.AnimationCompleteListener;
+
+public class SearchToolbar extends LinearLayout {
+
+ private float x, y;
+ private MenuItem searchItem;
+ private SearchListener listener;
+
+ public SearchToolbar(Context context) {
+ super(context);
+ initialize();
+ }
+
+ public SearchToolbar(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ initialize();
+ }
+
+ public SearchToolbar(Context context, @Nullable AttributeSet attrs, int defStyleAttr) {
+ super(context, attrs, defStyleAttr);
+ initialize();
+ }
+
+ @SuppressWarnings("ConstantConditions")
+ private void initialize() {
+ inflate(getContext(), R.layout.search_toolbar, this);
+ setOrientation(VERTICAL);
+
+ Toolbar toolbar = findViewById(R.id.toolbar);
+
+ toolbar.setNavigationIcon(R.drawable.ic_arrow_back_white_24dp);
+ toolbar.getNavigationIcon().setColorFilter(getContext().getResources().getColor(R.color.grey_700), PorterDuff.Mode.SRC_IN);
+ toolbar.inflateMenu(R.menu.conversation_list_search);
+
+ this.searchItem = toolbar.getMenu().findItem(R.id.action_filter_search);
+ SearchView searchView = (SearchView) searchItem.getActionView();
+ EditText searchText = searchView.findViewById(android.support.v7.appcompat.R.id.search_src_text);
+
+ searchView.setSubmitButtonEnabled(false);
+
+ if (searchText != null) searchText.setHint(R.string.SearchToolbar_search);
+ else searchView.setQueryHint(getResources().getString(R.string.SearchToolbar_search));
+
+ searchView.setOnQueryTextListener(new SearchView.OnQueryTextListener() {
+ @Override
+ public boolean onQueryTextSubmit(String query) {
+ if (listener != null) listener.onSearchTextChange(query);
+ return true;
+ }
+
+ @Override
+ public boolean onQueryTextChange(String newText) {
+ return onQueryTextSubmit(newText);
+ }
+ });
+
+ searchItem.setOnActionExpandListener(new MenuItem.OnActionExpandListener() {
+ @Override
+ public boolean onMenuItemActionExpand(MenuItem item) {
+ return true;
+ }
+
+ @Override
+ public boolean onMenuItemActionCollapse(MenuItem item) {
+ hide();
+ return true;
+ }
+ });
+
+ toolbar.setNavigationOnClickListener(v -> hide());
+ }
+
+ @MainThread
+ public void display(float x, float y) {
+ if (getVisibility() != View.VISIBLE) {
+ this.x = x;
+ this.y = y;
+
+ searchItem.expandActionView();
+
+ if (Build.VERSION.SDK_INT >= 21) {
+ Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, 0, getWidth());
+ animator.setDuration(400);
+
+ setVisibility(View.VISIBLE);
+ animator.start();
+ } else {
+ setVisibility(View.VISIBLE);
+ }
+ }
+ }
+
+ public void collapse() {
+ searchItem.collapseActionView();
+ }
+
+ @MainThread
+ private void hide() {
+ if (getVisibility() == View.VISIBLE) {
+
+ if (listener != null) listener.onSearchReset();
+
+ if (Build.VERSION.SDK_INT >= 21) {
+ Animator animator = ViewAnimationUtils.createCircularReveal(this, (int)x, (int)y, getWidth(), 0);
+ animator.setDuration(400);
+ animator.addListener(new AnimationCompleteListener() {
+ @Override
+ public void onAnimationEnd(Animator animation) {
+ setVisibility(View.INVISIBLE);
+ }
+ });
+ animator.start();
+ } else {
+ setVisibility(View.INVISIBLE);
+ }
+ }
+ }
+
+ public boolean isVisible() {
+ return getVisibility() == View.VISIBLE;
+ }
+
+ @MainThread
+ public void setListener(SearchListener listener) {
+ this.listener = listener;
+ }
+
+ public interface SearchListener {
+ void onSearchTextChange(String text);
+ void onSearchReset();
+ }
+
+}