1/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 *      http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.layoutlib.bridge.bars;
18
19import com.android.ide.common.rendering.api.ActionBarCallback;
20import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
21import com.android.ide.common.rendering.api.RenderResources;
22import com.android.ide.common.rendering.api.ResourceValue;
23import com.android.ide.common.rendering.api.SessionParams;
24import com.android.layoutlib.bridge.MockView;
25import com.android.layoutlib.bridge.android.BridgeContext;
26
27import android.annotation.NonNull;
28import android.annotation.Nullable;
29import android.view.LayoutInflater;
30import android.view.View;
31import android.view.ViewGroup;
32import android.view.ViewGroup.LayoutParams;
33import android.widget.FrameLayout;
34import android.widget.RelativeLayout;
35
36/**
37 * An abstraction over two implementations of the ActionBar - framework and appcompat.
38 */
39public abstract class BridgeActionBar {
40    // Store a reference to the context so that we don't have to cast it repeatedly.
41    @NonNull protected final BridgeContext mBridgeContext;
42    @NonNull protected final SessionParams mParams;
43    // A Layout that contains the inflated action bar. The menu popup is added to this layout.
44    @Nullable protected final ViewGroup mEnclosingLayout;
45
46    private final View mDecorContent;
47    private final ActionBarCallback mCallback;
48
49    @SuppressWarnings("NullableProblems")  // Should be initialized by subclasses.
50    @NonNull private FrameLayout mContentRoot;
51
52    public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
53        mBridgeContext = context;
54        mParams = params;
55        mCallback = params.getLayoutlibCallback().getActionBarCallback();
56        ResourceValue layoutName = getLayoutResource(context);
57
58        int layoutId = 0;
59        if (layoutName == null) {
60            assert false : "Unable to find the layout for Action Bar.";
61        }
62        else {
63            if (layoutName.isFramework()) {
64                layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
65                        layoutName.getName(), 0);
66            } else {
67                layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
68                        layoutName.getName(), 0);
69
70            }
71        }
72        if (layoutId == 0) {
73            assert false : String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
74                    layoutName.getName(), layoutName.getResourceType());
75            mDecorContent = new MockView(context);
76            mEnclosingLayout = null;
77        }
78        else {
79            if (mCallback.isOverflowPopupNeeded()) {
80                // Create a RelativeLayout around the action bar, to which the overflow popup may be
81                // added.
82                mEnclosingLayout = new RelativeLayout(mBridgeContext);
83                setMatchParent(mEnclosingLayout);
84            } else {
85                mEnclosingLayout = null;
86            }
87
88            // Inflate action bar layout.
89            mDecorContent = getInflater(context).inflate(layoutId, mEnclosingLayout,
90                    mEnclosingLayout != null);
91        }
92    }
93
94    /**
95     * Returns the Layout Resource that should be used to inflate the action bar. This layout
96     * should cover the complete screen, and have a FrameLayout included, where the content will
97     * be inflated.
98     */
99    protected abstract ResourceValue getLayoutResource(BridgeContext context);
100
101    protected LayoutInflater getInflater(BridgeContext context) {
102        return LayoutInflater.from(context);
103    }
104
105    protected void setContentRoot(@NonNull FrameLayout contentRoot) {
106        mContentRoot = contentRoot;
107    }
108
109    @NonNull
110    public FrameLayout getContentRoot() {
111        return mContentRoot;
112    }
113
114    /**
115     * Returns the view inflated. This should contain both the ActionBar and the app content in it.
116     */
117    protected View getDecorContent() {
118        return mDecorContent;
119    }
120
121    /** Setup things like the title, subtitle, icon etc. */
122    protected void setupActionBar() {
123        setTitle();
124        setSutTitle();
125        setIcon();
126        setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
127    }
128
129    protected abstract void setTitle(CharSequence title);
130    protected abstract void setSubtitle(CharSequence subtitle);
131    protected abstract void setIcon(String icon);
132    protected abstract void setHomeAsUp(boolean homeAsUp);
133
134    private void setTitle() {
135        RenderResources res = mBridgeContext.getRenderResources();
136
137        String title = mParams.getAppLabel();
138        ResourceValue titleValue = res.findResValue(title, false);
139        if (titleValue != null && titleValue.getValue() != null) {
140            setTitle(titleValue.getValue());
141        } else {
142            setTitle(title);
143        }
144    }
145
146    private void setSutTitle() {
147        String subTitle = mCallback.getSubTitle();
148        if (subTitle != null) {
149            setSubtitle(subTitle);
150        }
151    }
152
153    private void setIcon() {
154        String appIcon = mParams.getAppIcon();
155        if (appIcon != null) {
156            setIcon(appIcon);
157        }
158    }
159
160    public abstract void createMenuPopup();
161
162    /**
163     * The root view that represents the action bar and possibly the content included in it.
164     */
165    public View getRootView() {
166        return mEnclosingLayout == null ? mDecorContent : mEnclosingLayout;
167    }
168
169    public ActionBarCallback getCallBack() {
170        return mCallback;
171    }
172
173    protected static void setMatchParent(View view) {
174        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
175                LayoutParams.MATCH_PARENT));
176    }
177}
178