blob: 99dbe4ce6a44257f79388c5f8f9c0d7a417f3504 [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
109 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700110 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700111 mActionView.setCustomNavigationView(view);
112 mActionView.setCallback(null);
113 }
Adam Powell0e94b512010-06-29 17:58:20 -0700114
Adam Powell89e06452010-06-23 20:24:52 -0700115 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell17809772010-07-21 13:25:11 -0700116 setDropdownNavigationMode(adapter, callback, -1);
117 }
118
119 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback,
120 int defaultSelectedPosition) {
Adam Powell661c9082010-07-02 10:09:44 -0700121 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700122 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
123 mActionView.setDropdownAdapter(adapter);
Adam Powell17809772010-07-21 13:25:11 -0700124 if (defaultSelectedPosition >= 0) {
125 mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
126 }
127 mActionView.setCallback(callback);
Adam Powell89e06452010-06-23 20:24:52 -0700128 }
Adam Powell0e94b512010-06-29 17:58:20 -0700129
130 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700131 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700132 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
133 mActionView.setCallback(null);
134 }
135
Adam Powell89e06452010-06-23 20:24:52 -0700136 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700137 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700138 setStandardNavigationMode(title, null);
139 }
Adam Powell0e94b512010-06-29 17:58:20 -0700140
Adam Powell89e06452010-06-23 20:24:52 -0700141 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700142 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700143 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
144 mActionView.setTitle(title);
145 mActionView.setSubtitle(subtitle);
146 mActionView.setCallback(null);
147 }
148
Adam Powell17809772010-07-21 13:25:11 -0700149 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
Adam Powell661c9082010-07-02 10:09:44 -0700174 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
Adam Powell0e94b512010-06-29 17:58:20 -0700191 public void setTitle(CharSequence title) {
192 mActionView.setTitle(title);
193 }
194
195 public void setSubtitle(CharSequence subtitle) {
196 mActionView.setSubtitle(subtitle);
197 }
198
Adam Powell89e06452010-06-23 20:24:52 -0700199 public void setDisplayOptions(int options) {
200 mActionView.setDisplayOptions(options);
201 }
Adam Powell0e94b512010-06-29 17:58:20 -0700202
Adam Powell89e06452010-06-23 20:24:52 -0700203 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
Adam Powell5d279772010-07-27 16:34:07 -0700232 public ActionMode startActionMode(ActionMode.Callback callback) {
233 if (mActionMode != null) {
234 mActionMode.finish();
Adam Powell6e346362010-07-23 10:18:23 -0700235 }
Adam Powell3461b322010-07-14 11:34:43 -0700236
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
Adam Powell5d279772010-07-27 16:34:07 -0700244 ActionMode mode = new ActionModeImpl(callback);
Adam Powell6e346362010-07-23 10:18:23 -0700245 if (callback.onCreateActionMode(mode, mode.getMenu())) {
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700246 mode.invalidate();
247 mUpperContextView.initForMode(mode);
Adam Powell89e06452010-06-23 20:24:52 -0700248 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
249 if (mLowerContextView != null) {
250 // TODO animate this
251 mLowerContextView.setVisibility(View.VISIBLE);
252 }
Adam Powell5d279772010-07-27 16:34:07 -0700253 mActionMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700254 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700255 }
Adam Powellac695c62010-07-20 18:19:27 -0700256 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700257 }
258
Adam Powell661c9082010-07-02 10:09:44 -0700259 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
Adam Powell89e06452010-06-23 20:24:52 -0700361 /**
362 * @hide
363 */
Adam Powell5d279772010-07-27 16:34:07 -0700364 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
Adam Powell6e346362010-07-23 10:18:23 -0700365 private ActionMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700366 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700367 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700368
Adam Powell5d279772010-07-27 16:34:07 -0700369 public ActionModeImpl(ActionMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700370 mCallback = callback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700371 mMenu = new MenuBuilder(mActionView.getContext());
372 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700373 }
374
375 @Override
376 public Menu getMenu() {
377 return mMenu;
378 }
379
380 @Override
381 public void finish() {
Adam Powell5d279772010-07-27 16:34:07 -0700382 if (mActionMode != this) {
383 // Not the active action mode - no-op
Adam Powell93b6bc32010-07-22 11:36:35 -0700384 return;
385 }
386
Adam Powell6e346362010-07-23 10:18:23 -0700387 mCallback.onDestroyActionMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700388 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700389
390 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700391 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700392 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
393
Adam Powell89e06452010-06-23 20:24:52 -0700394 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
395 // TODO Animate this
396 mLowerContextView.setVisibility(View.GONE);
397 }
Adam Powell5d279772010-07-27 16:34:07 -0700398 mActionMode = null;
Adam Powell89e06452010-06-23 20:24:52 -0700399 }
400
401 @Override
402 public void invalidate() {
Adam Powell6e346362010-07-23 10:18:23 -0700403 if (mCallback.onPrepareActionMode(this, mMenu)) {
Adam Powell89e06452010-06-23 20:24:52 -0700404 // Refresh content in both context views
405 }
406 }
407
408 @Override
409 public void setCustomView(View view) {
410 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700411 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700412 }
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 }
Adam Powell29ed7572010-07-14 16:24:56 -0700423
424 @Override
Adam Powellc9ae2a22010-07-28 14:44:21 -0700425 public void setTitle(int resId) {
426 setTitle(mActivity.getString(resId));
427 }
428
429 @Override
430 public void setSubtitle(int resId) {
431 setSubtitle(mActivity.getString(resId));
432 }
433
434 @Override
Adam Powell29ed7572010-07-14 16:24:56 -0700435 public CharSequence getTitle() {
436 return mUpperContextView.getTitle();
437 }
438
439 @Override
440 public CharSequence getSubtitle() {
441 return mUpperContextView.getSubtitle();
442 }
Adam Powell89e06452010-06-23 20:24:52 -0700443
Adam Powell29ed7572010-07-14 16:24:56 -0700444 @Override
445 public View getCustomView() {
446 return mCustomView != null ? mCustomView.get() : null;
447 }
448
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700449 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
Adam Powell6e346362010-07-23 10:18:23 -0700450 return mCallback.onActionItemClicked(this, item);
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700451 }
452
453 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
454 }
455
456 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
457 if (!subMenu.hasVisibleItems()) {
458 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700459 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700460
461 new MenuPopupHelper(mActivity, subMenu).show();
462 return true;
463 }
464
465 public void onCloseSubMenu(SubMenuBuilder menu) {
466 }
467
468 public void onMenuModeChange(MenuBuilder menu) {
469 }
Adam Powell661c9082010-07-02 10:09:44 -0700470 }
471
472 /**
473 * @hide
474 */
475 public class TabImpl extends ActionBar.Tab {
476 private Fragment mFragment;
477 private Drawable mIcon;
478 private CharSequence mText;
479 private int mPosition;
480
481 @Override
482 public Fragment getFragment() {
483 return mFragment;
484 }
485
486 @Override
487 public Drawable getIcon() {
488 return mIcon;
489 }
490
491 @Override
492 public int getPosition() {
493 return mPosition;
494 }
495
496 public void setPosition(int position) {
497 mPosition = position;
498 }
499
500 @Override
501 public CharSequence getText() {
502 return mText;
503 }
504
505 @Override
506 public void setFragment(Fragment fragment) {
507 mFragment = fragment;
508 }
509
510 @Override
511 public void setIcon(Drawable icon) {
512 mIcon = icon;
513 }
514
515 @Override
516 public void setText(CharSequence text) {
517 mText = text;
518 }
519
520 @Override
521 public void select() {
522 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700523 }
524 }
525}