blob: c6be7c27dc3f9ef33e20881399bb683755873c7a [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 Powell661c9082010-07-02 10:09:44 -070037import java.util.ArrayList;
38
Adam Powell89e06452010-06-23 20:24:52 -070039/**
40 * ActionBarImpl is the ActionBar implementation used
41 * by devices of all screen sizes. If it detects a compatible decor,
42 * it will split contextual modes across both the ActionBarView at
43 * the top of the screen and a horizontal LinearLayout at the bottom
44 * which is normally hidden.
45 */
46public class ActionBarImpl extends ActionBar {
47 private static final int NORMAL_VIEW = 0;
48 private static final int CONTEXT_VIEW = 1;
49
Adam Powell661c9082010-07-02 10:09:44 -070050 private static final int TAB_SWITCH_SHOW_HIDE = 0;
51 private static final int TAB_SWITCH_ADD_REMOVE = 1;
52
53 private Activity mActivity;
54
Adam Powell89e06452010-06-23 20:24:52 -070055 private ViewAnimator mAnimatorView;
56 private ActionBarView mActionView;
57 private ActionBarContextView mUpperContextView;
58 private LinearLayout mLowerContextView;
Adam Powell661c9082010-07-02 10:09:44 -070059
60 private ArrayList<TabImpl> mTabs = new ArrayList<TabImpl>();
61
62 private int mTabContainerViewId = android.R.id.content;
63 private TabImpl mSelectedTab;
64 private int mTabSwitchMode = TAB_SWITCH_ADD_REMOVE;
Adam Powell89e06452010-06-23 20:24:52 -070065
66 private ContextMode mContextMode;
67
68 private static final int CONTEXT_DISPLAY_NORMAL = 0;
69 private static final int CONTEXT_DISPLAY_SPLIT = 1;
70
71 private int mContextDisplayMode;
Adam Powell0e94b512010-06-29 17:58:20 -070072
Adam Powell3461b322010-07-14 11:34:43 -070073 private boolean mClosingContext;
74
Adam Powell0e94b512010-06-29 17:58:20 -070075 final Handler mHandler = new Handler();
76 final Runnable mCloseContext = new Runnable() {
77 public void run() {
78 mUpperContextView.closeMode();
79 if (mLowerContextView != null) {
80 mLowerContextView.removeAllViews();
81 }
Adam Powell3461b322010-07-14 11:34:43 -070082 mClosingContext = false;
Adam Powell0e94b512010-06-29 17:58:20 -070083 }
84 };
85
Adam Powell661c9082010-07-02 10:09:44 -070086 public ActionBarImpl(Activity activity) {
87 final View decor = activity.getWindow().getDecorView();
88 mActivity = activity;
Adam Powell89e06452010-06-23 20:24:52 -070089 mActionView = (ActionBarView) decor.findViewById(com.android.internal.R.id.action_bar);
90 mUpperContextView = (ActionBarContextView) decor.findViewById(
91 com.android.internal.R.id.action_context_bar);
92 mLowerContextView = (LinearLayout) decor.findViewById(
93 com.android.internal.R.id.lower_action_context_bar);
94 mAnimatorView = (ViewAnimator) decor.findViewById(
95 com.android.internal.R.id.action_bar_animator);
96
97 if (mActionView == null || mUpperContextView == null || mAnimatorView == null) {
98 throw new IllegalStateException(getClass().getSimpleName() + " can only be used " +
99 "with a compatible window decor layout");
100 }
Adam Powell0e94b512010-06-29 17:58:20 -0700101
Adam Powell89e06452010-06-23 20:24:52 -0700102 mContextDisplayMode = mLowerContextView == null ?
103 CONTEXT_DISPLAY_NORMAL : CONTEXT_DISPLAY_SPLIT;
104 }
105
106 public void setCustomNavigationMode(View view) {
Adam Powell661c9082010-07-02 10:09:44 -0700107 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700108 mActionView.setCustomNavigationView(view);
109 mActionView.setCallback(null);
110 }
Adam Powell0e94b512010-06-29 17:58:20 -0700111
Adam Powell89e06452010-06-23 20:24:52 -0700112 public void setDropdownNavigationMode(SpinnerAdapter adapter, NavigationCallback callback) {
Adam Powell661c9082010-07-02 10:09:44 -0700113 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700114 mActionView.setCallback(callback);
115 mActionView.setNavigationMode(NAVIGATION_MODE_DROPDOWN_LIST);
116 mActionView.setDropdownAdapter(adapter);
117 }
Adam Powell0e94b512010-06-29 17:58:20 -0700118
119 public void setStandardNavigationMode() {
Adam Powell661c9082010-07-02 10:09:44 -0700120 cleanupTabs();
Adam Powell0e94b512010-06-29 17:58:20 -0700121 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
122 mActionView.setCallback(null);
123 }
124
Adam Powell89e06452010-06-23 20:24:52 -0700125 public void setStandardNavigationMode(CharSequence title) {
Adam Powell661c9082010-07-02 10:09:44 -0700126 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700127 setStandardNavigationMode(title, null);
128 }
Adam Powell0e94b512010-06-29 17:58:20 -0700129
Adam Powell89e06452010-06-23 20:24:52 -0700130 public void setStandardNavigationMode(CharSequence title, CharSequence subtitle) {
Adam Powell661c9082010-07-02 10:09:44 -0700131 cleanupTabs();
Adam Powell89e06452010-06-23 20:24:52 -0700132 mActionView.setNavigationMode(NAVIGATION_MODE_STANDARD);
133 mActionView.setTitle(title);
134 mActionView.setSubtitle(subtitle);
135 mActionView.setCallback(null);
136 }
137
Adam Powell661c9082010-07-02 10:09:44 -0700138 private void cleanupTabs() {
139 if (mSelectedTab != null) {
140 selectTab(null);
141 }
142 if (!mTabs.isEmpty()) {
143 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
144 final FragmentTransaction trans = mActivity.openFragmentTransaction();
145 final int tabCount = mTabs.size();
146 for (int i = 0; i < tabCount; i++) {
147 trans.remove(mTabs.get(i).getFragment());
148 }
149 trans.commit();
150 }
151 mTabs.clear();
152 }
153 }
154
Adam Powell0e94b512010-06-29 17:58:20 -0700155 public void setTitle(CharSequence title) {
156 mActionView.setTitle(title);
157 }
158
159 public void setSubtitle(CharSequence subtitle) {
160 mActionView.setSubtitle(subtitle);
161 }
162
Adam Powell89e06452010-06-23 20:24:52 -0700163 public void setDisplayOptions(int options) {
164 mActionView.setDisplayOptions(options);
165 }
Adam Powell0e94b512010-06-29 17:58:20 -0700166
Adam Powell89e06452010-06-23 20:24:52 -0700167 public void setDisplayOptions(int options, int mask) {
168 final int current = mActionView.getDisplayOptions();
169 mActionView.setDisplayOptions((options & mask) | (current & ~mask));
170 }
171
172 public void setBackgroundDrawable(Drawable d) {
173 mActionView.setBackgroundDrawable(d);
174 }
175
176 public View getCustomNavigationView() {
177 return mActionView.getCustomNavigationView();
178 }
179
180 public CharSequence getTitle() {
181 return mActionView.getTitle();
182 }
183
184 public CharSequence getSubtitle() {
185 return mActionView.getSubtitle();
186 }
187
188 public int getNavigationMode() {
189 return mActionView.getNavigationMode();
190 }
191
192 public int getDisplayOptions() {
193 return mActionView.getDisplayOptions();
194 }
195
196 @Override
197 public void startContextMode(ContextModeCallback callback) {
198 if (mContextMode != null) {
199 mContextMode.finish();
200 }
Adam Powell3461b322010-07-14 11:34:43 -0700201
202 // Don't wait for the close context mode animation to finish.
203 if (mClosingContext) {
204 mAnimatorView.clearAnimation();
205 mHandler.removeCallbacks(mCloseContext);
206 mCloseContext.run();
207 }
208
Adam Powell89e06452010-06-23 20:24:52 -0700209 mContextMode = new ContextMode(callback);
210 if (callback.onCreateContextMode(mContextMode, mContextMode.getMenu())) {
211 mContextMode.invalidate();
212 mUpperContextView.initForMode(mContextMode);
213 mAnimatorView.setDisplayedChild(CONTEXT_VIEW);
214 if (mLowerContextView != null) {
215 // TODO animate this
216 mLowerContextView.setVisibility(View.VISIBLE);
217 }
218 }
219 }
220
221 @Override
222 public void finishContextMode() {
223 if (mContextMode != null) {
224 mContextMode.finish();
225 }
226 }
227
Adam Powell661c9082010-07-02 10:09:44 -0700228 private void configureTab(Tab tab, int position) {
229 final TabImpl tabi = (TabImpl) tab;
230 final boolean isFirstTab = mTabs.isEmpty();
231 final FragmentTransaction trans = mActivity.openFragmentTransaction();
232 final Fragment frag = tabi.getFragment();
233
234 tabi.setPosition(position);
235 mTabs.add(position, tabi);
236
237 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
238 if (!frag.isAdded()) {
239 trans.add(mTabContainerViewId, frag);
240 }
241 }
242
243 if (isFirstTab) {
244 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
245 trans.show(frag);
246 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
247 trans.add(mTabContainerViewId, frag);
248 }
249 mSelectedTab = tabi;
250 } else {
251 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
252 trans.hide(frag);
253 }
254 }
255 trans.commit();
256 }
257
258 @Override
259 public void addTab(Tab tab) {
260 mActionView.addTab(tab);
261 configureTab(tab, mTabs.size());
262 }
263
264 @Override
265 public void insertTab(Tab tab, int position) {
266 mActionView.insertTab(tab, position);
267 configureTab(tab, position);
268 }
269
270 @Override
271 public Tab newTab() {
272 return new TabImpl();
273 }
274
275 @Override
276 public void removeTab(Tab tab) {
277 removeTabAt(tab.getPosition());
278 }
279
280 @Override
281 public void removeTabAt(int position) {
282 mActionView.removeTabAt(position);
283 mTabs.remove(position);
284
285 final int newTabCount = mTabs.size();
286 for (int i = position; i < newTabCount; i++) {
287 mTabs.get(i).setPosition(i);
288 }
289
290 selectTab(mTabs.isEmpty() ? null : mTabs.get(Math.max(0, position - 1)));
291 }
292
293 @Override
294 public void setTabNavigationMode() {
295 mActionView.setNavigationMode(NAVIGATION_MODE_TABS);
296 }
297
298 @Override
299 public void setTabNavigationMode(int containerViewId) {
300 mTabContainerViewId = containerViewId;
301 setTabNavigationMode();
302 }
303
304 @Override
305 public void selectTab(Tab tab) {
306 if (mSelectedTab == tab) {
307 return;
308 }
309
310 mActionView.setTabSelected(tab != null ? tab.getPosition() : Tab.INVALID_POSITION);
311 final FragmentTransaction trans = mActivity.openFragmentTransaction();
312 if (mSelectedTab != null) {
313 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
314 trans.hide(mSelectedTab.getFragment());
315 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
316 trans.remove(mSelectedTab.getFragment());
317 }
318 }
319 if (tab != null) {
320 if (mTabSwitchMode == TAB_SWITCH_SHOW_HIDE) {
321 trans.show(tab.getFragment());
322 } else if (mTabSwitchMode == TAB_SWITCH_ADD_REMOVE) {
323 trans.add(mTabContainerViewId, tab.getFragment());
324 }
325 }
326 mSelectedTab = (TabImpl) tab;
327 trans.commit();
328 }
329
330 @Override
331 public void selectTabAt(int position) {
332 selectTab(mTabs.get(position));
333 }
334
Adam Powell89e06452010-06-23 20:24:52 -0700335 /**
336 * @hide
337 */
338 public class ContextMode extends ActionBar.ContextMode {
339 private ContextModeCallback mCallback;
340 private ActionMenu mMenu;
341
342 public ContextMode(ContextModeCallback callback) {
343 mCallback = callback;
344 mMenu = new ActionMenu(mActionView.getContext());
345 }
346
347 @Override
348 public Menu getMenu() {
349 return mMenu;
350 }
351
352 @Override
353 public void finish() {
354 mCallback.onDestroyContextMode(this);
Adam Powell89e06452010-06-23 20:24:52 -0700355 mAnimatorView.setDisplayedChild(NORMAL_VIEW);
Adam Powell0e94b512010-06-29 17:58:20 -0700356
357 // Clear out the context mode views after the animation finishes
Adam Powell3461b322010-07-14 11:34:43 -0700358 mClosingContext = true;
Adam Powell0e94b512010-06-29 17:58:20 -0700359 mHandler.postDelayed(mCloseContext, mAnimatorView.getOutAnimation().getDuration());
360
Adam Powell89e06452010-06-23 20:24:52 -0700361 if (mLowerContextView != null && mLowerContextView.getVisibility() != View.GONE) {
362 // TODO Animate this
363 mLowerContextView.setVisibility(View.GONE);
364 }
365 mContextMode = null;
366 }
367
368 @Override
369 public void invalidate() {
370 if (mCallback.onPrepareContextMode(this, mMenu)) {
371 // Refresh content in both context views
372 }
373 }
374
375 @Override
376 public void setCustomView(View view) {
377 mUpperContextView.setCustomView(view);
378 }
379
380 @Override
381 public void setSubtitle(CharSequence subtitle) {
382 mUpperContextView.setSubtitle(subtitle);
383 }
384
385 @Override
386 public void setTitle(CharSequence title) {
387 mUpperContextView.setTitle(title);
388 }
389
390 public void dispatchOnContextItemClicked(MenuItem item) {
391 ActionMenuItem actionItem = (ActionMenuItem) item;
392 if (!actionItem.invoke()) {
393 mCallback.onContextItemClicked(this, item);
394 }
Adam Powell661c9082010-07-02 10:09:44 -0700395 }
396 }
397
398 /**
399 * @hide
400 */
401 public class TabImpl extends ActionBar.Tab {
402 private Fragment mFragment;
403 private Drawable mIcon;
404 private CharSequence mText;
405 private int mPosition;
406
407 @Override
408 public Fragment getFragment() {
409 return mFragment;
410 }
411
412 @Override
413 public Drawable getIcon() {
414 return mIcon;
415 }
416
417 @Override
418 public int getPosition() {
419 return mPosition;
420 }
421
422 public void setPosition(int position) {
423 mPosition = position;
424 }
425
426 @Override
427 public CharSequence getText() {
428 return mText;
429 }
430
431 @Override
432 public void setFragment(Fragment fragment) {
433 mFragment = fragment;
434 }
435
436 @Override
437 public void setIcon(Drawable icon) {
438 mIcon = icon;
439 }
440
441 @Override
442 public void setText(CharSequence text) {
443 mText = text;
444 }
445
446 @Override
447 public void select() {
448 selectTab(this);
Adam Powell89e06452010-06-23 20:24:52 -0700449 }
450 }
451}