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