blob: 281f32c83224d535f163c7b585d7b347815c616a [file] [log] [blame]
Adam Powell89e06452010-06-23 20:24:52 -07001/*
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
Adam Powell2c9c9fe2010-07-16 10:17:57 -070019import com.android.internal.view.menu.MenuBuilder;
Adam Powell2c9c9fe2010-07-16 10:17:57 -070020import com.android.internal.view.menu.MenuPopupHelper;
21import com.android.internal.view.menu.SubMenuBuilder;
Adam Powell89e06452010-06-23 20:24:52 -070022import com.android.internal.widget.ActionBarContextView;
23import com.android.internal.widget.ActionBarView;
24
25import android.app.ActionBar;
Adam Powell661c9082010-07-02 10:09:44 -070026import android.app.Activity;
27import android.app.Fragment;
28import android.app.FragmentTransaction;
Adam Powell89e06452010-06-23 20:24:52 -070029import android.graphics.drawable.Drawable;
Adam Powell0e94b512010-06-29 17:58:20 -070030import android.os.Handler;
Adam Powell6e346362010-07-23 10:18:23 -070031import android.view.ActionMode;
Adam Powell89e06452010-06-23 20:24:52 -070032import android.view.Menu;
Adam Powell9168f0b2010-08-02 15:46:24 -070033import android.view.MenuInflater;
Adam Powell89e06452010-06-23 20:24:52 -070034import android.view.MenuItem;
35import android.view.View;
36import android.widget.LinearLayout;
37import android.widget.SpinnerAdapter;
38import android.widget.ViewAnimator;
39
Adam Powell29ed7572010-07-14 16:24:56 -070040import java.lang.ref.WeakReference;
Adam Powell661c9082010-07-02 10:09:44 -070041import java.util.ArrayList;
42
Adam Powell89e06452010-06-23 20:24:52 -070043/**
44 * ActionBarImpl is the ActionBar implementation used
45 * by devices of all screen sizes. If it detects a compatible decor,
46 * it will split contextual modes across both the ActionBarView at
47 * the top of the screen and a horizontal LinearLayout at the bottom
48 * which is normally hidden.
49 */
50public class ActionBarImpl extends ActionBar {
51 private static final int NORMAL_VIEW = 0;
52 private static final int CONTEXT_VIEW = 1;
53
Adam Powell661c9082010-07-02 10:09:44 -070054 private static final int TAB_SWITCH_SHOW_HIDE = 0;
55 private static final int TAB_SWITCH_ADD_REMOVE = 1;
56
57 private Activity mActivity;
58
Adam Powell89e06452010-06-23 20:24:52 -070059 private ViewAnimator mAnimatorView;
60 private ActionBarView mActionView;
61 private ActionBarContextView mUpperContextView;
62 private LinearLayout mLowerContextView;
Adam Powell661c9082010-07-02 10:09:44 -070063
64 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
65
66 private int mTabContainerViewId = android.R.id.content;
67 private TabImpl mSelectedTab;
68 private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
Adam Powell89e06452010-06-23 20:24:52 -070069
Adam Powell5d279772010-07-27 16:34:07 -070070 private ActionMode mActionMode;
Adam Powell89e06452010-06-23 20:24:52 -070071
72 private static final int CONTEXT_DISPLAY_NORMAL = 0;
73 private static final int CONTEXT_DISPLAY_SPLIT = 1;
74
75 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070076
Adam Powell3461b322010-07-14 11:34:43 -070077 private boolean mClosingContext;
78
Adam Powell0e94b512010-06-29 17:58:20 -070079 final Handler mHandler = new Handler();
80 final Runnable mCloseContext = new Runnable() {
81 public void run() {
82 mUpperContextView.closeMode();
83 if (mLowerContextView != null) {
84 mLowerContextView.removeAllViews();
85 }
Adam Powell3461b322010-07-14 11:34:43 -070086 mClosingContext = false;
Adam Powell0e94b512010-06-29 17:58:20 -070087 }
88 };
89
Adam Powell661c9082010-07-02 10:09:44 -070090 public ActionBarImpl(Activity activity) {
91 final View decor = activity.getWindow().getDecorView();
92 mActivity = activity;
Adam Powell89e06452010-06-23 20:24:52 -070093 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
94 mUpperContextView = (ActionBarContextView) decor.findViewById(
95 com.android.internal.R.id.action_context_bar);
96 mLowerContextView = (LinearLayout) decor.findViewById(
97 com.android.internal.R.id.lower_action_context_bar);
98 mAnimatorView = (ViewAnimator) decor.findViewById(
99 com.android.internal.R.id.action_bar_animator);
100
101 if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
102 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
103 "with a compatible window decor layout");
104 }
Adam Powell0e94b512010-06-29 17:58:20 -0700105
Adam Powell89e06452010-06-23 20:24:52 -0700106 mContextDisplayMode = mLowerContextView == null ?
107 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
108 }
109
Adam Powella66c7b02010-07-28 15:28:25 -0700110 @Override
111 public void setStandardNavigationMode(int titleResId, int subtitleResId) {
112 setStandardNavigationMode(mActivity.getString(titleResId),
113 mActivity.getString(subtitleResId));
114 }
115
116 @Override
117 public void setStandardNavigationMode(int titleResId) {
118 setStandardNavigationMode(mActivity.getString(titleResId));
119 }
120
121 @Override
122 public void setTitle(int resId) {
123 setTitle(mActivity.getString(resId));
124 }
125
126 @Override
127 public void setSubtitle(int resId) {
128 setSubtitle(mActivity.getString(resId));
129 }
130
Adam Powell89e06452010-06-23 20:24:52 -0700131 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700132 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700133 mActionView.setCustomNavigationView(view);
134 mActionView.setCallback(null);
135 }
Adam Powell0e94b512010-06-29 17:58:20 -0700136
Adam Powell89e06452010-06-23 20:24:52 -0700137 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell17809772010-07-21 13:25:11 -0700138 setDropdownNavigationMode(adapter, callback, -1);
139 }
140
141 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
142 int defaultSelectedPosition) {
Adam Powell661c9082010-07-02 10:09:44 -0700143 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700144 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
145 mActionView.setDropdownAdapter(adapter);
Adam Powell17809772010-07-21 13:25:11 -0700146 if (defaultSelectedPosition >= 0) {
147 mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
148 }
149 mActionView.setCallback(callback);
Adam Powell89e06452010-06-23 20:24:52 -0700150 }
Adam Powell0e94b512010-06-29 17:58:20 -0700151
152 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700153 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700154 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
155 mActionView.setCallback(null);
156 }
157
Adam Powell89e06452010-06-23 20:24:52 -0700158 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700159 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700160 setStandardNavigationMode(title, null);
161 }
Adam Powell0e94b512010-06-29 17:58:20 -0700162
Adam Powell89e06452010-06-23 20:24:52 -0700163 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700164 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700165 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
166 mActionView.setTitle(title);
167 mActionView.setSubtitle(subtitle);
168 mActionView.setCallback(null);
169 }
170
Adam Powell17809772010-07-21 13:25:11 -0700171 public void setSelectedNavigationItem(int position) {
172 switch (mActionView.getNavigationMode()) {
173 case NAVIGATION_MODE_TABS:
174 selectTab(mTabs.get(position));
175 break;
176 case NAVIGATION_MODE_DROPDOWN_LIST:
177 mActionView.setDropdownSelectedPosition(position);
178 break;
179 default:
180 throw new IllegalStateException(
181 "setSelectedNavigationItem not valid for current navigation mode");
182 }
183 }
184
185 public int getSelectedNavigationItem() {
186 switch (mActionView.getNavigationMode()) {
187 case NAVIGATION_MODE_TABS:
188 return mSelectedTab.getPosition();
189 case NAVIGATION_MODE_DROPDOWN_LIST:
190 return mActionView.getDropdownSelectedPosition();
191 default:
192 return -1;
193 }
194 }
195
Adam Powell661c9082010-07-02 10:09:44 -0700196 private void cleanupTabs() {
197 if (mSelectedTab != null) {
198 selectTab(null);
199 }
200 if (!mTabs.isEmpty()) {
201 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
202 final FragmentTransaction trans = mActivity.openFragmentTransaction();
203 final int tabCount = mTabs.size();
204 for (int i = 0; i < tabCount; i++) {
205 trans.remove(mTabs.get(i).getFragment());
206 }
207 trans.commit();
208 }
209 mTabs.clear();
210 }
211 }
212
Adam Powell0e94b512010-06-29 17:58:20 -0700213 public void setTitle(CharSequence title) {
214 mActionView.setTitle(title);
215 }
216
217 public void setSubtitle(CharSequence subtitle) {
218 mActionView.setSubtitle(subtitle);
219 }
220
Adam Powell89e06452010-06-23 20:24:52 -0700221 public void setDisplayOptions(int options) {
222 mActionView.setDisplayOptions(options);
223 }
Adam Powell0e94b512010-06-29 17:58:20 -0700224
Adam Powell89e06452010-06-23 20:24:52 -0700225 public void setDisplayOptions(int options, int mask) {
226 final int current = mActionView.getDisplayOptions();
227 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
228 }
229
230 public void setBackgroundDrawable(Drawable d) {
231 mActionView.setBackgroundDrawable(d);
232 }
233
234 public View getCustomNavigationView() {
235 return mActionView.getCustomNavigationView();
236 }
237
238 public CharSequence getTitle() {
239 return mActionView.getTitle();
240 }
241
242 public CharSequence getSubtitle() {
243 return mActionView.getSubtitle();
244 }
245
246 public int getNavigationMode() {
247 return mActionView.getNavigationMode();
248 }
249
250 public int getDisplayOptions() {
251 return mActionView.getDisplayOptions();
252 }
253
Adam Powell5d279772010-07-27 16:34:07 -0700254 public ActionMode startActionMode(ActionMode.Callback callback) {
255 if (mActionMode != null) {
256 mActionMode.finish();
Adam Powell6e346362010-07-23 10:18:23 -0700257 }
Adam Powell3461b322010-07-14 11:34:43 -0700258
259 // Don't wait for the close context mode animation to finish.
260 if (mClosingContext) {
261 mAnimatorView.clearAnimation();
262 mHandler.removeCallbacks(mCloseContext);
263 mCloseContext.run();
264 }
265
Adam Powell5d279772010-07-27 16:34:07 -0700266 ActionMode mode = new ActionModeImpl(callback);
Adam Powell6e346362010-07-23 10:18:23 -0700267 if (callback.onCreateActionMode(mode, mode.getMenu())) {
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700268 mode.invalidate();
269 mUpperContextView.initForMode(mode);
Adam Powell89e06452010-06-23 20:24:52 -0700270 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
271 if (mLowerContextView != null) {
272 // TODO animate this
273 mLowerContextView.setVisibility(View.VISIBLE);
274 }
Adam Powell5d279772010-07-27 16:34:07 -0700275 mActionMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700276 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700277 }
Adam Powellac695c62010-07-20 18:19:27 -0700278 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700279 }
280
Adam Powell661c9082010-07-02 10:09:44 -0700281 private void configureTab(Tab tab, int position) {
282 final TabImpl tabi = (TabImpl) tab;
283 final boolean isFirstTab = mTabs.isEmpty();
284 final FragmentTransaction trans = mActivity.openFragmentTransaction();
285 final Fragment frag = tabi.getFragment();
286
287 tabi.setPosition(position);
288 mTabs.add(position, tabi);
289
290 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
291 if (!frag.isAdded()) {
292 trans.add(mTabContainerViewId, frag);
293 }
294 }
295
296 if (isFirstTab) {
297 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
298 trans.show(frag);
299 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
300 trans.add(mTabContainerViewId, frag);
301 }
302 mSelectedTab = tabi;
303 } else {
304 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
305 trans.hide(frag);
306 }
307 }
308 trans.commit();
309 }
310
311 @Override
312 public void addTab(Tab tab) {
313 mActionView.addTab(tab);
314 configureTab(tab, mTabs.size());
315 }
316
317 @Override
318 public void insertTab(Tab tab, int position) {
319 mActionView.insertTab(tab, position);
320 configureTab(tab, position);
321 }
322
323 @Override
324 public Tab newTab() {
325 return new TabImpl();
326 }
327
328 @Override
329 public void removeTab(Tab tab) {
330 removeTabAt(tab.getPosition());
331 }
332
333 @Override
334 public void removeTabAt(int position) {
335 mActionView.removeTabAt(position);
336 mTabs.remove(position);
337
338 final int newTabCount = mTabs.size();
339 for (int i = position; i < newTabCount; i++) {
340 mTabs.get(i).setPosition(i);
341 }
342
343 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
344 }
345
346 @Override
347 public void setTabNavigationMode() {
348 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
349 }
350
351 @Override
352 public void setTabNavigationMode(int containerViewId) {
353 mTabContainerViewId = containerViewId;
354 setTabNavigationMode();
355 }
356
357 @Override
358 public void selectTab(Tab tab) {
359 if (mSelectedTab == tab) {
360 return;
361 }
362
363 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
364 final FragmentTransaction trans = mActivity.openFragmentTransaction();
365 if (mSelectedTab != null) {
366 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
367 trans.hide(mSelectedTab.getFragment());
368 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
369 trans.remove(mSelectedTab.getFragment());
370 }
371 }
372 if (tab != null) {
373 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
374 trans.show(tab.getFragment());
375 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
376 trans.add(mTabContainerViewId, tab.getFragment());
377 }
378 }
379 mSelectedTab = (TabImpl) tab;
380 trans.commit();
381 }
382
Adam Powell89e06452010-06-23 20:24:52 -0700383 /**
384 * @hide
385 */
Adam Powell5d279772010-07-27 16:34:07 -0700386 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
Adam Powell6e346362010-07-23 10:18:23 -0700387 private ActionMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700388 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700389 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700390
Adam Powell5d279772010-07-27 16:34:07 -0700391 public ActionModeImpl(ActionMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700392 mCallback = callback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700393 mMenu = new MenuBuilder(mActionView.getContext());
394 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700395 }
Adam Powell9168f0b2010-08-02 15:46:24 -0700396
397 @Override
398 public MenuInflater getMenuInflater() {
399 return new MenuInflater(mActivity);
400 }
401
Adam Powell89e06452010-06-23 20:24:52 -0700402 @Override
403 public Menu getMenu() {
404 return mMenu;
405 }
406
407 @Override
408 public void finish() {
Adam Powell5d279772010-07-27 16:34:07 -0700409 if (mActionMode != this) {
410 // Not the active action mode - no-op
Adam Powell93b6bc32010-07-22 11:36:35 -0700411 return;
412 }
413
Adam Powell6e346362010-07-23 10:18:23 -0700414 mCallback.onDestroyActionMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700415 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700416
417 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700418 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700419 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
420
Adam Powell89e06452010-06-23 20:24:52 -0700421 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
422 // TODO Animate this
423 mLowerContextView.setVisibility(View.GONE);
424 }
Adam Powell5d279772010-07-27 16:34:07 -0700425 mActionMode = null;
Adam Powell89e06452010-06-23 20:24:52 -0700426 }
427
428 @Override
429 public void invalidate() {
Adam Powell6e346362010-07-23 10:18:23 -0700430 if (mCallback.onPrepareActionMode(this, mMenu)) {
Adam Powell89e06452010-06-23 20:24:52 -0700431 // Refresh content in both context views
432 }
433 }
434
435 @Override
436 public void setCustomView(View view) {
437 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700438 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700439 }
440
441 @Override
442 public void setSubtitle(CharSequence subtitle) {
443 mUpperContextView.setSubtitle(subtitle);
444 }
445
446 @Override
447 public void setTitle(CharSequence title) {
448 mUpperContextView.setTitle(title);
449 }
Adam Powell29ed7572010-07-14 16:24:56 -0700450
451 @Override
Adam Powellc9ae2a22010-07-28 14:44:21 -0700452 public void setTitle(int resId) {
453 setTitle(mActivity.getString(resId));
454 }
455
456 @Override
457 public void setSubtitle(int resId) {
458 setSubtitle(mActivity.getString(resId));
459 }
460
461 @Override
Adam Powell29ed7572010-07-14 16:24:56 -0700462 public CharSequence getTitle() {
463 return mUpperContextView.getTitle();
464 }
465
466 @Override
467 public CharSequence getSubtitle() {
468 return mUpperContextView.getSubtitle();
469 }
Adam Powell89e06452010-06-23 20:24:52 -0700470
Adam Powell29ed7572010-07-14 16:24:56 -0700471 @Override
472 public View getCustomView() {
473 return mCustomView != null ? mCustomView.get() : null;
474 }
475
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700476 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
Adam Powell6e346362010-07-23 10:18:23 -0700477 return mCallback.onActionItemClicked(this, item);
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700478 }
479
480 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
481 }
482
483 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
484 if (!subMenu.hasVisibleItems()) {
485 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700486 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700487
488 new MenuPopupHelper(mActivity, subMenu).show();
489 return true;
490 }
491
492 public void onCloseSubMenu(SubMenuBuilder menu) {
493 }
494
495 public void onMenuModeChange(MenuBuilder menu) {
496 }
Adam Powell661c9082010-07-02 10:09:44 -0700497 }
498
499 /**
500 * @hide
501 */
502 public class TabImpl extends ActionBar.Tab {
503 private Fragment mFragment;
504 private Drawable mIcon;
505 private CharSequence mText;
506 private int mPosition;
507
508 @Override
509 public Fragment getFragment() {
510 return mFragment;
511 }
512
513 @Override
514 public Drawable getIcon() {
515 return mIcon;
516 }
517
518 @Override
519 public int getPosition() {
520 return mPosition;
521 }
522
523 public void setPosition(int position) {
524 mPosition = position;
525 }
526
527 @Override
528 public CharSequence getText() {
529 return mText;
530 }
531
532 @Override
533 public void setFragment(Fragment fragment) {
534 mFragment = fragment;
535 }
536
537 @Override
538 public void setIcon(Drawable icon) {
539 mIcon = icon;
540 }
541
542 @Override
543 public void setText(CharSequence text) {
544 mText = text;
545 }
546
547 @Override
548 public void select() {
549 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700550 }
551 }
552}