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