/*
* 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 mLogo;
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();
}
}