From e021e6ed8931a0a8296af182fc9b0c76b64fb0c4 Mon Sep 17 00:00:00 2001 From: Adam Powell Date: Fri, 23 May 2014 17:27:24 -0700 Subject: [PATCH] Toolbar factoring and ActionBar functionality integration Toolbars now can act in the role of ActionBar with the exception of navigation modes. Expandable action views are now supported as well as populating menu items from a host window. Change-Id: If477db9c7ad9f95723f28cf73cbf03a07ce9d6ad --- api/current.txt | 16 +- core/java/android/app/ActionBar.java | 2 + .../android/widget/ActionMenuPresenter.java | 1 + core/java/android/widget/ActionMenuView.java | 53 +- core/java/android/widget/Toolbar.java | 532 +++++++++++++++--- .../internal/app/WindowDecorActionBar.java | 144 +++-- .../internal/widget/AbsActionBarView.java | 12 +- .../internal/widget/ActionBarContainer.java | 15 +- .../internal/widget/ActionBarContextView.java | 6 +- .../widget/ActionBarOverlayLayout.java | 72 ++- .../internal/widget/ActionBarView.java | 78 ++- .../android/internal/widget/DecorToolbar.java | 94 ++++ .../internal/widget/ToolbarWidgetWrapper.java | 526 +++++++++++++++++ core/res/res/layout/screen_toolbar.xml | 51 ++ core/res/res/values/attrs.xml | 28 +- core/res/res/values/styles.xml | 11 +- core/res/res/values/styles_quantum.xml | 3 +- core/res/res/values/symbols.xml | 1 + core/res/res/values/themes.xml | 1 + core/res/res/values/themes_quantum.xml | 2 + .../internal/policy/impl/PhoneWindow.java | 4 +- 21 files changed, 1438 insertions(+), 214 deletions(-) create mode 100644 core/java/com/android/internal/widget/DecorToolbar.java create mode 100644 core/java/com/android/internal/widget/ToolbarWidgetWrapper.java create mode 100644 core/res/res/layout/screen_toolbar.xml diff --git a/api/current.txt b/api/current.txt index 5c931f99d8680..679628a9d0695 100644 --- a/api/current.txt +++ b/api/current.txt @@ -35852,10 +35852,14 @@ package android.widget { public class ActionMenuView extends android.widget.LinearLayout { ctor public ActionMenuView(android.content.Context); ctor public ActionMenuView(android.content.Context, android.util.AttributeSet); + method public void dismissPopupMenus(); method public android.view.Menu getMenu(); + method public boolean hideOverflowMenu(); + method public boolean isOverflowMenuShowing(); method public void onConfigurationChanged(android.content.res.Configuration); method public void onDetachedFromWindow(); method public void setOnMenuItemClickListener(android.widget.ActionMenuView.OnMenuItemClickListener); + method public boolean showOverflowMenu(); } public static class ActionMenuView.LayoutParams extends android.widget.LinearLayout.LayoutParams { @@ -38033,6 +38037,8 @@ package android.widget { ctor public Toolbar(android.content.Context, android.util.AttributeSet); ctor public Toolbar(android.content.Context, android.util.AttributeSet, int); ctor public Toolbar(android.content.Context, android.util.AttributeSet, int, int); + method public void collapseActionView(); + method public void dismissPopupMenus(); method public int getContentInsetEnd(); method public int getContentInsetLeft(); method public int getContentInsetRight(); @@ -38043,7 +38049,10 @@ package android.widget { method public android.graphics.drawable.Drawable getNavigationIcon(); method public java.lang.CharSequence getSubtitle(); method public java.lang.CharSequence getTitle(); + method public boolean hasExpandedActionView(); + method public boolean hideOverflowMenu(); method public void inflateMenu(int); + method public boolean isOverflowMenuShowing(); method protected void onLayout(boolean, int, int, int, int); method public void setContentInsetsAbsolute(int, int); method public void setContentInsetsRelative(int, int); @@ -38051,6 +38060,8 @@ package android.widget { method public void setLogo(android.graphics.drawable.Drawable); method public void setLogoDescription(int); method public void setLogoDescription(java.lang.CharSequence); + method public void setNavigationContentDescription(java.lang.CharSequence); + method public void setNavigationContentDescription(int); method public void setNavigationDescription(int); method public void setNavigationDescription(java.lang.CharSequence); method public void setNavigationIcon(int); @@ -38061,17 +38072,18 @@ package android.widget { method public void setSubtitle(java.lang.CharSequence); method public void setTitle(int); method public void setTitle(java.lang.CharSequence); + method public boolean showOverflowMenu(); } - public static class Toolbar.LayoutParams extends android.view.ViewGroup.MarginLayoutParams { + public static class Toolbar.LayoutParams extends android.app.ActionBar.LayoutParams { ctor public Toolbar.LayoutParams(android.content.Context, android.util.AttributeSet); ctor public Toolbar.LayoutParams(int, int); ctor public Toolbar.LayoutParams(int, int, int); ctor public Toolbar.LayoutParams(int); ctor public Toolbar.LayoutParams(android.widget.Toolbar.LayoutParams); + ctor public Toolbar.LayoutParams(android.app.ActionBar.LayoutParams); ctor public Toolbar.LayoutParams(android.view.ViewGroup.MarginLayoutParams); ctor public Toolbar.LayoutParams(android.view.ViewGroup.LayoutParams); - field public int gravity; } public static abstract interface Toolbar.OnMenuItemClickListener { diff --git a/core/java/android/app/ActionBar.java b/core/java/android/app/ActionBar.java index 3c3df0156300f..f05f4c7c18d38 100644 --- a/core/java/android/app/ActionBar.java +++ b/core/java/android/app/ActionBar.java @@ -1291,6 +1291,7 @@ public abstract class ActionBar { public LayoutParams(int width, int height) { super(width, height); + this.gravity = Gravity.CENTER_VERTICAL | Gravity.START; } public LayoutParams(int width, int height, int gravity) { @@ -1305,6 +1306,7 @@ public abstract class ActionBar { public LayoutParams(LayoutParams source) { super(source); + this.gravity = source.gravity; } public LayoutParams(ViewGroup.LayoutParams source) { diff --git a/core/java/android/widget/ActionMenuPresenter.java b/core/java/android/widget/ActionMenuPresenter.java index 51759c5b227d8..1fddf3e78c355 100644 --- a/core/java/android/widget/ActionMenuPresenter.java +++ b/core/java/android/widget/ActionMenuPresenter.java @@ -544,6 +544,7 @@ public class ActionMenuPresenter extends BaseMenuPresenter public void setMenuView(ActionMenuView menuView) { mMenuView = menuView; + menuView.initialize(mMenu); } private static class SavedState implements Parcelable { diff --git a/core/java/android/widget/ActionMenuView.java b/core/java/android/widget/ActionMenuView.java index 3975edf002b44..a9a5eae452660 100644 --- a/core/java/android/widget/ActionMenuView.java +++ b/core/java/android/widget/ActionMenuView.java @@ -69,6 +69,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo /** @hide */ public void setPresenter(ActionMenuPresenter presenter) { mPresenter = presenter; + mPresenter.setMenuView(this); } @Override @@ -488,7 +489,7 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo @Override public void onDetachedFromWindow() { super.onDetachedFromWindow(); - mPresenter.dismissPopupMenus(); + dismissPopupMenus(); } /** @hide */ @@ -577,6 +578,56 @@ public class ActionMenuView extends LinearLayout implements MenuBuilder.ItemInvo return mMenu; } + /** + * Returns the current menu or null if one has not yet been configured. + * @hide Internal use only for action bar integration + */ + public MenuBuilder peekMenu() { + return mMenu; + } + + /** + * Show the overflow items from the associated menu. + * + * @return true if the menu was able to be shown, false otherwise + */ + public boolean showOverflowMenu() { + return mPresenter != null && mPresenter.showOverflowMenu(); + } + + /** + * Hide the overflow items from the associated menu. + * + * @return true if the menu was able to be hidden, false otherwise + */ + public boolean hideOverflowMenu() { + return mPresenter != null && mPresenter.hideOverflowMenu(); + } + + /** + * Check whether the overflow menu is currently showing. This may not reflect + * a pending show operation in progress. + * + * @return true if the overflow menu is currently showing + */ + public boolean isOverflowMenuShowing() { + return mPresenter != null && mPresenter.isOverflowMenuShowing(); + } + + /** @hide */ + public boolean isOverflowMenuShowPending() { + return mPresenter != null && mPresenter.isOverflowMenuShowPending(); + } + + /** + * Dismiss any popups associated with this menu view. + */ + public void dismissPopupMenus() { + if (mPresenter != null) { + mPresenter.dismissPopupMenus(); + } + } + /** * @hide Private LinearLayout (superclass) API. Un-hide if LinearLayout API is made public. */ diff --git a/core/java/android/widget/Toolbar.java b/core/java/android/widget/Toolbar.java index f9033463a080e..8c67bb770b5af 100644 --- a/core/java/android/widget/Toolbar.java +++ b/core/java/android/widget/Toolbar.java @@ -18,13 +18,17 @@ package android.widget; import android.annotation.NonNull; +import android.app.ActionBar; import android.content.Context; import android.content.res.TypedArray; import android.graphics.drawable.Drawable; import android.os.Parcel; import android.os.Parcelable; +import android.text.Layout; import android.text.TextUtils; import android.util.AttributeSet; +import android.util.Log; +import android.view.CollapsibleActionView; import android.view.Gravity; import android.view.Menu; import android.view.MenuInflater; @@ -32,7 +36,15 @@ import android.view.MenuItem; import android.view.View; import android.view.ViewDebug; import android.view.ViewGroup; +import android.view.Window; import com.android.internal.R; +import com.android.internal.view.menu.MenuBuilder; +import com.android.internal.view.menu.MenuItemImpl; +import com.android.internal.view.menu.MenuPresenter; +import com.android.internal.view.menu.MenuView; +import com.android.internal.view.menu.SubMenuBuilder; +import com.android.internal.widget.DecorToolbar; +import com.android.internal.widget.ToolbarWidgetWrapper; import java.util.ArrayList; import java.util.List; @@ -80,14 +92,25 @@ import java.util.List; * layout is discouraged on API 21 devices and newer.

*/ public class Toolbar extends ViewGroup { + private static final String TAG = "Toolbar"; + private ActionMenuView mMenuView; private TextView mTitleTextView; private TextView mSubtitleTextView; private ImageButton mNavButtonView; private ImageView mLogoView; + private Drawable mCollapseIcon; + private ImageButton mCollapseButtonView; + View mExpandedActionView; + private int mTitleTextAppearance; private int mSubtitleTextAppearance; + private int mNavButtonStyle; + + private int mButtonGravity; + + private int mMaxButtonHeight; private int mTitleMarginStart; private int mTitleMarginEnd; @@ -117,6 +140,10 @@ public class Toolbar extends ViewGroup { } }; + private ToolbarWidgetWrapper mWrapper; + private ActionMenuPresenter mOuterActionMenuPresenter; + private ExpandedActionViewMenuPresenter mExpandedMenuPresenter; + public Toolbar(Context context) { this(context, null); } @@ -137,7 +164,9 @@ public class Toolbar extends ViewGroup { mTitleTextAppearance = a.getResourceId(R.styleable.Toolbar_titleTextAppearance, 0); mSubtitleTextAppearance = a.getResourceId(R.styleable.Toolbar_subtitleTextAppearance, 0); + mNavButtonStyle = a.getResourceId(R.styleable.Toolbar_navigationButtonStyle, 0); mGravity = a.getInteger(R.styleable.Toolbar_gravity, mGravity); + mButtonGravity = a.getInteger(R.styleable.Toolbar_buttonGravity, Gravity.TOP); mTitleMarginStart = mTitleMarginEnd = mTitleMarginTop = mTitleMarginBottom = a.getDimensionPixelOffset(R.styleable.Toolbar_titleMargins, 0); @@ -162,6 +191,8 @@ public class Toolbar extends ViewGroup { mTitleMarginBottom = marginBottom; } + mMaxButtonHeight = a.getDimensionPixelSize(R.styleable.Toolbar_maxButtonHeight, -1); + final int contentInsetStart = a.getDimensionPixelOffset(R.styleable.Toolbar_contentInsetStart, RtlSpacingHelper.UNDEFINED); @@ -180,6 +211,8 @@ public class Toolbar extends ViewGroup { mContentInsets.setRelative(contentInsetStart, contentInsetEnd); } + mCollapseIcon = a.getDrawable(R.styleable.Toolbar_collapseIcon); + final CharSequence title = a.getText(R.styleable.Toolbar_title); if (!TextUtils.isEmpty(title)) { setTitle(title); @@ -211,6 +244,110 @@ public class Toolbar extends ViewGroup { setLogo(getContext().getDrawable(resId)); } + /** @hide */ + public boolean canShowOverflowMenu() { + return getVisibility() == VISIBLE && mMenuView != null && mMenuView.isOverflowReserved(); + } + + /** + * Check whether the overflow menu is currently showing. This may not reflect + * a pending show operation in progress. + * + * @return true if the overflow menu is currently showing + */ + public boolean isOverflowMenuShowing() { + return mMenuView != null && mMenuView.isOverflowMenuShowing(); + } + + /** @hide */ + public boolean isOverflowMenuShowPending() { + return mMenuView != null && mMenuView.isOverflowMenuShowPending(); + } + + /** + * Show the overflow items from the associated menu. + * + * @return true if the menu was able to be shown, false otherwise + */ + public boolean showOverflowMenu() { + return mMenuView != null && mMenuView.showOverflowMenu(); + } + + /** + * Hide the overflow items from the associated menu. + * + * @return true if the menu was able to be hidden, false otherwise + */ + public boolean hideOverflowMenu() { + return mMenuView != null && mMenuView.hideOverflowMenu(); + } + + /** @hide */ + public void setMenu(MenuBuilder menu, ActionMenuPresenter outerPresenter) { + if (menu == null && mMenuView == null) { + return; + } + + ensureMenuView(); + final MenuBuilder oldMenu = mMenuView.peekMenu(); + if (oldMenu == menu) { + return; + } + + if (oldMenu != null) { + oldMenu.removeMenuPresenter(mOuterActionMenuPresenter); + oldMenu.removeMenuPresenter(mExpandedMenuPresenter); + } + + final Context context = getContext(); + + if (mExpandedMenuPresenter == null) { + mExpandedMenuPresenter = new ExpandedActionViewMenuPresenter(); + } + + outerPresenter.setExpandedActionViewsExclusive(true); + if (menu != null) { + menu.addMenuPresenter(outerPresenter); + menu.addMenuPresenter(mExpandedMenuPresenter); + } else { + outerPresenter.initForMenu(context, null); + mExpandedMenuPresenter.initForMenu(context, null); + outerPresenter.updateMenuView(true); + mExpandedMenuPresenter.updateMenuView(true); + } + mMenuView.setPresenter(outerPresenter); + mOuterActionMenuPresenter = outerPresenter; + } + + /** + * Dismiss all currently showing popup menus, including overflow or submenus. + */ + public void dismissPopupMenus() { + if (mMenuView != null) { + mMenuView.dismissPopupMenus(); + } + } + + /** @hide */ + public boolean isTitleTruncated() { + if (mTitleTextView == null) { + return false; + } + + final Layout titleLayout = mTitleTextView.getLayout(); + if (titleLayout == null) { + return false; + } + + final int lineCount = titleLayout.getLineCount(); + for (int i = 0; i < lineCount; i++) { + if (titleLayout.getEllipsisCount(i) > 0) { + return true; + } + } + return false; + } + /** * Set a logo drawable. * @@ -222,9 +359,7 @@ public class Toolbar extends ViewGroup { */ public void setLogo(Drawable drawable) { if (drawable != null) { - if (mLogoView == null) { - mLogoView = new ImageView(getContext()); - } + ensureLogoView(); if (mLogoView.getParent() == null) { addSystemView(mLogoView); } @@ -268,8 +403,8 @@ public class Toolbar extends ViewGroup { * @param description Description to set */ public void setLogoDescription(CharSequence description) { - if (!TextUtils.isEmpty(description) && mLogoView == null) { - mLogoView = new ImageView(getContext()); + if (!TextUtils.isEmpty(description)) { + ensureLogoView(); } if (mLogoView != null) { mLogoView.setContentDescription(description); @@ -285,10 +420,48 @@ public class Toolbar extends ViewGroup { return mLogoView != null ? mLogoView.getContentDescription() : null; } + private void ensureLogoView() { + if (mLogoView == null) { + mLogoView = new ImageView(getContext()); + } + } + /** - * Return the current title displayed in the toolbar. + * Check whether this Toolbar is currently hosting an expanded action view. * - * @return The current title + *

An action view may be expanded either directly from the + * {@link android.view.MenuItem MenuItem} it belongs to or by user action. If the Toolbar + * has an expanded action view it can be collapsed using the {@link #collapseActionView()} + * method.

+ * + * @return true if the Toolbar has an expanded action view + */ + public boolean hasExpandedActionView() { + return mExpandedMenuPresenter != null && + mExpandedMenuPresenter.mCurrentExpandedItem != null; + } + + /** + * Collapse a currently expanded action view. If this Toolbar does not have an + * expanded action view this method has no effect. + * + *

An action view may be expanded either directly from the + * {@link android.view.MenuItem MenuItem} it belongs to or by user action.

+ * + * @see #hasExpandedActionView() + */ + public void collapseActionView() { + final MenuItemImpl item = mExpandedMenuPresenter == null ? null : + mExpandedMenuPresenter.mCurrentExpandedItem; + if (item != null) { + item.collapseActionView(); + } + } + + /** + * Returns the title of this toolbar. + * + * @return The current title. */ public CharSequence getTitle() { return mTitleText; @@ -319,6 +492,8 @@ public class Toolbar extends ViewGroup { if (mTitleTextView == null) { final Context context = getContext(); mTitleTextView = new TextView(context); + mTitleTextView.setSingleLine(); + mTitleTextView.setEllipsize(TextUtils.TruncateAt.END); mTitleTextView.setTextAppearance(context, mTitleTextAppearance); } if (mTitleTextView.getParent() == null) { @@ -365,6 +540,8 @@ public class Toolbar extends ViewGroup { if (mSubtitleTextView == null) { final Context context = getContext(); mSubtitleTextView = new TextView(context); + mSubtitleTextView.setSingleLine(); + mSubtitleTextView.setEllipsize(TextUtils.TruncateAt.END); mSubtitleTextView.setTextAppearance(context, mSubtitleTextAppearance); } if (mSubtitleTextView.getParent() == null) { @@ -394,6 +571,30 @@ public class Toolbar extends ViewGroup { setNavigationIcon(getContext().getDrawable(resId)); } + /** + * Set a content description for the navigation button if one is present. The content + * description will be read via screen readers or other accessibility systems to explain + * the action of the navigation button. + * + * @param description Content description to set + */ + public void setNavigationContentDescription(CharSequence description) { + ensureNavButtonView(); + mNavButtonView.setContentDescription(description); + } + + /** + * Set a content description for the navigation button if one is present. The content + * description will be read via screen readers or other accessibility systems to explain + * the action of the navigation button. + * + * @param resId Resource ID of a content description string to set + */ + public void setNavigationContentDescription(int resId) { + ensureNavButtonView(); + mNavButtonView.setContentDescription(getContext().getText(resId)); + } + /** * Set the icon to use for the toolbar's navigation button. * @@ -480,12 +681,19 @@ public class Toolbar extends ViewGroup { * @return The toolbar's Menu */ public Menu getMenu() { + ensureMenuView(); + return mMenuView.getMenu(); + } + + private void ensureMenuView() { if (mMenuView == null) { mMenuView = new ActionMenuView(getContext()); mMenuView.setOnMenuItemClickListener(mMenuViewItemClickListener); + final LayoutParams lp = generateDefaultLayoutParams(); + lp.gravity = Gravity.END | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); + mMenuView.setLayoutParams(lp); addSystemView(mMenuView); } - return mMenuView.getMenu(); } private MenuInflater getMenuInflater() { @@ -634,7 +842,27 @@ public class Toolbar extends ViewGroup { private void ensureNavButtonView() { if (mNavButtonView == null) { - mNavButtonView = new ImageButton(getContext(), null, R.attr.borderlessButtonStyle); + mNavButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); + final LayoutParams lp = generateDefaultLayoutParams(); + lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); + mNavButtonView.setLayoutParams(lp); + } + } + + private void ensureCollapseButtonView() { + if (mCollapseButtonView == null) { + mCollapseButtonView = new ImageButton(getContext(), null, 0, mNavButtonStyle); + mCollapseButtonView.setImageDrawable(mCollapseIcon); + final LayoutParams lp = generateDefaultLayoutParams(); + lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); + lp.mViewType = LayoutParams.EXPANDED; + mCollapseButtonView.setLayoutParams(lp); + mCollapseButtonView.setOnClickListener(new OnClickListener() { + @Override + public void onClick(View v) { + collapseActionView(); + } + }); } } @@ -657,6 +885,27 @@ public class Toolbar extends ViewGroup { super.onRestoreInstanceState(ss.getSuperState()); } + private void measureChildConstrained(View child, int parentWidthSpec, int widthUsed, + int parentHeightSpec, int heightUsed, int heightConstraint) { + final MarginLayoutParams lp = (MarginLayoutParams) child.getLayoutParams(); + + int childWidthSpec = getChildMeasureSpec(parentWidthSpec, + mPaddingLeft + mPaddingRight + lp.leftMargin + lp.rightMargin + + widthUsed, lp.width); + int childHeightSpec = getChildMeasureSpec(parentHeightSpec, + mPaddingTop + mPaddingBottom + lp.topMargin + lp.bottomMargin + + heightUsed, lp.height); + + final int childHeightMode = MeasureSpec.getMode(childHeightSpec); + if (childHeightMode != MeasureSpec.EXACTLY && heightConstraint >= 0) { + final int size = childHeightMode != MeasureSpec.UNSPECIFIED ? + Math.min(MeasureSpec.getSize(childHeightSpec), heightConstraint) : + heightConstraint; + childHeightSpec = MeasureSpec.makeMeasureSpec(size, MeasureSpec.EXACTLY); + } + child.measure(childWidthSpec, childHeightSpec); + } + @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { int width = 0; @@ -667,18 +916,30 @@ public class Toolbar extends ViewGroup { int navWidth = 0; if (shouldLayout(mNavButtonView)) { - measureChildWithMargins(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0); + measureChildConstrained(mNavButtonView, widthMeasureSpec, width, heightMeasureSpec, 0, + mMaxButtonHeight); navWidth = mNavButtonView.getMeasuredWidth() + getHorizontalMargins(mNavButtonView); height = Math.max(height, mNavButtonView.getMeasuredHeight() + getVerticalMargins(mNavButtonView)); childState = combineMeasuredStates(childState, mNavButtonView.getMeasuredState()); } + if (shouldLayout(mCollapseButtonView)) { + measureChildConstrained(mCollapseButtonView, widthMeasureSpec, width, + heightMeasureSpec, 0, mMaxButtonHeight); + navWidth = mCollapseButtonView.getMeasuredWidth() + + getHorizontalMargins(mCollapseButtonView); + height = Math.max(height, mCollapseButtonView.getMeasuredHeight() + + getVerticalMargins(mCollapseButtonView)); + childState = combineMeasuredStates(childState, mCollapseButtonView.getMeasuredState()); + } + width += Math.max(getContentInsetStart(), navWidth); int menuWidth = 0; if (shouldLayout(mMenuView)) { - measureChildWithMargins(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0); + measureChildConstrained(mMenuView, widthMeasureSpec, width, heightMeasureSpec, 0, + mMaxButtonHeight); menuWidth = mMenuView.getMeasuredWidth() + getHorizontalMargins(mMenuView); height = Math.max(height, mMenuView.getMeasuredHeight() + getVerticalMargins(mMenuView)); @@ -687,6 +948,16 @@ public class Toolbar extends ViewGroup { width += Math.max(getContentInsetEnd(), menuWidth); + if (shouldLayout(mExpandedActionView)) { + measureChildWithMargins(mExpandedActionView, widthMeasureSpec, width, + heightMeasureSpec, 0); + width += mExpandedActionView.getMeasuredWidth() + + getHorizontalMargins(mExpandedActionView); + height = Math.max(height, mExpandedActionView.getMeasuredHeight() + + getVerticalMargins(mExpandedActionView)); + childState = combineMeasuredStates(childState, mExpandedActionView.getMeasuredState()); + } + if (shouldLayout(mLogoView)) { measureChildWithMargins(mLogoView, widthMeasureSpec, width, heightMeasureSpec, 0); width += mLogoView.getMeasuredWidth() + getHorizontalMargins(mLogoView); @@ -723,7 +994,7 @@ public class Toolbar extends ViewGroup { for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp.mViewType == LayoutParams.SYSTEM || !shouldLayout(child)) { + if (lp.mViewType != LayoutParams.CUSTOM || !shouldLayout(child)) { // We already got all system views above. Skip them and GONE views. continue; } @@ -768,6 +1039,14 @@ public class Toolbar extends ViewGroup { } } + if (shouldLayout(mCollapseButtonView)) { + if (isRtl) { + right = layoutChildRight(mCollapseButtonView, right); + } else { + left = layoutChildLeft(mCollapseButtonView, left); + } + } + if (shouldLayout(mMenuView)) { if (isRtl) { left = layoutChildLeft(mMenuView, left); @@ -779,6 +1058,14 @@ public class Toolbar extends ViewGroup { left = Math.max(left, getContentInsetLeft()); right = Math.min(right, width - paddingRight - getContentInsetRight()); + if (shouldLayout(mExpandedActionView)) { + if (isRtl) { + right = layoutChildRight(mExpandedActionView, right); + } else { + left = layoutChildLeft(mExpandedActionView, left); + } + } + if (shouldLayout(mLogoView)) { if (isRtl) { right = layoutChildRight(mLogoView, right); @@ -801,40 +1088,42 @@ public class Toolbar extends ViewGroup { if (layoutTitle || layoutSubtitle) { int titleTop; + final View topChild = layoutTitle ? mTitleTextView : mSubtitleTextView; + final View bottomChild = layoutSubtitle ? mSubtitleTextView : mTitleTextView; + final LayoutParams toplp = (LayoutParams) topChild.getLayoutParams(); + final LayoutParams bottomlp = (LayoutParams) bottomChild.getLayoutParams(); + switch (mGravity & Gravity.VERTICAL_GRAVITY_MASK) { case Gravity.TOP: - titleTop = getPaddingTop(); + titleTop = getPaddingTop() + toplp.topMargin + mTitleMarginTop; break; default: case Gravity.CENTER_VERTICAL: - final View child = layoutTitle ? mTitleTextView : mSubtitleTextView; - final LayoutParams lp = (LayoutParams) child.getLayoutParams(); final int space = height - paddingTop - paddingBottom; int spaceAbove = (space - titleHeight) / 2; - if (spaceAbove < lp.topMargin + mTitleMarginTop) { - spaceAbove = lp.topMargin + mTitleMarginTop; + if (spaceAbove < toplp.topMargin + mTitleMarginTop) { + spaceAbove = toplp.topMargin + mTitleMarginTop; } else { final int spaceBelow = height - paddingBottom - titleHeight - spaceAbove - paddingTop; - if (spaceBelow < lp.bottomMargin + mTitleMarginBottom) { + if (spaceBelow < toplp.bottomMargin + mTitleMarginBottom) { spaceAbove = Math.max(0, spaceAbove - - (lp.bottomMargin + mTitleMarginBottom - spaceBelow)); + (bottomlp.bottomMargin + mTitleMarginBottom - spaceBelow)); } } titleTop = paddingTop + spaceAbove; break; case Gravity.BOTTOM: - titleTop = height - paddingBottom - titleHeight; + titleTop = height - paddingBottom - bottomlp.bottomMargin - mTitleMarginBottom - + titleHeight; break; } if (isRtl) { int titleRight = right; int subtitleRight = right; - titleTop += mTitleMarginTop; if (layoutTitle) { final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); titleRight -= lp.rightMargin + mTitleMarginStart; - titleTop += lp.topMargin; final int titleLeft = titleRight - mTitleTextView.getMeasuredWidth(); final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); @@ -855,11 +1144,9 @@ public class Toolbar extends ViewGroup { } else { int titleLeft = left; int subtitleLeft = left; - titleTop += mTitleMarginTop; if (layoutTitle) { final LayoutParams lp = (LayoutParams) mTitleTextView.getLayoutParams(); titleLeft += lp.leftMargin + mTitleMarginStart; - titleTop += lp.topMargin; final int titleRight = titleLeft + mTitleTextView.getMeasuredWidth(); final int titleBottom = titleTop + mTitleTextView.getMeasuredHeight(); mTitleTextView.layout(titleLeft, titleTop, titleRight, titleBottom); @@ -897,7 +1184,7 @@ public class Toolbar extends ViewGroup { // Centered views try to center with respect to the whole bar, but views pinned // to the left or right can push the mass of centered views to one side or the other. - addCustomViewsWithGravity(mTempViews, Gravity.CENTER); + addCustomViewsWithGravity(mTempViews, Gravity.CENTER_HORIZONTAL); final int centerViewsWidth = getViewListMeasuredWidth(mTempViews); final int parentCenter = paddingLeft + (width - paddingLeft - paddingRight) / 2; final int halfCenterViewsWidth = centerViewsWidth / 2; @@ -1007,17 +1294,16 @@ public class Toolbar extends ViewGroup { for (int i = childCount - 1; i >= 0; i--) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) && getChildHorizontalGravity(lp.gravity) == absGrav) { views.add(child); } - } } else { for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); final LayoutParams lp = (LayoutParams) child.getLayoutParams(); - if (lp.mViewType != LayoutParams.SYSTEM && shouldLayout(child) && + if (lp.mViewType == LayoutParams.CUSTOM && shouldLayout(child) && getChildHorizontalGravity(lp.gravity) == absGrav) { views.add(child); } @@ -1054,14 +1340,16 @@ public class Toolbar extends ViewGroup { } @Override - public ViewGroup.LayoutParams generateLayoutParams(AttributeSet attrs) { - return super.generateLayoutParams(attrs); + public LayoutParams generateLayoutParams(AttributeSet attrs) { + return new LayoutParams(getContext(), attrs); } @Override - protected ViewGroup.LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { + protected LayoutParams generateLayoutParams(ViewGroup.LayoutParams p) { if (p instanceof LayoutParams) { return new LayoutParams((LayoutParams) p); + } else if (p instanceof ActionBar.LayoutParams) { + return new LayoutParams((ActionBar.LayoutParams) p); } else if (p instanceof MarginLayoutParams) { return new LayoutParams((MarginLayoutParams) p); } else { @@ -1070,7 +1358,7 @@ public class Toolbar extends ViewGroup { } @Override - protected ViewGroup.LayoutParams generateDefaultLayoutParams() { + protected LayoutParams generateDefaultLayoutParams() { return new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.WRAP_CONTENT); } @@ -1083,6 +1371,25 @@ public class Toolbar extends ViewGroup { return ((LayoutParams) child.getLayoutParams()).mViewType == LayoutParams.CUSTOM; } + /** @hide */ + public DecorToolbar getWrapper() { + if (mWrapper == null) { + mWrapper = new ToolbarWidgetWrapper(this); + } + return mWrapper; + } + + private void setChildVisibilityForExpandedActionView(boolean expand) { + final int childCount = getChildCount(); + for (int i = 0; i < childCount; i++) { + final View child = getChildAt(i); + final LayoutParams lp = (LayoutParams) child.getLayoutParams(); + if (lp.mViewType != LayoutParams.EXPANDED && child != mMenuView) { + child.setVisibility(expand ? GONE : VISIBLE); + } + } + } + /** * Interface responsible for receiving menu item click events if the items themselves * do not have individual item click listeners. @@ -1103,44 +1410,15 @@ public class Toolbar extends ViewGroup { * * @attr ref android.R.styleable#Toolbar_LayoutParams_layout_gravity */ - public static class LayoutParams extends MarginLayoutParams { - /** - * Gravity for the view associated with these LayoutParams. - * - * @see android.view.Gravity - */ - @ViewDebug.ExportedProperty(category = "layout", mapping = { - @ViewDebug.IntToString(from = -1, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.NO_GRAVITY, to = "NONE"), - @ViewDebug.IntToString(from = Gravity.TOP, to = "TOP"), - @ViewDebug.IntToString(from = Gravity.BOTTOM, to = "BOTTOM"), - @ViewDebug.IntToString(from = Gravity.LEFT, to = "LEFT"), - @ViewDebug.IntToString(from = Gravity.RIGHT, to = "RIGHT"), - @ViewDebug.IntToString(from = Gravity.START, to = "START"), - @ViewDebug.IntToString(from = Gravity.END, to = "END"), - @ViewDebug.IntToString(from = Gravity.CENTER_VERTICAL, to = "CENTER_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.FILL_VERTICAL, to = "FILL_VERTICAL"), - @ViewDebug.IntToString(from = Gravity.CENTER_HORIZONTAL, to = "CENTER_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.FILL_HORIZONTAL, to = "FILL_HORIZONTAL"), - @ViewDebug.IntToString(from = Gravity.CENTER, to = "CENTER"), - @ViewDebug.IntToString(from = Gravity.FILL, to = "FILL") - }) - public int gravity = Gravity.NO_GRAVITY; - + public static class LayoutParams extends ActionBar.LayoutParams { static final int CUSTOM = 0; static final int SYSTEM = 1; + static final int EXPANDED = 2; int mViewType = CUSTOM; public LayoutParams(@NonNull Context c, AttributeSet attrs) { super(c, attrs); - - TypedArray a = c.obtainStyledAttributes(attrs, - com.android.internal.R.styleable.Toolbar_LayoutParams); - gravity = a.getInt( - com.android.internal.R.styleable.Toolbar_LayoutParams_layout_gravity, - Gravity.NO_GRAVITY); - a.recycle(); } public LayoutParams(int width, int height) { @@ -1160,7 +1438,11 @@ public class Toolbar extends ViewGroup { public LayoutParams(LayoutParams source) { super(source); - this.gravity = source.gravity; + mViewType = source.mViewType; + } + + public LayoutParams(ActionBar.LayoutParams source) { + super(source); } public LayoutParams(MarginLayoutParams source) { @@ -1199,4 +1481,126 @@ public class Toolbar extends ViewGroup { } }; } + + private class ExpandedActionViewMenuPresenter implements MenuPresenter { + MenuBuilder mMenu; + MenuItemImpl mCurrentExpandedItem; + + @Override + public void initForMenu(Context context, MenuBuilder menu) { + // Clear the expanded action view when menus change. + if (mMenu != null && mCurrentExpandedItem != null) { + mMenu.collapseItemActionView(mCurrentExpandedItem); + } + mMenu = menu; + } + + @Override + public MenuView getMenuView(ViewGroup root) { + return null; + } + + @Override + public void updateMenuView(boolean cleared) { + // Make sure the expanded item we have is still there. + if (mCurrentExpandedItem != null) { + boolean found = false; + + if (mMenu != null) { + final int count = mMenu.size(); + for (int i = 0; i < count; i++) { + final MenuItem item = mMenu.getItem(i); + if (item == mCurrentExpandedItem) { + found = true; + break; + } + } + } + + if (!found) { + // The item we had expanded disappeared. Collapse. + collapseItemActionView(mMenu, mCurrentExpandedItem); + } + } + } + + @Override + public void setCallback(Callback cb) { + } + + @Override + public boolean onSubMenuSelected(SubMenuBuilder subMenu) { + return false; + } + + @Override + public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { + } + + @Override + public boolean flagActionItems() { + return false; + } + + @Override + public boolean expandItemActionView(MenuBuilder menu, MenuItemImpl item) { + ensureCollapseButtonView(); + if (mCollapseButtonView.getParent() != Toolbar.this) { + addView(mCollapseButtonView); + } + mExpandedActionView = item.getActionView(); + mCurrentExpandedItem = item; + if (mExpandedActionView.getParent() != Toolbar.this) { + final LayoutParams lp = generateDefaultLayoutParams(); + lp.gravity = Gravity.START | (mButtonGravity & Gravity.VERTICAL_GRAVITY_MASK); + lp.mViewType = LayoutParams.EXPANDED; + mExpandedActionView.setLayoutParams(lp); + addView(mExpandedActionView); + } + + setChildVisibilityForExpandedActionView(true); + requestLayout(); + item.setActionViewExpanded(true); + + if (mExpandedActionView instanceof CollapsibleActionView) { + ((CollapsibleActionView) mExpandedActionView).onActionViewExpanded(); + } + + return true; + } + + @Override + public boolean collapseItemActionView(MenuBuilder menu, MenuItemImpl item) { + // Do this before detaching the actionview from the hierarchy, in case + // it needs to dismiss the soft keyboard, etc. + if (mExpandedActionView instanceof CollapsibleActionView) { + ((CollapsibleActionView) mExpandedActionView).onActionViewCollapsed(); + } + + removeView(mExpandedActionView); + removeView(mCollapseButtonView); + mExpandedActionView = null; + + setChildVisibilityForExpandedActionView(false); + mCurrentExpandedItem = null; + requestLayout(); + item.setActionViewExpanded(false); + + return true; + } + + @Override + public int getId() { + return 0; + } + + @Override + public Parcelable onSaveInstanceState() { + return null; + } + + @Override + public void onRestoreInstanceState(Parcelable state) { + } + } } diff --git a/core/java/com/android/internal/app/WindowDecorActionBar.java b/core/java/com/android/internal/app/WindowDecorActionBar.java index a238ae36991a5..a0c75a64624b9 100644 --- a/core/java/com/android/internal/app/WindowDecorActionBar.java +++ b/core/java/com/android/internal/app/WindowDecorActionBar.java @@ -18,7 +18,10 @@ package com.android.internal.app; import android.animation.ValueAnimator; import android.content.res.TypedArray; +import android.view.ViewGroup; import android.view.ViewParent; +import android.widget.AdapterView; +import android.widget.Toolbar; import com.android.internal.R; import com.android.internal.view.ActionBarPolicy; import com.android.internal.view.menu.MenuBuilder; @@ -28,6 +31,7 @@ import com.android.internal.widget.ActionBarContainer; import com.android.internal.widget.ActionBarContextView; import com.android.internal.widget.ActionBarOverlayLayout; import com.android.internal.widget.ActionBarView; +import com.android.internal.widget.DecorToolbar; import com.android.internal.widget.ScrollingTabContainerView; import android.animation.Animator; @@ -55,6 +59,7 @@ import android.view.Window; import android.view.accessibility.AccessibilityEvent; import android.view.animation.AnimationUtils; import android.widget.SpinnerAdapter; +import com.android.internal.widget.ToolbarWidgetWrapper; import java.lang.ref.WeakReference; import java.util.ArrayList; @@ -77,7 +82,7 @@ public class WindowDecorActionBar extends ActionBar implements private ActionBarOverlayLayout mOverlayLayout; private ActionBarContainer mContainerView; - private ActionBarView mActionView; + private DecorToolbar mDecorToolbar; private ActionBarContextView mContextView; private ActionBarContainer mSplitView; private View mContentView; @@ -187,7 +192,7 @@ public class WindowDecorActionBar extends ActionBar implements if (mOverlayLayout != null) { mOverlayLayout.setActionBarVisibilityCallback(this); } - mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); + mDecorToolbar = getDecorToolbar(decor.findViewById(com.android.internal.R.id.action_bar)); mContextView = (ActionBarContextView) decor.findViewById( com.android.internal.R.id.action_context_bar); mContainerView = (ActionBarContainer) decor.findViewById( @@ -195,18 +200,17 @@ public class WindowDecorActionBar extends ActionBar implements mSplitView = (ActionBarContainer) decor.findViewById( com.android.internal.R.id.split_action_bar); - if (mActionView == null || mContextView == null || mContainerView == null) { + if (mDecorToolbar == null || mContextView == null || mContainerView == null) { throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + "with a compatible window decor layout"); } - mContext = mActionView.getContext(); - mActionView.setContextView(mContextView); - mContextDisplayMode = mActionView.isSplitActionBar() ? + mContext = mDecorToolbar.getContext(); + mContextDisplayMode = mDecorToolbar.isSplit() ? CONTEXT_DISPLAY_SPLIT : CONTEXT_DISPLAY_NORMAL; // This was initially read from the action bar style - final int current = mActionView.getDisplayOptions(); + final int current = mDecorToolbar.getDisplayOptions(); final boolean homeAsUp = (current & DISPLAY_HOME_AS_UP) != 0; if (homeAsUp) { mDisplayHomeAsUpSet = true; @@ -225,6 +229,17 @@ public class WindowDecorActionBar extends ActionBar implements a.recycle(); } + private DecorToolbar getDecorToolbar(View view) { + if (view instanceof DecorToolbar) { + return (DecorToolbar) view; + } else if (view instanceof Toolbar) { + return ((Toolbar) view).getWrapper(); + } else { + throw new IllegalStateException("Can't make a decor toolbar out of " + + view.getClass().getSimpleName()); + } + } + public void onConfigurationChanged(Configuration newConfig) { setHasEmbeddedTabs(ActionBarPolicy.get(mContext).hasEmbeddedTabs()); } @@ -233,11 +248,11 @@ public class WindowDecorActionBar extends ActionBar implements mHasEmbeddedTabs = hasEmbeddedTabs; // Switch tab layout configuration if needed if (!mHasEmbeddedTabs) { - mActionView.setEmbeddedTabView(null); + mDecorToolbar.setEmbeddedTabView(null); mContainerView.setTabContainer(mTabScrollView); } else { mContainerView.setTabContainer(null); - mActionView.setEmbeddedTabView(mTabScrollView); + mDecorToolbar.setEmbeddedTabView(mTabScrollView); } final boolean isInTabMode = getNavigationMode() == NAVIGATION_MODE_TABS; if (mTabScrollView != null) { @@ -250,7 +265,7 @@ public class WindowDecorActionBar extends ActionBar implements mTabScrollView.setVisibility(View.GONE); } } - mActionView.setCollapsable(!mHasEmbeddedTabs && isInTabMode); + mDecorToolbar.setCollapsible(!mHasEmbeddedTabs && isInTabMode); mOverlayLayout.setHasNonEmbeddedTabs(!mHasEmbeddedTabs && isInTabMode); } @@ -263,7 +278,7 @@ public class WindowDecorActionBar extends ActionBar implements if (mHasEmbeddedTabs) { tabScroller.setVisibility(View.VISIBLE); - mActionView.setEmbeddedTabView(tabScroller); + mDecorToolbar.setEmbeddedTabView(tabScroller); } else { if (getNavigationMode() == NAVIGATION_MODE_TABS) { tabScroller.setVisibility(View.VISIBLE); @@ -326,7 +341,8 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void setCustomView(int resId) { - setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, mActionView, false)); + setCustomView(LayoutInflater.from(getThemedContext()).inflate(resId, + (ViewGroup) mDecorToolbar, false)); } @Override @@ -356,7 +372,7 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void setHomeButtonEnabled(boolean enable) { - mActionView.setHomeButtonEnabled(enable); + mDecorToolbar.setHomeButtonEnabled(enable); } @Override @@ -370,12 +386,12 @@ public class WindowDecorActionBar extends ActionBar implements } public void setSelectedNavigationItem(int position) { - switch (mActionView.getNavigationMode()) { + switch (mDecorToolbar.getNavigationMode()) { case NAVIGATION_MODE_TABS: selectTab(mTabs.get(position)); break; case NAVIGATION_MODE_LIST: - mActionView.setDropdownSelectedPosition(position); + mDecorToolbar.setDropdownSelectedPosition(position); break; default: throw new IllegalStateException( @@ -399,26 +415,26 @@ public class WindowDecorActionBar extends ActionBar implements } public void setTitle(CharSequence title) { - mActionView.setTitle(title); + mDecorToolbar.setTitle(title); } public void setSubtitle(CharSequence subtitle) { - mActionView.setSubtitle(subtitle); + mDecorToolbar.setSubtitle(subtitle); } public void setDisplayOptions(int options) { if ((options & DISPLAY_HOME_AS_UP) != 0) { mDisplayHomeAsUpSet = true; } - mActionView.setDisplayOptions(options); + mDecorToolbar.setDisplayOptions(options); } public void setDisplayOptions(int options, int mask) { - final int current = mActionView.getDisplayOptions(); + final int current = mDecorToolbar.getDisplayOptions(); if ((mask & DISPLAY_HOME_AS_UP) != 0) { mDisplayHomeAsUpSet = true; } - mActionView.setDisplayOptions((options & mask) | (current & ~mask)); + mDecorToolbar.setDisplayOptions((options & mask) | (current & ~mask)); } public void setBackgroundDrawable(Drawable d) { @@ -436,23 +452,23 @@ public class WindowDecorActionBar extends ActionBar implements } public View getCustomView() { - return mActionView.getCustomNavigationView(); + return mDecorToolbar.getCustomView(); } public CharSequence getTitle() { - return mActionView.getTitle(); + return mDecorToolbar.getTitle(); } public CharSequence getSubtitle() { - return mActionView.getSubtitle(); + return mDecorToolbar.getSubtitle(); } public int getNavigationMode() { - return mActionView.getNavigationMode(); + return mDecorToolbar.getNavigationMode(); } public int getDisplayOptions() { - return mActionView.getDisplayOptions(); + return mDecorToolbar.getDisplayOptions(); } public ActionMode startActionMode(ActionMode.Callback callback) { @@ -572,7 +588,7 @@ public class WindowDecorActionBar extends ActionBar implements return; } - final FragmentTransaction trans = mActionView.isInEditMode() ? null : + final FragmentTransaction trans = ((View) mDecorToolbar).isInEditMode() ? null : mActivity.getFragmentManager().beginTransaction().disallowAddToBackStack(); if (mSelectedTab == tab) { @@ -828,13 +844,18 @@ public class WindowDecorActionBar extends ActionBar implements hideForActionMode(); } - mActionView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); + mDecorToolbar.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); mContextView.animateToVisibility(toActionMode ? View.VISIBLE : View.GONE); - if (mTabScrollView != null && !mActionView.hasEmbeddedTabs() && mActionView.isCollapsed()) { + if (mTabScrollView != null && !mDecorToolbar.hasEmbeddedTabs() && + isCollapsed((View) mDecorToolbar)) { mTabScrollView.animateToVisibility(toActionMode ? View.GONE : View.VISIBLE); } } + private boolean isCollapsed(View view) { + return view == null || view.getVisibility() == View.GONE || view.getMeasuredHeight() == 0; + } + public Context getThemedContext() { if (mThemedContext == null) { TypedValue outValue = new TypedValue(); @@ -854,27 +875,27 @@ public class WindowDecorActionBar extends ActionBar implements @Override public boolean isTitleTruncated() { - return mActionView != null && mActionView.isTitleTruncated(); + return mDecorToolbar != null && mDecorToolbar.isTitleTruncated(); } @Override public void setHomeAsUpIndicator(Drawable indicator) { - mActionView.setHomeAsUpIndicator(indicator); + mDecorToolbar.setNavigationIcon(indicator); } @Override public void setHomeAsUpIndicator(int resId) { - mActionView.setHomeAsUpIndicator(resId); + mDecorToolbar.setNavigationIcon(resId); } @Override public void setHomeActionContentDescription(CharSequence description) { - mActionView.setHomeActionContentDescription(description); + mDecorToolbar.setNavigationContentDescription(description); } @Override public void setHomeActionContentDescription(int resId) { - mActionView.setHomeActionContentDescription(resId); + mDecorToolbar.setNavigationContentDescription(resId); } @Override @@ -938,7 +959,8 @@ public class WindowDecorActionBar extends ActionBar implements // Clear out the context mode views after the animation finishes mContextView.closeMode(); - mActionView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); + ((View) mDecorToolbar).sendAccessibilityEvent( + AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); mOverlayLayout.setHideOnContentScrollEnabled(mHideOnContentScroll); mActionMode = null; @@ -1178,28 +1200,27 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void setCustomView(View view) { - mActionView.setCustomNavigationView(view); + mDecorToolbar.setCustomView(view); } @Override public void setCustomView(View view, LayoutParams layoutParams) { view.setLayoutParams(layoutParams); - mActionView.setCustomNavigationView(view); + mDecorToolbar.setCustomView(view); } @Override public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { - mActionView.setDropdownAdapter(adapter); - mActionView.setCallback(callback); + mDecorToolbar.setDropdownParams(adapter, new NavItemSelectedListener(callback)); } @Override public int getSelectedNavigationIndex() { - switch (mActionView.getNavigationMode()) { + switch (mDecorToolbar.getNavigationMode()) { case NAVIGATION_MODE_TABS: return mSelectedTab != null ? mSelectedTab.getPosition() : -1; case NAVIGATION_MODE_LIST: - return mActionView.getDropdownSelectedPosition(); + return mDecorToolbar.getDropdownSelectedPosition(); default: return -1; } @@ -1207,12 +1228,11 @@ public class WindowDecorActionBar extends ActionBar implements @Override public int getNavigationItemCount() { - switch (mActionView.getNavigationMode()) { + switch (mDecorToolbar.getNavigationMode()) { case NAVIGATION_MODE_TABS: return mTabs.size(); case NAVIGATION_MODE_LIST: - SpinnerAdapter adapter = mActionView.getDropdownAdapter(); - return adapter != null ? adapter.getCount() : 0; + return mDecorToolbar.getDropdownItemCount(); default: return 0; } @@ -1225,7 +1245,7 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void setNavigationMode(int mode) { - final int oldMode = mActionView.getNavigationMode(); + final int oldMode = mDecorToolbar.getNavigationMode(); switch (oldMode) { case NAVIGATION_MODE_TABS: mSavedTabPosition = getSelectedNavigationIndex(); @@ -1238,7 +1258,7 @@ public class WindowDecorActionBar extends ActionBar implements mOverlayLayout.requestFitSystemWindows(); } } - mActionView.setNavigationMode(mode); + mDecorToolbar.setNavigationMode(mode); switch (mode) { case NAVIGATION_MODE_TABS: ensureTabsExist(); @@ -1249,7 +1269,7 @@ public class WindowDecorActionBar extends ActionBar implements } break; } - mActionView.setCollapsable(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); + mDecorToolbar.setCollapsible(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); mOverlayLayout.setHasNonEmbeddedTabs(mode == NAVIGATION_MODE_TABS && !mHasEmbeddedTabs); } @@ -1261,30 +1281,30 @@ public class WindowDecorActionBar extends ActionBar implements @Override public void setIcon(int resId) { - mActionView.setIcon(resId); + mDecorToolbar.setIcon(resId); } @Override public void setIcon(Drawable icon) { - mActionView.setIcon(icon); + mDecorToolbar.setIcon(icon); } public boolean hasIcon() { - return mActionView.hasIcon(); + return mDecorToolbar.hasIcon(); } @Override public void setLogo(int resId) { - mActionView.setLogo(resId); + mDecorToolbar.setLogo(resId); } @Override public void setLogo(Drawable logo) { - mActionView.setLogo(logo); + mDecorToolbar.setLogo(logo); } public boolean hasLogo() { - return mActionView.hasLogo(); + return mDecorToolbar.hasLogo(); } public void setDefaultDisplayHomeAsUpEnabled(boolean enable) { @@ -1292,4 +1312,24 @@ public class WindowDecorActionBar extends ActionBar implements setDisplayHomeAsUpEnabled(enable); } } + + static class NavItemSelectedListener implements AdapterView.OnItemSelectedListener { + private final OnNavigationListener mListener; + + public NavItemSelectedListener(OnNavigationListener listener) { + mListener = listener; + } + + @Override + public void onItemSelected(AdapterView parent, View view, int position, long id) { + if (mListener != null) { + mListener.onNavigationItemSelected(position, id); + } + } + + @Override + public void onNothingSelected(AdapterView parent) { + // Do nothing + } + } } diff --git a/core/java/com/android/internal/widget/AbsActionBarView.java b/core/java/com/android/internal/widget/AbsActionBarView.java index 183478faca52a..9e7ff938cc00f 100644 --- a/core/java/com/android/internal/widget/AbsActionBarView.java +++ b/core/java/com/android/internal/widget/AbsActionBarView.java @@ -34,7 +34,7 @@ import android.view.animation.DecelerateInterpolator; public abstract class AbsActionBarView extends ViewGroup { protected ActionMenuView mMenuView; protected ActionMenuPresenter mActionMenuPresenter; - protected ActionBarContainer mSplitView; + protected ViewGroup mSplitView; protected boolean mSplitActionBar; protected boolean mSplitWhenNarrow; protected int mContentHeight; @@ -74,7 +74,7 @@ public abstract class AbsActionBarView extends ViewGroup { setContentHeight(a.getLayoutDimension(R.styleable.ActionBar_height, 0)); a.recycle(); if (mSplitWhenNarrow) { - setSplitActionBar(getContext().getResources().getBoolean( + setSplitToolbar(getContext().getResources().getBoolean( com.android.internal.R.bool.split_action_bar_is_narrow)); } if (mActionMenuPresenter != null) { @@ -86,7 +86,7 @@ public abstract class AbsActionBarView extends ViewGroup { * Sets whether the bar should be split right now, no questions asked. * @param split true if the bar should split */ - public void setSplitActionBar(boolean split) { + public void setSplitToolbar(boolean split) { mSplitActionBar = split; } @@ -107,7 +107,7 @@ public abstract class AbsActionBarView extends ViewGroup { return mContentHeight; } - public void setSplitView(ActionBarContainer splitView) { + public void setSplitView(ViewGroup splitView) { mSplitView = splitView; } @@ -214,6 +214,10 @@ public abstract class AbsActionBarView extends ViewGroup { return mActionMenuPresenter != null && mActionMenuPresenter.isOverflowReserved(); } + public boolean canShowOverflowMenu() { + return isOverflowReserved() && getVisibility() == VISIBLE; + } + public void dismissPopupMenus() { if (mActionMenuPresenter != null) { mActionMenuPresenter.dismissPopupMenus(); diff --git a/core/java/com/android/internal/widget/ActionBarContainer.java b/core/java/com/android/internal/widget/ActionBarContainer.java index ed075142b3554..790b6116d1271 100644 --- a/core/java/com/android/internal/widget/ActionBarContainer.java +++ b/core/java/com/android/internal/widget/ActionBarContainer.java @@ -36,7 +36,7 @@ import android.widget.FrameLayout; public class ActionBarContainer extends FrameLayout { private boolean mIsTransitioning; private View mTabContainer; - private ActionBarView mActionBarView; + private View mActionBarView; private Drawable mBackground; private Drawable mStackedBackground; @@ -76,7 +76,7 @@ public class ActionBarContainer extends FrameLayout { @Override public void onFinishInflate() { super.onFinishInflate(); - mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); + mActionBarView = findViewById(com.android.internal.R.id.action_bar); } public void setPrimaryBackground(Drawable bg) { @@ -251,6 +251,10 @@ public class ActionBarContainer extends FrameLayout { return null; } + private boolean isCollapsed(View view) { + return view == null || view.getVisibility() == GONE || view.getMeasuredHeight() == 0; + } + @Override public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { if (mActionBarView == null && @@ -263,7 +267,7 @@ public class ActionBarContainer extends FrameLayout { if (mActionBarView == null) return; final LayoutParams lp = (LayoutParams) mActionBarView.getLayoutParams(); - final int actionBarViewHeight = mActionBarView.isCollapsed() ? 0 : + final int actionBarViewHeight = isCollapsed(mActionBarView) ? 0 : mActionBarView.getMeasuredHeight() + lp.topMargin + lp.bottomMargin; if (mTabContainer != null && mTabContainer.getVisibility() != GONE) { @@ -298,9 +302,8 @@ public class ActionBarContainer extends FrameLayout { } } else { if (mBackground != null) { - final ActionBarView actionBarView = mActionBarView; - mBackground.setBounds(actionBarView.getLeft(), actionBarView.getTop(), - actionBarView.getRight(), actionBarView.getBottom()); + mBackground.setBounds(mActionBarView.getLeft(), mActionBarView.getTop(), + mActionBarView.getRight(), mActionBarView.getBottom()); needsInvalidate = true; } mIsStacked = hasTabs; diff --git a/core/java/com/android/internal/widget/ActionBarContextView.java b/core/java/com/android/internal/widget/ActionBarContextView.java index e10070fb30c0f..6ff77a0487cea 100644 --- a/core/java/com/android/internal/widget/ActionBarContextView.java +++ b/core/java/com/android/internal/widget/ActionBarContextView.java @@ -83,7 +83,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi final TypedArray a = context.obtainStyledAttributes( attrs, R.styleable.ActionMode, defStyleAttr, defStyleRes); - setBackgroundDrawable(a.getDrawable( + setBackground(a.getDrawable( com.android.internal.R.styleable.ActionMode_background)); mTitleStyleRes = a.getResourceId( com.android.internal.R.styleable.ActionMode_titleTextStyle, 0); @@ -109,7 +109,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi } @Override - public void setSplitActionBar(boolean split) { + public void setSplitToolbar(boolean split) { if (mSplitActionBar != split) { if (mActionMenuPresenter != null) { // Mode is already active; move everything over and adjust the menu itself. @@ -137,7 +137,7 @@ public class ActionBarContextView extends AbsActionBarView implements AnimatorLi mSplitView.addView(mMenuView, layoutParams); } } - super.setSplitActionBar(split); + super.setSplitToolbar(split); } } diff --git a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java index 7ab4bedebaa23..8a9cb228da685 100644 --- a/core/java/com/android/internal/widget/ActionBarOverlayLayout.java +++ b/core/java/com/android/internal/widget/ActionBarOverlayLayout.java @@ -39,6 +39,7 @@ import android.view.ViewPropertyAnimator; import android.view.Window; import android.view.WindowInsets; import android.widget.OverScroller; +import android.widget.Toolbar; import com.android.internal.view.menu.MenuPresenter; /** @@ -59,7 +60,7 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar private ActionBarContainer mActionBarTop; // Some interior UI elements. - private ActionBarView mActionBarView; + private DecorToolbar mDecorToolbar; // Content overlay drawable - generally the action bar's shadow private Drawable mWindowContentOverlay; @@ -401,7 +402,7 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar topInset = mActionBarTop.getMeasuredHeight(); } - if (mActionBarView.isSplitActionBar()) { + if (mDecorToolbar.isSplit()) { // If action bar is split, adjust bottom insets for it. if (mActionBarBottom != null) { if (stable) { @@ -563,12 +564,23 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar mContent = findViewById(com.android.internal.R.id.content); mActionBarTop = (ActionBarContainer) findViewById( com.android.internal.R.id.action_bar_container); - mActionBarView = (ActionBarView) findViewById(com.android.internal.R.id.action_bar); + mDecorToolbar = getDecorToolbar(findViewById(com.android.internal.R.id.action_bar)); mActionBarBottom = (ActionBarContainer) findViewById( com.android.internal.R.id.split_action_bar); } } + private DecorToolbar getDecorToolbar(View view) { + if (view instanceof DecorToolbar) { + return (DecorToolbar) view; + } else if (view instanceof Toolbar) { + return ((Toolbar) view).getWrapper(); + } else { + throw new IllegalStateException("Can't make a decor toolbar out of " + + view.getClass().getSimpleName()); + } + } + public void setHideOnContentScrollEnabled(boolean hideOnContentScroll) { if (hideOnContentScroll != mHideOnContentScroll) { mHideOnContentScroll = hideOnContentScroll; @@ -648,9 +660,9 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar final int action = event.getAction(); // Collapse any expanded action views. - if (mActionBarView != null && mActionBarView.hasExpandedActionView()) { + if (mDecorToolbar != null && mDecorToolbar.hasExpandedActionView()) { if (action == KeyEvent.ACTION_UP) { - mActionBarView.collapseActionView(); + mDecorToolbar.collapseActionView(); } return true; } @@ -662,19 +674,19 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar @Override public void setWindowCallback(Window.Callback cb) { pullChildren(); - mActionBarView.setWindowCallback(cb); + mDecorToolbar.setWindowCallback(cb); } @Override public void setWindowTitle(CharSequence title) { pullChildren(); - mActionBarView.setWindowTitle(title); + mDecorToolbar.setWindowTitle(title); } @Override public CharSequence getTitle() { pullChildren(); - return mActionBarView.getTitle(); + return mDecorToolbar.getTitle(); } @Override @@ -682,10 +694,10 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar pullChildren(); switch (windowFeature) { case Window.FEATURE_PROGRESS: - mActionBarView.initProgress(); + mDecorToolbar.initProgress(); break; case Window.FEATURE_INDETERMINATE_PROGRESS: - mActionBarView.initIndeterminateProgress(); + mDecorToolbar.initIndeterminateProgress(); break; case Window.FEATURE_ACTION_BAR_OVERLAY: setOverlayMode(true); @@ -704,15 +716,15 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar } if (splitActionBar) { pullChildren(); - if (mActionBarBottom != null) { - mActionBarView.setSplitView(mActionBarBottom); - mActionBarView.setSplitActionBar(splitActionBar); - mActionBarView.setSplitWhenNarrow(splitWhenNarrow); + if (mActionBarBottom != null && mDecorToolbar.canSplit()) { + mDecorToolbar.setSplitView(mActionBarBottom); + mDecorToolbar.setSplitToolbar(splitActionBar); + mDecorToolbar.setSplitWhenNarrow(splitWhenNarrow); final ActionBarContextView cab = (ActionBarContextView) findViewById( com.android.internal.R.id.action_context_bar); cab.setSplitView(mActionBarBottom); - cab.setSplitActionBar(splitActionBar); + cab.setSplitToolbar(splitActionBar); cab.setSplitWhenNarrow(splitWhenNarrow); } else if (splitActionBar) { Log.e(TAG, "Requested split action bar with " + @@ -724,91 +736,91 @@ public class ActionBarOverlayLayout extends ViewGroup implements DecorContentPar @Override public boolean hasIcon() { pullChildren(); - return mActionBarView.hasIcon(); + return mDecorToolbar.hasIcon(); } @Override public boolean hasLogo() { pullChildren(); - return mActionBarView.hasLogo(); + return mDecorToolbar.hasLogo(); } @Override public void setIcon(int resId) { pullChildren(); - mActionBarView.setIcon(resId); + mDecorToolbar.setIcon(resId); } @Override public void setIcon(Drawable d) { pullChildren(); - mActionBarView.setIcon(d); + mDecorToolbar.setIcon(d); } @Override public void setLogo(int resId) { pullChildren(); - mActionBarView.setLogo(resId); + mDecorToolbar.setLogo(resId); } @Override public boolean canShowOverflowMenu() { pullChildren(); - return mActionBarView.isOverflowReserved() && mActionBarView.getVisibility() == VISIBLE; + return mDecorToolbar.canShowOverflowMenu(); } @Override public boolean isOverflowMenuShowing() { pullChildren(); - return mActionBarView.isOverflowMenuShowing(); + return mDecorToolbar.isOverflowMenuShowing(); } @Override public boolean isOverflowMenuShowPending() { pullChildren(); - return mActionBarView.isOverflowMenuShowPending(); + return mDecorToolbar.isOverflowMenuShowPending(); } @Override public boolean showOverflowMenu() { pullChildren(); - return mActionBarView.showOverflowMenu(); + return mDecorToolbar.showOverflowMenu(); } @Override public boolean hideOverflowMenu() { pullChildren(); - return mActionBarView.hideOverflowMenu(); + return mDecorToolbar.hideOverflowMenu(); } @Override public void setMenuPrepared() { pullChildren(); - mActionBarView.setMenuPrepared(); + mDecorToolbar.setMenuPrepared(); } @Override public void setMenu(Menu menu, MenuPresenter.Callback cb) { pullChildren(); - mActionBarView.setMenu(menu, cb); + mDecorToolbar.setMenu(menu, cb); } @Override public void saveToolbarHierarchyState(SparseArray toolbarStates) { pullChildren(); - mActionBarView.saveHierarchyState(toolbarStates); + mDecorToolbar.saveHierarchyState(toolbarStates); } @Override public void restoreToolbarHierarchyState(SparseArray toolbarStates) { pullChildren(); - mActionBarView.restoreHierarchyState(toolbarStates); + mDecorToolbar.restoreHierarchyState(toolbarStates); } @Override public void dismissPopups() { pullChildren(); - mActionBarView.dismissPopupMenus(); + mDecorToolbar.dismissPopupMenus(); } public static class LayoutParams extends MarginLayoutParams { diff --git a/core/java/com/android/internal/widget/ActionBarView.java b/core/java/com/android/internal/widget/ActionBarView.java index 60631b9949847..af827782f6995 100644 --- a/core/java/com/android/internal/widget/ActionBarView.java +++ b/core/java/com/android/internal/widget/ActionBarView.java @@ -18,7 +18,6 @@ package com.android.internal.widget; import android.animation.LayoutTransition; import android.app.ActionBar; -import android.app.ActionBar.OnNavigationListener; import android.content.Context; import android.content.pm.ApplicationInfo; import android.content.pm.PackageManager; @@ -63,7 +62,7 @@ import com.android.internal.view.menu.SubMenuBuilder; /** * @hide */ -public class ActionBarView extends AbsActionBarView { +public class ActionBarView extends AbsActionBarView implements DecorToolbar { private static final String TAG = "ActionBarView"; /** @@ -117,8 +116,7 @@ public class ActionBarView extends AbsActionBarView { private boolean mUserTitle; private boolean mIncludeTabs; - private boolean mIsCollapsable; - private boolean mIsCollapsed; + private boolean mIsCollapsible; private boolean mWasHomeEnabled; // Was it enabled before action view expansion? private MenuBuilder mOptionsMenu; @@ -129,7 +127,7 @@ public class ActionBarView extends AbsActionBarView { private ActionMenuItem mLogoNavItem; private SpinnerAdapter mSpinnerAdapter; - private OnNavigationListener mCallback; + private AdapterView.OnItemSelectedListener mNavItemSelectedListener; private Runnable mTabSelector; @@ -138,18 +136,6 @@ public class ActionBarView extends AbsActionBarView { Window.Callback mWindowCallback; - private final AdapterView.OnItemSelectedListener mNavItemSelectedListener = - new AdapterView.OnItemSelectedListener() { - public void onItemSelected(AdapterView parent, View view, int position, long id) { - if (mCallback != null) { - mCallback.onNavigationItemSelected(position, id); - } - } - public void onNothingSelected(AdapterView parent) { - // Do nothing - } - }; - private final OnClickListener mExpandedActionViewUpListener = new OnClickListener() { @Override public void onClick(View v) { @@ -178,8 +164,6 @@ public class ActionBarView extends AbsActionBarView { TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.ActionBar, com.android.internal.R.attr.actionBarStyle, 0); - ApplicationInfo appInfo = context.getApplicationInfo(); - PackageManager pm = context.getPackageManager(); mNavigationMode = a.getInt(R.styleable.ActionBar_navigationMode, ActionBar.NAVIGATION_MODE_STANDARD); mTitle = a.getText(R.styleable.ActionBar_title); @@ -260,7 +244,7 @@ public class ActionBarView extends AbsActionBarView { } if (mHomeDescriptionRes != 0) { - setHomeActionContentDescription(mHomeDescriptionRes); + setNavigationContentDescription(mHomeDescriptionRes); } if (mTabScrollView != null && mIncludeTabs) { @@ -313,7 +297,7 @@ public class ActionBarView extends AbsActionBarView { } @Override - public void setSplitActionBar(boolean splitActionBar) { + public void setSplitToolbar(boolean splitActionBar) { if (mSplitActionBar != splitActionBar) { if (mMenuView != null) { final ViewGroup oldParent = (ViewGroup) mMenuView.getParent(); @@ -349,18 +333,26 @@ public class ActionBarView extends AbsActionBarView { mActionMenuPresenter.setItemLimit(Integer.MAX_VALUE); } } - super.setSplitActionBar(splitActionBar); + super.setSplitToolbar(splitActionBar); } } - public boolean isSplitActionBar() { + public boolean isSplit() { return mSplitActionBar; } + public boolean canSplit() { + return true; + } + public boolean hasEmbeddedTabs() { return mIncludeTabs; } + public void setEmbeddedTabView(View view) { + setEmbeddedTabView((ScrollingTabContainerView) view); + } + public void setEmbeddedTabView(ScrollingTabContainerView tabs) { if (mTabScrollView != null) { removeView(mTabScrollView); @@ -376,10 +368,6 @@ public class ActionBarView extends AbsActionBarView { } } - public void setCallback(OnNavigationListener callback) { - mCallback = callback; - } - public void setMenuPrepared() { mMenuPrepared = true; } @@ -473,7 +461,7 @@ public class ActionBarView extends AbsActionBarView { } } - public void setCustomNavigationView(View view) { + public void setCustomView(View view) { final boolean showCustom = (mDisplayOptions & ActionBar.DISPLAY_SHOW_CUSTOM) != 0; if (showCustom) { ActionBarTransition.beginDelayedTransition(this); @@ -765,15 +753,16 @@ public class ActionBarView extends AbsActionBarView { } } - public void setDropdownAdapter(SpinnerAdapter adapter) { + public void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener l) { mSpinnerAdapter = adapter; + mNavItemSelectedListener = l; if (mSpinner != null) { mSpinner.setAdapter(adapter); } } - public SpinnerAdapter getDropdownAdapter() { - return mSpinnerAdapter; + public int getDropdownItemCount() { + return mSpinnerAdapter != null ? mSpinnerAdapter.getCount() : 0; } public void setDropdownSelectedPosition(int position) { @@ -784,7 +773,7 @@ public class ActionBarView extends AbsActionBarView { return mSpinner.getSelectedItemPosition(); } - public View getCustomNavigationView() { + public View getCustomView() { return mCustomNavView; } @@ -796,6 +785,11 @@ public class ActionBarView extends AbsActionBarView { return mDisplayOptions; } + @Override + public ViewGroup getViewGroup() { + return this; + } + @Override protected ViewGroup.LayoutParams generateDefaultLayoutParams() { // Used by custom nav views if they don't supply layout params. Everything else @@ -860,12 +854,8 @@ public class ActionBarView extends AbsActionBarView { mContextView = view; } - public void setCollapsable(boolean collapsable) { - mIsCollapsable = collapsable; - } - - public boolean isCollapsed() { - return mIsCollapsed; + public void setCollapsible(boolean collapsible) { + mIsCollapsible = collapsible; } /** @@ -893,7 +883,7 @@ public class ActionBarView extends AbsActionBarView { @Override protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { final int childCount = getChildCount(); - if (mIsCollapsable) { + if (mIsCollapsible) { int visibleChildren = 0; for (int i = 0; i < childCount; i++) { final View child = getChildAt(i); @@ -915,11 +905,9 @@ public class ActionBarView extends AbsActionBarView { if (visibleChildren == 0) { // No size for an empty action bar when collapsable. setMeasuredDimension(0, 0); - mIsCollapsed = true; return; } } - mIsCollapsed = false; int widthMode = MeasureSpec.getMode(widthMeasureSpec); if (widthMode != MeasureSpec.EXACTLY) { @@ -1323,20 +1311,20 @@ public class ActionBarView extends AbsActionBarView { } } - public void setHomeAsUpIndicator(Drawable indicator) { + public void setNavigationIcon(Drawable indicator) { mHomeLayout.setUpIndicator(indicator); } - public void setHomeAsUpIndicator(int resId) { + public void setNavigationIcon(int resId) { mHomeLayout.setUpIndicator(resId); } - public void setHomeActionContentDescription(CharSequence description) { + public void setNavigationContentDescription(CharSequence description) { mHomeDescription = description; updateHomeAccessibility(mUpGoerFive.isEnabled()); } - public void setHomeActionContentDescription(int resId) { + public void setNavigationContentDescription(int resId) { mHomeDescriptionRes = resId; mHomeDescription = resId != 0 ? getResources().getText(resId) : null; updateHomeAccessibility(mUpGoerFive.isEnabled()); diff --git a/core/java/com/android/internal/widget/DecorToolbar.java b/core/java/com/android/internal/widget/DecorToolbar.java new file mode 100644 index 0000000000000..ee6988e57d84a --- /dev/null +++ b/core/java/com/android/internal/widget/DecorToolbar.java @@ -0,0 +1,94 @@ +/* + * 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.content.Context; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.util.SparseArray; +import android.view.Menu; +import android.view.View; +import android.view.ViewGroup; +import android.view.Window; +import android.widget.AdapterView; +import android.widget.SpinnerAdapter; +import com.android.internal.view.menu.MenuPresenter; + +/** + * Common interface for a toolbar that sits as part of the window decor. + * Layouts that control window decor use this as a point of interaction with different + * bar implementations. + * + * @hide + */ +public interface DecorToolbar { + ViewGroup getViewGroup(); + Context getContext(); + boolean isSplit(); + boolean hasExpandedActionView(); + void collapseActionView(); + void setWindowCallback(Window.Callback cb); + void setWindowTitle(CharSequence title); + CharSequence getTitle(); + void setTitle(CharSequence title); + CharSequence getSubtitle(); + void setSubtitle(CharSequence subtitle); + void initProgress(); + void initIndeterminateProgress(); + boolean canSplit(); + void setSplitView(ViewGroup splitView); + void setSplitToolbar(boolean split); + void setSplitWhenNarrow(boolean splitWhenNarrow); + boolean hasIcon(); + boolean hasLogo(); + void setIcon(int resId); + void setIcon(Drawable d); + void setLogo(int resId); + void setLogo(Drawable d); + boolean canShowOverflowMenu(); + boolean isOverflowMenuShowing(); + boolean isOverflowMenuShowPending(); + boolean showOverflowMenu(); + boolean hideOverflowMenu(); + void setMenuPrepared(); + void setMenu(Menu menu, MenuPresenter.Callback cb); + void dismissPopupMenus(); + + int getDisplayOptions(); + void setDisplayOptions(int opts); + void setEmbeddedTabView(View tabView); + boolean hasEmbeddedTabs(); + boolean isTitleTruncated(); + void setCollapsible(boolean collapsible); + void setHomeButtonEnabled(boolean enable); + int getNavigationMode(); + void setNavigationMode(int mode); + void setDropdownParams(SpinnerAdapter adapter, AdapterView.OnItemSelectedListener listener); + void setDropdownSelectedPosition(int position); + int getDropdownSelectedPosition(); + int getDropdownItemCount(); + void setCustomView(View view); + View getCustomView(); + void animateToVisibility(int visibility); + void setNavigationIcon(Drawable icon); + void setNavigationIcon(int resId); + void setNavigationContentDescription(CharSequence description); + void setNavigationContentDescription(int resId); + void saveHierarchyState(SparseArray toolbarStates); + void restoreHierarchyState(SparseArray toolbarStates); +} diff --git a/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java new file mode 100644 index 0000000000000..f90aaea387326 --- /dev/null +++ b/core/java/com/android/internal/widget/ToolbarWidgetWrapper.java @@ -0,0 +1,526 @@ +/* + * 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.app.ActionBar; +import android.content.Context; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Parcelable; +import android.util.Log; +import android.util.SparseArray; +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 + * {@link com.android.internal.app.WindowDecorActionBar WindowDecorActionBar} can behave + * in the same way.

+ * + * @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; + + private Toolbar mToolbar; + + private int mDisplayOpts; + private View mTabView; + private Spinner mSpinner; + private View mCustomView; + + private Drawable mIcon; + private Drawable mLogo; + private Drawable mNavIcon; + + private boolean mTitleSet; + private CharSequence mTitle; + private CharSequence mSubtitle; + + private Window.Callback mWindowCallback; + private boolean mMenuPrepared; + private ActionMenuPresenter mActionMenuPresenter; + + public ToolbarWidgetWrapper(Toolbar toolbar) { + mToolbar = toolbar; + + final TypedArray a = toolbar.getContext().obtainStyledAttributes(null, + R.styleable.ActionBar, R.attr.actionBarStyle, 0); + + final CharSequence title = a.getText(R.styleable.ActionBar_title); + if (title != null) { + setTitle(title); + } + + final CharSequence subtitle = a.getText(R.styleable.ActionBar_subtitle); + if (subtitle != null) { + 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); + } + + final Drawable navIcon = a.getDrawable(R.styleable.ActionBar_homeAsUpIndicator); + if (navIcon != null) { + setNavigationIcon(navIcon); + } + + 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, 0); + final int contentInsetEnd = a.getDimensionPixelOffset( + R.styleable.ActionBar_contentInsetEnd, 0); + if (contentInsetStart > 0 || contentInsetEnd > 0) { + mToolbar.setContentInsetsRelative(contentInsetStart, contentInsetEnd); + } + + a.recycle(); + + 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 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) { + mToolbar.setNavigationIcon(mNavIcon); + } else { + mToolbar.setNavigationIcon(null); + } + } + + 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(View tabView) { + mTabView = tabView; + } + + @Override + public boolean hasEmbeddedTabs() { + return mTabView != null; + } + + @Override + public boolean isTitleTruncated() { + return mToolbar.isTitleTruncated(); + } + + @Override + public void setCollapsible(boolean collapsible) { + // Ignore + } + + @Override + public void setHomeButtonEnabled(boolean enable) { + // Ignore + } + + @Override + public int getNavigationMode() { + return 0; + } + + @Override + public void setNavigationMode(int mode) { + if (mode != ActionBar.NAVIGATION_MODE_STANDARD) { + throw new IllegalArgumentException( + "Navigation modes not supported in this configuration"); + } + } + + @Override + public void setDropdownParams(SpinnerAdapter adapter, + AdapterView.OnItemSelectedListener listener) { + if (mSpinner == null) { + mSpinner = new Spinner(getContext()); + } + 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) { + if (visibility == View.GONE) { + mToolbar.animate().translationY(mToolbar.getHeight()).alpha(0) + .setListener(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; + } + }); + } else if (visibility == View.VISIBLE) { + mToolbar.animate().translationY(0).alpha(1) + .setListener(new AnimatorListenerAdapter() { + @Override + public void onAnimationStart(Animator animation) { + mToolbar.setVisibility(View.VISIBLE); + } + }); + } + } + + @Override + public void setNavigationIcon(Drawable icon) { + mNavIcon = icon; + if ((mDisplayOpts & ActionBar.DISPLAY_HOME_AS_UP) != 0) { + mToolbar.setNavigationIcon(icon); + } + } + + @Override + public void setNavigationIcon(int resId) { + setNavigationIcon(mToolbar.getContext().getDrawable(resId)); + } + + @Override + public void setNavigationContentDescription(CharSequence description) { + mToolbar.setNavigationContentDescription(description); + } + + @Override + public void setNavigationContentDescription(int resId) { + mToolbar.setNavigationContentDescription(resId); + } + + @Override + public void saveHierarchyState(SparseArray toolbarStates) { + mToolbar.saveHierarchyState(toolbarStates); + } + + @Override + public void restoreHierarchyState(SparseArray toolbarStates) { + mToolbar.restoreHierarchyState(toolbarStates); + } + +} diff --git a/core/res/res/layout/screen_toolbar.xml b/core/res/res/layout/screen_toolbar.xml new file mode 100644 index 0000000000000..290c7daaf1b3e --- /dev/null +++ b/core/res/res/layout/screen_toolbar.xml @@ -0,0 +1,51 @@ + + + + + + + + + + + + diff --git a/core/res/res/values/attrs.xml b/core/res/res/values/attrs.xml index c05dfcab5b869..5fec9074a8fb5 100644 --- a/core/res/res/values/attrs.xml +++ b/core/res/res/values/attrs.xml @@ -493,6 +493,9 @@ {@link android.view.Window#setAllowExitTransitionOverlap(boolean)}. --> + + + @@ -685,6 +688,7 @@ + @@ -1713,6 +1717,7 @@ + + + + + + + @@ -6653,10 +6664,19 @@ - - - - + + + + + + + + + + + + + diff --git a/core/res/res/values/styles.xml b/core/res/res/values/styles.xml index f6cd9e8212fa7..fc6110dc4af86 100644 --- a/core/res/res/values/styles.xml +++ b/core/res/res/values/styles.xml @@ -1197,6 +1197,16 @@ please see styles_device_defaults.xml. @android:style/TextAppearance.Widget.Toolbar.Subtitle ?android:attr/actionBarSize 4dp + 56dp + top + @android:style/Widget.Toolbar.Button.Navigation + ?android:attr/homeAsUpIndicator + + + diff --git a/core/res/res/values/styles_quantum.xml b/core/res/res/values/styles_quantum.xml index 2e7a5b1797582..ea3268136160b 100644 --- a/core/res/res/values/styles_quantum.xml +++ b/core/res/res/values/styles_quantum.xml @@ -773,7 +773,7 @@ please see styles_device_defaults.xml. @null @null @null - useLogo|showHome|showTitle + showTitle ?attr/dividerVertical @style/TextAppearance.Quantum.Widget.ActionBar.Title @style/TextAppearance.Quantum.Widget.ActionBar.Subtitle @@ -783,6 +783,7 @@ please see styles_device_defaults.xml. 8dip @layout/action_bar_home_quantum center_vertical + 56dp