• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2014 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.RenderResources;
21 import com.android.ide.common.rendering.api.ResourceReference;
22 import com.android.ide.common.rendering.api.ResourceValue;
23 import com.android.internal.R;
24 import com.android.internal.app.ToolbarActionBar;
25 import com.android.internal.app.WindowDecorActionBar;
26 import com.android.internal.view.menu.MenuBuilder;
27 import com.android.internal.widget.ActionBarAccessor;
28 import com.android.internal.widget.ActionBarView;
29 import com.android.internal.widget.DecorToolbar;
30 import com.android.layoutlib.bridge.android.BridgeContext;
31 import com.android.layoutlib.bridge.impl.ResourceHelper;
32 
33 import android.annotation.NonNull;
34 import android.annotation.Nullable;
35 import android.app.ActionBar;
36 import android.app.ActionBar.Tab;
37 import android.app.ActionBar.TabListener;
38 import android.app.FragmentTransaction;
39 import android.content.Context;
40 import android.content.res.Resources;
41 import android.graphics.drawable.Drawable;
42 import android.view.MenuInflater;
43 import android.view.View;
44 import android.view.ViewGroup;
45 import android.view.WindowCallback;
46 import android.widget.ActionMenuPresenter;
47 import android.widget.ActionMenuView;
48 import android.widget.Toolbar;
49 import android.widget.Toolbar_Accessor;
50 
51 /**
52  * A common API to access {@link ToolbarActionBar} and {@link WindowDecorActionBar}.
53  */
54 public abstract class FrameworkActionBarWrapper {
55 
56     @NonNull protected ActionBar mActionBar;
57     @NonNull protected ActionBarCallback mCallback;
58     @NonNull protected BridgeContext mContext;
59 
60     /**
61      * Returns a wrapper around different implementations of the Action Bar to provide a common API.
62      *
63      * @param decorContent the top level view returned by inflating
64      *                     ?attr/windowActionBarFullscreenDecorLayout
65      */
66     @NonNull
getActionBarWrapper(@onNull BridgeContext context, @NonNull ActionBarCallback callback, @NonNull View decorContent)67     public static FrameworkActionBarWrapper getActionBarWrapper(@NonNull BridgeContext context,
68             @NonNull ActionBarCallback callback, @NonNull View decorContent) {
69         View view = decorContent.findViewById(R.id.action_bar);
70         if (view instanceof Toolbar) {
71             return new ToolbarWrapper(context, callback, (Toolbar) view);
72         } else if (view instanceof ActionBarView) {
73             return new WindowActionBarWrapper(context, callback, decorContent,
74                     (ActionBarView) view);
75         } else {
76             throw new IllegalStateException("Can't make an action bar out of " +
77                     view.getClass().getSimpleName());
78         }
79     }
80 
FrameworkActionBarWrapper(@onNull BridgeContext context, @NonNull ActionBarCallback callback, @NonNull ActionBar actionBar)81     FrameworkActionBarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
82             @NonNull ActionBar actionBar) {
83         mActionBar = actionBar;
84         mCallback = callback;
85         mContext = context;
86     }
87 
88     /** A call to setup any custom properties. */
setupActionBar()89     protected void setupActionBar() {
90         // Nothing to do here.
91     }
92 
setTitle(CharSequence title)93     public void setTitle(CharSequence title) {
94         mActionBar.setTitle(title);
95     }
96 
setSubTitle(CharSequence subTitle)97     public void setSubTitle(CharSequence subTitle) {
98         if (subTitle != null) {
99             mActionBar.setSubtitle(subTitle);
100         }
101     }
102 
setHomeAsUp(boolean homeAsUp)103     public void setHomeAsUp(boolean homeAsUp) {
104         mActionBar.setDisplayHomeAsUpEnabled(homeAsUp);
105     }
106 
setIcon(ResourceValue icon)107     public void setIcon(ResourceValue icon) {
108         // Nothing to do.
109     }
110 
isSplit()111     protected boolean isSplit() {
112         return getDecorToolbar().isSplit();
113     }
114 
isOverflowPopupNeeded()115     protected boolean isOverflowPopupNeeded() {
116         return mCallback.isOverflowPopupNeeded();
117     }
118 
119     /**
120      * Gets the menus to add to the action bar from the callback, resolves them, inflates them and
121      * adds them to the action bar.
122      */
inflateMenus()123     protected void inflateMenus() {
124         MenuInflater inflater = new MenuInflater(getActionMenuContext());
125         MenuBuilder menuBuilder = getMenuBuilder();
126         for (ResourceReference menuId : mCallback.getMenuIds()) {
127             int id = mContext.getResourceId(menuId, -1);
128             if (id >= 0) {
129                 inflater.inflate(id, menuBuilder);
130             }
131         }
132     }
133 
134     /**
135      * The context used for the ActionBar and the menus in the ActionBarView.
136      */
137     @NonNull
getActionMenuContext()138     protected Context getActionMenuContext() {
139         return mActionBar.getThemedContext();
140     }
141 
142     /**
143      * The context used to inflate the popup menu.
144      */
145     @NonNull
getPopupContext()146     abstract Context getPopupContext();
147 
148     /**
149      * The Menu in which to inflate the user's menus.
150      */
151     @NonNull
getMenuBuilder()152     abstract MenuBuilder getMenuBuilder();
153 
154     @Nullable
getActionMenuPresenter()155     abstract ActionMenuPresenter getActionMenuPresenter();
156 
157     /**
158      * Framework's wrapper over two ActionBar implementations.
159      */
160     @NonNull
getDecorToolbar()161     abstract DecorToolbar getDecorToolbar();
162 
getMenuPopupElevation()163     abstract int getMenuPopupElevation();
164 
165     /**
166      * Margin between the menu popup and the action bar.
167      */
getMenuPopupMargin()168     abstract int getMenuPopupMargin();
169 
170     // ---- The implementations ----
171 
172     /**
173      * Material theme uses {@link Toolbar} as the action bar. This wrapper provides access to
174      * Toolbar using a common API.
175      */
176     private static class ToolbarWrapper extends FrameworkActionBarWrapper {
177 
178         @NonNull
179         private final Toolbar mToolbar;  // This is the view.
180 
ToolbarWrapper(@onNull BridgeContext context, @NonNull ActionBarCallback callback, @NonNull Toolbar toolbar)181         private ToolbarWrapper(@NonNull BridgeContext context, @NonNull ActionBarCallback callback,
182                 @NonNull Toolbar toolbar) {
183             super(context, callback, new ToolbarActionBar(toolbar, "", new WindowCallback()));
184             mToolbar = toolbar;
185         }
186 
187         @Override
inflateMenus()188         protected void inflateMenus() {
189             super.inflateMenus();
190             // Inflating the menus isn't enough. ActionMenuPresenter needs to be initialized too.
191             MenuBuilder menu = getMenuBuilder();
192             DecorToolbar decorToolbar = getDecorToolbar();
193             // Setting a menu different from the above initializes the presenter.
194             decorToolbar.setMenu(new MenuBuilder(getActionMenuContext()), null);
195             // ActionMenuView needs to be recreated to be able to set the menu back.
196             ActionMenuPresenter presenter = getActionMenuPresenter();
197             if (presenter != null) {
198                 presenter.setMenuView(new ActionMenuView(getPopupContext()));
199             }
200             decorToolbar.setMenu(menu, null);
201         }
202 
203         @NonNull
204         @Override
getPopupContext()205         Context getPopupContext() {
206             return Toolbar_Accessor.getPopupContext(mToolbar);
207         }
208 
209         @NonNull
210         @Override
getMenuBuilder()211         MenuBuilder getMenuBuilder() {
212             return (MenuBuilder) mToolbar.getMenu();
213         }
214 
215         @Nullable
216         @Override
getActionMenuPresenter()217         ActionMenuPresenter getActionMenuPresenter() {
218             return Toolbar_Accessor.getActionMenuPresenter(mToolbar);
219         }
220 
221         @NonNull
222         @Override
getDecorToolbar()223         DecorToolbar getDecorToolbar() {
224             return mToolbar.getWrapper();
225         }
226 
227         @Override
getMenuPopupElevation()228         int getMenuPopupElevation() {
229             return 10;
230         }
231 
232         @Override
getMenuPopupMargin()233         int getMenuPopupMargin() {
234             return 0;
235         }
236     }
237 
238     /**
239      * Holo theme uses {@link WindowDecorActionBar} as the action bar. This wrapper provides
240      * access to it using a common API.
241      */
242     private static class WindowActionBarWrapper extends FrameworkActionBarWrapper {
243 
244         @NonNull private final WindowDecorActionBar mActionBar;
245         @NonNull private final ActionBarView mActionBarView;
246         @NonNull private final View mDecorContentRoot;
247         private MenuBuilder mMenuBuilder;
248 
WindowActionBarWrapper(@onNull BridgeContext context, @NonNull ActionBarCallback callback, @NonNull View decorContentRoot, @NonNull ActionBarView actionBarView)249         private WindowActionBarWrapper(@NonNull BridgeContext context,
250                 @NonNull ActionBarCallback callback, @NonNull View decorContentRoot,
251                 @NonNull ActionBarView actionBarView) {
252             super(context, callback, new WindowDecorActionBar(decorContentRoot));
253             mActionBarView = actionBarView;
254             mActionBar = (WindowDecorActionBar) super.mActionBar;
255             mDecorContentRoot = decorContentRoot;
256         }
257 
258         @Override
setupActionBar()259         protected void setupActionBar() {
260 
261             // Set the navigation mode.
262             int navMode = mCallback.getNavigationMode();
263             mActionBar.setNavigationMode(navMode);
264             //noinspection deprecation
265             if (navMode == ActionBar.NAVIGATION_MODE_TABS) {
266                 setupTabs(3);
267             }
268 
269             // Set action bar to be split, if needed.
270             ViewGroup splitView = mDecorContentRoot.findViewById(R.id.split_action_bar);
271             if (splitView != null) {
272                 mActionBarView.setSplitView(splitView);
273                 Resources res = mContext.getResources();
274                 boolean split = res.getBoolean(R.bool.split_action_bar_is_narrow)
275                         && mCallback.getSplitActionBarWhenNarrow();
276                 mActionBarView.setSplitToolbar(split);
277             }
278         }
279 
280         @Override
setIcon(ResourceValue icon)281         public void setIcon(ResourceValue icon) {
282             // Set the icon only if the action bar doesn't specify an icon.
283             if (!mActionBar.hasIcon() && icon != null) {
284                 Drawable iconDrawable = getDrawable(icon);
285                 if (iconDrawable != null) {
286                     mActionBar.setIcon(iconDrawable);
287                 }
288             }
289         }
290 
291         @Override
inflateMenus()292         protected void inflateMenus() {
293             super.inflateMenus();
294             // The super implementation doesn't set the menu on the view. Set it here.
295             mActionBarView.setMenu(getMenuBuilder(), null);
296         }
297 
298         @NonNull
299         @Override
getPopupContext()300         Context getPopupContext() {
301             return getActionMenuContext();
302         }
303 
304         @NonNull
305         @Override
getMenuBuilder()306         MenuBuilder getMenuBuilder() {
307             if (mMenuBuilder == null) {
308                 mMenuBuilder = new MenuBuilder(getActionMenuContext());
309             }
310             return mMenuBuilder;
311         }
312 
313         @Nullable
314         @Override
getActionMenuPresenter()315         ActionMenuPresenter getActionMenuPresenter() {
316             return ActionBarAccessor.getActionMenuPresenter(mActionBarView);
317         }
318 
319         @NonNull
320         @Override
getDecorToolbar()321         ActionBarView getDecorToolbar() {
322             return mActionBarView;
323         }
324 
325         @Override
getMenuPopupElevation()326         int getMenuPopupElevation() {
327             return 0;
328         }
329 
330         @Override
getMenuPopupMargin()331         int getMenuPopupMargin() {
332             return -FrameworkActionBar.getPixelValue("10dp", mContext.getMetrics());
333         }
334 
335         // TODO: Use an adapter, like List View to set up tabs.
336         @SuppressWarnings("deprecation")  // For Tab
setupTabs(int num)337         private void setupTabs(int num) {
338             for (int i = 1; i <= num; i++) {
339                 Tab tab = mActionBar.newTab().setText("Tab" + i).setTabListener(new TabListener() {
340                     @Override
341                     public void onTabUnselected(Tab t, FragmentTransaction ft) {
342                         // pass
343                     }
344                     @Override
345                     public void onTabSelected(Tab t, FragmentTransaction ft) {
346                         // pass
347                     }
348                     @Override
349                     public void onTabReselected(Tab t, FragmentTransaction ft) {
350                         // pass
351                     }
352                 });
353                 mActionBar.addTab(tab);
354             }
355         }
356 
357         @Nullable
getDrawable(@onNull ResourceValue value)358         private Drawable getDrawable(@NonNull ResourceValue value) {
359             RenderResources res = mContext.getRenderResources();
360             value = res.resolveResValue(value);
361             if (value != null) {
362                 return ResourceHelper.getDrawable(value, mContext);
363             }
364             return null;
365         }
366     }
367 }
368