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
19224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.RenderResources;
20224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.ResourceValue;
21224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.ide.common.rendering.api.SessionParams;
22ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport com.android.ide.common.rendering.api.StyleResourceValue;
23224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.layoutlib.bridge.android.BridgeContext;
24224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.layoutlib.bridge.impl.ResourceHelper;
25224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport com.android.resources.ResourceType;
26224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
27476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.NonNull;
28476e582d2ffdf25102d4c55f8c242baa3d21d37fDeepanshu Guptaimport android.annotation.Nullable;
29ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.content.Context;
30224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.graphics.drawable.Drawable;
31ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.view.ContextThemeWrapper;
32ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Guptaimport android.view.LayoutInflater;
33224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.view.View;
34224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport android.widget.FrameLayout;
35224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
36224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport java.lang.reflect.InvocationTargetException;
37224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptaimport java.lang.reflect.Method;
38224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
39224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
40224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta/**
41224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * Assumes that the AppCompat library is present in the project's classpath and creates an
42224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta * actionbar around it.
43224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta */
44224e931fed1220f175805fdec71a882649270aa7Deepanshu Guptapublic class AppCompatActionBar extends BridgeActionBar {
45224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
46224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private Object mWindowDecorActionBar;
47224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private static final String WINDOW_ACTION_BAR_CLASS = "android.support.v7.internal.app.WindowDecorActionBar";
48224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private Class<?> mWindowActionBarClass;
49224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
50224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    /**
51224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta     * Inflate the action bar and attach it to {@code parentView}
52224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta     */
53b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta    public AppCompatActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
54b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta        super(context, params);
55224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        int contentRootId = context.getProjectResourceValue(ResourceType.ID,
56224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                "action_bar_activity_content", 0);
57224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        View contentView = getDecorContent().findViewById(contentRootId);
58224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (contentView != null) {
59224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            assert contentView instanceof FrameLayout;
602e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta            setContentRoot((FrameLayout) contentView);
61224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        } else {
62224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            // Something went wrong. Create a new FrameLayout in the enclosing layout.
63224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            FrameLayout contentRoot = new FrameLayout(context);
64224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            setMatchParent(contentRoot);
65b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta            if (mEnclosingLayout != null) {
66b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta                mEnclosingLayout.addView(contentRoot);
67b1484862e2367d87d3ccbd0fd0a6d2598ed5918aDeepanshu Gupta            }
68224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            setContentRoot(contentRoot);
69224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
70224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        try {
71224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            Class[] constructorParams = {View.class};
72224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            Object[] constructorArgs = {getDecorContent()};
7337dbb8b7f3c069196040eed3a03006647db7fa5bDeepanshu Gupta            mWindowDecorActionBar = params.getLayoutlibCallback().loadView(WINDOW_ACTION_BAR_CLASS,
74224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                    constructorParams, constructorArgs);
75224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
76224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            mWindowActionBarClass = mWindowDecorActionBar == null ? null :
77224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                    mWindowDecorActionBar.getClass();
78224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            setupActionBar();
79224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        } catch (Exception e) {
80224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            e.printStackTrace();
81224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
82224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
83224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
84224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
85224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    protected ResourceValue getLayoutResource(BridgeContext context) {
86224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        // We always assume that the app has requested the action bar.
87224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        return context.getRenderResources().getProjectResource(ResourceType.LAYOUT,
88224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                "abc_screen_toolbar");
89224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
90224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
91224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
92ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta    protected LayoutInflater getInflater(BridgeContext context) {
93ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        // Other than the resource resolution part, the code has been taken from the support
94ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        // library. see code from line 269 onwards in
95ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        // https://android.googlesource.com/platform/frameworks/support/+/android-5.1.0_r1/v7/appcompat/src/android/support/v7/app/ActionBarActivityDelegateBase.java
96ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        Context themedContext = context;
97ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        RenderResources resources = context.getRenderResources();
98ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        ResourceValue actionBarTheme = resources.findItemInTheme("actionBarTheme", false);
99ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        if (actionBarTheme != null) {
100ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta            // resolve it, if needed.
101ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta            actionBarTheme = resources.resolveResValue(actionBarTheme);
102ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        }
103ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        if (actionBarTheme instanceof StyleResourceValue) {
104ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta            int styleId = context.getDynamicIdByStyle(((StyleResourceValue) actionBarTheme));
105ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta            if (styleId != 0) {
106ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta                themedContext = new ContextThemeWrapper(context, styleId);
107ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta            }
108ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        }
109ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta        return LayoutInflater.from(themedContext);
110ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta    }
111ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta
112ada8590547348b77d8cbd56abdb8fadb3e72c015Deepanshu Gupta    @Override
113224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    protected void setTitle(CharSequence title) {
114224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (title != null && mWindowDecorActionBar != null) {
115224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            Method setTitle = getMethod(mWindowActionBarClass, "setTitle", CharSequence.class);
116224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            invoke(setTitle, mWindowDecorActionBar, title);
117224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
118224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
119224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
120224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
121224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    protected void setSubtitle(CharSequence subtitle) {
122224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (subtitle != null && mWindowDecorActionBar != null) {
123224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            Method setSubtitle = getMethod(mWindowActionBarClass, "setSubtitle", CharSequence.class);
124224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            invoke(setSubtitle, mWindowDecorActionBar, subtitle);
125224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
126224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
127224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
128224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
129224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    protected void setIcon(String icon) {
130224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        // Do this only if the action bar doesn't already have an icon.
131224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (icon != null && !icon.isEmpty() && mWindowDecorActionBar != null) {
1322e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta            if (invoke(getMethod(mWindowActionBarClass, "hasIcon"), mWindowDecorActionBar)
1332e4d93245764a1c1d7515361ddbb8fc1665f814eDeepanshu Gupta                    == Boolean.TRUE) {
134224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                Drawable iconDrawable = getDrawable(icon, false);
135224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                if (iconDrawable != null) {
136224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                    Method setIcon = getMethod(mWindowActionBarClass, "setIcon", Drawable.class);
137224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                    invoke(setIcon, mWindowDecorActionBar, iconDrawable);
138224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                }
139224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            }
140224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
141224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
142224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
143224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
144224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    protected void setHomeAsUp(boolean homeAsUp) {
145224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (mWindowDecorActionBar != null) {
146224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            Method setHomeAsUp = getMethod(mWindowActionBarClass,
147224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta                    "setDefaultDisplayHomeAsUpEnabled", boolean.class);
148224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            invoke(setHomeAsUp, mWindowDecorActionBar, homeAsUp);
149224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
150224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
151224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
152224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Override
153224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    public void createMenuPopup() {
154c77f9c76e4142db8dd76a28ad99b8f0395b82ebaDeepanshu Gupta        // it's hard to add menus to appcompat's actionbar, since it'll use a lot of reflection.
155224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        // so we skip it for now.
156224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
157224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
158224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Nullable
159224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private static Method getMethod(Class<?> owner, String name, Class<?>... parameterTypes) {
160224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        try {
161224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            return owner == null ? null : owner.getMethod(name, parameterTypes);
162224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        } catch (NoSuchMethodException e) {
163224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            e.printStackTrace();
164224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
165224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        return null;
166224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
167224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
168224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Nullable
169224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private static Object invoke(Method method, Object owner, Object... args) {
170224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        try {
171224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            return method == null ? null : method.invoke(owner, args);
172224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        } catch (InvocationTargetException e) {
173224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            e.printStackTrace();
174224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        } catch (IllegalAccessException e) {
175224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            e.printStackTrace();
176224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
177224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        return null;
178224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
179224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta
180224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    // TODO: this is duplicated from FrameworkActionBarWrapper$WindowActionBarWrapper
181224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    @Nullable
182224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    private Drawable getDrawable(@NonNull String name, boolean isFramework) {
183224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        RenderResources res = mBridgeContext.getRenderResources();
184224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        ResourceValue value = res.findResValue(name, isFramework);
185224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        value = res.resolveResValue(value);
186224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        if (value != null) {
187224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta            return ResourceHelper.getDrawable(value, mBridgeContext);
188224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        }
189224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta        return null;
190224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta    }
191224e931fed1220f175805fdec71a882649270aa7Deepanshu Gupta}
192