blob: 9b26aeb19e26a6017e6c465a491f018929e43aa4 [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
Adam Powelld8b3f2e2010-12-02 13:37:03 -080025import android.animation.Animator;
Adam Powelld8b3f2e2010-12-02 13:37:03 -080026import android.animation.Animator.AnimatorListener;
27import android.animation.AnimatorSet;
Adam Powelle6ec7322010-12-07 15:29:26 -080028import android.animation.ObjectAnimator;
Adam Powell45f1e082010-12-10 14:20:13 -080029import android.animation.TimeInterpolator;
Adam Powell89e06452010-06-23 20:24:52 -070030import android.app.ActionBar;
Adam Powell661c9082010-07-02 10:09:44 -070031import android.app.Activity;
Adam Powelldec9dfd2010-08-09 15:27:54 -070032import android.app.Dialog;
Adam Powell661c9082010-07-02 10:09:44 -070033import android.app.Fragment;
34import android.app.FragmentTransaction;
Adam Powelldec9dfd2010-08-09 15:27:54 -070035import android.content.Context;
Adam Powell89e06452010-06-23 20:24:52 -070036import android.graphics.drawable.Drawable;
Adam Powell0e94b512010-06-29 17:58:20 -070037import android.os.Handler;
Adam Powell6e346362010-07-23 10:18:23 -070038import android.view.ActionMode;
Adam Powell32555f32010-11-17 13:49:22 -080039import android.view.LayoutInflater;
Adam Powell89e06452010-06-23 20:24:52 -070040import android.view.Menu;
Adam Powell9168f0b2010-08-02 15:46:24 -070041import android.view.MenuInflater;
Adam Powell89e06452010-06-23 20:24:52 -070042import android.view.MenuItem;
43import android.view.View;
Adam Powelle6ec7322010-12-07 15:29:26 -080044import android.view.Window;
Adam Powell45f1e082010-12-10 14:20:13 -080045import android.view.animation.DecelerateInterpolator;
Adam Powelld8b3f2e2010-12-02 13:37:03 -080046import android.widget.FrameLayout;
Adam Powell89e06452010-06-23 20:24:52 -070047import android.widget.LinearLayout;
48import android.widget.SpinnerAdapter;
Adam Powell89e06452010-06-23 20:24:52 -070049
Adam Powell29ed7572010-07-14 16:24:56 -070050import java.lang.ref.WeakReference;
Adam Powell661c9082010-07-02 10:09:44 -070051import java.util.ArrayList;
52
Adam Powell89e06452010-06-23 20:24:52 -070053/**
54 * ActionBarImpl is the ActionBar implementation used
55 * by devices of all screen sizes. If it detects a compatible decor,
56 * it will split contextual modes across both the ActionBarView at
57 * the top of the screen and a horizontal LinearLayout at the bottom
58 * which is normally hidden.
59 */
60public class ActionBarImpl extends ActionBar {
61 private static final int NORMAL_VIEW = 0;
62 private static final int CONTEXT_VIEW = 1;
Adam Powell661c9082010-07-02 10:09:44 -070063
Adam Powelldec9dfd2010-08-09 15:27:54 -070064 private Context mContext;
Adam Powell661c9082010-07-02 10:09:44 -070065 private Activity mActivity;
Adam Powelldec9dfd2010-08-09 15:27:54 -070066 private Dialog mDialog;
Adam Powell661c9082010-07-02 10:09:44 -070067
Adam Powelld8b3f2e2010-12-02 13:37:03 -080068 private FrameLayout mContainerView;
Adam Powell89e06452010-06-23 20:24:52 -070069 private ActionBarView mActionView;
70 private ActionBarContextView mUpperContextView;
71 private LinearLayout mLowerContextView;
Adam Powelle6ec7322010-12-07 15:29:26 -080072 private View mContentView;
Adam Powell661c9082010-07-02 10:09:44 -070073
74 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
75
Adam Powell661c9082010-07-02 10:09:44 -070076 private TabImpl mSelectedTab;
Adam Powell0c24a552010-11-03 16:44:11 -070077 private int mSavedTabPosition = INVALID_POSITION;
Adam Powell89e06452010-06-23 20:24:52 -070078
Adam Powell5d279772010-07-27 16:34:07 -070079 private ActionMode mActionMode;
Adam Powell89e06452010-06-23 20:24:52 -070080
Adam Powell8515ee82010-11-30 14:09:55 -080081 private boolean mLastMenuVisibility;
82 private ArrayList<OnMenuVisibilityListener> mMenuVisibilityListeners =
83 new ArrayList<OnMenuVisibilityListener>();
84
Adam Powell89e06452010-06-23 20:24:52 -070085 private static final int CONTEXT_DISPLAY_NORMAL = 0;
86 private static final int CONTEXT_DISPLAY_SPLIT = 1;
87
Adam Powell0c24a552010-11-03 16:44:11 -070088 private static final int INVALID_POSITION = -1;
89
Adam Powell89e06452010-06-23 20:24:52 -070090 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070091
92 final Handler mHandler = new Handler();
Adam Powelld8b3f2e2010-12-02 13:37:03 -080093
Adam Powelle6ec7322010-12-07 15:29:26 -080094 private Animator mCurrentAnim;
Adam Powelld8b3f2e2010-12-02 13:37:03 -080095
Adam Powell45f1e082010-12-10 14:20:13 -080096 private static final TimeInterpolator sFadeOutInterpolator = new DecelerateInterpolator();
97
Adam Powelld8b3f2e2010-12-02 13:37:03 -080098 final AnimatorListener[] mAfterAnimation = new AnimatorListener[] {
99 new AnimatorListener() { // NORMAL_VIEW
100 @Override
101 public void onAnimationStart(Animator animation) {
102 }
103
104 @Override
105 public void onAnimationEnd(Animator animation) {
106 if (mLowerContextView != null) {
107 mLowerContextView.removeAllViews();
108 }
Adam Powelle6ec7322010-12-07 15:29:26 -0800109 mCurrentAnim = null;
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800110 hideAllExcept(NORMAL_VIEW);
111 }
112
113 @Override
114 public void onAnimationCancel(Animator animation) {
115 }
116
117 @Override
118 public void onAnimationRepeat(Animator animation) {
119 }
120 },
121 new AnimatorListener() { // CONTEXT_VIEW
122 @Override
123 public void onAnimationStart(Animator animation) {
124 }
125
126 @Override
127 public void onAnimationEnd(Animator animation) {
Adam Powelle6ec7322010-12-07 15:29:26 -0800128 mCurrentAnim = null;
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800129 hideAllExcept(CONTEXT_VIEW);
130 }
131
132 @Override
133 public void onAnimationCancel(Animator animation) {
134 }
135
136 @Override
137 public void onAnimationRepeat(Animator animation) {
138 }
Adam Powell0e94b512010-06-29 17:58:20 -0700139 }
Adam Powell0e94b512010-06-29 17:58:20 -0700140 };
141
Adam Powelle6ec7322010-12-07 15:29:26 -0800142 final AnimatorListener mHideListener = new AnimatorListener() {
143 @Override
144 public void onAnimationStart(Animator animation) {
145 }
146
147 @Override
148 public void onAnimationEnd(Animator animation) {
149 if (mContentView != null) {
150 mContentView.setTranslationY(0);
151 }
152 mContainerView.setVisibility(View.GONE);
153 mCurrentAnim = null;
154 }
155
156 @Override
157 public void onAnimationCancel(Animator animation) {
158 }
159
160 @Override
161 public void onAnimationRepeat(Animator animation) {
162 }
163 };
164
165 final AnimatorListener mShowListener = new AnimatorListener() {
166 @Override
167 public void onAnimationStart(Animator animation) {
168 }
169
170 @Override
171 public void onAnimationEnd(Animator animation) {
172 mCurrentAnim = null;
Adam Powell45f1e082010-12-10 14:20:13 -0800173 mContainerView.requestLayout();
Adam Powelle6ec7322010-12-07 15:29:26 -0800174 }
175
176 @Override
177 public void onAnimationCancel(Animator animation) {
178 }
179
180 @Override
181 public void onAnimationRepeat(Animator animation) {
182 }
183 };
184
Adam Powell661c9082010-07-02 10:09:44 -0700185 public ActionBarImpl(Activity activity) {
Adam Powell661c9082010-07-02 10:09:44 -0700186 mActivity = activity;
Adam Powelle6ec7322010-12-07 15:29:26 -0800187 Window window = activity.getWindow();
188 View decor = window.getDecorView();
189 init(decor);
190 if (!mActivity.getWindow().hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
191 mContentView = decor.findViewById(android.R.id.content);
192 }
Adam Powelldec9dfd2010-08-09 15:27:54 -0700193 }
194
195 public ActionBarImpl(Dialog dialog) {
196 mDialog = dialog;
197 init(dialog.getWindow().getDecorView());
198 }
199
200 private void init(View decor) {
201 mContext = decor.getContext();
Adam Powell89e06452010-06-23 20:24:52 -0700202 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
203 mUpperContextView = (ActionBarContextView) decor.findViewById(
204 com.android.internal.R.id.action_context_bar);
205 mLowerContextView = (LinearLayout) decor.findViewById(
206 com.android.internal.R.id.lower_action_context_bar);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800207 mContainerView = (FrameLayout) decor.findViewById(
208 com.android.internal.R.id.action_bar_container);
Steve Block08f194b2010-08-24 18:20:48 +0100209
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800210 if (mActionView == null || mUpperContextView == null || mContainerView == null) {
Adam Powell89e06452010-06-23 20:24:52 -0700211 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
212 "with a compatible window decor layout");
213 }
Adam Powell0e94b512010-06-29 17:58:20 -0700214
Steve Block08f194b2010-08-24 18:20:48 +0100215 mActionView.setContextView(mUpperContextView);
Adam Powell89e06452010-06-23 20:24:52 -0700216 mContextDisplayMode = mLowerContextView == null ?
217 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
218 }
219
Adam Powell8515ee82010-11-30 14:09:55 -0800220 public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
221 mMenuVisibilityListeners.add(listener);
222 }
223
224 public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) {
225 mMenuVisibilityListeners.remove(listener);
226 }
227
228 public void dispatchMenuVisibilityChanged(boolean isVisible) {
229 if (isVisible == mLastMenuVisibility) {
230 return;
231 }
232 mLastMenuVisibility = isVisible;
233
234 final int count = mMenuVisibilityListeners.size();
235 for (int i = 0; i < count; i++) {
236 mMenuVisibilityListeners.get(i).onMenuVisibilityChanged(isVisible);
237 }
238 }
239
Adam Powella66c7b02010-07-28 15:28:25 -0700240 @Override
Adam Powella66c7b02010-07-28 15:28:25 -0700241 public void setTitle(int resId) {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700242 setTitle(mContext.getString(resId));
Adam Powella66c7b02010-07-28 15:28:25 -0700243 }
244
245 @Override
246 public void setSubtitle(int resId) {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700247 setSubtitle(mContext.getString(resId));
Adam Powella66c7b02010-07-28 15:28:25 -0700248 }
249
Adam Powell89e06452010-06-23 20:24:52 -0700250 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700251 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700252 setCustomView(view);
253 setDisplayOptions(DISPLAY_SHOW_CUSTOM, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
254 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
Adam Powell89e06452010-06-23 20:24:52 -0700255 mActionView.setCallback(null);
256 }
Adam Powell0e94b512010-06-29 17:58:20 -0700257
Adam Powell8515ee82010-11-30 14:09:55 -0800258 public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) {
Adam Powell17809772010-07-21 13:25:11 -0700259 setDropdownNavigationMode(adapter, callback, -1);
260 }
261
Adam Powell8515ee82010-11-30 14:09:55 -0800262 public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback,
Adam Powell17809772010-07-21 13:25:11 -0700263 int defaultSelectedPosition) {
Adam Powell661c9082010-07-02 10:09:44 -0700264 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700265 setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
266 mActionView.setNavigationMode(NAVIGATION_MODE_LIST);
267 setListNavigationCallbacks(adapter, callback);
Adam Powell17809772010-07-21 13:25:11 -0700268 if (defaultSelectedPosition >= 0) {
269 mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
270 }
Adam Powell89e06452010-06-23 20:24:52 -0700271 }
Adam Powell0e94b512010-06-29 17:58:20 -0700272
273 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700274 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700275 setDisplayOptions(DISPLAY_SHOW_TITLE, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
Adam Powell0e94b512010-06-29 17:58:20 -0700276 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
277 mActionView.setCallback(null);
278 }
279
Adam Powell17809772010-07-21 13:25:11 -0700280 public void setSelectedNavigationItem(int position) {
281 switch (mActionView.getNavigationMode()) {
282 case NAVIGATION_MODE_TABS:
283 selectTab(mTabs.get(position));
284 break;
Adam Powell9ab97872010-10-26 21:47:29 -0700285 case NAVIGATION_MODE_LIST:
Adam Powell17809772010-07-21 13:25:11 -0700286 mActionView.setDropdownSelectedPosition(position);
287 break;
288 default:
289 throw new IllegalStateException(
Adam Powell9ab97872010-10-26 21:47:29 -0700290 "setSelectedNavigationIndex not valid for current navigation mode");
Adam Powell17809772010-07-21 13:25:11 -0700291 }
292 }
293
294 public int getSelectedNavigationItem() {
Adam Powell9ab97872010-10-26 21:47:29 -0700295 return getSelectedNavigationIndex();
296 }
297
298 public void removeAllTabs() {
299 cleanupTabs();
Adam Powell17809772010-07-21 13:25:11 -0700300 }
301
Adam Powell661c9082010-07-02 10:09:44 -0700302 private void cleanupTabs() {
303 if (mSelectedTab != null) {
304 selectTab(null);
305 }
Adam Powell2b6230e2010-09-07 17:55:25 -0700306 mTabs.clear();
Adam Powell0c24a552010-11-03 16:44:11 -0700307 mActionView.removeAllTabs();
308 mSavedTabPosition = INVALID_POSITION;
Adam Powell661c9082010-07-02 10:09:44 -0700309 }
310
Adam Powell0e94b512010-06-29 17:58:20 -0700311 public void setTitle(CharSequence title) {
312 mActionView.setTitle(title);
313 }
314
315 public void setSubtitle(CharSequence subtitle) {
316 mActionView.setSubtitle(subtitle);
317 }
318
Adam Powell89e06452010-06-23 20:24:52 -0700319 public void setDisplayOptions(int options) {
320 mActionView.setDisplayOptions(options);
321 }
Adam Powell0e94b512010-06-29 17:58:20 -0700322
Adam Powell89e06452010-06-23 20:24:52 -0700323 public void setDisplayOptions(int options, int mask) {
324 final int current = mActionView.getDisplayOptions();
325 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
326 }
327
328 public void setBackgroundDrawable(Drawable d) {
329 mActionView.setBackgroundDrawable(d);
330 }
331
332 public View getCustomNavigationView() {
Adam Powellef704442010-11-16 14:48:22 -0800333 return getCustomView();
334 }
335
336 public View getCustomView() {
Adam Powell89e06452010-06-23 20:24:52 -0700337 return mActionView.getCustomNavigationView();
338 }
339
340 public CharSequence getTitle() {
341 return mActionView.getTitle();
342 }
343
344 public CharSequence getSubtitle() {
345 return mActionView.getSubtitle();
346 }
347
348 public int getNavigationMode() {
349 return mActionView.getNavigationMode();
350 }
351
352 public int getDisplayOptions() {
353 return mActionView.getDisplayOptions();
354 }
355
Adam Powell5d279772010-07-27 16:34:07 -0700356 public ActionMode startActionMode(ActionMode.Callback callback) {
357 if (mActionMode != null) {
358 mActionMode.finish();
Adam Powell6e346362010-07-23 10:18:23 -0700359 }
Adam Powell3461b322010-07-14 11:34:43 -0700360
Adam Powell5d279772010-07-27 16:34:07 -0700361 ActionMode mode = new ActionModeImpl(callback);
Adam Powell6e346362010-07-23 10:18:23 -0700362 if (callback.onCreateActionMode(mode, mode.getMenu())) {
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700363 mode.invalidate();
364 mUpperContextView.initForMode(mode);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800365 animateTo(CONTEXT_VIEW);
Adam Powell89e06452010-06-23 20:24:52 -0700366 if (mLowerContextView != null) {
367 // TODO animate this
368 mLowerContextView.setVisibility(View.VISIBLE);
369 }
Adam Powell5d279772010-07-27 16:34:07 -0700370 mActionMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700371 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700372 }
Adam Powellac695c62010-07-20 18:19:27 -0700373 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700374 }
375
Adam Powell661c9082010-07-02 10:09:44 -0700376 private void configureTab(Tab tab, int position) {
377 final TabImpl tabi = (TabImpl) tab;
Adam Powell2b6230e2010-09-07 17:55:25 -0700378 final ActionBar.TabListener callback = tabi.getCallback();
379
380 if (callback == null) {
381 throw new IllegalStateException("Action Bar Tab must have a Callback");
382 }
Adam Powell661c9082010-07-02 10:09:44 -0700383
384 tabi.setPosition(position);
385 mTabs.add(position, tabi);
386
Adam Powell81b89442010-11-02 17:58:56 -0700387 final int count = mTabs.size();
388 for (int i = position + 1; i < count; i++) {
389 mTabs.get(i).setPosition(i);
Adam Powell661c9082010-07-02 10:09:44 -0700390 }
Adam Powell661c9082010-07-02 10:09:44 -0700391 }
392
393 @Override
394 public void addTab(Tab tab) {
Adam Powell81b89442010-11-02 17:58:56 -0700395 addTab(tab, mTabs.isEmpty());
Adam Powell661c9082010-07-02 10:09:44 -0700396 }
397
398 @Override
Adam Powell2b6230e2010-09-07 17:55:25 -0700399 public void addTab(Tab tab, int position) {
Adam Powell81b89442010-11-02 17:58:56 -0700400 addTab(tab, position, mTabs.isEmpty());
401 }
402
403 @Override
404 public void addTab(Tab tab, boolean setSelected) {
405 mActionView.addTab(tab, setSelected);
406 configureTab(tab, mTabs.size());
407 if (setSelected) {
408 selectTab(tab);
409 }
410 }
411
412 @Override
413 public void addTab(Tab tab, int position, boolean setSelected) {
414 mActionView.addTab(tab, position, setSelected);
Adam Powell661c9082010-07-02 10:09:44 -0700415 configureTab(tab, position);
Adam Powell81b89442010-11-02 17:58:56 -0700416 if (setSelected) {
417 selectTab(tab);
418 }
Adam Powell661c9082010-07-02 10:09:44 -0700419 }
420
421 @Override
422 public Tab newTab() {
423 return new TabImpl();
424 }
425
426 @Override
427 public void removeTab(Tab tab) {
428 removeTabAt(tab.getPosition());
429 }
430
431 @Override
432 public void removeTabAt(int position) {
Adam Powell0c24a552010-11-03 16:44:11 -0700433 int selectedTabPosition = mSelectedTab != null
434 ? mSelectedTab.getPosition() : mSavedTabPosition;
Adam Powell661c9082010-07-02 10:09:44 -0700435 mActionView.removeTabAt(position);
436 mTabs.remove(position);
437
438 final int newTabCount = mTabs.size();
439 for (int i = position; i < newTabCount; i++) {
440 mTabs.get(i).setPosition(i);
441 }
442
Adam Powell0c24a552010-11-03 16:44:11 -0700443 if (selectedTabPosition == position) {
444 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
445 }
Adam Powell661c9082010-07-02 10:09:44 -0700446 }
447
448 @Override
449 public void setTabNavigationMode() {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700450 if (mActivity == null) {
451 throw new IllegalStateException(
452 "Tab navigation mode cannot be used outside of an Activity");
453 }
Adam Powell9ab97872010-10-26 21:47:29 -0700454 setDisplayOptions(0, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
Adam Powell661c9082010-07-02 10:09:44 -0700455 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
456 }
457
458 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700459 public void selectTab(Tab tab) {
Adam Powell0c24a552010-11-03 16:44:11 -0700460 if (getNavigationMode() != NAVIGATION_MODE_TABS) {
461 mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
462 return;
463 }
464
465 final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction()
466 .disallowAddToBackStack();
Adam Powell7f9b9052010-10-19 16:56:07 -0700467
468 if (mSelectedTab == tab) {
469 if (mSelectedTab != null) {
470 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
471 }
472 } else {
473 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
474 if (mSelectedTab != null) {
475 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
476 }
477 mSelectedTab = (TabImpl) tab;
478 if (mSelectedTab != null) {
479 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
480 }
Adam Powell2b6230e2010-09-07 17:55:25 -0700481 }
482
483 if (!trans.isEmpty()) {
484 trans.commit();
485 }
486 }
487
488 @Override
489 public Tab getSelectedTab() {
490 return mSelectedTab;
Adam Powell661c9082010-07-02 10:09:44 -0700491 }
492
Adam Powell6b336f82010-08-10 20:13:01 -0700493 @Override
494 public int getHeight() {
495 return mActionView.getHeight();
496 }
497
498 @Override
499 public void show() {
Adam Powelle6ec7322010-12-07 15:29:26 -0800500 if (mContainerView.getVisibility() == View.VISIBLE) {
501 return;
502 }
Adam Powell45f1e082010-12-10 14:20:13 -0800503 if (mCurrentAnim != null) {
504 mCurrentAnim.end();
505 }
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800506 mContainerView.setVisibility(View.VISIBLE);
Adam Powelle6ec7322010-12-07 15:29:26 -0800507 mContainerView.setAlpha(0);
508 mContainerView.setTranslationY(-mContainerView.getHeight());
509 AnimatorSet anim = new AnimatorSet();
510 AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "translationY", 0))
511 .with(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
512 if (mContentView != null) {
513 b.with(ObjectAnimator.ofFloat(mContentView, "translationY", -mContainerView.getHeight(), 0));
514 }
515 anim.addListener(mShowListener);
516 mCurrentAnim = anim;
517 anim.start();
Adam Powell6b336f82010-08-10 20:13:01 -0700518 }
519
520 @Override
521 public void hide() {
Adam Powelle6ec7322010-12-07 15:29:26 -0800522 if (mCurrentAnim != null) {
523 mCurrentAnim.end();
524 }
525 if (mContainerView.getVisibility() == View.GONE) {
526 return;
527 }
528 mContainerView.setAlpha(1);
529 AnimatorSet anim = new AnimatorSet();
530 AnimatorSet.Builder b = anim.play(
531 ObjectAnimator.ofFloat(mContainerView, "translationY", -mContainerView.getHeight()))
532 .with(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
533 if (mContentView != null) {
534 b.with(ObjectAnimator.ofFloat(mContentView, "translationY", 0, -mContainerView.getHeight()));
535 }
536 anim.addListener(mHideListener);
537 mCurrentAnim = anim;
538 anim.start();
Adam Powell6b336f82010-08-10 20:13:01 -0700539 }
540
541 public boolean isShowing() {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800542 return mContainerView.getVisibility() == View.VISIBLE;
543 }
544
545 private long animateTo(int viewIndex) {
Adam Powelle6ec7322010-12-07 15:29:26 -0800546 show();
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800547
548 AnimatorSet set = new AnimatorSet();
549
550 final View targetChild = mContainerView.getChildAt(viewIndex);
551 targetChild.setVisibility(View.VISIBLE);
552 AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1));
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800553
554 final int count = mContainerView.getChildCount();
555 for (int i = 0; i < count; i++) {
556 final View child = mContainerView.getChildAt(i);
557 if (i == viewIndex) {
558 continue;
559 }
560
561 if (child.getVisibility() != View.GONE) {
Adam Powell45f1e082010-12-10 14:20:13 -0800562 Animator a = ObjectAnimator.ofFloat(child, "alpha", 0);
563 a.setInterpolator(sFadeOutInterpolator);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800564 b.with(a);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800565 }
566 }
567
568 set.addListener(mAfterAnimation[viewIndex]);
569
Adam Powelle6ec7322010-12-07 15:29:26 -0800570 mCurrentAnim = set;
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800571 set.start();
572 return set.getDuration();
573 }
574
575 private void hideAllExcept(int viewIndex) {
576 final int count = mContainerView.getChildCount();
577 for (int i = 0; i < count; i++) {
578 mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE);
579 }
Adam Powell6b336f82010-08-10 20:13:01 -0700580 }
581
Adam Powell89e06452010-06-23 20:24:52 -0700582 /**
583 * @hide
584 */
Adam Powell5d279772010-07-27 16:34:07 -0700585 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
Adam Powell6e346362010-07-23 10:18:23 -0700586 private ActionMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700587 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700588 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700589
Adam Powell5d279772010-07-27 16:34:07 -0700590 public ActionModeImpl(ActionMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700591 mCallback = callback;
Adam Powell4d9861e2010-08-17 11:14:40 -0700592 mMenu = new MenuBuilder(mActionView.getContext())
593 .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700594 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700595 }
Adam Powell9168f0b2010-08-02 15:46:24 -0700596
597 @Override
598 public MenuInflater getMenuInflater() {
Adam Powell4d9861e2010-08-17 11:14:40 -0700599 return new MenuInflater(mContext);
Adam Powell9168f0b2010-08-02 15:46:24 -0700600 }
601
Adam Powell89e06452010-06-23 20:24:52 -0700602 @Override
603 public Menu getMenu() {
604 return mMenu;
605 }
606
607 @Override
608 public void finish() {
Adam Powell5d279772010-07-27 16:34:07 -0700609 if (mActionMode != this) {
610 // Not the active action mode - no-op
Adam Powell93b6bc32010-07-22 11:36:35 -0700611 return;
612 }
613
Adam Powell6e346362010-07-23 10:18:23 -0700614 mCallback.onDestroyActionMode(this);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800615 mCallback = null;
616 animateTo(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700617
618 // Clear out the context mode views after the animation finishes
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800619 mUpperContextView.closeMode();
Adam Powell89e06452010-06-23 20:24:52 -0700620 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
621 // TODO Animate this
622 mLowerContextView.setVisibility(View.GONE);
623 }
Adam Powell5d279772010-07-27 16:34:07 -0700624 mActionMode = null;
Adam Powell89e06452010-06-23 20:24:52 -0700625 }
626
627 @Override
628 public void invalidate() {
Adam Powell6e346362010-07-23 10:18:23 -0700629 if (mCallback.onPrepareActionMode(this, mMenu)) {
Adam Powell89e06452010-06-23 20:24:52 -0700630 // Refresh content in both context views
631 }
632 }
633
634 @Override
635 public void setCustomView(View view) {
636 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700637 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700638 }
639
640 @Override
641 public void setSubtitle(CharSequence subtitle) {
642 mUpperContextView.setSubtitle(subtitle);
643 }
644
645 @Override
646 public void setTitle(CharSequence title) {
647 mUpperContextView.setTitle(title);
648 }
Adam Powell29ed7572010-07-14 16:24:56 -0700649
650 @Override
Adam Powellc9ae2a22010-07-28 14:44:21 -0700651 public void setTitle(int resId) {
652 setTitle(mActivity.getString(resId));
653 }
654
655 @Override
656 public void setSubtitle(int resId) {
657 setSubtitle(mActivity.getString(resId));
658 }
659
660 @Override
Adam Powell29ed7572010-07-14 16:24:56 -0700661 public CharSequence getTitle() {
662 return mUpperContextView.getTitle();
663 }
664
665 @Override
666 public CharSequence getSubtitle() {
667 return mUpperContextView.getSubtitle();
668 }
Adam Powell89e06452010-06-23 20:24:52 -0700669
Adam Powell29ed7572010-07-14 16:24:56 -0700670 @Override
671 public View getCustomView() {
672 return mCustomView != null ? mCustomView.get() : null;
673 }
674
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700675 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800676 if (mCallback != null) {
677 return mCallback.onActionItemClicked(this, item);
678 } else {
679 return false;
680 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700681 }
682
683 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
684 }
685
686 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800687 if (mCallback == null) {
688 return false;
689 }
690
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700691 if (!subMenu.hasVisibleItems()) {
692 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700693 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700694
Adam Powelldec9dfd2010-08-09 15:27:54 -0700695 new MenuPopupHelper(mContext, subMenu).show();
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700696 return true;
697 }
698
699 public void onCloseSubMenu(SubMenuBuilder menu) {
700 }
701
702 public void onMenuModeChange(MenuBuilder menu) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800703 if (mCallback == null) {
704 return;
705 }
Adam Powellf6148c52010-08-11 21:10:16 -0700706 invalidate();
Adam Powell8515ee82010-11-30 14:09:55 -0800707 mUpperContextView.openOverflowMenu();
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700708 }
Adam Powell661c9082010-07-02 10:09:44 -0700709 }
710
711 /**
712 * @hide
713 */
714 public class TabImpl extends ActionBar.Tab {
Adam Powell2b6230e2010-09-07 17:55:25 -0700715 private ActionBar.TabListener mCallback;
716 private Object mTag;
Adam Powell661c9082010-07-02 10:09:44 -0700717 private Drawable mIcon;
718 private CharSequence mText;
719 private int mPosition;
Adam Powell2b6230e2010-09-07 17:55:25 -0700720 private View mCustomView;
Adam Powell661c9082010-07-02 10:09:44 -0700721
722 @Override
Adam Powell2b6230e2010-09-07 17:55:25 -0700723 public Object getTag() {
724 return mTag;
725 }
726
727 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700728 public Tab setTag(Object tag) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700729 mTag = tag;
Adam Powell9ab97872010-10-26 21:47:29 -0700730 return this;
Adam Powell2b6230e2010-09-07 17:55:25 -0700731 }
732
733 public ActionBar.TabListener getCallback() {
734 return mCallback;
735 }
736
737 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700738 public Tab setTabListener(ActionBar.TabListener callback) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700739 mCallback = callback;
Adam Powell9ab97872010-10-26 21:47:29 -0700740 return this;
Adam Powell2b6230e2010-09-07 17:55:25 -0700741 }
742
743 @Override
744 public View getCustomView() {
745 return mCustomView;
746 }
747
748 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700749 public Tab setCustomView(View view) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700750 mCustomView = view;
Adam Powell9ab97872010-10-26 21:47:29 -0700751 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700752 }
753
754 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800755 public Tab setCustomView(int layoutResId) {
756 return setCustomView(LayoutInflater.from(mContext).inflate(layoutResId, null));
757 }
758
759 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700760 public Drawable getIcon() {
761 return mIcon;
762 }
763
764 @Override
765 public int getPosition() {
766 return mPosition;
767 }
768
769 public void setPosition(int position) {
770 mPosition = position;
771 }
772
773 @Override
774 public CharSequence getText() {
775 return mText;
776 }
777
778 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700779 public Tab setIcon(Drawable icon) {
Adam Powell661c9082010-07-02 10:09:44 -0700780 mIcon = icon;
Adam Powell9ab97872010-10-26 21:47:29 -0700781 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700782 }
783
784 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800785 public Tab setIcon(int resId) {
786 return setIcon(mContext.getResources().getDrawable(resId));
787 }
788
789 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700790 public Tab setText(CharSequence text) {
Adam Powell661c9082010-07-02 10:09:44 -0700791 mText = text;
Adam Powell9ab97872010-10-26 21:47:29 -0700792 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700793 }
794
795 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800796 public Tab setText(int resId) {
797 return setText(mContext.getResources().getText(resId));
798 }
799
800 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700801 public void select() {
802 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700803 }
804 }
Adam Powell9ab97872010-10-26 21:47:29 -0700805
806 @Override
807 public void setCustomView(View view) {
808 mActionView.setCustomNavigationView(view);
809 }
810
811 @Override
812 public void setCustomView(View view, LayoutParams layoutParams) {
813 view.setLayoutParams(layoutParams);
814 mActionView.setCustomNavigationView(view);
815 }
816
817 @Override
Adam Powell8515ee82010-11-30 14:09:55 -0800818 public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
Adam Powell9ab97872010-10-26 21:47:29 -0700819 mActionView.setDropdownAdapter(adapter);
820 mActionView.setCallback(callback);
821 }
822
823 @Override
824 public int getSelectedNavigationIndex() {
825 switch (mActionView.getNavigationMode()) {
826 case NAVIGATION_MODE_TABS:
Adam Powell04587962010-11-11 22:15:07 -0800827 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
Adam Powell9ab97872010-10-26 21:47:29 -0700828 case NAVIGATION_MODE_LIST:
829 return mActionView.getDropdownSelectedPosition();
830 default:
831 return -1;
832 }
833 }
834
835 @Override
836 public int getNavigationItemCount() {
837 switch (mActionView.getNavigationMode()) {
838 case NAVIGATION_MODE_TABS:
839 return mTabs.size();
840 case NAVIGATION_MODE_LIST:
841 SpinnerAdapter adapter = mActionView.getDropdownAdapter();
842 return adapter != null ? adapter.getCount() : 0;
843 default:
844 return 0;
845 }
846 }
847
848 @Override
Adam Powell0c24a552010-11-03 16:44:11 -0700849 public int getTabCount() {
850 return mTabs.size();
851 }
852
853 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700854 public void setNavigationMode(int mode) {
Adam Powell0c24a552010-11-03 16:44:11 -0700855 final int oldMode = mActionView.getNavigationMode();
856 switch (oldMode) {
857 case NAVIGATION_MODE_TABS:
858 mSavedTabPosition = getSelectedNavigationIndex();
859 selectTab(null);
860 break;
861 }
Adam Powell9ab97872010-10-26 21:47:29 -0700862 mActionView.setNavigationMode(mode);
Adam Powell0c24a552010-11-03 16:44:11 -0700863 switch (mode) {
864 case NAVIGATION_MODE_TABS:
865 if (mSavedTabPosition != INVALID_POSITION) {
866 setSelectedNavigationItem(mSavedTabPosition);
867 mSavedTabPosition = INVALID_POSITION;
868 }
869 break;
870 }
Adam Powell9ab97872010-10-26 21:47:29 -0700871 }
872
873 @Override
874 public Tab getTabAt(int index) {
875 return mTabs.get(index);
876 }
Adam Powell0c24a552010-11-03 16:44:11 -0700877
878 /**
879 * This fragment is added when we're keeping a back stack in a tab switch
880 * transaction. We use it to change the selected tab in the action bar view
881 * when we back out.
882 */
883 private class SwitchSelectedTabViewFragment extends Fragment {
884 private int mSelectedTabIndex;
885
886 public SwitchSelectedTabViewFragment(int oldSelectedTab) {
887 mSelectedTabIndex = oldSelectedTab;
888 }
889
890 @Override
891 public void onDetach() {
892 if (mSelectedTabIndex >= 0 && mSelectedTabIndex < getTabCount()) {
893 mActionView.setTabSelected(mSelectedTabIndex);
894 }
895 }
896 }
Adam Powell89e06452010-06-23 20:24:52 -0700897}