ActionBarImpl.java revision 2b6230e0de4bac2829ac27b19e95ba75c3da82b4
1/* 2 * Copyright (C) 2010 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.internal.app; 18 19import com.android.internal.view.menu.MenuBuilder; 20import com.android.internal.view.menu.MenuPopupHelper; 21import com.android.internal.view.menu.SubMenuBuilder; 22import com.android.internal.widget.ActionBarContextView; 23import com.android.internal.widget.ActionBarView; 24 25import android.app.ActionBar; 26import android.app.Activity; 27import android.app.Dialog; 28import android.app.Fragment; 29import android.app.FragmentTransaction; 30import android.content.Context; 31import android.graphics.drawable.Drawable; 32import android.os.Handler; 33import android.view.ActionMode; 34import android.view.Menu; 35import android.view.MenuInflater; 36import android.view.MenuItem; 37import android.view.View; 38import android.widget.LinearLayout; 39import android.widget.SpinnerAdapter; 40import android.widget.ViewAnimator; 41 42import java.lang.ref.WeakReference; 43import java.util.ArrayList; 44 45/** 46 * ActionBarImpl is the ActionBar implementation used 47 * by devices of all screen sizes. If it detects a compatible decor, 48 * it will split contextual modes across both the ActionBarView at 49 * the top of the screen and a horizontal LinearLayout at the bottom 50 * which is normally hidden. 51 */ 52public class ActionBarImpl extends ActionBar { 53 private static final int NORMAL_VIEW = 0; 54 private static final int CONTEXT_VIEW = 1; 55 56 private static final int TAB_SWITCH_SHOW_HIDE = 0; 57 private static final int TAB_SWITCH_ADD_REMOVE = 1; 58 59 private Context mContext; 60 private Activity mActivity; 61 private Dialog mDialog; 62 63 private ViewAnimator mAnimatorView; 64 private ActionBarView mActionView; 65 private ActionBarContextView mUpperContextView; 66 private LinearLayout mLowerContextView; 67 68 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>(); 69 70 private int mTabContainerViewId = android.R.id.content; 71 private TabImpl mSelectedTab; 72 private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE; 73 74 private ActionMode mActionMode; 75 76 private static final int CONTEXT_DISPLAY_NORMAL = 0; 77 private static final int CONTEXT_DISPLAY_SPLIT = 1; 78 79 private int mContextDisplayMode; 80 81 private boolean mClosingContext; 82 83 final Handler mHandler = new Handler(); 84 final Runnable mCloseContext = new Runnable() { 85 public void run() { 86 mUpperContextView.closeMode(); 87 if (mLowerContextView != null) { 88 mLowerContextView.removeAllViews(); 89 } 90 mClosingContext = false; 91 } 92 }; 93 94 public ActionBarImpl(Activity activity) { 95 mActivity = activity; 96 init(activity.getWindow().getDecorView()); 97 } 98 99 public ActionBarImpl(Dialog dialog) { 100 mDialog = dialog; 101 init(dialog.getWindow().getDecorView()); 102 } 103 104 private void init(View decor) { 105 mContext = decor.getContext(); 106 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar); 107 mUpperContextView = (ActionBarContextView) decor.findViewById( 108 com.android.internal.R.id.action_context_bar); 109 mLowerContextView = (LinearLayout) decor.findViewById( 110 com.android.internal.R.id.lower_action_context_bar); 111 mAnimatorView = (ViewAnimator) decor.findViewById( 112 com.android.internal.R.id.action_bar_animator); 113 114 if (mActionView == null || mUpperContextView == null || mAnimatorView == null) { 115 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " + 116 "with a compatible window decor layout"); 117 } 118 119 mActionView.setContextView(mUpperContextView); 120 mContextDisplayMode = mLowerContextView == null ? 121 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT; 122 } 123 124 @Override 125 public void setTitle(int resId) { 126 setTitle(mContext.getString(resId)); 127 } 128 129 @Override 130 public void setSubtitle(int resId) { 131 setSubtitle(mContext.getString(resId)); 132 } 133 134 public void setCustomNavigationMode(View view) { 135 cleanupTabs(); 136 mActionView.setCustomNavigationView(view); 137 mActionView.setCallback(null); 138 } 139 140 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) { 141 setDropdownNavigationMode(adapter, callback, -1); 142 } 143 144 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback, 145 int defaultSelectedPosition) { 146 cleanupTabs(); 147 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST); 148 mActionView.setDropdownAdapter(adapter); 149 if (defaultSelectedPosition >= 0) { 150 mActionView.setDropdownSelectedPosition(defaultSelectedPosition); 151 } 152 mActionView.setCallback(callback); 153 } 154 155 public void setStandardNavigationMode() { 156 cleanupTabs(); 157 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD); 158 mActionView.setCallback(null); 159 } 160 161 public void setSelectedNavigationItem(int position) { 162 switch (mActionView.getNavigationMode()) { 163 case NAVIGATION_MODE_TABS: 164 selectTab(mTabs.get(position)); 165 break; 166 case NAVIGATION_MODE_DROPDOWN_LIST: 167 mActionView.setDropdownSelectedPosition(position); 168 break; 169 default: 170 throw new IllegalStateException( 171 "setSelectedNavigationItem not valid for current navigation mode"); 172 } 173 } 174 175 public int getSelectedNavigationItem() { 176 switch (mActionView.getNavigationMode()) { 177 case NAVIGATION_MODE_TABS: 178 return mSelectedTab.getPosition(); 179 case NAVIGATION_MODE_DROPDOWN_LIST: 180 return mActionView.getDropdownSelectedPosition(); 181 default: 182 return -1; 183 } 184 } 185 186 private void cleanupTabs() { 187 if (mSelectedTab != null) { 188 selectTab(null); 189 } 190 mTabs.clear(); 191 } 192 193 public void setTitle(CharSequence title) { 194 mActionView.setTitle(title); 195 } 196 197 public void setSubtitle(CharSequence subtitle) { 198 mActionView.setSubtitle(subtitle); 199 } 200 201 public void setDisplayOptions(int options) { 202 mActionView.setDisplayOptions(options); 203 } 204 205 public void setDisplayOptions(int options, int mask) { 206 final int current = mActionView.getDisplayOptions(); 207 mActionView.setDisplayOptions((options & mask) | (current & ~mask)); 208 } 209 210 public void setBackgroundDrawable(Drawable d) { 211 mActionView.setBackgroundDrawable(d); 212 } 213 214 public View getCustomNavigationView() { 215 return mActionView.getCustomNavigationView(); 216 } 217 218 public CharSequence getTitle() { 219 return mActionView.getTitle(); 220 } 221 222 public CharSequence getSubtitle() { 223 return mActionView.getSubtitle(); 224 } 225 226 public int getNavigationMode() { 227 return mActionView.getNavigationMode(); 228 } 229 230 public int getDisplayOptions() { 231 return mActionView.getDisplayOptions(); 232 } 233 234 public ActionMode startActionMode(ActionMode.Callback callback) { 235 if (mActionMode != null) { 236 mActionMode.finish(); 237 } 238 239 // Don't wait for the close context mode animation to finish. 240 if (mClosingContext) { 241 mAnimatorView.clearAnimation(); 242 mHandler.removeCallbacks(mCloseContext); 243 mCloseContext.run(); 244 } 245 246 ActionMode mode = new ActionModeImpl(callback); 247 if (callback.onCreateActionMode(mode, mode.getMenu())) { 248 mode.invalidate(); 249 mUpperContextView.initForMode(mode); 250 mAnimatorView.setDisplayedChild(CONTEXT_VIEW); 251 if (mLowerContextView != null) { 252 // TODO animate this 253 mLowerContextView.setVisibility(View.VISIBLE); 254 } 255 mActionMode = mode; 256 return mode; 257 } 258 return null; 259 } 260 261 private void configureTab(Tab tab, int position) { 262 final TabImpl tabi = (TabImpl) tab; 263 final boolean isFirstTab = mTabs.isEmpty(); 264 final ActionBar.TabListener callback = tabi.getCallback(); 265 266 if (callback == null) { 267 throw new IllegalStateException("Action Bar Tab must have a Callback"); 268 } 269 270 tabi.setPosition(position); 271 mTabs.add(position, tabi); 272 273 if (isFirstTab) { 274 final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction(); 275 mSelectedTab = tabi; 276 callback.onTabSelected(tab, trans); 277 if (!trans.isEmpty()) { 278 trans.commit(); 279 } 280 } 281 } 282 283 @Override 284 public void addTab(Tab tab) { 285 mActionView.addTab(tab); 286 configureTab(tab, mTabs.size()); 287 } 288 289 @Override 290 public void addTab(Tab tab, int position) { 291 mActionView.addTab(tab, position); 292 configureTab(tab, position); 293 } 294 295 @Override 296 public Tab newTab() { 297 return new TabImpl(); 298 } 299 300 @Override 301 public void removeTab(Tab tab) { 302 removeTabAt(tab.getPosition()); 303 } 304 305 @Override 306 public void removeTabAt(int position) { 307 mActionView.removeTabAt(position); 308 mTabs.remove(position); 309 310 final int newTabCount = mTabs.size(); 311 for (int i = position; i < newTabCount; i++) { 312 mTabs.get(i).setPosition(i); 313 } 314 315 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1))); 316 } 317 318 @Override 319 public void setTabNavigationMode() { 320 if (mActivity == null) { 321 throw new IllegalStateException( 322 "Tab navigation mode cannot be used outside of an Activity"); 323 } 324 mActionView.setNavigationMode(NAVIGATION_MODE_TABS); 325 } 326 327 @Override 328 public void selectTab(Tab tab) { 329 if (mSelectedTab == tab) { 330 return; 331 } 332 333 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION); 334 final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction(); 335 if (mSelectedTab != null) { 336 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans); 337 } 338 mSelectedTab = (TabImpl) tab; 339 if (mSelectedTab != null) { 340 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans); 341 } 342 343 if (!trans.isEmpty()) { 344 trans.commit(); 345 } 346 } 347 348 @Override 349 public Tab getSelectedTab() { 350 return mSelectedTab; 351 } 352 353 @Override 354 public int getHeight() { 355 return mActionView.getHeight(); 356 } 357 358 @Override 359 public void show() { 360 // TODO animate! 361 mAnimatorView.setVisibility(View.VISIBLE); 362 } 363 364 @Override 365 public void hide() { 366 // TODO animate! 367 mAnimatorView.setVisibility(View.GONE); 368 } 369 370 public boolean isShowing() { 371 return mAnimatorView.getVisibility() == View.VISIBLE; 372 } 373 374 /** 375 * @hide 376 */ 377 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback { 378 private ActionMode.Callback mCallback; 379 private MenuBuilder mMenu; 380 private WeakReference<View> mCustomView; 381 382 public ActionModeImpl(ActionMode.Callback callback) { 383 mCallback = callback; 384 mMenu = new MenuBuilder(mActionView.getContext()) 385 .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM); 386 mMenu.setCallback(this); 387 } 388 389 @Override 390 public MenuInflater getMenuInflater() { 391 return new MenuInflater(mContext); 392 } 393 394 @Override 395 public Menu getMenu() { 396 return mMenu; 397 } 398 399 @Override 400 public void finish() { 401 if (mActionMode != this) { 402 // Not the active action mode - no-op 403 return; 404 } 405 406 mCallback.onDestroyActionMode(this); 407 mAnimatorView.setDisplayedChild(NORMAL_VIEW); 408 409 // Clear out the context mode views after the animation finishes 410 mClosingContext = true; 411 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration()); 412 413 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) { 414 // TODO Animate this 415 mLowerContextView.setVisibility(View.GONE); 416 } 417 mActionMode = null; 418 } 419 420 @Override 421 public void invalidate() { 422 if (mCallback.onPrepareActionMode(this, mMenu)) { 423 // Refresh content in both context views 424 } 425 } 426 427 @Override 428 public void setCustomView(View view) { 429 mUpperContextView.setCustomView(view); 430 mCustomView = new WeakReference<View>(view); 431 } 432 433 @Override 434 public void setSubtitle(CharSequence subtitle) { 435 mUpperContextView.setSubtitle(subtitle); 436 } 437 438 @Override 439 public void setTitle(CharSequence title) { 440 mUpperContextView.setTitle(title); 441 } 442 443 @Override 444 public void setTitle(int resId) { 445 setTitle(mActivity.getString(resId)); 446 } 447 448 @Override 449 public void setSubtitle(int resId) { 450 setSubtitle(mActivity.getString(resId)); 451 } 452 453 @Override 454 public CharSequence getTitle() { 455 return mUpperContextView.getTitle(); 456 } 457 458 @Override 459 public CharSequence getSubtitle() { 460 return mUpperContextView.getSubtitle(); 461 } 462 463 @Override 464 public View getCustomView() { 465 return mCustomView != null ? mCustomView.get() : null; 466 } 467 468 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) { 469 return mCallback.onActionItemClicked(this, item); 470 } 471 472 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) { 473 } 474 475 public boolean onSubMenuSelected(SubMenuBuilder subMenu) { 476 if (!subMenu.hasVisibleItems()) { 477 return true; 478 } 479 480 new MenuPopupHelper(mContext, subMenu).show(); 481 return true; 482 } 483 484 public void onCloseSubMenu(SubMenuBuilder menu) { 485 } 486 487 public void onMenuModeChange(MenuBuilder menu) { 488 invalidate(); 489 mUpperContextView.showOverflowMenu(); 490 } 491 } 492 493 /** 494 * @hide 495 */ 496 public class TabImpl extends ActionBar.Tab { 497 private ActionBar.TabListener mCallback; 498 private Object mTag; 499 private Drawable mIcon; 500 private CharSequence mText; 501 private int mPosition; 502 private View mCustomView; 503 504 @Override 505 public Object getTag() { 506 return mTag; 507 } 508 509 @Override 510 public void setTag(Object tag) { 511 mTag = tag; 512 } 513 514 public ActionBar.TabListener getCallback() { 515 return mCallback; 516 } 517 518 @Override 519 public void setTabListener(ActionBar.TabListener callback) { 520 mCallback = callback; 521 } 522 523 @Override 524 public View getCustomView() { 525 return mCustomView; 526 } 527 528 @Override 529 public void setCustomView(View view) { 530 mCustomView = view; 531 } 532 533 @Override 534 public Drawable getIcon() { 535 return mIcon; 536 } 537 538 @Override 539 public int getPosition() { 540 return mPosition; 541 } 542 543 public void setPosition(int position) { 544 mPosition = position; 545 } 546 547 @Override 548 public CharSequence getText() { 549 return mText; 550 } 551 552 @Override 553 public void setIcon(Drawable icon) { 554 mIcon = icon; 555 } 556 557 @Override 558 public void setText(CharSequence text) { 559 mText = text; 560 } 561 562 @Override 563 public void select() { 564 selectTab(this); 565 } 566 } 567} 568