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 
17 package com.android.layoutlib.bridge.bars;
18 
19 import com.android.ide.common.rendering.api.ActionBarCallback;
20 import com.android.ide.common.rendering.api.ActionBarCallback.HomeButtonStyle;
21 import com.android.ide.common.rendering.api.RenderResources;
22 import com.android.ide.common.rendering.api.ResourceValue;
23 import com.android.ide.common.rendering.api.SessionParams;
24 import com.android.layoutlib.bridge.MockView;
25 import com.android.layoutlib.bridge.android.BridgeContext;
26 
27 import android.annotation.NonNull;
28 import android.annotation.Nullable;
29 import android.view.LayoutInflater;
30 import android.view.View;
31 import android.view.ViewGroup;
32 import android.view.ViewGroup.LayoutParams;
33 import android.widget.FrameLayout;
34 import android.widget.RelativeLayout;
35 
36 /**
37  * An abstraction over two implementations of the ActionBar - framework and appcompat.
38  */
39 public 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 
BridgeActionBar(@onNull BridgeContext context, @NonNull SessionParams params)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      */
getLayoutResource(BridgeContext context)99     protected abstract ResourceValue getLayoutResource(BridgeContext context);
100 
getInflater(BridgeContext context)101     protected LayoutInflater getInflater(BridgeContext context) {
102         return LayoutInflater.from(context);
103     }
104 
setContentRoot(@onNull FrameLayout contentRoot)105     protected void setContentRoot(@NonNull FrameLayout contentRoot) {
106         mContentRoot = contentRoot;
107     }
108 
109     @NonNull
getContentRoot()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      */
getDecorContent()117     protected View getDecorContent() {
118         return mDecorContent;
119     }
120 
121     /** Setup things like the title, subtitle, icon etc. */
setupActionBar()122     protected void setupActionBar() {
123         setTitle();
124         setSutTitle();
125         setIcon();
126         setHomeAsUp(mCallback.getHomeButtonStyle() == HomeButtonStyle.SHOW_HOME_AS_UP);
127     }
128 
setTitle(CharSequence title)129     protected abstract void setTitle(CharSequence title);
setSubtitle(CharSequence subtitle)130     protected abstract void setSubtitle(CharSequence subtitle);
setIcon(String icon)131     protected abstract void setIcon(String icon);
setHomeAsUp(boolean homeAsUp)132     protected abstract void setHomeAsUp(boolean homeAsUp);
133 
setTitle()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 
setSutTitle()146     private void setSutTitle() {
147         String subTitle = mCallback.getSubTitle();
148         if (subTitle != null) {
149             setSubtitle(subTitle);
150         }
151     }
152 
setIcon()153     private void setIcon() {
154         String appIcon = mParams.getAppIcon();
155         if (appIcon != null) {
156             setIcon(appIcon);
157         }
158     }
159 
createMenuPopup()160     public abstract void createMenuPopup();
161 
162     /**
163      * The root view that represents the action bar and possibly the content included in it.
164      */
getRootView()165     public View getRootView() {
166         return mEnclosingLayout == null ? mDecorContent : mEnclosingLayout;
167     }
168 
getCallBack()169     public ActionBarCallback getCallBack() {
170         return mCallback;
171     }
172 
setMatchParent(View view)173     protected static void setMatchParent(View view) {
174         view.setLayoutParams(new LayoutParams(LayoutParams.MATCH_PARENT,
175                 LayoutParams.MATCH_PARENT));
176     }
177 }
178