Chromium Code Reviews| Index: chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java |
| diff --git a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionToolbar.java b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java |
| similarity index 51% |
| rename from chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionToolbar.java |
| rename to chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java |
| index ae56de8498abd5007a9d1287e2c778c6c4de9bc4..ae1defbb87bbad21a5810e0c764c3379792707cc 100644 |
| --- a/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectionToolbar.java |
| +++ b/chrome/android/java/src/org/chromium/chrome/browser/widget/selection/SelectableListToolbar.java |
| @@ -1,4 +1,4 @@ |
| -// Copyright 2016 The Chromium Authors. All rights reserved. |
| +// Copyright 2017 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| @@ -6,31 +6,64 @@ package org.chromium.chrome.browser.widget.selection; |
| import android.app.Activity; |
| import android.content.Context; |
| +import android.graphics.Color; |
| import android.support.annotation.CallSuper; |
| import android.support.v4.widget.DrawerLayout; |
| import android.support.v7.app.ActionBarDrawerToggle; |
| import android.support.v7.widget.Toolbar; |
| +import android.text.Editable; |
| +import android.text.TextUtils; |
| +import android.text.TextWatcher; |
| import android.util.AttributeSet; |
| +import android.view.KeyEvent; |
| +import android.view.LayoutInflater; |
| import android.view.View; |
| import android.view.View.OnClickListener; |
| +import android.view.inputmethod.EditorInfo; |
| +import android.widget.EditText; |
| +import android.widget.LinearLayout; |
| +import android.widget.TextView; |
| +import android.widget.TextView.OnEditorActionListener; |
| import org.chromium.base.ApiCompatibilityUtils; |
| +import org.chromium.base.VisibleForTesting; |
| import org.chromium.chrome.R; |
| import org.chromium.chrome.browser.widget.NumberRollView; |
| import org.chromium.chrome.browser.widget.TintedDrawable; |
| +import org.chromium.chrome.browser.widget.TintedImageButton; |
| import org.chromium.chrome.browser.widget.selection.SelectionDelegate.SelectionObserver; |
| +import org.chromium.ui.UiUtils; |
| import java.util.List; |
| import javax.annotation.Nullable; |
| /** |
| - * A toolbar that changes its view depending on whether a selection is established. The XML inflated |
| - * for this class must include number_roll_view.xml. |
| + * A toolbar that changes its view depending on whether a selection is established. The toolbar |
| + * also optionally shows a search view depending on whether {@link #initializeSearchView()} has |
| + * been called. |
| * |
| * @param <E> The type of the selectable items this toolbar interacts with. |
| */ |
| -public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E>, OnClickListener { |
| +public class SelectableListToolbar<E> extends Toolbar implements SelectionObserver<E>, |
| + OnClickListener, OnEditorActionListener { |
| + |
| + /** |
| + * A delegate that handles searching the list of selectable items associated with this toolbar. |
| + */ |
| + public interface SearchDelegate { |
| + /** |
| + * Called when the text in the search EditText box has changed. |
| + * @param query The text in the search EditText box. |
| + */ |
| + void onSearchTextChanged(String query); |
| + |
| + /** |
| + * Called when a search is ended. |
| + */ |
| + void onEndSearch(); |
| + } |
| + |
| /** No navigation button is displayed. **/ |
| protected static final int NAVIGATION_BUTTON_NONE = 0; |
| /** Button to open the DrawerLayout. Only valid if mDrawerLayout is set. **/ |
| @@ -43,19 +76,33 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| protected boolean mIsSelectionEnabled; |
| protected SelectionDelegate<E> mSelectionDelegate; |
| + private boolean mHasSearchView; |
| + private boolean mIsSearching; |
| + private LinearLayout mSearchView; |
| + private EditText mSearchEditText; |
| + private TintedImageButton mClearTextButton; |
| + private SearchDelegate mSearchDelegate; |
| + |
| protected NumberRollView mNumberRollView; |
| private DrawerLayout mDrawerLayout; |
| private ActionBarDrawerToggle mActionBarDrawerToggle; |
| + private TintedDrawable mNormalMenuButton; |
| + private TintedDrawable mSelectionMenuButton; |
| + |
| private int mNavigationButton; |
| private int mTitleResId; |
| + private int mSearchMenuItemId; |
| + private int mNormalGroupResId; |
| + private int mSelectedGroupResId; |
| + |
| private int mNormalBackgroundColor; |
| - protected int mNormalGroupResId; |
| - protected int mSelectedGroupResId; |
| + private int mSelectionBackgroundColor; |
| + private int mSearchBackgroundColor; |
| /** |
| * Constructor for inflating from XML. |
| */ |
| - public SelectionToolbar(Context context, AttributeSet attrs) { |
| + public SelectableListToolbar(Context context, AttributeSet attrs) { |
| super(context, attrs); |
| } |
| @@ -101,12 +148,71 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| mNormalBackgroundColor = |
| ApiCompatibilityUtils.getColor(getResources(), normalBackgroundColorResId); |
| setBackgroundColor(mNormalBackgroundColor); |
| + |
| + mSelectionBackgroundColor = ApiCompatibilityUtils.getColor( |
| + getResources(), R.color.light_active_color); |
| + |
| if (mTitleResId != 0) setTitle(mTitleResId); |
| + |
| + // TODO(twellington): add the concept of normal & selected tint to apply to all toolbar |
| + // buttons. |
| + mNormalMenuButton = TintedDrawable.constructTintedDrawable(getResources(), |
| + R.drawable.btn_menu); |
| + mSelectionMenuButton = TintedDrawable.constructTintedDrawable(getResources(), |
| + R.drawable.btn_menu, android.R.color.white); |
| + } |
| + |
| + /** |
| + * Inflates and initializes the search view. |
| + * @param searchDelegate The delegate that will handle performing searches. |
| + * @param hintStringResId The hint text to show in the search view's EditText box. |
| + * @param searchMenuItemId The menu item used to activate the search view. This item will be |
| + * hidden when selection is enabled or if the list of selectable items |
| + * associated with this toolbar is empty. |
| + */ |
| + public void initializeSearchView(SearchDelegate searchDelegate, int hintStringResId, |
| + int searchMenuItemId) { |
| + mHasSearchView = true; |
| + mSearchDelegate = searchDelegate; |
| + mSearchMenuItemId = searchMenuItemId; |
| + mSearchBackgroundColor = Color.WHITE; |
| + |
| + LayoutInflater.from(getContext()).inflate(R.layout.search_toolbar, this); |
| + |
| + mSearchView = (LinearLayout) findViewById(R.id.search_view); |
| + |
| + mSearchEditText = (EditText) findViewById(R.id.search_text); |
| + mSearchEditText.setHint(hintStringResId); |
| + mSearchEditText.setOnEditorActionListener(this); |
| + mSearchEditText.addTextChangedListener(new TextWatcher() { |
| + @Override |
| + public void onTextChanged(CharSequence s, int start, int before, int count) { |
| + mClearTextButton.setVisibility( |
| + TextUtils.isEmpty(s) ? View.INVISIBLE : View.VISIBLE); |
| + if (mIsSearching) mSearchDelegate.onSearchTextChanged(s.toString()); |
| + } |
| + |
| + @Override |
| + public void beforeTextChanged(CharSequence s, int start, int count, int after) {} |
| + |
| + @Override |
| + public void afterTextChanged(Editable s) {} |
| + }); |
| + |
| + mClearTextButton = (TintedImageButton) findViewById(R.id.clear_text_button); |
| + mClearTextButton.setOnClickListener(new OnClickListener() { |
| + @Override |
| + public void onClick(View v) { |
| + mSearchEditText.setText(""); |
| + } |
| + }); |
| } |
| @Override |
| protected void onFinishInflate() { |
| super.onFinishInflate(); |
| + |
| + LayoutInflater.from(getContext()).inflate(R.layout.number_roll_view, this); |
| mNumberRollView = (NumberRollView) findViewById(R.id.selection_mode_number); |
| mNumberRollView.setContentDescriptionString(R.plurals.accessibility_selected_items); |
| } |
| @@ -124,39 +230,18 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| } |
| if (mIsSelectionEnabled) { |
| - // TODO(twellington): add the concept of normal & selected tint to apply to all |
| - // toolbar buttons. |
| - setOverflowIcon(TintedDrawable.constructTintedDrawable(getResources(), |
| - R.drawable.btn_menu, android.R.color.white)); |
| - setNavigationButton(NAVIGATION_BUTTON_SELECTION_BACK); |
| - setTitle(null); |
| - |
| - getMenu().setGroupVisible(mNormalGroupResId, false); |
| - getMenu().setGroupVisible(mSelectedGroupResId, true); |
| - |
| - setBackgroundColor( |
| - ApiCompatibilityUtils.getColor(getResources(), R.color.light_active_color)); |
| - |
| - mNumberRollView.setVisibility(View.VISIBLE); |
| - if (!wasSelectionEnabled) mNumberRollView.setNumber(0, false); |
| - mNumberRollView.setNumber(selectedItems.size(), true); |
| + showSelectionView(selectedItems, wasSelectionEnabled); |
| + } else if (mIsSearching) { |
| + showSearchViewInternal(); |
| } else { |
| - setOverflowIcon(TintedDrawable.constructTintedDrawable(getResources(), |
| - R.drawable.btn_menu)); |
| - getMenu().setGroupVisible(mNormalGroupResId, true); |
| - getMenu().setGroupVisible(mSelectedGroupResId, false); |
| - setBackgroundColor(mNormalBackgroundColor); |
| - |
| - if (mTitleResId != 0) setTitle(mTitleResId); |
| - setNavigationButton(NAVIGATION_BUTTON_MENU); |
| - |
| - mNumberRollView.setVisibility(View.GONE); |
| - mNumberRollView.setNumber(0, false); |
| + showNormalView(); |
| } |
| if (mIsSelectionEnabled && !wasSelectionEnabled) { |
| announceForAccessibility( |
| getResources().getString(R.string.accessibility_toolbar_screen_position)); |
| + |
| + if (mIsSearching) UiUtils.hideKeyboard(mSearchEditText); |
|
gone
2017/01/20 20:13:00
Hiding the keyboard seems like something that shou
Theresa
2017/01/20 21:16:18
I was trying to avoid hiding it multiple times unn
|
| } |
| } |
| @@ -180,10 +265,15 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| } |
| /** |
| - * Handle a click on the navigation back button. Subclasses should override this method if |
| - * navigation back is a valid toolbar action. |
| + * Handle a click on the navigation back button. If this toolbar has a search view, the search |
| + * view will be hidden. Subclasses should override this method if navigation back is also a |
| + * valid toolbar action when not searching. |
| */ |
| - protected void onNavigationBack() {} |
| + public void onNavigationBack() { |
| + if (!mHasSearchView || !mIsSearching) return; |
| + |
| + hideSearchView(); |
| + } |
| /** |
| * Update the current navigation button (the top-left icon on LTR) |
| @@ -238,10 +328,54 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| } |
| /** |
| + * Shows the search edit text box and related views. |
| + */ |
| + public void showSearchView() { |
| + assert mHasSearchView; |
| + |
| + mIsSearching = true; |
| + mSelectionDelegate.clearSelection(); |
| + |
| + showSearchViewInternal(); |
| + |
| + mSearchEditText.requestFocus(); |
| + UiUtils.showKeyboard(mSearchEditText); |
| + setTitle(null); |
| + } |
| + |
| + /** |
| + * Hides the search edit text box and related views. |
| + */ |
| + public void hideSearchView() { |
| + assert mHasSearchView; |
| + |
| + mIsSearching = false; |
| + |
| + mSearchEditText.setText(""); |
| + UiUtils.hideKeyboard(mSearchEditText); |
| + showNormalView(); |
| + |
| + mSearchDelegate.onEndSearch(); |
| + } |
| + |
| + /** |
| * Called when the data in the selectable list this toolbar is associated with changes. |
| * @param numItems The number of items in the selectable list. |
| */ |
| - protected void onDataChanged(int numItems) {} |
| + protected void onDataChanged(int numItems) { |
| + if (mHasSearchView) { |
| + getMenu().findItem(mSearchMenuItemId).setVisible( |
| + !mIsSelectionEnabled && !mIsSearching && numItems != 0); |
| + } |
| + } |
| + |
| + @Override |
| + public boolean onEditorAction(TextView v, int actionId, KeyEvent event) { |
| + if (actionId == EditorInfo.IME_ACTION_SEARCH) { |
| + UiUtils.hideKeyboard(v); |
| + } |
| + return false; |
| + } |
| /** |
| * Set up ActionBarDrawerToggle, a.k.a. hamburger button. |
| @@ -256,4 +390,47 @@ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E> |
| mDrawerLayout.addDrawerListener(mActionBarDrawerToggle); |
| mActionBarDrawerToggle.syncState(); |
| } |
| + |
| + private void showNormalView() { |
| + getMenu().setGroupVisible(mNormalGroupResId, true); |
| + getMenu().setGroupVisible(mSelectedGroupResId, false); |
| + if (mHasSearchView) mSearchView.setVisibility(View.GONE); |
| + |
| + setNavigationButton(NAVIGATION_BUTTON_MENU); |
| + setBackgroundColor(mNormalBackgroundColor); |
| + setOverflowIcon(mNormalMenuButton); |
| + if (mTitleResId != 0) setTitle(mTitleResId); |
| + |
| + mNumberRollView.setVisibility(View.GONE); |
| + mNumberRollView.setNumber(0, false); |
| + } |
| + |
| + private void showSelectionView(List<E> selectedItems, boolean wasSelectionEnabled) { |
| + getMenu().setGroupVisible(mNormalGroupResId, false); |
| + getMenu().setGroupVisible(mSelectedGroupResId, true); |
| + if (mHasSearchView) mSearchView.setVisibility(View.GONE); |
| + |
| + setNavigationButton(NAVIGATION_BUTTON_SELECTION_BACK); |
| + setBackgroundColor(mSelectionBackgroundColor); |
| + setOverflowIcon(mSelectionMenuButton); |
| + setTitle(null); |
| + |
| + mNumberRollView.setVisibility(View.VISIBLE); |
| + if (!wasSelectionEnabled) mNumberRollView.setNumber(0, false); |
| + mNumberRollView.setNumber(selectedItems.size(), true); |
| + } |
| + |
| + private void showSearchViewInternal() { |
| + getMenu().setGroupVisible(mNormalGroupResId, false); |
| + getMenu().setGroupVisible(mSelectedGroupResId, false); |
| + mSearchView.setVisibility(View.VISIBLE); |
| + |
| + setNavigationButton(NAVIGATION_BUTTON_BACK); |
| + setBackgroundColor(mSearchBackgroundColor); |
| + } |
| + |
| + @VisibleForTesting |
| + public View getSearchViewForTests() { |
| + return mSearchView; |
| + } |
| } |