blob: f37021b7ede2445d7ab3fc3f113a3d7a4dfb1ae5 [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
19import com.android.internal.view.menu.ActionMenu;
20import com.android.internal.view.menu.ActionMenuItem;
21import com.android.internal.widget.ActionBarContextView;
22import com.android.internal.widget.ActionBarView;
23
24import android.app.ActionBar;
Adam Powell661c9082010-07-02 10:09:44 -070025import android.app.Activity;
26import android.app.Fragment;
27import android.app.FragmentTransaction;
Adam Powell89e06452010-06-23 20:24:52 -070028import android.graphics.drawable.Drawable;
Adam Powell0e94b512010-06-29 17:58:20 -070029import android.os.Handler;
Adam Powell89e06452010-06-23 20:24:52 -070030import android.view.Menu;
31import android.view.MenuItem;
32import android.view.View;
33import android.widget.LinearLayout;
34import android.widget.SpinnerAdapter;
35import android.widget.ViewAnimator;
36
Adam Powell29ed7572010-07-14 16:24:56 -070037import java.lang.ref.WeakReference;
Adam Powell661c9082010-07-02 10:09:44 -070038import java.util.ArrayList;
39
Adam Powell89e06452010-06-23 20:24:52 -070040/**
41 * ActionBarImpl is the ActionBar implementation used
42 * by devices of all screen sizes. If it detects a compatible decor,
43 * it will split contextual modes across both the ActionBarView at
44 * the top of the screen and a horizontal LinearLayout at the bottom
45 * which is normally hidden.
46 */
47public class ActionBarImpl extends ActionBar {
48 private static final int NORMAL_VIEW = 0;
49 private static final int CONTEXT_VIEW = 1;
50
Adam Powell661c9082010-07-02 10:09:44 -070051 private static final int TAB_SWITCH_SHOW_HIDE = 0;
52 private static final int TAB_SWITCH_ADD_REMOVE = 1;
53
54 private Activity mActivity;
55
Adam Powell89e06452010-06-23 20:24:52 -070056 private ViewAnimator mAnimatorView;
57 private ActionBarView mActionView;
58 private ActionBarContextView mUpperContextView;
59 private LinearLayout mLowerContextView;
Adam Powell661c9082010-07-02 10:09:44 -070060
61 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
62
63 private int mTabContainerViewId = android.R.id.content;
64 private TabImpl mSelectedTab;
65 private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
Adam Powell89e06452010-06-23 20:24:52 -070066
67 private ContextMode mContextMode;
68
69 private static final int CONTEXT_DISPLAY_NORMAL = 0;
70 private static final int CONTEXT_DISPLAY_SPLIT = 1;
71
72 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070073
Adam Powell3461b322010-07-14 11:34:43 -070074 private boolean mClosingContext;
75
Adam Powell0e94b512010-06-29 17:58:20 -070076 final Handler mHandler = new Handler();
77 final Runnable mCloseContext = new Runnable() {
78 public void run() {
79 mUpperContextView.closeMode();
80 if (mLowerContextView != null) {
81 mLowerContextView.removeAllViews();
82 }
Adam Powell3461b322010-07-14 11:34:43 -070083 mClosingContext = false;
Adam Powell0e94b512010-06-29 17:58:20 -070084 }
85 };
86
Adam Powell661c9082010-07-02 10:09:44 -070087 public ActionBarImpl(Activity activity) {
88 final View decor = activity.getWindow().getDecorView();
89 mActivity = activity;
Adam Powell89e06452010-06-23 20:24:52 -070090 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
91 mUpperContextView = (ActionBarContextView) decor.findViewById(
92 com.android.internal.R.id.action_context_bar);
93 mLowerContextView = (LinearLayout) decor.findViewById(
94 com.android.internal.R.id.lower_action_context_bar);
95 mAnimatorView = (ViewAnimator) decor.findViewById(
96 com.android.internal.R.id.action_bar_animator);
97
98 if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
99 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
100 "with a compatible window decor layout");
101 }
Adam Powell0e94b512010-06-29 17:58:20 -0700102
Adam Powell89e06452010-06-23 20:24:52 -0700103 mContextDisplayMode = mLowerContextView == null ?
104 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
105 }
106
107 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700108 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700109 mActionView.setCustomNavigationView(view);
110 mActionView.setCallback(null);
111 }
Adam Powell0e94b512010-06-29 17:58:20 -0700112
Adam Powell89e06452010-06-23 20:24:52 -0700113 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell661c9082010-07-02 10:09:44 -0700114 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700115 mActionView.setCallback(callback);
116 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
117 mActionView.setDropdownAdapter(adapter);
118 }
Adam Powell0e94b512010-06-29 17:58:20 -0700119
120 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700121 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700122 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
123 mActionView.setCallback(null);
124 }
125
Adam Powell89e06452010-06-23 20:24:52 -0700126 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700127 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700128 setStandardNavigationMode(title, null);
129 }
Adam Powell0e94b512010-06-29 17:58:20 -0700130
Adam Powell89e06452010-06-23 20:24:52 -0700131 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700132 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700133 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
134 mActionView.setTitle(title);
135 mActionView.setSubtitle(subtitle);
136 mActionView.setCallback(null);
137 }
138
Adam Powell661c9082010-07-02 10:09:44 -0700139 private void cleanupTabs() {
140 if (mSelectedTab != null) {
141 selectTab(null);
142 }
143 if (!mTabs.isEmpty()) {
144 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
145 final FragmentTransaction trans = mActivity.openFragmentTransaction();
146 final int tabCount = mTabs.size();
147 for (int i = 0; i < tabCount; i++) {
148 trans.remove(mTabs.get(i).getFragment());
149 }
150 trans.commit();
151 }
152 mTabs.clear();
153 }
154 }
155
Adam Powell0e94b512010-06-29 17:58:20 -0700156 public void setTitle(CharSequence title) {
157 mActionView.setTitle(title);
158 }
159
160 public void setSubtitle(CharSequence subtitle) {
161 mActionView.setSubtitle(subtitle);
162 }
163
Adam Powell89e06452010-06-23 20:24:52 -0700164 public void setDisplayOptions(int options) {
165 mActionView.setDisplayOptions(options);
166 }
Adam Powell0e94b512010-06-29 17:58:20 -0700167
Adam Powell89e06452010-06-23 20:24:52 -0700168 public void setDisplayOptions(int options, int mask) {
169 final int current = mActionView.getDisplayOptions();
170 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
171 }
172
173 public void setBackgroundDrawable(Drawable d) {
174 mActionView.setBackgroundDrawable(d);
175 }
176
177 public View getCustomNavigationView() {
178 return mActionView.getCustomNavigationView();
179 }
180
181 public CharSequence getTitle() {
182 return mActionView.getTitle();
183 }
184
185 public CharSequence getSubtitle() {
186 return mActionView.getSubtitle();
187 }
188
189 public int getNavigationMode() {
190 return mActionView.getNavigationMode();
191 }
192
193 public int getDisplayOptions() {
194 return mActionView.getDisplayOptions();
195 }
196
197 @Override
198 public void startContextMode(ContextModeCallback callback) {
199 if (mContextMode != null) {
200 mContextMode.finish();
201 }
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 Powell89e06452010-06-23 20:24:52 -0700210 mContextMode = new ContextMode(callback);
211 if (callback.onCreateContextMode(mContextMode, mContextMode.getMenu())) {
212 mContextMode.invalidate();
213 mUpperContextView.initForMode(mContextMode);
214 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
215 if (mLowerContextView != null) {
216 // TODO animate this
217 mLowerContextView.setVisibility(View.VISIBLE);
218 }
219 }
220 }
221
222 @Override
223 public void finishContextMode() {
224 if (mContextMode != null) {
225 mContextMode.finish();
226 }
227 }
228
Adam Powell661c9082010-07-02 10:09:44 -0700229 private void configureTab(Tab tab, int position) {
230 final TabImpl tabi = (TabImpl) tab;
231 final boolean isFirstTab = mTabs.isEmpty();
232 final FragmentTransaction trans = mActivity.openFragmentTransaction();
233 final Fragment frag = tabi.getFragment();
234
235 tabi.setPosition(position);
236 mTabs.add(position, tabi);
237
238 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
239 if (!frag.isAdded()) {
240 trans.add(mTabContainerViewId, frag);
241 }
242 }
243
244 if (isFirstTab) {
245 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
246 trans.show(frag);
247 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
248 trans.add(mTabContainerViewId, frag);
249 }
250 mSelectedTab = tabi;
251 } else {
252 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
253 trans.hide(frag);
254 }
255 }
256 trans.commit();
257 }
258
259 @Override
260 public void addTab(Tab tab) {
261 mActionView.addTab(tab);
262 configureTab(tab, mTabs.size());
263 }
264
265 @Override
266 public void insertTab(Tab tab, int position) {
267 mActionView.insertTab(tab, position);
268 configureTab(tab, position);
269 }
270
271 @Override
272 public Tab newTab() {
273 return new TabImpl();
274 }
275
276 @Override
277 public void removeTab(Tab tab) {
278 removeTabAt(tab.getPosition());
279 }
280
281 @Override
282 public void removeTabAt(int position) {
283 mActionView.removeTabAt(position);
284 mTabs.remove(position);
285
286 final int newTabCount = mTabs.size();
287 for (int i = position; i < newTabCount; i++) {
288 mTabs.get(i).setPosition(i);
289 }
290
291 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
292 }
293
294 @Override
295 public void setTabNavigationMode() {
296 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
297 }
298
299 @Override
300 public void setTabNavigationMode(int containerViewId) {
301 mTabContainerViewId = containerViewId;
302 setTabNavigationMode();
303 }
304
305 @Override
306 public void selectTab(Tab tab) {
307 if (mSelectedTab == tab) {
308 return;
309 }
310
311 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
312 final FragmentTransaction trans = mActivity.openFragmentTransaction();
313 if (mSelectedTab != null) {
314 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
315 trans.hide(mSelectedTab.getFragment());
316 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
317 trans.remove(mSelectedTab.getFragment());
318 }
319 }
320 if (tab != null) {
321 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
322 trans.show(tab.getFragment());
323 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
324 trans.add(mTabContainerViewId, tab.getFragment());
325 }
326 }
327 mSelectedTab = (TabImpl) tab;
328 trans.commit();
329 }
330
331 @Override
332 public void selectTabAt(int position) {
333 selectTab(mTabs.get(position));
334 }
335
Adam Powell89e06452010-06-23 20:24:52 -0700336 /**
337 * @hide
338 */
339 public class ContextMode extends ActionBar.ContextMode {
340 private ContextModeCallback mCallback;
341 private ActionMenu mMenu;
Adam Powell29ed7572010-07-14 16:24:56 -0700342 private WeakReference<View> mCustomView;
Adam Powell89e06452010-06-23 20:24:52 -0700343
344 public ContextMode(ContextModeCallback callback) {
345 mCallback = callback;
346 mMenu = new ActionMenu(mActionView.getContext());
347 }
348
349 @Override
350 public Menu getMenu() {
351 return mMenu;
352 }
353
354 @Override
355 public void finish() {
356 mCallback.onDestroyContextMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700357 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700358
359 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700360 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700361 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
362
Adam Powell89e06452010-06-23 20:24:52 -0700363 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
364 // TODO Animate this
365 mLowerContextView.setVisibility(View.GONE);
366 }
367 mContextMode = null;
368 }
369
370 @Override
371 public void invalidate() {
372 if (mCallback.onPrepareContextMode(this, mMenu)) {
373 // Refresh content in both context views
374 }
375 }
376
377 @Override
378 public void setCustomView(View view) {
379 mUpperContextView.setCustomView(view);
Adam Powell29ed7572010-07-14 16:24:56 -0700380 mCustomView = new WeakReference<View>(view);
Adam Powell89e06452010-06-23 20:24:52 -0700381 }
382
383 @Override
384 public void setSubtitle(CharSequence subtitle) {
385 mUpperContextView.setSubtitle(subtitle);
386 }
387
388 @Override
389 public void setTitle(CharSequence title) {
390 mUpperContextView.setTitle(title);
391 }
Adam Powell29ed7572010-07-14 16:24:56 -0700392
393 @Override
394 public CharSequence getTitle() {
395 return mUpperContextView.getTitle();
396 }
397
398 @Override
399 public CharSequence getSubtitle() {
400 return mUpperContextView.getSubtitle();
401 }
Adam Powell89e06452010-06-23 20:24:52 -0700402
Adam Powell29ed7572010-07-14 16:24:56 -0700403 @Override
404 public View getCustomView() {
405 return mCustomView != null ? mCustomView.get() : null;
406 }
407
Adam Powell89e06452010-06-23 20:24:52 -0700408 public void dispatchOnContextItemClicked(MenuItem item) {
409 ActionMenuItem actionItem = (ActionMenuItem) item;
410 if (!actionItem.invoke()) {
411 mCallback.onContextItemClicked(this, item);
412 }
Adam Powell661c9082010-07-02 10:09:44 -0700413 }
414 }
415
416 /**
417 * @hide
418 */
419 public class TabImpl extends ActionBar.Tab {
420 private Fragment mFragment;
421 private Drawable mIcon;
422 private CharSequence mText;
423 private int mPosition;
424
425 @Override
426 public Fragment getFragment() {
427 return mFragment;
428 }
429
430 @Override
431 public Drawable getIcon() {
432 return mIcon;
433 }
434
435 @Override
436 public int getPosition() {
437 return mPosition;
438 }
439
440 public void setPosition(int position) {
441 mPosition = position;
442 }
443
444 @Override
445 public CharSequence getText() {
446 return mText;
447 }
448
449 @Override
450 public void setFragment(Fragment fragment) {
451 mFragment = fragment;
452 }
453
454 @Override
455 public void setIcon(Drawable icon) {
456 mIcon = icon;
457 }
458
459 @Override
460 public void setText(CharSequence text) {
461 mText = text;
462 }
463
464 @Override
465 public void select() {
466 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700467 }
468 }
469}