1224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta/* 2224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Copyright (C) 2015 The Android Open Source Project 3224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * 4224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Licensed under the Apache License, Version 2.0 (the "License"); 5224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * you may not use this file except in compliance with the License. 6224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * You may obtain a copy of the License at 7224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * 8224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * http://www.apache.org/licenses/LICENSE-2.0 9224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * 10224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Unless required by applicable law or agreed to in writing, software 11224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * distributed under the License is distributed on an "AS IS" BASIS, 12224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * See the License for the specific language governing permissions and 14224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * limitations under the License. 15224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta */ 16224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 17224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptapackage com.android.layoutlib.bridge.bars; 18224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 192b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutLog; 202b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Guptaimport com.android.ide.common.rendering.api.LayoutlibCallback; 21224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.RenderResources; 22224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.ResourceValue; 23224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.SessionParams; 24ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport com.android.ide.common.rendering.api.StyleResourceValue; 252b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Guptaimport com.android.layoutlib.bridge.Bridge; 26224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.layoutlib.bridge.android.BridgeContext; 27224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.layoutlib.bridge.impl.ResourceHelper; 28224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.resources.ResourceType; 29224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 30476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull; 31476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable; 32ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.content.Context; 33224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.graphics.drawable.Drawable; 34ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.view.ContextThemeWrapper; 35ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.view.LayoutInflater; 36224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.view.View; 37224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.widget.FrameLayout; 38224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 39224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport java.lang.reflect.InvocationTargetException; 40224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport java.lang.reflect.Method; 41224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 42224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 43224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta/** 44224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Assumes that the AppCompat library is present in the project's classpath and creates an 45224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * actionbar around it. 46224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta */ 47224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptapublic class AppCompatActionBar extends BridgeActionBar { 48224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 49224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private Object mWindowDecorActionBar; 50224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar"; 512b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta // This is used on v23.1.1 and later. 522b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta private static final String WINDOW_ACTION_BAR_CLASS_NEW = "android.support.v7.app.WindowDecorActionBar"; 53224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private Class<?> mWindowActionBarClass; 54224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 55224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta /** 56224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Inflate the action bar and attach it to {@code parentView} 57224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta */ 58b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) { 59b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta super(context, params); 60224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta int contentRootId = context.getProjectResourceValue(ResourceType.ID, 61224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta "action_bar_activity_content", 0); 62224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta View contentView = getDecorContent().findViewById(contentRootId); 63224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (contentView != null) { 64224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta assert contentView instanceof FrameLayout; 652e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta setContentRoot((FrameLayout) contentView); 66224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } else { 67224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta // Something went wrong. Create a new FrameLayout in the enclosing layout. 68224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta FrameLayout contentRoot = new FrameLayout(context); 69224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta setMatchParent(contentRoot); 70b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta if (mEnclosingLayout != null) { 71b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta mEnclosingLayout.addView(contentRoot); 72b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta } 73224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta setContentRoot(contentRoot); 74224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 75224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta try { 76224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Class[] constructorParams = {View.class}; 77224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Object[] constructorArgs = {getDecorContent()}; 782b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta LayoutlibCallback callback = params.getLayoutlibCallback(); 7999b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta 8099b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta // Check if the old action bar class is present. 8199b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta String actionBarClass = WINDOW_ACTION_BAR_CLASS; 822b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta try { 8399b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta callback.findClass(actionBarClass); 8499b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta } catch (ClassNotFoundException expected) { 8599b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta // Failed to find the old class, use the newer one. 8699b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta actionBarClass = WINDOW_ACTION_BAR_CLASS_NEW; 872b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta } 88224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 8999b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta mWindowDecorActionBar = callback.loadView(actionBarClass, 9099b54b364b6d4e0689933393a9b584e542fc2c54Deepanshu Gupta constructorParams, constructorArgs); 91224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta mWindowActionBarClass = mWindowDecorActionBar == null ? null : 92224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta mWindowDecorActionBar.getClass(); 93224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta setupActionBar(); 94224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } catch (Exception e) { 952b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta Bridge.getLog().warning(LayoutLog.TAG_BROKEN, 962b0551175d57ce0f063566eea7670442b8b624fcDeepanshu Gupta "Failed to load AppCompat ActionBar with unknown error.", e); 97224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 98224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 99224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 100224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 101224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta protected ResourceValue getLayoutResource(BridgeContext context) { 102224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta // We always assume that the app has requested the action bar. 103224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return context.getRenderResources().getProjectResource(ResourceType.LAYOUT, 104224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta "abc_screen_toolbar"); 105224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 106224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 107224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 108ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta protected LayoutInflater getInflater(BridgeContext context) { 109ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta // Other than the resource resolution part, the code has been taken from the support 110ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta // library. see code from line 269 onwards in 111ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta // https://android.googlesource.com/platform/frameworks/support/+/android-5.1.0_r1/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java 112ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta Context themedContext = context; 113ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta RenderResources resources = context.getRenderResources(); 114ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta ResourceValue actionBarTheme = resources.findItemInTheme("actionBarTheme", false); 115ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta if (actionBarTheme != null) { 116ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta // resolve it, if needed. 117ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta actionBarTheme = resources.resolveResValue(actionBarTheme); 118ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta } 119ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta if (actionBarTheme instanceof StyleResourceValue) { 120ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta int styleId = context.getDynamicIdByStyle(((StyleResourceValue) actionBarTheme)); 121ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta if (styleId != 0) { 122ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta themedContext = new ContextThemeWrapper(context, styleId); 123ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta } 124ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta } 125ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta return LayoutInflater.from(themedContext); 126ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta } 127ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta 128ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta @Override 129224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta protected void setTitle(CharSequence title) { 130224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (title != null && mWindowDecorActionBar != null) { 131224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class); 132224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta invoke(setTitle, mWindowDecorActionBar, title); 133224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 134224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 135224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 136224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 137224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta protected void setSubtitle(CharSequence subtitle) { 138224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (subtitle != null && mWindowDecorActionBar != null) { 139224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class); 140224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta invoke(setSubtitle, mWindowDecorActionBar, subtitle); 141224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 142224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 143224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 144224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 145224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta protected void setIcon(String icon) { 146224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta // Do this only if the action bar doesn't already have an icon. 147224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) { 1482e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta if (invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar) 1492e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta == Boolean.TRUE) { 150224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Drawable iconDrawable = getDrawable(icon, false); 151224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (iconDrawable != null) { 152224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class); 153224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta invoke(setIcon, mWindowDecorActionBar, iconDrawable); 154224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 155224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 156224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 157224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 158224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 159224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 160224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta protected void setHomeAsUp(boolean homeAsUp) { 161224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (mWindowDecorActionBar != null) { 162224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta Method setHomeAsUp = getMethod(mWindowActionBarClass, 163224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta "setDefaultDisplayHomeAsUpEnabled", boolean.class); 164224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp); 165224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 166224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 167224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 168224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Override 169224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta public void createMenuPopup() { 170c77f9c76e4142db8dd76a28ad99b8f0395b82ebaDeepanshu Gupta // it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection. 171224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta // so we skip it for now. 172224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 173224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 174224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Nullable 175224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) { 176224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta try { 177224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return owner == null ? null : owner.getMethod(name, parameterTypes); 178224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } catch (NoSuchMethodException e) { 179224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta e.printStackTrace(); 180224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 181224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return null; 182224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 183224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 184224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Nullable 185224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private static Object invoke(Method method, Object owner, Object... args) { 186224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta try { 187224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return method == null ? null : method.invoke(owner, args); 188224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } catch (InvocationTargetException e) { 189224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta e.printStackTrace(); 190224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } catch (IllegalAccessException e) { 191224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta e.printStackTrace(); 192224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 193224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return null; 194224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 195224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta 196224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper 197224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta @Nullable 198224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta private Drawable getDrawable(@NonNull String name, boolean isFramework) { 199224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta RenderResources res = mBridgeContext.getRenderResources(); 200224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta ResourceValue value = res.findResValue(name, isFramework); 201224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta value = res.resolveResValue(value); 202224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta if (value != null) { 203224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return ResourceHelper.getDrawable(value, mBridgeContext); 204224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 205224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta return null; 206224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta } 207224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta} 208