// Copyright 2016 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. package org.chromium.chrome.browser.widget.selection; import android.app.Activity; import android.content.Context; 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.util.AttributeSet; import android.view.View; import android.view.View.OnClickListener; import org.chromium.base.ApiCompatibilityUtils; 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.selection.SelectionDelegate.SelectionObserver; 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. * * @param <E> The type of the selectable items this toolbar interacts with. */ public class SelectionToolbar<E> extends Toolbar implements SelectionObserver<E>, OnClickListener { /** No navigation button is displayed. **/ protected static final int NAVIGATION_BUTTON_NONE = 0; /** Button to open the DrawerLayout. Only valid if mDrawerLayout is set. **/ protected static final int NAVIGATION_BUTTON_MENU = 1; /** Button to navigate back. This calls {@link #onNavigationBack()}. **/ protected static final int NAVIGATION_BUTTON_BACK = 2; /** Button to clear the selection. **/ protected static final int NAVIGATION_BUTTON_SELECTION_BACK = 3; protected boolean mIsSelectionEnabled; protected SelectionDelegate<E> mSelectionDelegate; protected NumberRollView mNumberRollView; private DrawerLayout mDrawerLayout; private ActionBarDrawerToggle mActionBarDrawerToggle; private int mNavigationButton; private int mTitleResId; private int mNormalGroupResId; private int mSelectedGroupResId; /** * Constructor for inflating from XML. */ public SelectionToolbar(Context context, AttributeSet attrs) { super(context, attrs); } /** * Destroys and cleans up itself. */ public void destroy() { if (mSelectionDelegate != null) { mSelectionDelegate.removeObserver(this); } } /** * Initializes the SelectionToolbar. * * @param delegate The SelectionDelegate that will inform the toolbar of selection changes. * @param titleResId The resource id of the title string. May be 0 if this class shouldn't set * set a title when the selection is cleared. * @param drawerLayout The DrawerLayout whose navigation icon is displayed in this toolbar. * @param normalGroupResId The resource id of the menu group to show when a selection isn't * established. * @param selectedGroupResId The resource id of the menu item to show when a selection is * established. */ public void initialize(SelectionDelegate<E> delegate, int titleResId, @Nullable DrawerLayout drawerLayout, int normalGroupResId, int selectedGroupResId) { mTitleResId = titleResId; mDrawerLayout = drawerLayout; mNormalGroupResId = normalGroupResId; mSelectedGroupResId = selectedGroupResId; mSelectionDelegate = delegate; mSelectionDelegate.addObserver(this); if (mDrawerLayout != null) initActionBarDrawerToggle(); } @Override protected void onFinishInflate() { super.onFinishInflate(); mNumberRollView = (NumberRollView) findViewById(R.id.selection_mode_number); } @Override @CallSuper public void onSelectionStateChange(List<E> selectedItems) { boolean wasSelectionEnabled = mIsSelectionEnabled; mIsSelectionEnabled = mSelectionDelegate.isSelectionEnabled(); // If onSelectionStateChange() gets called before onFinishInflate(), mNumberRollView // will be uninitialized. See crbug.com/637948. if (mNumberRollView == null) { mNumberRollView = (NumberRollView) findViewById(R.id.selection_mode_number); } 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); } else { setOverflowIcon(TintedDrawable.constructTintedDrawable(getResources(), R.drawable.btn_menu)); getMenu().setGroupVisible(mNormalGroupResId, true); getMenu().setGroupVisible(mSelectedGroupResId, false); setBackgroundColor(ApiCompatibilityUtils.getColor(getResources(), R.color.appbar_background)); if (mTitleResId != 0) setTitle(mTitleResId); setNavigationButton(NAVIGATION_BUTTON_MENU); mNumberRollView.setVisibility(View.GONE); mNumberRollView.setNumber(0, false); } if (mIsSelectionEnabled && !wasSelectionEnabled) { announceForAccessibility( getResources().getString(R.string.accessibility_toolbar_screen_position)); } } @Override public void onClick(View view) { switch (mNavigationButton) { case NAVIGATION_BUTTON_NONE: break; case NAVIGATION_BUTTON_MENU: // ActionBarDrawerToggle handles this. break; case NAVIGATION_BUTTON_BACK: onNavigationBack(); break; case NAVIGATION_BUTTON_SELECTION_BACK: mSelectionDelegate.clearSelection(); break; default: assert false : "Incorrect navigation button state"; } } /** * Handle a click on the navigation back button. Subclasses should override this method if * navigation back is a valid toolbar action. */ protected void onNavigationBack() {} /** * Update the current navigation button (the top-left icon on LTR) * @param navigationButton one of NAVIGATION_BUTTON_* constants. */ protected void setNavigationButton(int navigationButton) { int iconResId = 0; int contentDescriptionId = 0; if (navigationButton == NAVIGATION_BUTTON_MENU && mDrawerLayout == null) { mNavigationButton = NAVIGATION_BUTTON_NONE; } else { mNavigationButton = navigationButton; } if (mNavigationButton == NAVIGATION_BUTTON_MENU) { initActionBarDrawerToggle(); // ActionBarDrawerToggle will take care of icon and content description, so just return. return; } if (mActionBarDrawerToggle != null) { mActionBarDrawerToggle.setDrawerIndicatorEnabled(false); mDrawerLayout.addDrawerListener(null); } setNavigationOnClickListener(this); switch (mNavigationButton) { case NAVIGATION_BUTTON_NONE: break; case NAVIGATION_BUTTON_BACK: // TODO(twellington): use ic_arrow_back_white_24dp and tint it. iconResId = R.drawable.back_normal; contentDescriptionId = R.string.accessibility_toolbar_btn_back; break; case NAVIGATION_BUTTON_SELECTION_BACK: // TODO(twellington): use btn_close and tint it. iconResId = R.drawable.btn_close_white; contentDescriptionId = R.string.accessibility_cancel_selection; break; default: assert false : "Incorrect navigationButton argument"; } if (iconResId == 0) { setNavigationIcon(null); } else { setNavigationIcon(iconResId); } setNavigationContentDescription(contentDescriptionId); } /** * Set up ActionBarDrawerToggle, a.k.a. hamburger button. */ private void initActionBarDrawerToggle() { // Sadly, the only way to set correct toolbar button listener for ActionBarDrawerToggle // is constructing, so we will need to construct every time we re-show this button. mActionBarDrawerToggle = new ActionBarDrawerToggle((Activity) getContext(), mDrawerLayout, this, R.string.accessibility_drawer_toggle_btn_open, R.string.accessibility_drawer_toggle_btn_close); mDrawerLayout.addDrawerListener(mActionBarDrawerToggle); mActionBarDrawerToggle.syncState(); } }