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.android.BridgeContext;
25
26import android.annotation.NonNull;
27import android.annotation.Nullable;
28import android.view.LayoutInflater;
29import android.view.View;
30import android.view.ViewGroup;
31import android.view.ViewGroup.LayoutParams;
32import android.widget.FrameLayout;
33import android.widget.RelativeLayout;
34
35/**
36 * An abstraction over two implementations of the ActionBar - framework and appcompat.
37 */
38public abstract class BridgeActionBar {
39    // Store a reference to the context so that we don't have to cast it repeatedly.
40    @NonNull protected final BridgeContext mBridgeContext;
41    @NonNull protected final SessionParams mParams;
42    // A Layout that contains the inflated action bar. The menu popup is added to this layout.
43    @Nullable protected final ViewGroup mEnclosingLayout;
44
45    private final View mDecorContent;
46    private final ActionBarCallback mCallback;
47
48    @SuppressWarnings("NullableProblems")  // Should be initialized by subclasses.
49    @NonNull private FrameLayout mContentRoot;
50
51    public BridgeActionBar(@NonNull BridgeContext context, @NonNull SessionParams params) {
52        mBridgeContext = context;
53        mParams = params;
54        mCallback = params.getLayoutlibCallback().getActionBarCallback();
55        ResourceValue layoutName = getLayoutResource(context);
56        if (layoutName == null) {
57            throw new RuntimeException("Unable to find the layout for Action Bar.");
58        }
59        int layoutId;
60        if (layoutName.isFramework()) {
61            layoutId = context.getFrameworkResourceValue(layoutName.getResourceType(),
62                    layoutName.getName(), 0);
63        } else {
64            layoutId = context.getProjectResourceValue(layoutName.getResourceType(),
65                    layoutName.getName(), 0);
66
67        }
68        if (layoutId == 0) {
69            throw new RuntimeException(
70                    String.format("Unable to resolve attribute \"%1$s\" of type \"%2$s\"",
71                            layoutName.getName(), layoutName.getResourceType()));
72        }
73        if (mCallback.isOverflowPopupNeeded()) {
74            // Create a RelativeLayout around the action bar, to which the overflow popup may be
75            // added.
76            mEnclosingLayout = new RelativeLayout(mBridgeContext);
77            setMatchParent(mEnclosingLayout);
78        } else {
79            mEnclosingLayout = null;
80        }
81
82        // Inflate action bar layout.
83        mDecorContent =
84                getInflater(context).inflate(layoutId, mEnclosingLayout, mEnclosingLayout != null);
85    }
86
87    /**
88     * Returns the Layout Resource that should be used to inflate the action bar. This layout
89     * should cover the complete screen, and have a FrameLayout included, where the content will
90     * be inflated.
91     */
92    protected abstract ResourceValue getLayoutResource(BridgeContext context);
93
94    protected LayoutInflater getInflater(BridgeContext context) {
95        return LayoutInflater.from(context);
96    }
97
98    protected void setContentRoot(@NonNull FrameLayout contentRoot) {
99        mContentRoot = contentRoot;
100    }
101
102    @NonNull
103    public FrameLayout getContentRoot() {
104        return mContentRoot;
105    }
106
107    /**
108     * Returns the view inflated. This should contain both the ActionBar and the app content in it.
109     */
110    protected View getDecorContent() {
111        return mDecorContent;
112    }
113
114    /** Setup things like the title, subtitle, icon etc. */
115    protected void setupActionBar() {
116        setTitle();
117        setSutTitle();
118        setIcon();
119        setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
120    }
121
122    protected abstract void setTitle(CharSequence title);
123    protected abstract void setSubtitle(CharSequence subtitle);
124    protected abstract void setIcon(String icon);
125    protected abstract void setHomeAsUp(boolean homeAsUp);
126
127    private void setTitle() {
128        RenderResources res = mBridgeContext.getRenderResources();
129
130        String title = mParams.getAppLabel();
131        ResourceValue titleValue = res.findResValue(title, false);
132        if (titleValue != null && titleValue.getValue() != null) {
133            setTitle(titleValue.getValue());
134        } else {
135            setTitle(title);
136        }
137    }
138
139    private void setSutTitle() {
140        String subTitle = mCallback.getSubTitle();
141        if (subTitle != null) {
142            setSubtitle(subTitle);
143        }
144    }
145
146    private void setIcon() {
147        String appIcon = mParams.getAppIcon();
148        if (appIcon != null) {
149            setIcon(appIcon);
150        }
151    }
152
153    public abstract void createMenuPopup();
154
155    /**
156     * The root view that represents the action bar and possibly the content included in it.
157     */
158    public View getRootView() {
159        return mEnclosingLayout == null ? mDecorContent : mEnclosingLayout;
160    }
161
162    public ActionBarCallback getCallBack() {
163        return mCallback;
164    }
165
166    protected static void setMatchParent(View view) {
167        view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
168                LayoutParams.MATCH_PARENT));
169    }
170}
171