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