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