| /* |
| * 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.layoutlib.bridge.bars; |
| |
| import com.android.ide.common.rendering.api.ActionBarCallback; |
| import com.android.ide.common.rendering.api.RenderResources; |
| import com.android.ide.common.rendering.api.ResourceReference; |
| import com.android.ide.common.rendering.api.ResourceValue; |
| import com.android.internal.R; |
| import com.android.internal.app.ToolbarActionBar; |
| import com.android.internal.app.WindowDecorActionBar; |
| import com.android.internal.view.menu.MenuBuilder; |
| import com.android.internal.widget.ActionBarAccessor; |
| import com.android.internal.widget.ActionBarView; |
| import com.android.internal.widget.DecorToolbar; |
| import com.android.layoutlib.bridge.android.BridgeContext; |
| import com.android.layoutlib.bridge.impl.ResourceHelper; |
| |
| import android.annotation.NonNull; |
| import android.annotation.Nullable; |
| import android.app.ActionBar; |
| import android.app.ActionBar.Tab; |
| import android.app.ActionBar.TabListener; |
| import android.app.FragmentTransaction; |
| import android.content.Context; |
| import android.content.res.Resources; |
| import android.graphics.drawable.Drawable; |
| import android.view.MenuInflater; |
| import android.view.View; |
| import android.view.ViewGroup; |
| import android.view.WindowCallback; |
| import android.widget.ActionMenuPresenter; |
| import android.widget.ActionMenuView; |
| import android.widget.Toolbar; |
| import android.widget.Toolbar_Accessor; |
| |
| /** |
| * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}. |
| */ |
| public abstract class FrameworkActionBarWrapper { |
| |
| @NonNull protected ActionBar mActionBar; |
| @NonNull protected ActionBarCallback mCallback; |
| @NonNull protected BridgeContext mContext; |
| |
| /** |
| * Returns a wrapper around different implementations of the Action Bar to provide a common API. |
| * |
| * @param decorContent the top level view returned by inflating |
| * ?attr/windowActionBarFullscreenDecorLayout |
| */ |
| @NonNull |
| public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context, |
| @NonNull ActionBarCallback callback, @NonNull View decorContent) { |
| View view = decorContent.findViewById(R.id.action_bar); |
| if (view instanceof Toolbar) { |
| return new ToolbarWrapper(context, callback, (Toolbar) view); |
| } else if (view instanceof ActionBarView) { |
| return new WindowActionBarWrapper(context, callback, decorContent, |
| (ActionBarView) view); |
| } else { |
| throw new IllegalStateException("Can't make an action bar out of " + |
| view.getClass().getSimpleName()); |
| } |
| } |
| |
| FrameworkActionBarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback, |
| @NonNull ActionBar actionBar) { |
| mActionBar = actionBar; |
| mCallback = callback; |
| mContext = context; |
| } |
| |
| /** A call to setup any custom properties. */ |
| protected void setupActionBar() { |
| // Nothing to do here. |
| } |
| |
| public void setTitle(CharSequence title) { |
| mActionBar.setTitle(title); |
| } |
| |
| public void setSubTitle(CharSequence subTitle) { |
| if (subTitle != null) { |
| mActionBar.setSubtitle(subTitle); |
| } |
| } |
| |
| public void setHomeAsUp(boolean homeAsUp) { |
| mActionBar.setDisplayHomeAsUpEnabled(homeAsUp); |
| } |
| |
| public void setIcon(ResourceValue icon) { |
| // Nothing to do. |
| } |
| |
| protected boolean isSplit() { |
| return getDecorToolbar().isSplit(); |
| } |
| |
| protected boolean isOverflowPopupNeeded() { |
| return mCallback.isOverflowPopupNeeded(); |
| } |
| |
| /** |
| * Gets the menus to add to the action bar from the callback, resolves them, inflates them and |
| * adds them to the action bar. |
| */ |
| protected void inflateMenus() { |
| MenuInflater inflater = new MenuInflater(getActionMenuContext()); |
| MenuBuilder menuBuilder = getMenuBuilder(); |
| for (ResourceReference menuId : mCallback.getMenuIds()) { |
| int id = mContext.getResourceId(menuId, -1); |
| if (id >= 0) { |
| inflater.inflate(id, menuBuilder); |
| } |
| } |
| } |
| |
| /** |
| * The context used for the ActionBar and the menus in the ActionBarView. |
| */ |
| @NonNull |
| protected Context getActionMenuContext() { |
| return mActionBar.getThemedContext(); |
| } |
| |
| /** |
| * The context used to inflate the popup menu. |
| */ |
| @NonNull |
| abstract Context getPopupContext(); |
| |
| /** |
| * The Menu in which to inflate the user's menus. |
| */ |
| @NonNull |
| abstract MenuBuilder getMenuBuilder(); |
| |
| @Nullable |
| abstract ActionMenuPresenter getActionMenuPresenter(); |
| |
| /** |
| * Framework's wrapper over two ActionBar implementations. |
| */ |
| @NonNull |
| abstract DecorToolbar getDecorToolbar(); |
| |
| abstract int getMenuPopupElevation(); |
| |
| /** |
| * Margin between the menu popup and the action bar. |
| */ |
| abstract int getMenuPopupMargin(); |
| |
| // ---- The implementations ---- |
| |
| /** |
| * Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to |
| * Toolbar using a common API. |
| */ |
| private static class ToolbarWrapper extends FrameworkActionBarWrapper { |
| |
| @NonNull |
| private final Toolbar mToolbar; // This is the view. |
| |
| ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback, |
| @NonNull Toolbar toolbar) { |
| super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback())); |
| mToolbar = toolbar; |
| } |
| |
| @Override |
| protected void inflateMenus() { |
| super.inflateMenus(); |
| // Inflating the menus isn't enough. ActionMenuPresenter needs to be initialized too. |
| MenuBuilder menu = getMenuBuilder(); |
| DecorToolbar decorToolbar = getDecorToolbar(); |
| // Setting a menu different from the above initializes the presenter. |
| decorToolbar.setMenu(new MenuBuilder(getActionMenuContext()), null); |
| // ActionMenuView needs to be recreated to be able to set the menu back. |
| ActionMenuPresenter presenter = getActionMenuPresenter(); |
| if (presenter != null) { |
| presenter.setMenuView(new ActionMenuView(getPopupContext())); |
| } |
| decorToolbar.setMenu(menu, null); |
| } |
| |
| @NonNull |
| @Override |
| Context getPopupContext() { |
| return Toolbar_Accessor.getPopupContext(mToolbar); |
| } |
| |
| @NonNull |
| @Override |
| MenuBuilder getMenuBuilder() { |
| return (MenuBuilder) mToolbar.getMenu(); |
| } |
| |
| @Nullable |
| @Override |
| ActionMenuPresenter getActionMenuPresenter() { |
| return Toolbar_Accessor.getActionMenuPresenter(mToolbar); |
| } |
| |
| @NonNull |
| @Override |
| DecorToolbar getDecorToolbar() { |
| return mToolbar.getWrapper(); |
| } |
| |
| @Override |
| int getMenuPopupElevation() { |
| return 10; |
| } |
| |
| @Override |
| int getMenuPopupMargin() { |
| return 0; |
| } |
| } |
| |
| /** |
| * Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides |
| * access to it using a common API. |
| */ |
| private static class WindowActionBarWrapper extends FrameworkActionBarWrapper { |
| |
| @NonNull private final WindowDecorActionBar mActionBar; |
| @NonNull private final ActionBarView mActionBarView; |
| @NonNull private final View mDecorContentRoot; |
| private MenuBuilder mMenuBuilder; |
| |
| public WindowActionBarWrapper(@NonNull BridgeContext context, |
| @NonNull ActionBarCallback callback, @NonNull View decorContentRoot, |
| @NonNull ActionBarView actionBarView) { |
| super(context, callback, new WindowDecorActionBar(decorContentRoot)); |
| mActionBarView = actionBarView; |
| mActionBar = (WindowDecorActionBar) super.mActionBar; |
| mDecorContentRoot = decorContentRoot; |
| } |
| |
| @Override |
| protected void setupActionBar() { |
| |
| // Set the navigation mode. |
| int navMode = mCallback.getNavigationMode(); |
| mActionBar.setNavigationMode(navMode); |
| //noinspection deprecation |
| if (navMode == ActionBar.NAVIGATION_MODE_TABS) { |
| setupTabs(3); |
| } |
| |
| // Set action bar to be split, if needed. |
| ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar); |
| if (splitView != null) { |
| mActionBarView.setSplitView(splitView); |
| Resources res = mContext.getResources(); |
| boolean split = res.getBoolean(R.bool.split_action_bar_is_narrow) |
| && mCallback.getSplitActionBarWhenNarrow(); |
| mActionBarView.setSplitToolbar(split); |
| } |
| } |
| |
| @Override |
| public void setIcon(ResourceValue icon) { |
| // Set the icon only if the action bar doesn't specify an icon. |
| if (!mActionBar.hasIcon() && icon != null) { |
| Drawable iconDrawable = getDrawable(icon); |
| if (iconDrawable != null) { |
| mActionBar.setIcon(iconDrawable); |
| } |
| } |
| } |
| |
| @Override |
| protected void inflateMenus() { |
| super.inflateMenus(); |
| // The super implementation doesn't set the menu on the view. Set it here. |
| mActionBarView.setMenu(getMenuBuilder(), null); |
| } |
| |
| @NonNull |
| @Override |
| Context getPopupContext() { |
| return getActionMenuContext(); |
| } |
| |
| @NonNull |
| @Override |
| MenuBuilder getMenuBuilder() { |
| if (mMenuBuilder == null) { |
| mMenuBuilder = new MenuBuilder(getActionMenuContext()); |
| } |
| return mMenuBuilder; |
| } |
| |
| @Nullable |
| @Override |
| ActionMenuPresenter getActionMenuPresenter() { |
| return ActionBarAccessor.getActionMenuPresenter(mActionBarView); |
| } |
| |
| @NonNull |
| @Override |
| ActionBarView getDecorToolbar() { |
| return mActionBarView; |
| } |
| |
| @Override |
| int getMenuPopupElevation() { |
| return 0; |
| } |
| |
| @Override |
| int getMenuPopupMargin() { |
| return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics()); |
| } |
| |
| // TODO: Use an adapter, like List View to set up tabs. |
| @SuppressWarnings("deprecation") // For Tab |
| private void setupTabs(int num) { |
| for (int i = 1; i <= num; i++) { |
| Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() { |
| @Override |
| public void onTabUnselected(Tab t, FragmentTransaction ft) { |
| // pass |
| } |
| @Override |
| public void onTabSelected(Tab t, FragmentTransaction ft) { |
| // pass |
| } |
| @Override |
| public void onTabReselected(Tab t, FragmentTransaction ft) { |
| // pass |
| } |
| }); |
| mActionBar.addTab(tab); |
| } |
| } |
| |
| @Nullable |
| private Drawable getDrawable(@NonNull ResourceValue value) { |
| RenderResources res = mContext.getRenderResources(); |
| value = res.resolveResValue(value); |
| if (value != null) { |
| return ResourceHelper.getDrawable(value, mContext); |
| } |
| return null; |
| } |
| } |
| } |