blob: 4ee3083887acc029febd18cf69b60498c2c46271 [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 Powell3f476b32011-01-03 19:25:36 -0800241 public void setCustomView(int resId) {
242 setCustomView(LayoutInflater.from(mContext).inflate(resId, mActionView, false));
243 }
244
245 @Override
246 public void setDisplayUseLogoEnabled(boolean useLogo) {
247 setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO);
248 }
249
250 @Override
251 public void setDisplayShowHomeEnabled(boolean showHome) {
252 setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME);
253 }
254
255 @Override
256 public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) {
257 setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP);
258 }
259
260 @Override
261 public void setDisplayShowTitleEnabled(boolean showTitle) {
262 setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE);
263 }
264
265 @Override
266 public void setDisplayShowCustomEnabled(boolean showCustom) {
267 setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM);
268 }
269
270 @Override
Adam Powella66c7b02010-07-28 15:28:25 -0700271 public void setTitle(int resId) {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700272 setTitle(mContext.getString(resId));
Adam Powella66c7b02010-07-28 15:28:25 -0700273 }
274
275 @Override
276 public void setSubtitle(int resId) {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700277 setSubtitle(mContext.getString(resId));
Adam Powella66c7b02010-07-28 15:28:25 -0700278 }
279
Adam Powell89e06452010-06-23 20:24:52 -0700280 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700281 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700282 setCustomView(view);
283 setDisplayOptions(DISPLAY_SHOW_CUSTOM, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
284 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
Adam Powell89e06452010-06-23 20:24:52 -0700285 mActionView.setCallback(null);
286 }
Adam Powell0e94b512010-06-29 17:58:20 -0700287
Adam Powell8515ee82010-11-30 14:09:55 -0800288 public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback) {
Adam Powell17809772010-07-21 13:25:11 -0700289 setDropdownNavigationMode(adapter, callback, -1);
290 }
291
Adam Powell8515ee82010-11-30 14:09:55 -0800292 public void setDropdownNavigationMode(SpinnerAdapter adapter, OnNavigationListener callback,
Adam Powell17809772010-07-21 13:25:11 -0700293 int defaultSelectedPosition) {
Adam Powell661c9082010-07-02 10:09:44 -0700294 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700295 setDisplayOptions(0, DISPLAY_SHOW_CUSTOM | DISPLAY_SHOW_TITLE);
296 mActionView.setNavigationMode(NAVIGATION_MODE_LIST);
297 setListNavigationCallbacks(adapter, callback);
Adam Powell17809772010-07-21 13:25:11 -0700298 if (defaultSelectedPosition >= 0) {
299 mActionView.setDropdownSelectedPosition(defaultSelectedPosition);
300 }
Adam Powell89e06452010-06-23 20:24:52 -0700301 }
Adam Powell0e94b512010-06-29 17:58:20 -0700302
303 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700304 cleanupTabs();
Adam Powell9ab97872010-10-26 21:47:29 -0700305 setDisplayOptions(DISPLAY_SHOW_TITLE, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
Adam Powell0e94b512010-06-29 17:58:20 -0700306 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
307 mActionView.setCallback(null);
308 }
309
Adam Powell17809772010-07-21 13:25:11 -0700310 public void setSelectedNavigationItem(int position) {
311 switch (mActionView.getNavigationMode()) {
312 case NAVIGATION_MODE_TABS:
313 selectTab(mTabs.get(position));
314 break;
Adam Powell9ab97872010-10-26 21:47:29 -0700315 case NAVIGATION_MODE_LIST:
Adam Powell17809772010-07-21 13:25:11 -0700316 mActionView.setDropdownSelectedPosition(position);
317 break;
318 default:
319 throw new IllegalStateException(
Adam Powell9ab97872010-10-26 21:47:29 -0700320 "setSelectedNavigationIndex not valid for current navigation mode");
Adam Powell17809772010-07-21 13:25:11 -0700321 }
322 }
323
324 public int getSelectedNavigationItem() {
Adam Powell9ab97872010-10-26 21:47:29 -0700325 return getSelectedNavigationIndex();
326 }
327
328 public void removeAllTabs() {
329 cleanupTabs();
Adam Powell17809772010-07-21 13:25:11 -0700330 }
331
Adam Powell661c9082010-07-02 10:09:44 -0700332 private void cleanupTabs() {
333 if (mSelectedTab != null) {
334 selectTab(null);
335 }
Adam Powell2b6230e2010-09-07 17:55:25 -0700336 mTabs.clear();
Adam Powell0c24a552010-11-03 16:44:11 -0700337 mActionView.removeAllTabs();
338 mSavedTabPosition = INVALID_POSITION;
Adam Powell661c9082010-07-02 10:09:44 -0700339 }
340
Adam Powell0e94b512010-06-29 17:58:20 -0700341 public void setTitle(CharSequence title) {
342 mActionView.setTitle(title);
343 }
344
345 public void setSubtitle(CharSequence subtitle) {
346 mActionView.setSubtitle(subtitle);
347 }
348
Adam Powell89e06452010-06-23 20:24:52 -0700349 public void setDisplayOptions(int options) {
350 mActionView.setDisplayOptions(options);
351 }
Adam Powell0e94b512010-06-29 17:58:20 -0700352
Adam Powell89e06452010-06-23 20:24:52 -0700353 public void setDisplayOptions(int options, int mask) {
354 final int current = mActionView.getDisplayOptions();
355 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
356 }
357
358 public void setBackgroundDrawable(Drawable d) {
Adam Powelle8c1e5c2010-12-13 10:49:32 -0800359 mContainerView.setBackgroundDrawable(d);
Adam Powell89e06452010-06-23 20:24:52 -0700360 }
361
362 public View getCustomNavigationView() {
Adam Powellef704442010-11-16 14:48:22 -0800363 return getCustomView();
364 }
365
366 public View getCustomView() {
Adam Powell89e06452010-06-23 20:24:52 -0700367 return mActionView.getCustomNavigationView();
368 }
369
370 public CharSequence getTitle() {
371 return mActionView.getTitle();
372 }
373
374 public CharSequence getSubtitle() {
375 return mActionView.getSubtitle();
376 }
377
378 public int getNavigationMode() {
379 return mActionView.getNavigationMode();
380 }
381
382 public int getDisplayOptions() {
383 return mActionView.getDisplayOptions();
384 }
385
Adam Powell5d279772010-07-27 16:34:07 -0700386 public ActionMode startActionMode(ActionMode.Callback callback) {
387 if (mActionMode != null) {
388 mActionMode.finish();
Adam Powell6e346362010-07-23 10:18:23 -0700389 }
Adam Powell3461b322010-07-14 11:34:43 -0700390
Adam Powell5d279772010-07-27 16:34:07 -0700391 ActionMode mode = new ActionModeImpl(callback);
Adam Powell6e346362010-07-23 10:18:23 -0700392 if (callback.onCreateActionMode(mode, mode.getMenu())) {
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700393 mode.invalidate();
394 mUpperContextView.initForMode(mode);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800395 animateTo(CONTEXT_VIEW);
Adam Powell89e06452010-06-23 20:24:52 -0700396 if (mLowerContextView != null) {
397 // TODO animate this
398 mLowerContextView.setVisibility(View.VISIBLE);
399 }
Adam Powell5d279772010-07-27 16:34:07 -0700400 mActionMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700401 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700402 }
Adam Powellac695c62010-07-20 18:19:27 -0700403 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700404 }
405
Adam Powell661c9082010-07-02 10:09:44 -0700406 private void configureTab(Tab tab, int position) {
407 final TabImpl tabi = (TabImpl) tab;
Adam Powell2b6230e2010-09-07 17:55:25 -0700408 final ActionBar.TabListener callback = tabi.getCallback();
409
410 if (callback == null) {
411 throw new IllegalStateException("Action Bar Tab must have a Callback");
412 }
Adam Powell661c9082010-07-02 10:09:44 -0700413
414 tabi.setPosition(position);
415 mTabs.add(position, tabi);
416
Adam Powell81b89442010-11-02 17:58:56 -0700417 final int count = mTabs.size();
418 for (int i = position + 1; i < count; i++) {
419 mTabs.get(i).setPosition(i);
Adam Powell661c9082010-07-02 10:09:44 -0700420 }
Adam Powell661c9082010-07-02 10:09:44 -0700421 }
422
423 @Override
424 public void addTab(Tab tab) {
Adam Powell81b89442010-11-02 17:58:56 -0700425 addTab(tab, mTabs.isEmpty());
Adam Powell661c9082010-07-02 10:09:44 -0700426 }
427
428 @Override
Adam Powell2b6230e2010-09-07 17:55:25 -0700429 public void addTab(Tab tab, int position) {
Adam Powell81b89442010-11-02 17:58:56 -0700430 addTab(tab, position, mTabs.isEmpty());
431 }
432
433 @Override
434 public void addTab(Tab tab, boolean setSelected) {
435 mActionView.addTab(tab, setSelected);
436 configureTab(tab, mTabs.size());
437 if (setSelected) {
438 selectTab(tab);
439 }
440 }
441
442 @Override
443 public void addTab(Tab tab, int position, boolean setSelected) {
444 mActionView.addTab(tab, position, setSelected);
Adam Powell661c9082010-07-02 10:09:44 -0700445 configureTab(tab, position);
Adam Powell81b89442010-11-02 17:58:56 -0700446 if (setSelected) {
447 selectTab(tab);
448 }
Adam Powell661c9082010-07-02 10:09:44 -0700449 }
450
451 @Override
452 public Tab newTab() {
453 return new TabImpl();
454 }
455
456 @Override
457 public void removeTab(Tab tab) {
458 removeTabAt(tab.getPosition());
459 }
460
461 @Override
462 public void removeTabAt(int position) {
Adam Powell0c24a552010-11-03 16:44:11 -0700463 int selectedTabPosition = mSelectedTab != null
464 ? mSelectedTab.getPosition() : mSavedTabPosition;
Adam Powell661c9082010-07-02 10:09:44 -0700465 mActionView.removeTabAt(position);
466 mTabs.remove(position);
467
468 final int newTabCount = mTabs.size();
469 for (int i = position; i < newTabCount; i++) {
470 mTabs.get(i).setPosition(i);
471 }
472
Adam Powell0c24a552010-11-03 16:44:11 -0700473 if (selectedTabPosition == position) {
474 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
475 }
Adam Powell661c9082010-07-02 10:09:44 -0700476 }
477
478 @Override
479 public void setTabNavigationMode() {
Adam Powelldec9dfd2010-08-09 15:27:54 -0700480 if (mActivity == null) {
481 throw new IllegalStateException(
482 "Tab navigation mode cannot be used outside of an Activity");
483 }
Adam Powell9ab97872010-10-26 21:47:29 -0700484 setDisplayOptions(0, DISPLAY_SHOW_TITLE | DISPLAY_SHOW_CUSTOM);
Adam Powell661c9082010-07-02 10:09:44 -0700485 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
486 }
487
488 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700489 public void selectTab(Tab tab) {
Adam Powell0c24a552010-11-03 16:44:11 -0700490 if (getNavigationMode() != NAVIGATION_MODE_TABS) {
491 mSavedTabPosition = tab != null ? tab.getPosition() : INVALID_POSITION;
492 return;
493 }
494
495 final FragmentTransaction trans = mActivity.getFragmentManager().openTransaction()
496 .disallowAddToBackStack();
Adam Powell7f9b9052010-10-19 16:56:07 -0700497
498 if (mSelectedTab == tab) {
499 if (mSelectedTab != null) {
500 mSelectedTab.getCallback().onTabReselected(mSelectedTab, trans);
501 }
502 } else {
503 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
504 if (mSelectedTab != null) {
505 mSelectedTab.getCallback().onTabUnselected(mSelectedTab, trans);
506 }
507 mSelectedTab = (TabImpl) tab;
508 if (mSelectedTab != null) {
509 mSelectedTab.getCallback().onTabSelected(mSelectedTab, trans);
510 }
Adam Powell2b6230e2010-09-07 17:55:25 -0700511 }
512
513 if (!trans.isEmpty()) {
514 trans.commit();
515 }
516 }
517
518 @Override
519 public Tab getSelectedTab() {
520 return mSelectedTab;
Adam Powell661c9082010-07-02 10:09:44 -0700521 }
522
Adam Powell6b336f82010-08-10 20:13:01 -0700523 @Override
524 public int getHeight() {
525 return mActionView.getHeight();
526 }
527
528 @Override
529 public void show() {
Adam Powelle6ec7322010-12-07 15:29:26 -0800530 if (mContainerView.getVisibility() == View.VISIBLE) {
531 return;
532 }
Adam Powell45f1e082010-12-10 14:20:13 -0800533 if (mCurrentAnim != null) {
534 mCurrentAnim.end();
535 }
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800536 mContainerView.setVisibility(View.VISIBLE);
Adam Powelle6ec7322010-12-07 15:29:26 -0800537 mContainerView.setAlpha(0);
Adam Powelle6ec7322010-12-07 15:29:26 -0800538 AnimatorSet anim = new AnimatorSet();
Adam Powell73e371ff72010-12-17 16:07:20 -0800539 AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 1));
Adam Powelle6ec7322010-12-07 15:29:26 -0800540 if (mContentView != null) {
Adam Powell73e371ff72010-12-17 16:07:20 -0800541 b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
542 -mContainerView.getHeight(), 0));
543 mContainerView.setTranslationY(-mContainerView.getHeight());
544 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY", 0));
Adam Powelle6ec7322010-12-07 15:29:26 -0800545 }
546 anim.addListener(mShowListener);
547 mCurrentAnim = anim;
548 anim.start();
Adam Powell6b336f82010-08-10 20:13:01 -0700549 }
550
551 @Override
552 public void hide() {
Adam Powelle6ec7322010-12-07 15:29:26 -0800553 if (mCurrentAnim != null) {
554 mCurrentAnim.end();
555 }
556 if (mContainerView.getVisibility() == View.GONE) {
557 return;
558 }
559 mContainerView.setAlpha(1);
560 AnimatorSet anim = new AnimatorSet();
Adam Powell73e371ff72010-12-17 16:07:20 -0800561 AnimatorSet.Builder b = anim.play(ObjectAnimator.ofFloat(mContainerView, "alpha", 0));
Adam Powelle6ec7322010-12-07 15:29:26 -0800562 if (mContentView != null) {
Adam Powell73e371ff72010-12-17 16:07:20 -0800563 b.with(ObjectAnimator.ofFloat(mContentView, "translationY",
564 0, -mContainerView.getHeight()));
565 b.with(ObjectAnimator.ofFloat(mContainerView, "translationY",
566 -mContainerView.getHeight()));
Adam Powelle6ec7322010-12-07 15:29:26 -0800567 }
568 anim.addListener(mHideListener);
569 mCurrentAnim = anim;
570 anim.start();
Adam Powell6b336f82010-08-10 20:13:01 -0700571 }
572
573 public boolean isShowing() {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800574 return mContainerView.getVisibility() == View.VISIBLE;
575 }
576
577 private long animateTo(int viewIndex) {
Adam Powelle6ec7322010-12-07 15:29:26 -0800578 show();
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800579
580 AnimatorSet set = new AnimatorSet();
581
582 final View targetChild = mContainerView.getChildAt(viewIndex);
583 targetChild.setVisibility(View.VISIBLE);
584 AnimatorSet.Builder b = set.play(ObjectAnimator.ofFloat(targetChild, "alpha", 1));
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800585
586 final int count = mContainerView.getChildCount();
587 for (int i = 0; i < count; i++) {
588 final View child = mContainerView.getChildAt(i);
589 if (i == viewIndex) {
590 continue;
591 }
592
593 if (child.getVisibility() != View.GONE) {
Adam Powell45f1e082010-12-10 14:20:13 -0800594 Animator a = ObjectAnimator.ofFloat(child, "alpha", 0);
595 a.setInterpolator(sFadeOutInterpolator);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800596 b.with(a);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800597 }
598 }
599
600 set.addListener(mAfterAnimation[viewIndex]);
601
Adam Powelle6ec7322010-12-07 15:29:26 -0800602 mCurrentAnim = set;
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800603 set.start();
604 return set.getDuration();
605 }
606
607 private void hideAllExcept(int viewIndex) {
608 final int count = mContainerView.getChildCount();
609 for (int i = 0; i < count; i++) {
610 mContainerView.getChildAt(i).setVisibility(i == viewIndex ? View.VISIBLE : View.GONE);
611 }
Adam Powell6b336f82010-08-10 20:13:01 -0700612 }
613
Adam Powell89e06452010-06-23 20:24:52 -0700614 /**
615 * @hide
616 */
Adam Powell5d279772010-07-27 16:34:07 -0700617 public class ActionModeImpl extends ActionMode implements MenuBuilder.Callback {
Adam Powell6e346362010-07-23 10:18:23 -0700618 private ActionMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700619 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700620 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700621
Adam Powell5d279772010-07-27 16:34:07 -0700622 public ActionModeImpl(ActionMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700623 mCallback = callback;
Adam Powell4d9861e2010-08-17 11:14:40 -0700624 mMenu = new MenuBuilder(mActionView.getContext())
625 .setDefaultShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700626 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700627 }
Adam Powell9168f0b2010-08-02 15:46:24 -0700628
629 @Override
630 public MenuInflater getMenuInflater() {
Adam Powell4d9861e2010-08-17 11:14:40 -0700631 return new MenuInflater(mContext);
Adam Powell9168f0b2010-08-02 15:46:24 -0700632 }
633
Adam Powell89e06452010-06-23 20:24:52 -0700634 @Override
635 public Menu getMenu() {
636 return mMenu;
637 }
638
639 @Override
640 public void finish() {
Adam Powell5d279772010-07-27 16:34:07 -0700641 if (mActionMode != this) {
642 // Not the active action mode - no-op
Adam Powell93b6bc32010-07-22 11:36:35 -0700643 return;
644 }
645
Adam Powell6e346362010-07-23 10:18:23 -0700646 mCallback.onDestroyActionMode(this);
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800647 mCallback = null;
648 animateTo(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700649
650 // Clear out the context mode views after the animation finishes
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800651 mUpperContextView.closeMode();
Adam Powell89e06452010-06-23 20:24:52 -0700652 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
653 // TODO Animate this
654 mLowerContextView.setVisibility(View.GONE);
655 }
Adam Powell5d279772010-07-27 16:34:07 -0700656 mActionMode = null;
Adam Powell89e06452010-06-23 20:24:52 -0700657 }
658
659 @Override
660 public void invalidate() {
Adam Powell6e346362010-07-23 10:18:23 -0700661 if (mCallback.onPrepareActionMode(this, mMenu)) {
Adam Powell89e06452010-06-23 20:24:52 -0700662 // Refresh content in both context views
663 }
664 }
665
666 @Override
667 public void setCustomView(View view) {
668 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700669 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700670 }
671
672 @Override
673 public void setSubtitle(CharSequence subtitle) {
674 mUpperContextView.setSubtitle(subtitle);
675 }
676
677 @Override
678 public void setTitle(CharSequence title) {
679 mUpperContextView.setTitle(title);
680 }
Adam Powell29ed7572010-07-14 16:24:56 -0700681
682 @Override
Adam Powellc9ae2a22010-07-28 14:44:21 -0700683 public void setTitle(int resId) {
Adam Powell48e8ac32011-01-14 12:10:24 -0800684 setTitle(mContext.getResources().getString(resId));
Adam Powellc9ae2a22010-07-28 14:44:21 -0700685 }
686
687 @Override
688 public void setSubtitle(int resId) {
Adam Powell48e8ac32011-01-14 12:10:24 -0800689 setSubtitle(mContext.getResources().getString(resId));
Adam Powellc9ae2a22010-07-28 14:44:21 -0700690 }
691
692 @Override
Adam Powell29ed7572010-07-14 16:24:56 -0700693 public CharSequence getTitle() {
694 return mUpperContextView.getTitle();
695 }
696
697 @Override
698 public CharSequence getSubtitle() {
699 return mUpperContextView.getSubtitle();
700 }
Adam Powell89e06452010-06-23 20:24:52 -0700701
Adam Powell29ed7572010-07-14 16:24:56 -0700702 @Override
703 public View getCustomView() {
704 return mCustomView != null ? mCustomView.get() : null;
705 }
706
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700707 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800708 if (mCallback != null) {
709 return mCallback.onActionItemClicked(this, item);
710 } else {
711 return false;
712 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700713 }
714
715 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
716 }
717
718 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800719 if (mCallback == null) {
720 return false;
721 }
722
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700723 if (!subMenu.hasVisibleItems()) {
724 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700725 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700726
Adam Powelldec9dfd2010-08-09 15:27:54 -0700727 new MenuPopupHelper(mContext, subMenu).show();
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700728 return true;
729 }
730
731 public void onCloseSubMenu(SubMenuBuilder menu) {
732 }
733
734 public void onMenuModeChange(MenuBuilder menu) {
Adam Powelld8b3f2e2010-12-02 13:37:03 -0800735 if (mCallback == null) {
736 return;
737 }
Adam Powellf6148c52010-08-11 21:10:16 -0700738 invalidate();
Adam Powell8515ee82010-11-30 14:09:55 -0800739 mUpperContextView.openOverflowMenu();
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700740 }
Adam Powell661c9082010-07-02 10:09:44 -0700741 }
742
743 /**
744 * @hide
745 */
746 public class TabImpl extends ActionBar.Tab {
Adam Powell2b6230e2010-09-07 17:55:25 -0700747 private ActionBar.TabListener mCallback;
748 private Object mTag;
Adam Powell661c9082010-07-02 10:09:44 -0700749 private Drawable mIcon;
750 private CharSequence mText;
751 private int mPosition;
Adam Powell2b6230e2010-09-07 17:55:25 -0700752 private View mCustomView;
Adam Powell661c9082010-07-02 10:09:44 -0700753
754 @Override
Adam Powell2b6230e2010-09-07 17:55:25 -0700755 public Object getTag() {
756 return mTag;
757 }
758
759 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700760 public Tab setTag(Object tag) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700761 mTag = tag;
Adam Powell9ab97872010-10-26 21:47:29 -0700762 return this;
Adam Powell2b6230e2010-09-07 17:55:25 -0700763 }
764
765 public ActionBar.TabListener getCallback() {
766 return mCallback;
767 }
768
769 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700770 public Tab setTabListener(ActionBar.TabListener callback) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700771 mCallback = callback;
Adam Powell9ab97872010-10-26 21:47:29 -0700772 return this;
Adam Powell2b6230e2010-09-07 17:55:25 -0700773 }
774
775 @Override
776 public View getCustomView() {
777 return mCustomView;
778 }
779
780 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700781 public Tab setCustomView(View view) {
Adam Powell2b6230e2010-09-07 17:55:25 -0700782 mCustomView = view;
Adam Powell9ab97872010-10-26 21:47:29 -0700783 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700784 }
785
786 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800787 public Tab setCustomView(int layoutResId) {
788 return setCustomView(LayoutInflater.from(mContext).inflate(layoutResId, null));
789 }
790
791 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700792 public Drawable getIcon() {
793 return mIcon;
794 }
795
796 @Override
797 public int getPosition() {
798 return mPosition;
799 }
800
801 public void setPosition(int position) {
802 mPosition = position;
803 }
804
805 @Override
806 public CharSequence getText() {
807 return mText;
808 }
809
810 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700811 public Tab setIcon(Drawable icon) {
Adam Powell661c9082010-07-02 10:09:44 -0700812 mIcon = icon;
Adam Powell9ab97872010-10-26 21:47:29 -0700813 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700814 }
815
816 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800817 public Tab setIcon(int resId) {
818 return setIcon(mContext.getResources().getDrawable(resId));
819 }
820
821 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700822 public Tab setText(CharSequence text) {
Adam Powell661c9082010-07-02 10:09:44 -0700823 mText = text;
Adam Powell9ab97872010-10-26 21:47:29 -0700824 return this;
Adam Powell661c9082010-07-02 10:09:44 -0700825 }
826
827 @Override
Adam Powell32555f32010-11-17 13:49:22 -0800828 public Tab setText(int resId) {
829 return setText(mContext.getResources().getText(resId));
830 }
831
832 @Override
Adam Powell661c9082010-07-02 10:09:44 -0700833 public void select() {
834 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700835 }
836 }
Adam Powell9ab97872010-10-26 21:47:29 -0700837
838 @Override
839 public void setCustomView(View view) {
840 mActionView.setCustomNavigationView(view);
841 }
842
843 @Override
844 public void setCustomView(View view, LayoutParams layoutParams) {
845 view.setLayoutParams(layoutParams);
846 mActionView.setCustomNavigationView(view);
847 }
848
849 @Override
Adam Powell8515ee82010-11-30 14:09:55 -0800850 public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) {
Adam Powell9ab97872010-10-26 21:47:29 -0700851 mActionView.setDropdownAdapter(adapter);
852 mActionView.setCallback(callback);
853 }
854
855 @Override
856 public int getSelectedNavigationIndex() {
857 switch (mActionView.getNavigationMode()) {
858 case NAVIGATION_MODE_TABS:
Adam Powell04587962010-11-11 22:15:07 -0800859 return mSelectedTab != null ? mSelectedTab.getPosition() : -1;
Adam Powell9ab97872010-10-26 21:47:29 -0700860 case NAVIGATION_MODE_LIST:
861 return mActionView.getDropdownSelectedPosition();
862 default:
863 return -1;
864 }
865 }
866
867 @Override
868 public int getNavigationItemCount() {
869 switch (mActionView.getNavigationMode()) {
870 case NAVIGATION_MODE_TABS:
871 return mTabs.size();
872 case NAVIGATION_MODE_LIST:
873 SpinnerAdapter adapter = mActionView.getDropdownAdapter();
874 return adapter != null ? adapter.getCount() : 0;
875 default:
876 return 0;
877 }
878 }
879
880 @Override
Adam Powell0c24a552010-11-03 16:44:11 -0700881 public int getTabCount() {
882 return mTabs.size();
883 }
884
885 @Override
Adam Powell9ab97872010-10-26 21:47:29 -0700886 public void setNavigationMode(int mode) {
Adam Powell0c24a552010-11-03 16:44:11 -0700887 final int oldMode = mActionView.getNavigationMode();
888 switch (oldMode) {
889 case NAVIGATION_MODE_TABS:
890 mSavedTabPosition = getSelectedNavigationIndex();
891 selectTab(null);
892 break;
893 }
Adam Powell9ab97872010-10-26 21:47:29 -0700894 mActionView.setNavigationMode(mode);
Adam Powell0c24a552010-11-03 16:44:11 -0700895 switch (mode) {
896 case NAVIGATION_MODE_TABS:
897 if (mSavedTabPosition != INVALID_POSITION) {
898 setSelectedNavigationItem(mSavedTabPosition);
899 mSavedTabPosition = INVALID_POSITION;
900 }
901 break;
902 }
Adam Powell9ab97872010-10-26 21:47:29 -0700903 }
904
905 @Override
906 public Tab getTabAt(int index) {
907 return mTabs.get(index);
908 }
Adam Powell0c24a552010-11-03 16:44:11 -0700909
910 /**
911 * This fragment is added when we're keeping a back stack in a tab switch
912 * transaction. We use it to change the selected tab in the action bar view
913 * when we back out.
914 */
915 private class SwitchSelectedTabViewFragment extends Fragment {
916 private int mSelectedTabIndex;
917
918 public SwitchSelectedTabViewFragment(int oldSelectedTab) {
919 mSelectedTabIndex = oldSelectedTab;
920 }
921
922 @Override
923 public void onDetach() {
924 if (mSelectedTabIndex >= 0 && mSelectedTabIndex < getTabCount()) {
925 mActionView.setTabSelected(mSelectedTabIndex);
926 }
927 }
928 }
Adam Powell89e06452010-06-23 20:24:52 -0700929}