/*
 * Copyright (C) 2014 The Android Open Source Project
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */


package com.android.internal.widget;

import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.ObjectAnimator;
import android.app.ActionBar;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.os.Parcelable;
import android.text.TextUtils;
import android.util.Log;
import android.util.SparseArray;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.ActionMenuPresenter;
import android.widget.AdapterView;
import android.widget.Spinner;
import android.widget.SpinnerAdapter;
import android.widget.Toolbar;
import com.android.internal.R;
import com.android.internal.view.menu.ActionMenuItem;
import com.android.internal.view.menu.MenuBuilder;
import com.android.internal.view.menu.MenuPresenter;

Internal class used to interact with the Toolbar widget without exposing interface methods to the public API.

ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView so that either variant acting as a WindowDecorActionBar can behave in the same way.

@hide
/** * Internal class used to interact with the Toolbar widget without * exposing interface methods to the public API. * * <p>ToolbarWidgetWrapper manages the differences between Toolbar and ActionBarView * so that either variant acting as a * {@link com.android.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave * in the same way.</p> * * @hide */
public class ToolbarWidgetWrapper implements DecorToolbar { private static final String TAG = "ToolbarWidgetWrapper"; private static final int AFFECTS_LOGO_MASK = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO; // Default fade duration for fading in/out tool bar. private static final long DEFAULT_FADE_DURATION_MS = 200; private Toolbar mToolbar; private int mDisplayOpts; private View mTabView; private Spinner mSpinner; private View mCustomView; private Drawable mIcon; private Drawable ; private Drawable mNavIcon; private boolean mTitleSet; private CharSequence mTitle; private CharSequence mSubtitle; private CharSequence mHomeDescription; private Window.Callback mWindowCallback; private boolean mMenuPrepared; private ActionMenuPresenter mActionMenuPresenter; private int mNavigationMode = ActionBar.NAVIGATION_MODE_STANDARD; private int mDefaultNavigationContentDescription = 0; private Drawable mDefaultNavigationIcon; public ToolbarWidgetWrapper(Toolbar toolbar, boolean style) { this(toolbar, style, R.string.action_bar_up_description); } public ToolbarWidgetWrapper(Toolbar toolbar, boolean style, int defaultNavigationContentDescription) { mToolbar = toolbar; mTitle = toolbar.getTitle(); mSubtitle = toolbar.getSubtitle(); mTitleSet = mTitle != null; mNavIcon = mToolbar.getNavigationIcon(); final TypedArray a = toolbar.getContext().obtainStyledAttributes(null, R.styleable.ActionBar, R.attr.actionBarStyle, 0); mDefaultNavigationIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); if (style) { final CharSequence title = a.getText(R.styleable.ActionBar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); } final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); if (!TextUtils.isEmpty(subtitle)) { setSubtitle(subtitle); } final Drawable logo = a.getDrawable(R.styleable.ActionBar_logo); if (logo != null) { setLogo(logo); } final Drawable icon = a.getDrawable(R.styleable.ActionBar_icon); if (icon != null) { setIcon(icon); } if (mNavIcon == null && mDefaultNavigationIcon != null) { setNavigationIcon(mDefaultNavigationIcon); } setDisplayOptions(a.getInt(R.styleable.ActionBar_displayOptions, 0)); final int customNavId = a.getResourceId( R.styleable.ActionBar_customNavigationLayout, 0); if (customNavId != 0) { setCustomView(LayoutInflater.from(mToolbar.getContext()).inflate(customNavId, mToolbar, false)); setDisplayOptions(mDisplayOpts | ActionBar.DISPLAY_SHOW_CUSTOM); } final int height = a.getLayoutDimension(R.styleable.ActionBar_height, 0); if (height > 0) { final ViewGroup.LayoutParams lp = mToolbar.getLayoutParams(); lp.height = height; mToolbar.setLayoutParams(lp); } final int contentInsetStart = a.getDimensionPixelOffset( R.styleable.ActionBar_contentInsetStart, -1); final int contentInsetEnd = a.getDimensionPixelOffset( R.styleable.ActionBar_contentInsetEnd, -1); if (contentInsetStart >= 0 || contentInsetEnd >= 0) { mToolbar.setContentInsetsRelative(Math.max(contentInsetStart, 0), Math.max(contentInsetEnd, 0)); } final int titleTextStyle = a.getResourceId(R.styleable.ActionBar_titleTextStyle, 0); if (titleTextStyle != 0) { mToolbar.setTitleTextAppearance(mToolbar.getContext(), titleTextStyle); } final int subtitleTextStyle = a.getResourceId( R.styleable.ActionBar_subtitleTextStyle, 0); if (subtitleTextStyle != 0) { mToolbar.setSubtitleTextAppearance(mToolbar.getContext(), subtitleTextStyle); } final int popupTheme = a.getResourceId(R.styleable.ActionBar_popupTheme, 0); if (popupTheme != 0) { mToolbar.setPopupTheme(popupTheme); } } else { mDisplayOpts = detectDisplayOptions(); } a.recycle(); setDefaultNavigationContentDescription(defaultNavigationContentDescription); mHomeDescription = mToolbar.getNavigationContentDescription(); mToolbar.setNavigationOnClickListener(new View.OnClickListener() { final ActionMenuItem mNavItem = new ActionMenuItem(mToolbar.getContext(), 0, android.R.id.home, 0, 0, mTitle); @Override public void onClick(View v) { if (mWindowCallback != null && mMenuPrepared) { mWindowCallback.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, mNavItem); } } }); } @Override public void setDefaultNavigationContentDescription(int defaultNavigationContentDescription) { if (defaultNavigationContentDescription == mDefaultNavigationContentDescription) { return; } mDefaultNavigationContentDescription = defaultNavigationContentDescription; if (TextUtils.isEmpty(mToolbar.getNavigationContentDescription())) { setNavigationContentDescription(mDefaultNavigationContentDescription); } } private int detectDisplayOptions() { int opts = ActionBar.DISPLAY_SHOW_TITLE | ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO; if (mToolbar.getNavigationIcon() != null) { opts |= ActionBar.DISPLAY_HOME_AS_UP; mDefaultNavigationIcon = mToolbar.getNavigationIcon(); } return opts; } @Override public ViewGroup getViewGroup() { return mToolbar; } @Override public Context getContext() { return mToolbar.getContext(); } @Override public boolean isSplit() { return false; } @Override public boolean hasExpandedActionView() { return mToolbar.hasExpandedActionView(); } @Override public void collapseActionView() { mToolbar.collapseActionView(); } @Override public void setWindowCallback(Window.Callback cb) { mWindowCallback = cb; } @Override public void setWindowTitle(CharSequence title) { // "Real" title always trumps window title. if (!mTitleSet) { setTitleInt(title); } } @Override public CharSequence getTitle() { return mToolbar.getTitle(); } @Override public void setTitle(CharSequence title) { mTitleSet = true; setTitleInt(title); } private void setTitleInt(CharSequence title) { mTitle = title; if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { mToolbar.setTitle(title); } } @Override public CharSequence getSubtitle() { return mToolbar.getSubtitle(); } @Override public void setSubtitle(CharSequence subtitle) { mSubtitle = subtitle; if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { mToolbar.setSubtitle(subtitle); } } @Override public void initProgress() { Log.i(TAG, "Progress display unsupported"); } @Override public void initIndeterminateProgress() { Log.i(TAG, "Progress display unsupported"); } @Override public boolean canSplit() { return false; } @Override public void setSplitView(ViewGroup splitView) { } @Override public void setSplitToolbar(boolean split) { if (split) { throw new UnsupportedOperationException("Cannot split an android.widget.Toolbar"); } } @Override public void setSplitWhenNarrow(boolean splitWhenNarrow) { // Ignore. } @Override public boolean hasIcon() { return mIcon != null; } @Override public boolean hasLogo() { return mLogo != null; } @Override public void setIcon(int resId) { setIcon(resId != 0 ? getContext().getDrawable(resId) : null); } @Override public void setIcon(Drawable d) { mIcon = d; updateToolbarLogo(); } @Override public void setLogo(int resId) { setLogo(resId != 0 ? getContext().getDrawable(resId) : null); } @Override public void setLogo(Drawable d) { mLogo = d; updateToolbarLogo(); } private void updateToolbarLogo() { Drawable logo = null; if ((mDisplayOpts & ActionBar.DISPLAY_SHOW_HOME) != 0) { if ((mDisplayOpts & ActionBar.DISPLAY_USE_LOGO) != 0) { logo = mLogo != null ? mLogo : mIcon; } else { logo = mIcon; } } mToolbar.setLogo(logo); } @Override public boolean canShowOverflowMenu() { return mToolbar.canShowOverflowMenu(); } @Override public boolean isOverflowMenuShowing() { return mToolbar.isOverflowMenuShowing(); } @Override public boolean isOverflowMenuShowPending() { return mToolbar.isOverflowMenuShowPending(); } @Override public boolean showOverflowMenu() { return mToolbar.showOverflowMenu(); } @Override public boolean hideOverflowMenu() { return mToolbar.hideOverflowMenu(); } @Override public void setMenuPrepared() { mMenuPrepared = true; } @Override public void setMenu(Menu menu, MenuPresenter.Callback cb) { if (mActionMenuPresenter == null) { mActionMenuPresenter = new ActionMenuPresenter(mToolbar.getContext()); mActionMenuPresenter.setId(com.android.internal.R.id.action_menu_presenter); } mActionMenuPresenter.setCallback(cb); mToolbar.setMenu((MenuBuilder) menu, mActionMenuPresenter); } @Override public void dismissPopupMenus() { mToolbar.dismissPopupMenus(); } @Override public int getDisplayOptions() { return mDisplayOpts; } @Override public void setDisplayOptions(int newOpts) { final int oldOpts = mDisplayOpts; final int changed = oldOpts ^ newOpts; mDisplayOpts = newOpts; if (changed != 0) { if ((changed & ActionBar.DISPLAY_HOME_AS_UP) != 0) { if ((newOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { updateHomeAccessibility(); } updateNavigationIcon(); } if ((changed & AFFECTS_LOGO_MASK) != 0) { updateToolbarLogo(); } if ((changed & ActionBar.DISPLAY_SHOW_TITLE) != 0) { if ((newOpts & ActionBar.DISPLAY_SHOW_TITLE) != 0) { mToolbar.setTitle(mTitle); mToolbar.setSubtitle(mSubtitle); } else { mToolbar.setTitle(null); mToolbar.setSubtitle(null); } } if ((changed & ActionBar.DISPLAY_SHOW_CUSTOM) != 0 && mCustomView != null) { if ((newOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { mToolbar.addView(mCustomView); } else { mToolbar.removeView(mCustomView); } } } } @Override public void setEmbeddedTabView(ScrollingTabContainerView tabView) { if (mTabView != null && mTabView.getParent() == mToolbar) { mToolbar.removeView(mTabView); } mTabView = tabView; if (tabView != null && mNavigationMode == ActionBar.NAVIGATION_MODE_TABS) { mToolbar.addView(mTabView, 0); Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; lp.gravity = Gravity.START | Gravity.BOTTOM; tabView.setAllowCollapse(true); } } @Override public boolean hasEmbeddedTabs() { return mTabView != null; } @Override public boolean isTitleTruncated() { return mToolbar.isTitleTruncated(); } @Override public void setCollapsible(boolean collapsible) { mToolbar.setCollapsible(collapsible); } @Override public void setHomeButtonEnabled(boolean enable) { // Ignore } @Override public int getNavigationMode() { return mNavigationMode; } @Override public void setNavigationMode(int mode) { final int oldMode = mNavigationMode; if (mode != oldMode) { switch (oldMode) { case ActionBar.NAVIGATION_MODE_LIST: if (mSpinner != null && mSpinner.getParent() == mToolbar) { mToolbar.removeView(mSpinner); } break; case ActionBar.NAVIGATION_MODE_TABS: if (mTabView != null && mTabView.getParent() == mToolbar) { mToolbar.removeView(mTabView); } break; } mNavigationMode = mode; switch (mode) { case ActionBar.NAVIGATION_MODE_STANDARD: break; case ActionBar.NAVIGATION_MODE_LIST: ensureSpinner(); mToolbar.addView(mSpinner, 0); break; case ActionBar.NAVIGATION_MODE_TABS: if (mTabView != null) { mToolbar.addView(mTabView, 0); Toolbar.LayoutParams lp = (Toolbar.LayoutParams) mTabView.getLayoutParams(); lp.width = ViewGroup.LayoutParams.WRAP_CONTENT; lp.height = ViewGroup.LayoutParams.WRAP_CONTENT; lp.gravity = Gravity.START | Gravity.BOTTOM; } break; default: throw new IllegalArgumentException("Invalid navigation mode " + mode); } } } private void ensureSpinner() { if (mSpinner == null) { mSpinner = new Spinner(getContext(), null, R.attr.actionDropDownStyle); Toolbar.LayoutParams lp = new Toolbar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT, Gravity.START | Gravity.CENTER_VERTICAL); mSpinner.setLayoutParams(lp); } } @Override public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener) { ensureSpinner(); mSpinner.setAdapter(adapter); mSpinner.setOnItemSelectedListener(listener); } @Override public void setDropdownSelectedPosition(int position) { if (mSpinner == null) { throw new IllegalStateException( "Can't set dropdown selected position without an adapter"); } mSpinner.setSelection(position); } @Override public int getDropdownSelectedPosition() { return mSpinner != null ? mSpinner.getSelectedItemPosition() : 0; } @Override public int getDropdownItemCount() { return mSpinner != null ? mSpinner.getCount() : 0; } @Override public void setCustomView(View view) { if (mCustomView != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { mToolbar.removeView(mCustomView); } mCustomView = view; if (view != null && (mDisplayOpts & ActionBar.DISPLAY_SHOW_CUSTOM) != 0) { mToolbar.addView(mCustomView); } } @Override public View getCustomView() { return mCustomView; } @Override public void animateToVisibility(int visibility) { Animator anim = setupAnimatorToVisibility(visibility, DEFAULT_FADE_DURATION_MS); if (anim != null) { anim.start(); } } @Override public Animator setupAnimatorToVisibility(int visibility, long duration) { if (visibility == View.GONE) { ObjectAnimator anim = ObjectAnimator.ofFloat(mToolbar, View.ALPHA, 1, 0); anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { private boolean mCanceled = false; @Override public void onAnimationEnd(Animator animation) { if (!mCanceled) { mToolbar.setVisibility(View.GONE); } } @Override public void onAnimationCancel(Animator animation) { mCanceled = true; } }); return anim; } else if (visibility == View.VISIBLE) { ObjectAnimator anim = ObjectAnimator.ofFloat(mToolbar, View.ALPHA, 0, 1); anim.setDuration(duration); anim.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationStart(Animator animation) { mToolbar.setVisibility(View.VISIBLE); } }); return anim; } return null; } @Override public void setNavigationIcon(Drawable icon) { mNavIcon = icon; updateNavigationIcon(); } @Override public void setNavigationIcon(int resId) { setNavigationIcon(resId != 0 ? mToolbar.getContext().getDrawable(resId) : null); } @Override public void setDefaultNavigationIcon(Drawable defaultNavigationIcon) { if (mDefaultNavigationIcon != defaultNavigationIcon) { mDefaultNavigationIcon = defaultNavigationIcon; updateNavigationIcon(); } } private void updateNavigationIcon() { if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { mToolbar.setNavigationIcon(mNavIcon != null ? mNavIcon : mDefaultNavigationIcon); } else { mToolbar.setNavigationIcon(null); } } @Override public void setNavigationContentDescription(CharSequence description) { mHomeDescription = description; updateHomeAccessibility(); } @Override public void setNavigationContentDescription(int resId) { setNavigationContentDescription(resId == 0 ? null : getContext().getString(resId)); } private void updateHomeAccessibility() { if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { if (TextUtils.isEmpty(mHomeDescription)) { mToolbar.setNavigationContentDescription(mDefaultNavigationContentDescription); } else { mToolbar.setNavigationContentDescription(mHomeDescription); } } } @Override public void saveHierarchyState(SparseArray<Parcelable> toolbarStates) { mToolbar.saveHierarchyState(toolbarStates); } @Override public void restoreHierarchyState(SparseArray<Parcelable> toolbarStates) { mToolbar.restoreHierarchyState(toolbarStates); } @Override public void setBackgroundDrawable(Drawable d) { //noinspection deprecation mToolbar.setBackgroundDrawable(d); } @Override public int getHeight() { return mToolbar.getHeight(); } @Override public void setVisibility(int visible) { mToolbar.setVisibility(visible); } @Override public int getVisibility() { return mToolbar.getVisibility(); } @Override public void setMenuCallbacks(MenuPresenter.Callback presenterCallback, MenuBuilder.Callback menuBuilderCallback) { mToolbar.setMenuCallbacks(presenterCallback, menuBuilderCallback); } @Override public Menu getMenu() { return mToolbar.getMenu(); } }