blob: ec6d2be44c44373ad493fdfb4edeb26ba34bb45a [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;
20import com.android.internal.view.menu.MenuItemImpl;
21import com.android.internal.view.menu.MenuPopupHelper;
22import com.android.internal.view.menu.SubMenuBuilder;
Adam Powell89e06452010-06-23 20:24:52 -070023import com.android.internal.widget.ActionBarContextView;
24import com.android.internal.widget.ActionBarView;
25
26import android.app.ActionBar;
Adam Powell661c9082010-07-02 10:09:44 -070027import android.app.Activity;
Adam Powellac695c62010-07-20 18:19:27 -070028import android.app.ContextualMode;
Adam Powell661c9082010-07-02 10:09:44 -070029import android.app.Fragment;
30import android.app.FragmentTransaction;
Adam Powell89e06452010-06-23 20:24:52 -070031import android.graphics.drawable.Drawable;
Adam Powell0e94b512010-06-29 17:58:20 -070032import android.os.Handler;
Adam Powell89e06452010-06-23 20:24:52 -070033import android.view.Menu;
34import android.view.MenuItem;
35import android.view.View;
36import android.widget.LinearLayout;
37import android.widget.SpinnerAdapter;
38import android.widget.ViewAnimator;
39
Adam Powell29ed7572010-07-14 16:24:56 -070040import java.lang.ref.WeakReference;
Adam Powell661c9082010-07-02 10:09:44 -070041import java.util.ArrayList;
42
Adam Powell89e06452010-06-23 20:24:52 -070043/**
44 * ActionBarImpl is the ActionBar implementation used
45 * by devices of all screen sizes. If it detects a compatible decor,
46 * it will split contextual modes across both the ActionBarView at
47 * the top of the screen and a horizontal LinearLayout at the bottom
48 * which is normally hidden.
49 */
50public class ActionBarImpl extends ActionBar {
51 private static final int NORMAL_VIEW = 0;
52 private static final int CONTEXT_VIEW = 1;
53
Adam Powell661c9082010-07-02 10:09:44 -070054 private static final int TAB_SWITCH_SHOW_HIDE = 0;
55 private static final int TAB_SWITCH_ADD_REMOVE = 1;
56
57 private Activity mActivity;
58
Adam Powell89e06452010-06-23 20:24:52 -070059 private ViewAnimator mAnimatorView;
60 private ActionBarView mActionView;
61 private ActionBarContextView mUpperContextView;
62 private LinearLayout mLowerContextView;
Adam Powell661c9082010-07-02 10:09:44 -070063
64 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
65
66 private int mTabContainerViewId = android.R.id.content;
67 private TabImpl mSelectedTab;
68 private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
Adam Powell89e06452010-06-23 20:24:52 -070069
70 private ContextMode mContextMode;
71
72 private static final int CONTEXT_DISPLAY_NORMAL = 0;
73 private static final int CONTEXT_DISPLAY_SPLIT = 1;
74
75 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070076
Adam Powell3461b322010-07-14 11:34:43 -070077 private boolean mClosingContext;
78
Adam Powell0e94b512010-06-29 17:58:20 -070079 final Handler mHandler = new Handler();
80 final Runnable mCloseContext = new Runnable() {
81 public void run() {
82 mUpperContextView.closeMode();
83 if (mLowerContextView != null) {
84 mLowerContextView.removeAllViews();
85 }
Adam Powell3461b322010-07-14 11:34:43 -070086 mClosingContext = false;
Adam Powell0e94b512010-06-29 17:58:20 -070087 }
88 };
89
Adam Powell661c9082010-07-02 10:09:44 -070090 public ActionBarImpl(Activity activity) {
91 final View decor = activity.getWindow().getDecorView();
92 mActivity = activity;
Adam Powell89e06452010-06-23 20:24:52 -070093 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
94 mUpperContextView = (ActionBarContextView) decor.findViewById(
95 com.android.internal.R.id.action_context_bar);
96 mLowerContextView = (LinearLayout) decor.findViewById(
97 com.android.internal.R.id.lower_action_context_bar);
98 mAnimatorView = (ViewAnimator) decor.findViewById(
99 com.android.internal.R.id.action_bar_animator);
100
101 if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
102 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
103 "with a compatible window decor layout");
104 }
Adam Powell0e94b512010-06-29 17:58:20 -0700105
Adam Powell89e06452010-06-23 20:24:52 -0700106 mContextDisplayMode = mLowerContextView == null ?
107 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
108 }
109
110 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700111 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700112 mActionView.setCustomNavigationView(view);
113 mActionView.setCallback(null);
114 }
Adam Powell0e94b512010-06-29 17:58:20 -0700115
Adam Powell89e06452010-06-23 20:24:52 -0700116 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell661c9082010-07-02 10:09:44 -0700117 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700118 mActionView.setCallback(callback);
119 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
120 mActionView.setDropdownAdapter(adapter);
121 }
Adam Powell0e94b512010-06-29 17:58:20 -0700122
123 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700124 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700125 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
126 mActionView.setCallback(null);
127 }
128
Adam Powell89e06452010-06-23 20:24:52 -0700129 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700130 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700131 setStandardNavigationMode(title, null);
132 }
Adam Powell0e94b512010-06-29 17:58:20 -0700133
Adam Powell89e06452010-06-23 20:24:52 -0700134 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700135 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700136 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
137 mActionView.setTitle(title);
138 mActionView.setSubtitle(subtitle);
139 mActionView.setCallback(null);
140 }
141
Adam Powell661c9082010-07-02 10:09:44 -0700142 private void cleanupTabs() {
143 if (mSelectedTab != null) {
144 selectTab(null);
145 }
146 if (!mTabs.isEmpty()) {
147 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
148 final FragmentTransaction trans = mActivity.openFragmentTransaction();
149 final int tabCount = mTabs.size();
150 for (int i = 0; i < tabCount; i++) {
151 trans.remove(mTabs.get(i).getFragment());
152 }
153 trans.commit();
154 }
155 mTabs.clear();
156 }
157 }
158
Adam Powell0e94b512010-06-29 17:58:20 -0700159 public void setTitle(CharSequence title) {
160 mActionView.setTitle(title);
161 }
162
163 public void setSubtitle(CharSequence subtitle) {
164 mActionView.setSubtitle(subtitle);
165 }
166
Adam Powell89e06452010-06-23 20:24:52 -0700167 public void setDisplayOptions(int options) {
168 mActionView.setDisplayOptions(options);
169 }
Adam Powell0e94b512010-06-29 17:58:20 -0700170
Adam Powell89e06452010-06-23 20:24:52 -0700171 public void setDisplayOptions(int options, int mask) {
172 final int current = mActionView.getDisplayOptions();
173 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
174 }
175
176 public void setBackgroundDrawable(Drawable d) {
177 mActionView.setBackgroundDrawable(d);
178 }
179
180 public View getCustomNavigationView() {
181 return mActionView.getCustomNavigationView();
182 }
183
184 public CharSequence getTitle() {
185 return mActionView.getTitle();
186 }
187
188 public CharSequence getSubtitle() {
189 return mActionView.getSubtitle();
190 }
191
192 public int getNavigationMode() {
193 return mActionView.getNavigationMode();
194 }
195
196 public int getDisplayOptions() {
197 return mActionView.getDisplayOptions();
198 }
199
Adam Powellac695c62010-07-20 18:19:27 -0700200 public ContextualMode startContextualMode(ContextualMode.Callback callback) {
201 finishContextualMode();
Adam Powell3461b322010-07-14 11:34:43 -0700202
203 // Don't wait for the close context mode animation to finish.
204 if (mClosingContext) {
205 mAnimatorView.clearAnimation();
206 mHandler.removeCallbacks(mCloseContext);
207 mCloseContext.run();
208 }
209
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700210 ContextMode mode = new ContextMode(callback);
211 if (callback.onCreateContextMode(mode, mode.getMenu())) {
212 mode.invalidate();
213 mUpperContextView.initForMode(mode);
Adam Powell89e06452010-06-23 20:24:52 -0700214 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
215 if (mLowerContextView != null) {
216 // TODO animate this
217 mLowerContextView.setVisibility(View.VISIBLE);
218 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700219 mContextMode = mode;
Adam Powellac695c62010-07-20 18:19:27 -0700220 return mode;
Adam Powell89e06452010-06-23 20:24:52 -0700221 }
Adam Powellac695c62010-07-20 18:19:27 -0700222 return null;
Adam Powell89e06452010-06-23 20:24:52 -0700223 }
224
Adam Powellac695c62010-07-20 18:19:27 -0700225 public void finishContextualMode() {
Adam Powell89e06452010-06-23 20:24:52 -0700226 if (mContextMode != null) {
227 mContextMode.finish();
228 }
229 }
230
Adam Powell661c9082010-07-02 10:09:44 -0700231 private void configureTab(Tab tab, int position) {
232 final TabImpl tabi = (TabImpl) tab;
233 final boolean isFirstTab = mTabs.isEmpty();
234 final FragmentTransaction trans = mActivity.openFragmentTransaction();
235 final Fragment frag = tabi.getFragment();
236
237 tabi.setPosition(position);
238 mTabs.add(position, tabi);
239
240 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
241 if (!frag.isAdded()) {
242 trans.add(mTabContainerViewId, frag);
243 }
244 }
245
246 if (isFirstTab) {
247 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
248 trans.show(frag);
249 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
250 trans.add(mTabContainerViewId, frag);
251 }
252 mSelectedTab = tabi;
253 } else {
254 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
255 trans.hide(frag);
256 }
257 }
258 trans.commit();
259 }
260
261 @Override
262 public void addTab(Tab tab) {
263 mActionView.addTab(tab);
264 configureTab(tab, mTabs.size());
265 }
266
267 @Override
268 public void insertTab(Tab tab, int position) {
269 mActionView.insertTab(tab, position);
270 configureTab(tab, position);
271 }
272
273 @Override
274 public Tab newTab() {
275 return new TabImpl();
276 }
277
278 @Override
279 public void removeTab(Tab tab) {
280 removeTabAt(tab.getPosition());
281 }
282
283 @Override
284 public void removeTabAt(int position) {
285 mActionView.removeTabAt(position);
286 mTabs.remove(position);
287
288 final int newTabCount = mTabs.size();
289 for (int i = position; i < newTabCount; i++) {
290 mTabs.get(i).setPosition(i);
291 }
292
293 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
294 }
295
296 @Override
297 public void setTabNavigationMode() {
298 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
299 }
300
301 @Override
302 public void setTabNavigationMode(int containerViewId) {
303 mTabContainerViewId = containerViewId;
304 setTabNavigationMode();
305 }
306
307 @Override
308 public void selectTab(Tab tab) {
309 if (mSelectedTab == tab) {
310 return;
311 }
312
313 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
314 final FragmentTransaction trans = mActivity.openFragmentTransaction();
315 if (mSelectedTab != null) {
316 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
317 trans.hide(mSelectedTab.getFragment());
318 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
319 trans.remove(mSelectedTab.getFragment());
320 }
321 }
322 if (tab != null) {
323 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
324 trans.show(tab.getFragment());
325 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
326 trans.add(mTabContainerViewId, tab.getFragment());
327 }
328 }
329 mSelectedTab = (TabImpl) tab;
330 trans.commit();
331 }
332
333 @Override
334 public void selectTabAt(int position) {
335 selectTab(mTabs.get(position));
336 }
337
Adam Powell89e06452010-06-23 20:24:52 -0700338 /**
339 * @hide
340 */
Adam Powellac695c62010-07-20 18:19:27 -0700341 public class ContextMode extends ContextualMode implements MenuBuilder.Callback {
342 private ContextualMode.Callback mCallback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700343 private MenuBuilder mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700344 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700345
Adam Powellac695c62010-07-20 18:19:27 -0700346 public ContextMode(ContextualMode.Callback callback) {
Adam Powell89e06452010-06-23 20:24:52 -0700347 mCallback = callback;
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700348 mMenu = new MenuBuilder(mActionView.getContext());
349 mMenu.setCallback(this);
Adam Powell89e06452010-06-23 20:24:52 -0700350 }
351
352 @Override
353 public Menu getMenu() {
354 return mMenu;
355 }
356
357 @Override
358 public void finish() {
359 mCallback.onDestroyContextMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700360 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700361
362 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700363 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700364 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
365
Adam Powell89e06452010-06-23 20:24:52 -0700366 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
367 // TODO Animate this
368 mLowerContextView.setVisibility(View.GONE);
369 }
370 mContextMode = null;
371 }
372
373 @Override
374 public void invalidate() {
375 if (mCallback.onPrepareContextMode(this, mMenu)) {
376 // Refresh content in both context views
377 }
378 }
379
380 @Override
381 public void setCustomView(View view) {
382 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700383 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700384 }
385
386 @Override
387 public void setSubtitle(CharSequence subtitle) {
388 mUpperContextView.setSubtitle(subtitle);
389 }
390
391 @Override
392 public void setTitle(CharSequence title) {
393 mUpperContextView.setTitle(title);
394 }
Adam Powell29ed7572010-07-14 16:24:56 -0700395
396 @Override
397 public CharSequence getTitle() {
398 return mUpperContextView.getTitle();
399 }
400
401 @Override
402 public CharSequence getSubtitle() {
403 return mUpperContextView.getSubtitle();
404 }
Adam Powell89e06452010-06-23 20:24:52 -0700405
Adam Powell29ed7572010-07-14 16:24:56 -0700406 @Override
407 public View getCustomView() {
408 return mCustomView != null ? mCustomView.get() : null;
409 }
410
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700411 public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
412 return mCallback.onContextItemClicked(this, item);
413 }
414
415 public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
416 }
417
418 public boolean onSubMenuSelected(SubMenuBuilder subMenu) {
419 if (!subMenu.hasVisibleItems()) {
420 return true;
Adam Powell89e06452010-06-23 20:24:52 -0700421 }
Adam Powell2c9c9fe2010-07-16 10:17:57 -0700422
423 new MenuPopupHelper(mActivity, subMenu).show();
424 return true;
425 }
426
427 public void onCloseSubMenu(SubMenuBuilder menu) {
428 }
429
430 public void onMenuModeChange(MenuBuilder menu) {
431 }
Adam Powell661c9082010-07-02 10:09:44 -0700432 }
433
434 /**
435 * @hide
436 */
437 public class TabImpl extends ActionBar.Tab {
438 private Fragment mFragment;
439 private Drawable mIcon;
440 private CharSequence mText;
441 private int mPosition;
442
443 @Override
444 public Fragment getFragment() {
445 return mFragment;
446 }
447
448 @Override
449 public Drawable getIcon() {
450 return mIcon;
451 }
452
453 @Override
454 public int getPosition() {
455 return mPosition;
456 }
457
458 public void setPosition(int position) {
459 mPosition = position;
460 }
461
462 @Override
463 public CharSequence getText() {
464 return mText;
465 }
466
467 @Override
468 public void setFragment(Fragment fragment) {
469 mFragment = fragment;
470 }
471
472 @Override
473 public void setIcon(Drawable icon) {
474 mIcon = icon;
475 }
476
477 @Override
478 public void setText(CharSequence text) {
479 mText = text;
480 }
481
482 @Override
483 public void select() {
484 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700485 }
486 }
487}