blob: 0860bfeee5110732e92e7e0d4dbcde553f7cdd9b [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;
33import android.view.MenuItem;
34import android.view.View;
35import android.widget.LinearLayout;
36import android.widget.SpinnerAdapter;
37import android.widget.ViewAnimator;
38
Adam Powell29ed7572010-07-14 16:24:56 -070039import java.lang.ref.WeakReference;
Adam Powell661c9082010-07-02 10:09:44 -070040import java.util.ArrayList;
41
Adam Powell89e06452010-06-23 20:24:52 -070042/**
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
Adam Powell661c9082010-07-02 10:09:44 -070053 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
Adam Powell89e06452010-06-23 20:24:52 -070058 private ViewAnimator mAnimatorView;
59 private ActionBarView mActionView;
60 private ActionBarContextView mUpperContextView;
61 private LinearLayout mLowerContextView;
Adam Powell661c9082010-07-02 10:09:44 -070062
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;
Adam Powell89e06452010-06-23 20:24:52 -070068
Adam Powell5d279772010-07-27 16:34:07 -070069 private ActionMode mActionMode;
Adam Powell89e06452010-06-23 20:24:52 -070070
71 private static final int CONTEXT_DISPLAY_NORMAL = 0;
72 private static final int CONTEXT_DISPLAY_SPLIT = 1;
73
74 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070075
Adam Powell3461b322010-07-14 11:34:43 -070076 private boolean mClosingContext;
77
Adam Powell0e94b512010-06-29 17:58:20 -070078 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 }
Adam Powell3461b322010-07-14 11:34:43 -070085 mClosingContext = false;
Adam Powell0e94b512010-06-29 17:58:20 -070086 }
87 };
88
Adam Powell661c9082010-07-02 10:09:44 -070089 public ActionBarImpl(Activity activity) {
90 final View decor = activity.getWindow().getDecorView();
91 mActivity = activity;
Adam Powell89e06452010-06-23 20:24:52 -070092 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 }
Adam Powell0e94b512010-06-29 17:58:20 -0700104
Adam Powell89e06452010-06-23 20:24:52 -0700105 mContextDisplayMode = mLowerContextView == null ?
106 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
107 }
108
Adam Powella66c7b02010-07-28 15:28:25 -0700109 @Override
110 public void setStandardNavigationMode(int titleResId, int subtitleResId) {
111 setStandardNavigationMode(mActivity.getString(titleResId),
112 mActivity.getString(subtitleResId));
113 }
114
115 @Override
116 public void setStandardNavigationMode(int titleResId) {
117 setStandardNavigationMode(mActivity.getString(titleResId));
118 }
119
120 @Override
121 public void setTitle(int resId) {
122 setTitle(mActivity.getString(resId));
123 }
124
125 @Override
126 public void setSubtitle(int resId) {
127 setSubtitle(mActivity.getString(resId));
128 }
129
Adam Powell89e06452010-06-23 20:24:52 -0700130 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700131 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700132 mActionView.setCustomNavigationView(view);
133 mActionView.setCallback(null);
134 }
Adam Powell0e94b512010-06-29 17:58:20 -0700135
Adam Powell89e06452010-06-23 20:24:52 -0700136 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell17809772010-07-21 13:25:11 -0700137 setDropdownNavigationMode(adapter, callback, -1);
138 }
139
140 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
141 int defaultSelectedPosition) {
Adam Powell661c9082010-07-02 10:09:44 -0700142 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700143 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
144 mActionView.setDropdownAdapter(adapter);
Adam Powell17809772010-07-21 13:25:11 -0700145 if (defaultSelectedPosition >= 0) {
146 mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
147 }
148 mActionView.setCallback(callback);
Adam Powell89e06452010-06-23 20:24:52 -0700149 }
Adam Powell0e94b512010-06-29 17:58:20 -0700150
151 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700152 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700153 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
154 mActionView.setCallback(null);
155 }
156
Adam Powell89e06452010-06-23 20:24:52 -0700157 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700158 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700159 setStandardNavigationMode(title, null);
160 }
Adam Powell0e94b512010-06-29 17:58:20 -0700161
Adam Powell89e06452010-06-23 20:24:52 -0700162 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700163 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700164 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
165 mActionView.setTitle(title);
166 mActionView.setSubtitle(subtitle);
167 mActionView.setCallback(null);
168 }
169
Adam Powell17809772010-07-21 13:25:11 -0700170 public void setSelectedNavigationItem(int position) {
171 switch (mActionView.getNavigationMode()) {
172 case NAVIGATION_MODE_TABS:
173 selectTab(mTabs.get(position));
174 break;
175 case NAVIGATION_MODE_DROPDOWN_LIST:
176 mActionView.setDropdownSelectedPosition(position);
177 break;
178 default:
179 throw new IllegalStateException(
180 "setSelectedNavigationItem not valid for current navigation mode");
181 }
182 }
183
184 public int getSelectedNavigationItem() {
185 switch (mActionView.getNavigationMode()) {
186 case NAVIGATION_MODE_TABS:
187 return mSelectedTab.getPosition();
188 case NAVIGATION_MODE_DROPDOWN_LIST:
189 return mActionView.getDropdownSelectedPosition();
190 default:
191 return -1;
192 }
193 }
194
Adam Powell661c9082010-07-02 10:09:44 -0700195 private void cleanupTabs() {
196 if (mSelectedTab != null) {
197 selectTab(null);
198 }
199 if (!mTabs.isEmpty()) {
200 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
201 final FragmentTransaction trans = mActivity.openFragmentTransaction();
202 final int tabCount = mTabs.size();
203 for (int i = 0; i < tabCount; i++) {
204 trans.remove(mTabs.get(i).getFragment());
205 }
206 trans.commit();
207 }
208 mTabs.clear();
209 }
210 }
211
Adam Powell0e94b512010-06-29 17:58:20 -0700212 public void setTitle(CharSequence title) {
213 mActionView.setTitle(title);
214 }
215
216 public void setSubtitle(CharSequence subtitle) {
217 mActionView.setSubtitle(subtitle);
218 }
219
Adam Powell89e06452010-06-23 20:24:52 -0700220 public void setDisplayOptions(int options) {
221 mActionView.setDisplayOptions(options);
222 }
Adam Powell0e94b512010-06-29 17:58:20 -0700223
Adam Powell89e06452010-06-23 20:24:52 -0700224 public void setDisplayOptions(int options, int mask) {
225 final int current = mActionView.getDisplayOptions();
226 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
227 }
228
229 public void setBackgroundDrawable(Drawable d) {
230 mActionView.setBackgroundDrawable(d);
231 }
232
233 public View getCustomNavigationView() {
234 return mActionView.getCustomNavigationView();
235 }
236
237 public CharSequence getTitle() {
238 return mActionView.getTitle();
239 }
240
241 public CharSequence getSubtitle() {
242 return mActionView.getSubtitle();
243 }
244
245 public int getNavigationMode() {
246 return mActionView.getNavigationMode();
247 }
248
249 public int getDisplayOptions() {
250 return mActionView.getDisplayOptions();
251 }
252
Adam Powell5d279772010-07-27 16:34:07 -0700253 public ActionMode startActionMode(ActionMode.Callback callback) {
254 if (mActionMode != null) {
255 mActionMode.finish();
Adam Powell6e346362010-07-23 10:18:23 -0700256 }
Adam Powell3461b322010-07-14 11:34:43 -0700257
258 // Don't wait for the close context mode animation to finish.
259 if (mClosingContext) {
260 mAnimatorView.clearAnimation();
261 mHandler.removeCallbacks(mCloseContext);
262 mCloseContext.run();
263 }
264
Adam Powell5d279772010-07-27 16:34:07 -0700265 ActionMode mode = new ActionModeImpl(callback);
Adam Powell6e346362010-07-23 10:18:23 -0700266 if (callback.onCreateActionMode(mode, mode.getMenu())) {
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700267 mode.invalidate();
268 mUpperContextView.initForMode(mode);
Adam Powell89e06452010-06-23 20:24:52 -0700269 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
270 if (mLowerContextView != null) {
271 // TODO animate this
272 mLowerContextView.setVisibility(View.VISIBLE);
273 }
Adam Powell5d279772010-07-27 16:34:07 -0700274 mActionMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700275 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700276 }
Adam Powellac695c62010-07-20 18:19:27 -0700277 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700278 }
279
Adam Powell661c9082010-07-02 10:09:44 -0700280 private void configureTab(Tab tab, int position) {
281 final TabImpl tabi = (TabImpl) tab;
282 final boolean isFirstTab = mTabs.isEmpty();
283 final FragmentTransaction trans = mActivity.openFragmentTransaction();
284 final Fragment frag = tabi.getFragment();
285
286 tabi.setPosition(position);
287 mTabs.add(position, tabi);
288
289 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
290 if (!frag.isAdded()) {
291 trans.add(mTabContainerViewId, frag);
292 }
293 }
294
295 if (isFirstTab) {
296 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
297 trans.show(frag);
298 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
299 trans.add(mTabContainerViewId, frag);
300 }
301 mSelectedTab = tabi;
302 } else {
303 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
304 trans.hide(frag);
305 }
306 }
307 trans.commit();
308 }
309
310 @Override
311 public void addTab(Tab tab) {
312 mActionView.addTab(tab);
313 configureTab(tab, mTabs.size());
314 }
315
316 @Override
317 public void insertTab(Tab tab, int position) {
318 mActionView.insertTab(tab, position);
319 configureTab(tab, position);
320 }
321
322 @Override
323 public Tab newTab() {
324 return new TabImpl();
325 }
326
327 @Override
328 public void removeTab(Tab tab) {
329 removeTabAt(tab.getPosition());
330 }
331
332 @Override
333 public void removeTabAt(int position) {
334 mActionView.removeTabAt(position);
335 mTabs.remove(position);
336
337 final int newTabCount = mTabs.size();
338 for (int i = position; i < newTabCount; i++) {
339 mTabs.get(i).setPosition(i);
340 }
341
342 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
343 }
344
345 @Override
346 public void setTabNavigationMode() {
347 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
348 }
349
350 @Override
351 public void setTabNavigationMode(int containerViewId) {
352 mTabContainerViewId = containerViewId;
353 setTabNavigationMode();
354 }
355
356 @Override
357 public void selectTab(Tab tab) {
358 if (mSelectedTab == tab) {
359 return;
360 }
361
362 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
363 final FragmentTransaction trans = mActivity.openFragmentTransaction();
364 if (mSelectedTab != null) {
365 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
366 trans.hide(mSelectedTab.getFragment());
367 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
368 trans.remove(mSelectedTab.getFragment());
369 }
370 }
371 if (tab != null) {
372 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
373 trans.show(tab.getFragment());
374 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
375 trans.add(mTabContainerViewId, tab.getFragment());
376 }
377 }
378 mSelectedTab = (TabImpl) tab;
379 trans.commit();
380 }
381
Adam Powell89e06452010-06-23 20:24:52 -0700382 /**
383 * @hide
384 */
Adam Powell5d279772010-07-27 16:34:07 -0700385 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
Adam Powell6e346362010-07-23 10:18:23 -0700386 private ActionMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700387 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700388 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700389
Adam Powell5d279772010-07-27 16:34:07 -0700390 public ActionModeImpl(ActionMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700391 mCallback = callback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700392 mMenu = new MenuBuilder(mActionView.getContext());
393 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700394 }
395
396 @Override
397 public Menu getMenu() {
398 return mMenu;
399 }
400
401 @Override
402 public void finish() {
Adam Powell5d279772010-07-27 16:34:07 -0700403 if (mActionMode != this) {
404 // Not the active action mode - no-op
Adam Powell93b6bc32010-07-22 11:36:35 -0700405 return;
406 }
407
Adam Powell6e346362010-07-23 10:18:23 -0700408 mCallback.onDestroyActionMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700409 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700410
411 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700412 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700413 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
414
Adam Powell89e06452010-06-23 20:24:52 -0700415 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
416 // TODO Animate this
417 mLowerContextView.setVisibility(View.GONE);
418 }
Adam Powell5d279772010-07-27 16:34:07 -0700419 mActionMode = null;
Adam Powell89e06452010-06-23 20:24:52 -0700420 }
421
422 @Override
423 public void invalidate() {
Adam Powell6e346362010-07-23 10:18:23 -0700424 if (mCallback.onPrepareActionMode(this, mMenu)) {
Adam Powell89e06452010-06-23 20:24:52 -0700425 // Refresh content in both context views
426 }
427 }
428
429 @Override
430 public void setCustomView(View view) {
431 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700432 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700433 }
434
435 @Override
436 public void setSubtitle(CharSequence subtitle) {
437 mUpperContextView.setSubtitle(subtitle);
438 }
439
440 @Override
441 public void setTitle(CharSequence title) {
442 mUpperContextView.setTitle(title);
443 }
Adam Powell29ed7572010-07-14 16:24:56 -0700444
445 @Override
Adam Powellc9ae2a22010-07-28 14:44:21 -0700446 public void setTitle(int resId) {
447 setTitle(mActivity.getString(resId));
448 }
449
450 @Override
451 public void setSubtitle(int resId) {
452 setSubtitle(mActivity.getString(resId));
453 }
454
455 @Override
Adam Powell29ed7572010-07-14 16:24:56 -0700456 public CharSequence getTitle() {
457 return mUpperContextView.getTitle();
458 }
459
460 @Override
461 public CharSequence getSubtitle() {
462 return mUpperContextView.getSubtitle();
463 }
Adam Powell89e06452010-06-23 20:24:52 -0700464
Adam Powell29ed7572010-07-14 16:24:56 -0700465 @Override
466 public View getCustomView() {
467 return mCustomView != null ? mCustomView.get() : null;
468 }
469
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700470 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
Adam Powell6e346362010-07-23 10:18:23 -0700471 return mCallback.onActionItemClicked(this, item);
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700472 }
473
474 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
475 }
476
477 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
478 if (!subMenu.hasVisibleItems()) {
479 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700480 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700481
482 new MenuPopupHelper(mActivity, subMenu).show();
483 return true;
484 }
485
486 public void onCloseSubMenu(SubMenuBuilder menu) {
487 }
488
489 public void onMenuModeChange(MenuBuilder menu) {
490 }
Adam Powell661c9082010-07-02 10:09:44 -0700491 }
492
493 /**
494 * @hide
495 */
496 public class TabImpl extends ActionBar.Tab {
497 private Fragment mFragment;
498 private Drawable mIcon;
499 private CharSequence mText;
500 private int mPosition;
501
502 @Override
503 public Fragment getFragment() {
504 return mFragment;
505 }
506
507 @Override
508 public Drawable getIcon() {
509 return mIcon;
510 }
511
512 @Override
513 public int getPosition() {
514 return mPosition;
515 }
516
517 public void setPosition(int position) {
518 mPosition = position;
519 }
520
521 @Override
522 public CharSequence getText() {
523 return mText;
524 }
525
526 @Override
527 public void setFragment(Fragment fragment) {
528 mFragment = fragment;
529 }
530
531 @Override
532 public void setIcon(Drawable icon) {
533 mIcon = icon;
534 }
535
536 @Override
537 public void setText(CharSequence text) {
538 mText = text;
539 }
540
541 @Override
542 public void select() {
543 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700544 }
545 }
546}