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