blob: 9811af4ec1c36e4f397722e9eeaff92647e23c07 [file] [log] [blame]
/*
* 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;
}
}
}