Improve search bar visibility

This commit is contained in:
Moxie Marlinspike 2017-11-13 12:44:51 -08:00
parent 9a75f49aa1
commit 405ee3b741
10 changed files with 251 additions and 60 deletions

View File

@ -0,0 +1,7 @@
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<gradient
android:startColor="@android:color/transparent"
android:endColor="#40000000"
android:angle="90" />
</shape>

View File

@ -1,13 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:id="@id/container"
android:orientation="vertical"
android:layout_width="match_parent"
android:layout_height="match_parent">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_alignParentTop="true"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:minHeight="?attr/actionBarSize"
@ -18,10 +19,10 @@
android:layout_width="match_parent"
android:layout_height="match_parent">
<ImageView android:layout_width="36dp"
android:layout_height="36dp"
android:layout_marginLeft="8dp"
android:layout_marginStart="8dp"
android:src="@drawable/icon_transparent"
android:layout_alignParentLeft="true"
android:layout_alignParentStart="true"
@ -35,20 +36,36 @@
android:layout_alignParentStart="true"
android:layout_alignParentLeft="true"
android:layout_centerVertical="true"
android:layout_toLeftOf="@+id/search_action"
android:layout_toStartOf="@+id/search_action"
android:layout_marginStart="66dp"
android:layout_marginLeft="66dp"/>
<ImageView android:id="@+id/search_action"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:src="@drawable/ic_search_white_24dp"
android:paddingRight="16dp"
android:paddingEnd="16dp"
android:layout_alignParentRight="true"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"/>
</RelativeLayout>
</android.support.v7.widget.Toolbar>
<FrameLayout android:id="@+id/fragment_container"
android:layout_below="@id/toolbar"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
<!--<fragment android:id="@+id/fragment"-->
<!--android:name="org.thoughtcrime.securesms.ConversationListFragment"-->
<!--android:layout_width="match_parent"-->
<!--android:layout_height="match_parent"-->
<!--tools:layout="@layout/conversation_list_fragment"/>-->
<org.thoughtcrime.securesms.components.SearchToolbar
android:id="@+id/search_toolbar"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_alignParentTop="true"
android:visibility="invisible"
tools:visibility="visible"/>
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<merge xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_height="wrap_content"
android:layout_width="match_parent"
tools:parentTag="android.widget.LinearLayout"
tools:orientation="vertical">
<android.support.v7.widget.Toolbar
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:background="?attr/search_toolbar_background"/>
<View android:layout_width="match_parent"
android:layout_height="7dp"
android:background="@drawable/search_toolbar_shadow"/>
</merge>

View File

@ -1,11 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item android:title="@string/conversation_list__menu_search"
android:id="@+id/menu_search"
android:icon="?attr/menu_search_icon"
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="ifRoom|collapseActionView" />
</menu>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<menu xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto">
<item
android:id="@+id/action_filter_search"
android:title=" "
app:actionViewClass="android.support.v7.widget.SearchView"
app:showAsAction="collapseActionView|always" />
</menu>

View File

@ -136,6 +136,8 @@
<attr name="media_overview_document_background" format="color"/>
<attr name="media_overview_document_foreground" format="color"/>
<attr name="search_toolbar_background" format="color"/>
<declare-styleable name="ColorPreference">
<attr name="itemLayout" format="reference" />
<attr name="choices" format="reference" />

View File

@ -1494,6 +1494,7 @@
<string name="conversation_list_item_inbox_zero__inbox_zeeerrro">Inbox zeeerrro</string>
<string name="conversation_list_item_inbox_zero__zip_zilch_zero_nada_nyou_re_all_caught_up">Zip. Zilch. Zero. Nada. You\'re all caught up!</string>
<string name="ConversationListFragment_no_results_found_for_s_">No results found for \'%s\'</string>
<string name="SearchToolbar_search">Search</string>
<!-- EOF -->

View File

@ -224,6 +224,8 @@
<item name="group_members_dialog_icon">@drawable/ic_group_grey600_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item>
<item name="search_toolbar_background">@color/white</item>
</style>
<style name="TextSecure.DarkTheme" parent="@style/Theme.AppCompat">
@ -348,5 +350,6 @@
<item name="group_members_dialog_icon">@drawable/ic_group_white_24dp</item>
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Fix</item>
<item name="search_toolbar_background">@color/black</item>
</style>
</resources>

View File

@ -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);

View File

@ -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();
}
}