automerge: 580937f
* commit '580937fc45bc5ef97e45f1eaf67945a07c5ce41f':
Limited AppCompat support
This commit is contained in:
@ -0,0 +1,166 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.annotations.NonNull;
|
||||||
|
import com.android.annotations.Nullable;
|
||||||
|
import com.android.ide.common.rendering.api.RenderResources;
|
||||||
|
import com.android.ide.common.rendering.api.ResourceValue;
|
||||||
|
import com.android.ide.common.rendering.api.SessionParams;
|
||||||
|
import com.android.layoutlib.bridge.android.BridgeContext;
|
||||||
|
import com.android.layoutlib.bridge.impl.ResourceHelper;
|
||||||
|
import com.android.resources.ResourceType;
|
||||||
|
|
||||||
|
import android.graphics.drawable.Drawable;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
|
||||||
|
import java.lang.reflect.InvocationTargetException;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Assumes that the AppCompat library is present in the project's classpath and creates an
|
||||||
|
* actionbar around it.
|
||||||
|
*/
|
||||||
|
public class AppCompatActionBar extends BridgeActionBar {
|
||||||
|
|
||||||
|
private Object mWindowDecorActionBar;
|
||||||
|
private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
|
||||||
|
private Class<?> mWindowActionBarClass;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Inflate the action bar and attach it to {@code parentView}
|
||||||
|
*/
|
||||||
|
public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
|
||||||
|
@NonNull ViewGroup parentView) {
|
||||||
|
super(context, params, parentView);
|
||||||
|
int contentRootId = context.getProjectResourceValue(ResourceType.ID,
|
||||||
|
"action_bar_activity_content", 0);
|
||||||
|
View contentView = getDecorContent().findViewById(contentRootId);
|
||||||
|
if (contentView != null) {
|
||||||
|
assert contentView instanceof FrameLayout;
|
||||||
|
setContentRoot(((FrameLayout) contentView));
|
||||||
|
} else {
|
||||||
|
// Something went wrong. Create a new FrameLayout in the enclosing layout.
|
||||||
|
FrameLayout contentRoot = new FrameLayout(context);
|
||||||
|
setMatchParent(contentRoot);
|
||||||
|
mEnclosingLayout.addView(contentRoot);
|
||||||
|
setContentRoot(contentRoot);
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
Class[] constructorParams = {View.class};
|
||||||
|
Object[] constructorArgs = {getDecorContent()};
|
||||||
|
mWindowDecorActionBar = params.getProjectCallback().loadView(WINDOW_ACTION_BAR_CLASS,
|
||||||
|
constructorParams, constructorArgs);
|
||||||
|
|
||||||
|
mWindowActionBarClass = mWindowDecorActionBar == null ? null :
|
||||||
|
mWindowDecorActionBar.getClass();
|
||||||
|
setupActionBar();
|
||||||
|
} catch (Exception e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected ResourceValue getLayoutResource(BridgeContext context) {
|
||||||
|
// We always assume that the app has requested the action bar.
|
||||||
|
return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
|
||||||
|
"abc_screen_toolbar");
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setTitle(CharSequence title) {
|
||||||
|
if (title != null && mWindowDecorActionBar != null) {
|
||||||
|
Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class);
|
||||||
|
invoke(setTitle, mWindowDecorActionBar, title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setSubtitle(CharSequence subtitle) {
|
||||||
|
if (subtitle != null && mWindowDecorActionBar != null) {
|
||||||
|
Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class);
|
||||||
|
invoke(setSubtitle, mWindowDecorActionBar, subtitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setIcon(String icon) {
|
||||||
|
// Do this only if the action bar doesn't already have an icon.
|
||||||
|
if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
|
||||||
|
if (((Boolean) invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
|
||||||
|
)) {
|
||||||
|
Drawable iconDrawable = getDrawable(icon, false);
|
||||||
|
if (iconDrawable != null) {
|
||||||
|
Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
|
||||||
|
invoke(setIcon, mWindowDecorActionBar, iconDrawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setHomeAsUp(boolean homeAsUp) {
|
||||||
|
if (mWindowDecorActionBar != null) {
|
||||||
|
Method setHomeAsUp = getMethod(mWindowActionBarClass,
|
||||||
|
"setDefaultDisplayHomeAsUpEnabled", boolean.class);
|
||||||
|
invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void createMenuPopup() {
|
||||||
|
// it's hard to addd menus to appcompat's actionbar, since it'll use a lot of reflection.
|
||||||
|
// so we skip it for now.
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) {
|
||||||
|
try {
|
||||||
|
return owner == null ? null : owner.getMethod(name, parameterTypes);
|
||||||
|
} catch (NoSuchMethodException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Nullable
|
||||||
|
private static Object invoke(Method method, Object owner, Object... args) {
|
||||||
|
try {
|
||||||
|
return method == null ? null : method.invoke(owner, args);
|
||||||
|
} catch (InvocationTargetException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
} catch (IllegalAccessException e) {
|
||||||
|
e.printStackTrace();
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
|
||||||
|
@Nullable
|
||||||
|
private Drawable getDrawable(@NonNull String name, boolean isFramework) {
|
||||||
|
RenderResources res = mBridgeContext.getRenderResources();
|
||||||
|
ResourceValue value = res.findResValue(name, isFramework);
|
||||||
|
value = res.resolveResValue(value);
|
||||||
|
if (value != null) {
|
||||||
|
return ResourceHelper.getDrawable(value, mBridgeContext);
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
@ -0,0 +1,159 @@
|
|||||||
|
/*
|
||||||
|
* Copyright (C) 2015 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.annotations.NonNull;
|
||||||
|
import com.android.ide.common.rendering.api.ActionBarCallback;
|
||||||
|
import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
|
||||||
|
import com.android.ide.common.rendering.api.RenderResources;
|
||||||
|
import com.android.ide.common.rendering.api.ResourceValue;
|
||||||
|
import com.android.ide.common.rendering.api.SessionParams;
|
||||||
|
import com.android.layoutlib.bridge.android.BridgeContext;
|
||||||
|
|
||||||
|
import android.view.LayoutInflater;
|
||||||
|
import android.view.View;
|
||||||
|
import android.view.ViewGroup;
|
||||||
|
import android.view.ViewGroup.LayoutParams;
|
||||||
|
import android.widget.FrameLayout;
|
||||||
|
import android.widget.RelativeLayout;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* An abstraction over two implementations of the ActionBar - framework and appcompat.
|
||||||
|
*/
|
||||||
|
public abstract class BridgeActionBar {
|
||||||
|
// Store a reference to the context so that we don't have to cast it repeatedly.
|
||||||
|
@NonNull protected final BridgeContext mBridgeContext;
|
||||||
|
@NonNull protected final SessionParams mParams;
|
||||||
|
// A Layout that contains the inflated action bar. The menu popup is added to this layout.
|
||||||
|
@NonNull protected final ViewGroup mEnclosingLayout;
|
||||||
|
|
||||||
|
private final View mDecorContent;
|
||||||
|
private final ActionBarCallback mCallback;
|
||||||
|
|
||||||
|
@NonNull private FrameLayout mContentRoot;
|
||||||
|
|
||||||
|
public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
|
||||||
|
@NonNull ViewGroup parentView) {
|
||||||
|
mBridgeContext = context;
|
||||||
|
mParams = params;
|
||||||
|
mCallback = params.getProjectCallback().getActionBarCallback();
|
||||||
|
ResourceValue layoutName = getLayoutResource(context);
|
||||||
|
if (layoutName == null) {
|
||||||
|
throw new RuntimeException("Unable to find the layout for Action Bar.");
|
||||||
|
}
|
||||||
|
int layoutId;
|
||||||
|
if (layoutName.isFramework()) {
|
||||||
|
layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
|
||||||
|
layoutName.getName(), 0);
|
||||||
|
} else {
|
||||||
|
layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
|
||||||
|
layoutName.getName(), 0);
|
||||||
|
|
||||||
|
}
|
||||||
|
if (layoutId == 0) {
|
||||||
|
throw new RuntimeException(
|
||||||
|
String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
|
||||||
|
layoutName.getName(), layoutName.getResourceType()));
|
||||||
|
}
|
||||||
|
if (mCallback.isOverflowPopupNeeded()) {
|
||||||
|
// Create a RelativeLayout around the action bar, to which the overflow popup may be
|
||||||
|
// added.
|
||||||
|
mEnclosingLayout = new RelativeLayout(mBridgeContext);
|
||||||
|
setMatchParent(mEnclosingLayout);
|
||||||
|
parentView.addView(mEnclosingLayout);
|
||||||
|
} else {
|
||||||
|
mEnclosingLayout = parentView;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inflate action bar layout.
|
||||||
|
mDecorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the Layout Resource that should be used to inflate the action bar. This layout
|
||||||
|
* should cover the complete screen, and have a FrameLayout included, where the content will
|
||||||
|
* be inflated.
|
||||||
|
*/
|
||||||
|
protected abstract ResourceValue getLayoutResource(BridgeContext context);
|
||||||
|
|
||||||
|
protected void setContentRoot(FrameLayout contentRoot) {
|
||||||
|
mContentRoot = contentRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
@NonNull
|
||||||
|
public FrameLayout getContentRoot() {
|
||||||
|
return mContentRoot;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the view inflated. This should contain both the ActionBar and the app content in it.
|
||||||
|
*/
|
||||||
|
protected View getDecorContent() {
|
||||||
|
return mDecorContent;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Setup things like the title, subtitle, icon etc. */
|
||||||
|
protected void setupActionBar() {
|
||||||
|
setTitle();
|
||||||
|
setSutTitle();
|
||||||
|
setIcon();
|
||||||
|
setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected abstract void setTitle(CharSequence title);
|
||||||
|
protected abstract void setSubtitle(CharSequence subtitle);
|
||||||
|
protected abstract void setIcon(String icon);
|
||||||
|
protected abstract void setHomeAsUp(boolean homeAsUp);
|
||||||
|
|
||||||
|
private void setTitle() {
|
||||||
|
RenderResources res = mBridgeContext.getRenderResources();
|
||||||
|
|
||||||
|
String title = mParams.getAppLabel();
|
||||||
|
ResourceValue titleValue = res.findResValue(title, false);
|
||||||
|
if (titleValue != null && titleValue.getValue() != null) {
|
||||||
|
setTitle(titleValue.getValue());
|
||||||
|
} else {
|
||||||
|
setTitle(title);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setSutTitle() {
|
||||||
|
String subTitle = mCallback.getSubTitle();
|
||||||
|
if (subTitle != null) {
|
||||||
|
setSubtitle(subTitle);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void setIcon() {
|
||||||
|
String appIcon = mParams.getAppIcon();
|
||||||
|
if (appIcon != null) {
|
||||||
|
setIcon(appIcon);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public abstract void createMenuPopup();
|
||||||
|
|
||||||
|
public ActionBarCallback getCallBack() {
|
||||||
|
return mCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected static void setMatchParent(View view) {
|
||||||
|
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
|
||||||
|
LayoutParams.MATCH_PARENT));
|
||||||
|
}
|
||||||
|
}
|
@ -31,7 +31,7 @@ import android.content.Context;
|
|||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.util.DisplayMetrics;
|
import android.util.DisplayMetrics;
|
||||||
import android.util.TypedValue;
|
import android.util.TypedValue;
|
||||||
import android.view.LayoutInflater;
|
import android.view.InflateException;
|
||||||
import android.view.View;
|
import android.view.View;
|
||||||
import android.view.View.MeasureSpec;
|
import android.view.View.MeasureSpec;
|
||||||
import android.view.ViewGroup;
|
import android.view.ViewGroup;
|
||||||
@ -44,70 +44,30 @@ import android.widget.RelativeLayout;
|
|||||||
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
|
|
||||||
public class ActionBarLayout {
|
/**
|
||||||
|
* Creates the ActionBar as done by the framework.
|
||||||
|
*/
|
||||||
|
public class FrameworkActionBar extends BridgeActionBar {
|
||||||
|
|
||||||
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
|
private static final String LAYOUT_ATTR_NAME = "windowActionBarFullscreenDecorLayout";
|
||||||
|
|
||||||
// The Action Bar
|
// The Action Bar
|
||||||
@NonNull
|
@NonNull private FrameworkActionBarWrapper mActionBar;
|
||||||
private CustomActionBarWrapper mActionBar;
|
|
||||||
|
|
||||||
// Store another reference to the context so that we don't have to cast it repeatedly.
|
|
||||||
@NonNull
|
|
||||||
private final BridgeContext mBridgeContext;
|
|
||||||
|
|
||||||
@NonNull
|
|
||||||
private FrameLayout mContentRoot;
|
|
||||||
|
|
||||||
// A fake parent for measuring views.
|
// A fake parent for measuring views.
|
||||||
@Nullable
|
@Nullable private ViewGroup mMeasureParent;
|
||||||
private ViewGroup mMeasureParent;
|
|
||||||
|
|
||||||
// A Layout that contains the inflated action bar. The menu popup is added to this layout.
|
|
||||||
@NonNull
|
|
||||||
private final RelativeLayout mEnclosingLayout;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Inflate the action bar and attach it to {@code parentView}
|
* Inflate the action bar and attach it to {@code parentView}
|
||||||
*/
|
*/
|
||||||
public ActionBarLayout(@NonNull BridgeContext context, @NonNull SessionParams params,
|
public FrameworkActionBar(@NonNull BridgeContext context, @NonNull SessionParams params,
|
||||||
@NonNull ViewGroup parentView) {
|
@NonNull ViewGroup parentView) {
|
||||||
|
super(context, params, parentView);
|
||||||
|
|
||||||
mBridgeContext = context;
|
View decorContent = getDecorContent();
|
||||||
|
|
||||||
ResourceValue layoutName = context.getRenderResources()
|
mActionBar = FrameworkActionBarWrapper.getActionBarWrapper(context, getCallBack(),
|
||||||
.findItemInTheme(LAYOUT_ATTR_NAME, true);
|
decorContent);
|
||||||
if (layoutName != null) {
|
|
||||||
// We may need to resolve the reference obtained.
|
|
||||||
layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
|
|
||||||
layoutName.isFramework());
|
|
||||||
}
|
|
||||||
int layoutId = 0;
|
|
||||||
String error = null;
|
|
||||||
if (layoutName == null) {
|
|
||||||
error = "Unable to find action bar layout (" + LAYOUT_ATTR_NAME
|
|
||||||
+ ") in the current theme.";
|
|
||||||
} else {
|
|
||||||
layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
|
|
||||||
layoutName.getName(), 0);
|
|
||||||
if (layoutId == 0) {
|
|
||||||
error = String.format("Unable to resolve attribute \"%s\" of type \"%s\"",
|
|
||||||
layoutName.getName(), layoutName.getResourceType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (layoutId == 0) {
|
|
||||||
throw new RuntimeException(error);
|
|
||||||
}
|
|
||||||
// Create a RelativeLayout to hold the action bar. The layout is needed so that we may
|
|
||||||
// add the menu popup to it.
|
|
||||||
mEnclosingLayout = new RelativeLayout(mBridgeContext);
|
|
||||||
setMatchParent(mEnclosingLayout);
|
|
||||||
parentView.addView(mEnclosingLayout);
|
|
||||||
|
|
||||||
// Inflate action bar layout.
|
|
||||||
View decorContent = LayoutInflater.from(context).inflate(layoutId, mEnclosingLayout, true);
|
|
||||||
|
|
||||||
mActionBar = CustomActionBarWrapper.getActionBarWrapper(context, params, decorContent);
|
|
||||||
|
|
||||||
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
|
FrameLayout contentRoot = (FrameLayout) mEnclosingLayout.findViewById(android.R.id.content);
|
||||||
|
|
||||||
@ -117,27 +77,62 @@ public class ActionBarLayout {
|
|||||||
contentRoot = new FrameLayout(context);
|
contentRoot = new FrameLayout(context);
|
||||||
setMatchParent(contentRoot);
|
setMatchParent(contentRoot);
|
||||||
mEnclosingLayout.addView(contentRoot);
|
mEnclosingLayout.addView(contentRoot);
|
||||||
mContentRoot = contentRoot;
|
setContentRoot(contentRoot);
|
||||||
} else {
|
} else {
|
||||||
mContentRoot = contentRoot;
|
setContentRoot(contentRoot);
|
||||||
mActionBar.setupActionBar();
|
setupActionBar();
|
||||||
mActionBar.inflateMenus();
|
mActionBar.inflateMenus();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void setMatchParent(View view) {
|
@Override
|
||||||
view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
|
protected ResourceValue getLayoutResource(BridgeContext context) {
|
||||||
LayoutParams.MATCH_PARENT));
|
ResourceValue layoutName =
|
||||||
|
context.getRenderResources().findItemInTheme(LAYOUT_ATTR_NAME, true);
|
||||||
|
if (layoutName != null) {
|
||||||
|
// We may need to resolve the reference obtained.
|
||||||
|
layoutName = context.getRenderResources().findResValue(layoutName.getValue(),
|
||||||
|
layoutName.isFramework());
|
||||||
|
}
|
||||||
|
if (layoutName == null) {
|
||||||
|
throw new InflateException("Unable to find action bar layout (" + LAYOUT_ATTR_NAME
|
||||||
|
+ ") in the current theme.");
|
||||||
|
}
|
||||||
|
return layoutName;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setupActionBar() {
|
||||||
|
super.setupActionBar();
|
||||||
|
mActionBar.setupActionBar();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setHomeAsUp(boolean homeAsUp) {
|
||||||
|
mActionBar.setHomeAsUp(homeAsUp);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setTitle(CharSequence title) {
|
||||||
|
mActionBar.setTitle(title);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setSubtitle(CharSequence subtitle) {
|
||||||
|
mActionBar.setSubTitle(subtitle);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected void setIcon(String icon) {
|
||||||
|
mActionBar.setIcon(icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
|
* Creates a Popup and adds it to the content frame. It also adds another {@link FrameLayout} to
|
||||||
* the content frame which shall serve as the new content root.
|
* the content frame which shall serve as the new content root.
|
||||||
*/
|
*/
|
||||||
|
@Override
|
||||||
public void createMenuPopup() {
|
public void createMenuPopup() {
|
||||||
assert mEnclosingLayout.getChildCount() == 1
|
|
||||||
: "Action Bar Menus have already been created.";
|
|
||||||
|
|
||||||
if (!isOverflowPopupNeeded()) {
|
if (!isOverflowPopupNeeded()) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
@ -193,11 +188,6 @@ public class ActionBarLayout {
|
|||||||
return needed;
|
return needed;
|
||||||
}
|
}
|
||||||
|
|
||||||
@NonNull
|
|
||||||
public FrameLayout getContentRoot() {
|
|
||||||
return mContentRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
|
// Copied from com.android.internal.view.menu.MenuPopHelper.measureContentWidth()
|
||||||
private int measureContentWidth(@NonNull ListAdapter adapter) {
|
private int measureContentWidth(@NonNull ListAdapter adapter) {
|
||||||
// Menus don't tend to be long, so this is more sane than it looks.
|
// Menus don't tend to be long, so this is more sane than it looks.
|
@ -19,10 +19,8 @@ package com.android.layoutlib.bridge.bars;
|
|||||||
import com.android.annotations.NonNull;
|
import com.android.annotations.NonNull;
|
||||||
import com.android.annotations.Nullable;
|
import com.android.annotations.Nullable;
|
||||||
import com.android.ide.common.rendering.api.ActionBarCallback;
|
import com.android.ide.common.rendering.api.ActionBarCallback;
|
||||||
import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
|
|
||||||
import com.android.ide.common.rendering.api.RenderResources;
|
import com.android.ide.common.rendering.api.RenderResources;
|
||||||
import com.android.ide.common.rendering.api.ResourceValue;
|
import com.android.ide.common.rendering.api.ResourceValue;
|
||||||
import com.android.ide.common.rendering.api.SessionParams;
|
|
||||||
import com.android.internal.R;
|
import com.android.internal.R;
|
||||||
import com.android.internal.app.ToolbarActionBar;
|
import com.android.internal.app.ToolbarActionBar;
|
||||||
import com.android.internal.app.WindowDecorActionBar;
|
import com.android.internal.app.WindowDecorActionBar;
|
||||||
@ -54,10 +52,9 @@ import static com.android.resources.ResourceType.MENU;
|
|||||||
/**
|
/**
|
||||||
* A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
|
* A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
|
||||||
*/
|
*/
|
||||||
public abstract class CustomActionBarWrapper {
|
public abstract class FrameworkActionBarWrapper {
|
||||||
|
|
||||||
@NonNull protected ActionBar mActionBar;
|
@NonNull protected ActionBar mActionBar;
|
||||||
@NonNull protected SessionParams mParams;
|
|
||||||
@NonNull protected ActionBarCallback mCallback;
|
@NonNull protected ActionBarCallback mCallback;
|
||||||
@NonNull protected BridgeContext mContext;
|
@NonNull protected BridgeContext mContext;
|
||||||
|
|
||||||
@ -68,49 +65,48 @@ public abstract class CustomActionBarWrapper {
|
|||||||
* ?attr/windowActionBarFullscreenDecorLayout
|
* ?attr/windowActionBarFullscreenDecorLayout
|
||||||
*/
|
*/
|
||||||
@NonNull
|
@NonNull
|
||||||
public static CustomActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
|
public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
|
||||||
@NonNull SessionParams params, @NonNull View decorContent) {
|
@NonNull ActionBarCallback callback, @NonNull View decorContent) {
|
||||||
View view = decorContent.findViewById(R.id.action_bar);
|
View view = decorContent.findViewById(R.id.action_bar);
|
||||||
if (view instanceof Toolbar) {
|
if (view instanceof Toolbar) {
|
||||||
return new ToolbarWrapper(context, params, ((Toolbar) view));
|
return new ToolbarWrapper(context, callback, (Toolbar) view);
|
||||||
} else if (view instanceof ActionBarView) {
|
} else if (view instanceof ActionBarView) {
|
||||||
return new WindowActionBarWrapper(context, params, decorContent,
|
return new WindowActionBarWrapper(context, callback, decorContent,
|
||||||
((ActionBarView) view));
|
(ActionBarView) view);
|
||||||
} else {
|
} else {
|
||||||
throw new IllegalStateException("Can't make an action bar out of " +
|
throw new IllegalStateException("Can't make an action bar out of " +
|
||||||
view.getClass().getSimpleName());
|
view.getClass().getSimpleName());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
CustomActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
|
FrameworkActionBarWrapper(@NonNull BridgeContext context, ActionBarCallback callback,
|
||||||
@NonNull ActionBar actionBar) {
|
@NonNull ActionBar actionBar) {
|
||||||
mActionBar = actionBar;
|
mActionBar = actionBar;
|
||||||
mParams = params;
|
mCallback = callback;
|
||||||
mCallback = params.getProjectCallback().getActionBarCallback();
|
|
||||||
mContext = context;
|
mContext = context;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** A call to setup any custom properties. */
|
||||||
protected void setupActionBar() {
|
protected void setupActionBar() {
|
||||||
// Do the things that are common to all implementations.
|
// Nothing to do here.
|
||||||
RenderResources res = mContext.getRenderResources();
|
}
|
||||||
|
|
||||||
String title = mParams.getAppLabel();
|
public void setTitle(CharSequence title) {
|
||||||
ResourceValue titleValue = res.findResValue(title, false);
|
mActionBar.setTitle(title);
|
||||||
if (titleValue != null && titleValue.getValue() != null) {
|
}
|
||||||
mActionBar.setTitle(titleValue.getValue());
|
|
||||||
} else {
|
|
||||||
mActionBar.setTitle(title);
|
|
||||||
}
|
|
||||||
|
|
||||||
String subTitle = mCallback.getSubTitle();
|
public void setSubTitle(CharSequence subTitle) {
|
||||||
if (subTitle != null) {
|
if (subTitle != null) {
|
||||||
mActionBar.setSubtitle(subTitle);
|
mActionBar.setSubtitle(subTitle);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Add show home as up icon.
|
public void setHomeAsUp(boolean homeAsUp) {
|
||||||
if (mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP) {
|
mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
|
||||||
mActionBar.setDisplayOptions(0xFF, ActionBar.DISPLAY_HOME_AS_UP);
|
}
|
||||||
}
|
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
// Nothing to do.
|
||||||
}
|
}
|
||||||
|
|
||||||
protected boolean isSplit() {
|
protected boolean isSplit() {
|
||||||
@ -186,15 +182,14 @@ public abstract class CustomActionBarWrapper {
|
|||||||
* Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
|
* Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
|
||||||
* Toolbar using a common API.
|
* Toolbar using a common API.
|
||||||
*/
|
*/
|
||||||
private static class ToolbarWrapper extends CustomActionBarWrapper {
|
private static class ToolbarWrapper extends FrameworkActionBarWrapper {
|
||||||
|
|
||||||
@NonNull
|
@NonNull
|
||||||
private final Toolbar mToolbar; // This is the view.
|
private final Toolbar mToolbar; // This is the view.
|
||||||
|
|
||||||
ToolbarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
|
ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
|
||||||
@NonNull Toolbar toolbar) {
|
@NonNull Toolbar toolbar) {
|
||||||
super(context, params, new ToolbarActionBar(toolbar, "", new WindowCallback())
|
super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
|
||||||
);
|
|
||||||
mToolbar = toolbar;
|
mToolbar = toolbar;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -248,19 +243,17 @@ public abstract class CustomActionBarWrapper {
|
|||||||
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
|
* Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
|
||||||
* access to it using a common API.
|
* access to it using a common API.
|
||||||
*/
|
*/
|
||||||
private static class WindowActionBarWrapper extends CustomActionBarWrapper {
|
private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
|
||||||
|
|
||||||
@NonNull
|
@NonNull private final WindowDecorActionBar mActionBar;
|
||||||
private final WindowDecorActionBar mActionBar;
|
@NonNull private final ActionBarView mActionBarView;
|
||||||
@NonNull
|
@NonNull private final View mDecorContentRoot;
|
||||||
private final ActionBarView mActionBarView;
|
|
||||||
@NonNull
|
|
||||||
private final View mDecorContentRoot;
|
|
||||||
private MenuBuilder mMenuBuilder;
|
private MenuBuilder mMenuBuilder;
|
||||||
|
|
||||||
public WindowActionBarWrapper(@NonNull BridgeContext context, @NonNull SessionParams params,
|
public WindowActionBarWrapper(@NonNull BridgeContext context,
|
||||||
@NonNull View decorContentRoot, @NonNull ActionBarView actionBarView) {
|
@NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
|
||||||
super(context, params, new WindowDecorActionBar(decorContentRoot));
|
@NonNull ActionBarView actionBarView) {
|
||||||
|
super(context, callback, new WindowDecorActionBar(decorContentRoot));
|
||||||
mActionBarView = actionBarView;
|
mActionBarView = actionBarView;
|
||||||
mActionBar = ((WindowDecorActionBar) super.mActionBar);
|
mActionBar = ((WindowDecorActionBar) super.mActionBar);
|
||||||
mDecorContentRoot = decorContentRoot;
|
mDecorContentRoot = decorContentRoot;
|
||||||
@ -268,7 +261,6 @@ public abstract class CustomActionBarWrapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void setupActionBar() {
|
protected void setupActionBar() {
|
||||||
super.setupActionBar();
|
|
||||||
|
|
||||||
// Set the navigation mode.
|
// Set the navigation mode.
|
||||||
int navMode = mCallback.getNavigationMode();
|
int navMode = mCallback.getNavigationMode();
|
||||||
@ -278,16 +270,6 @@ public abstract class CustomActionBarWrapper {
|
|||||||
setupTabs(3);
|
setupTabs(3);
|
||||||
}
|
}
|
||||||
|
|
||||||
String icon = mParams.getAppIcon();
|
|
||||||
// If the action bar style doesn't specify an icon, set the icon obtained from the
|
|
||||||
// session params.
|
|
||||||
if (!mActionBar.hasIcon() && icon != null) {
|
|
||||||
Drawable iconDrawable = getDrawable(icon, false);
|
|
||||||
if (iconDrawable != null) {
|
|
||||||
mActionBar.setIcon(iconDrawable);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Set action bar to be split, if needed.
|
// Set action bar to be split, if needed.
|
||||||
ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
|
ViewGroup splitView = (ViewGroup) mDecorContentRoot.findViewById(R.id.split_action_bar);
|
||||||
if (splitView != null) {
|
if (splitView != null) {
|
||||||
@ -299,6 +281,17 @@ public abstract class CustomActionBarWrapper {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void setIcon(String icon) {
|
||||||
|
// Set the icon only if the action bar doesn't specify an icon.
|
||||||
|
if (!mActionBar.hasIcon() && icon != null) {
|
||||||
|
Drawable iconDrawable = getDrawable(icon, false);
|
||||||
|
if (iconDrawable != null) {
|
||||||
|
mActionBar.setIcon(iconDrawable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void inflateMenus() {
|
protected void inflateMenus() {
|
||||||
super.inflateMenus();
|
super.inflateMenus();
|
||||||
@ -340,7 +333,7 @@ public abstract class CustomActionBarWrapper {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
int getMenuPopupMargin() {
|
int getMenuPopupMargin() {
|
||||||
return -ActionBarLayout.getPixelValue("10dp", mContext.getMetrics());
|
return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics());
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: Use an adapter, like List View to set up tabs.
|
// TODO: Use an adapter, like List View to set up tabs.
|
@ -51,11 +51,13 @@ import com.android.layoutlib.bridge.android.BridgeContext;
|
|||||||
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
|
import com.android.layoutlib.bridge.android.BridgeLayoutParamsMapAttributes;
|
||||||
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
|
import com.android.layoutlib.bridge.android.BridgeXmlBlockParser;
|
||||||
import com.android.layoutlib.bridge.android.SessionParamsFlags;
|
import com.android.layoutlib.bridge.android.SessionParamsFlags;
|
||||||
|
import com.android.layoutlib.bridge.bars.BridgeActionBar;
|
||||||
|
import com.android.layoutlib.bridge.bars.AppCompatActionBar;
|
||||||
import com.android.layoutlib.bridge.bars.Config;
|
import com.android.layoutlib.bridge.bars.Config;
|
||||||
import com.android.layoutlib.bridge.bars.NavigationBar;
|
import com.android.layoutlib.bridge.bars.NavigationBar;
|
||||||
import com.android.layoutlib.bridge.bars.StatusBar;
|
import com.android.layoutlib.bridge.bars.StatusBar;
|
||||||
import com.android.layoutlib.bridge.bars.TitleBar;
|
import com.android.layoutlib.bridge.bars.TitleBar;
|
||||||
import com.android.layoutlib.bridge.bars.ActionBarLayout;
|
import com.android.layoutlib.bridge.bars.FrameworkActionBar;
|
||||||
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
|
import com.android.layoutlib.bridge.impl.binding.FakeAdapter;
|
||||||
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
|
import com.android.layoutlib.bridge.impl.binding.FakeExpandableAdapter;
|
||||||
import com.android.resources.Density;
|
import com.android.resources.Density;
|
||||||
@ -354,7 +356,7 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
|
|
||||||
// if the theme says no title/action bar, then the size will be 0
|
// if the theme says no title/action bar, then the size will be 0
|
||||||
if (mActionBarSize > 0) {
|
if (mActionBarSize > 0) {
|
||||||
ActionBarLayout actionBar = createActionBar(context, params, backgroundLayout);
|
BridgeActionBar actionBar = createActionBar(context, params, backgroundLayout);
|
||||||
actionBar.createMenuPopup();
|
actionBar.createMenuPopup();
|
||||||
mContentRoot = actionBar.getContentRoot();
|
mContentRoot = actionBar.getContentRoot();
|
||||||
} else if (mTitleBarSize > 0) {
|
} else if (mTitleBarSize > 0) {
|
||||||
@ -1190,8 +1192,22 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
|
// android.support.v7.app.ActionBarActivity, and not care about the theme name at all.
|
||||||
if (mIsThemeAppCompat == null) {
|
if (mIsThemeAppCompat == null) {
|
||||||
StyleResourceValue defaultTheme = resources.getDefaultTheme();
|
StyleResourceValue defaultTheme = resources.getDefaultTheme();
|
||||||
StyleResourceValue val = resources.getStyle("Theme.AppCompat", false);
|
// We can't simply check for parent using resources.themeIsParentOf() since the
|
||||||
mIsThemeAppCompat = defaultTheme == val || resources.themeIsParentOf(val, defaultTheme);
|
// inheritance structure isn't really what one would expect. The first common parent
|
||||||
|
// between Theme.AppCompat.Light and Theme.AppCompat is Theme.Material (for v21).
|
||||||
|
boolean isThemeAppCompat = false;
|
||||||
|
for (int i = 0; i < 50; i++) {
|
||||||
|
// for loop ensures that we don't run into cyclic theme inheritance.
|
||||||
|
if (defaultTheme.getName().startsWith("Theme.AppCompat")) {
|
||||||
|
isThemeAppCompat = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
defaultTheme = resources.getParent(defaultTheme);
|
||||||
|
if (defaultTheme == null) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
mIsThemeAppCompat = isThemeAppCompat;
|
||||||
}
|
}
|
||||||
return mIsThemeAppCompat;
|
return mIsThemeAppCompat;
|
||||||
}
|
}
|
||||||
@ -1647,9 +1663,13 @@ public class RenderSessionImpl extends RenderAction<SessionParams> {
|
|||||||
/**
|
/**
|
||||||
* Creates the action bar. Also queries the project callback for missing information.
|
* Creates the action bar. Also queries the project callback for missing information.
|
||||||
*/
|
*/
|
||||||
private ActionBarLayout createActionBar(BridgeContext context, SessionParams params,
|
private BridgeActionBar createActionBar(BridgeContext context, SessionParams params,
|
||||||
ViewGroup parentView) {
|
ViewGroup parentView) {
|
||||||
return new ActionBarLayout(context, params, parentView);
|
if (mIsThemeAppCompat == Boolean.TRUE) {
|
||||||
|
return new AppCompatActionBar(context, params, parentView);
|
||||||
|
} else {
|
||||||
|
return new FrameworkActionBar(context, params, parentView);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public BufferedImage getImage() {
|
public BufferedImage getImage() {
|
||||||
|
Reference in New Issue
Block a user