blob: 238dc559b38a98d0c371e5a74312233f70921c7f [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2006 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 android.widget;
18
Gilles Debunnece2baf02010-02-10 15:19:14 -080019import com.android.internal.R;
20
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080021import android.app.LocalActivityManager;
22import android.content.Context;
23import android.content.Intent;
Gilles Debunne44c14732010-10-19 11:56:59 -070024import android.content.res.TypedArray;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080025import android.graphics.drawable.Drawable;
Mike Cleron76097642009-09-25 17:53:56 -070026import android.os.Build;
Jeff Sharkey11f4a482011-08-08 21:05:40 -070027import android.text.TextUtils;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.util.AttributeSet;
29import android.view.KeyEvent;
30import android.view.LayoutInflater;
31import android.view.SoundEffectConstants;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.ViewTreeObserver;
35import android.view.Window;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080036import android.view.accessibility.AccessibilityEvent;
37import android.view.accessibility.AccessibilityNodeInfo;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080038
39import java.util.ArrayList;
40import java.util.List;
41
42/**
43 * Container for a tabbed window view. This object holds two children: a set of tab labels that the
44 * user clicks to select a specific tab, and a FrameLayout object that displays the contents of that
45 * page. The individual elements are typically controlled using this container object, rather than
46 * setting values on the child elements themselves.
Scott Main41ec6532010-08-19 16:57:07 -070047 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048 */
49public class TabHost extends FrameLayout implements ViewTreeObserver.OnTouchModeChangeListener {
50
Roger Olsson54ed87c2011-01-24 11:19:11 +010051 private static final int TABWIDGET_LOCATION_LEFT = 0;
52 private static final int TABWIDGET_LOCATION_TOP = 1;
53 private static final int TABWIDGET_LOCATION_RIGHT = 2;
54 private static final int TABWIDGET_LOCATION_BOTTOM = 3;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080055 private TabWidget mTabWidget;
56 private FrameLayout mTabContent;
57 private List<TabSpec> mTabSpecs = new ArrayList<TabSpec>(2);
58 /**
59 * This field should be made private, so it is hidden from the SDK.
60 * {@hide}
61 */
62 protected int mCurrentTab = -1;
63 private View mCurrentView = null;
64 /**
65 * This field should be made private, so it is hidden from the SDK.
66 * {@hide}
67 */
68 protected LocalActivityManager mLocalActivityManager = null;
69 private OnTabChangeListener mOnTabChangeListener;
70 private OnKeyListener mTabKeyListener;
71
Gilles Debunne44c14732010-10-19 11:56:59 -070072 private int mTabLayoutId;
73
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 public TabHost(Context context) {
75 super(context);
76 initTabHost();
77 }
78
79 public TabHost(Context context, AttributeSet attrs) {
80 super(context, attrs);
Gilles Debunne44c14732010-10-19 11:56:59 -070081
82 TypedArray a = context.obtainStyledAttributes(attrs,
83 com.android.internal.R.styleable.TabWidget,
84 com.android.internal.R.attr.tabWidgetStyle, 0);
85
86 mTabLayoutId = a.getResourceId(R.styleable.TabWidget_tabLayout, 0);
Gilles Debunne44c14732010-10-19 11:56:59 -070087 a.recycle();
88
Gilles Debunne0e23ab72010-12-14 19:26:37 -080089 if (mTabLayoutId == 0) {
90 // In case the tabWidgetStyle does not inherit from Widget.TabWidget and tabLayout is
91 // not defined.
92 mTabLayoutId = R.layout.tab_indicator_holo;
93 }
94
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080095 initTabHost();
96 }
97
Romain Guy7237c562009-08-18 17:38:14 -070098 private void initTabHost() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 setFocusableInTouchMode(true);
100 setDescendantFocusability(FOCUS_AFTER_DESCENDANTS);
101
102 mCurrentTab = -1;
103 mCurrentView = null;
104 }
105
106 /**
107 * Get a new {@link TabSpec} associated with this tab host.
108 * @param tag required tag of tab.
109 */
110 public TabSpec newTabSpec(String tag) {
111 return new TabSpec(tag);
112 }
113
114
115
116 /**
Jack Veenstra982be3b2009-05-27 15:07:59 -0700117 * <p>Call setup() before adding tabs if loading TabHost using findViewById().
118 * <i><b>However</i></b>: You do not need to call setup() after getTabHost()
119 * in {@link android.app.TabActivity TabActivity}.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800120 * Example:</p>
121<pre>mTabHost = (TabHost)findViewById(R.id.tabhost);
122mTabHost.setup();
123mTabHost.addTab(TAB_TAG_1, "Hello, world!", "Tab 1");
124 */
125 public void setup() {
126 mTabWidget = (TabWidget) findViewById(com.android.internal.R.id.tabs);
127 if (mTabWidget == null) {
128 throw new RuntimeException(
129 "Your TabHost must have a TabWidget whose id attribute is 'android.R.id.tabs'");
130 }
Evan Millar3730bb12009-08-21 13:58:41 -0700131
132 // KeyListener to attach to all tabs. Detects non-navigation keys
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800133 // and relays them to the tab content.
134 mTabKeyListener = new OnKeyListener() {
135 public boolean onKey(View v, int keyCode, KeyEvent event) {
136 switch (keyCode) {
137 case KeyEvent.KEYCODE_DPAD_CENTER:
138 case KeyEvent.KEYCODE_DPAD_LEFT:
139 case KeyEvent.KEYCODE_DPAD_RIGHT:
140 case KeyEvent.KEYCODE_DPAD_UP:
141 case KeyEvent.KEYCODE_DPAD_DOWN:
142 case KeyEvent.KEYCODE_ENTER:
143 return false;
Evan Millar3730bb12009-08-21 13:58:41 -0700144
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800145 }
146 mTabContent.requestFocus(View.FOCUS_FORWARD);
147 return mTabContent.dispatchKeyEvent(event);
148 }
Evan Millar3730bb12009-08-21 13:58:41 -0700149
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800150 };
Evan Millar3730bb12009-08-21 13:58:41 -0700151
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800152 mTabWidget.setTabSelectionListener(new TabWidget.OnTabSelectionChanged() {
153 public void onTabSelectionChanged(int tabIndex, boolean clicked) {
154 setCurrentTab(tabIndex);
155 if (clicked) {
156 mTabContent.requestFocus(View.FOCUS_FORWARD);
157 }
158 }
159 });
160
161 mTabContent = (FrameLayout) findViewById(com.android.internal.R.id.tabcontent);
162 if (mTabContent == null) {
163 throw new RuntimeException(
Evan Millar3730bb12009-08-21 13:58:41 -0700164 "Your TabHost must have a FrameLayout whose id attribute is "
Romain Guy7237c562009-08-18 17:38:14 -0700165 + "'android.R.id.tabcontent'");
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800166 }
167 }
168
Svetoslav Ganov5ac413a2010-10-05 15:59:25 -0700169 @Override
170 public void sendAccessibilityEvent(int eventType) {
171 /* avoid super class behavior - TabWidget sends the right events */
172 }
173
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800174 /**
175 * If you are using {@link TabSpec#setContent(android.content.Intent)}, this
176 * must be called since the activityGroup is needed to launch the local activity.
177 *
178 * This is done for you if you extend {@link android.app.TabActivity}.
179 * @param activityGroup Used to launch activities for tab content.
180 */
181 public void setup(LocalActivityManager activityGroup) {
182 setup();
183 mLocalActivityManager = activityGroup;
184 }
185
186
187 @Override
188 protected void onAttachedToWindow() {
189 super.onAttachedToWindow();
190 final ViewTreeObserver treeObserver = getViewTreeObserver();
Gilles Debunne0e7d652d2011-02-22 15:26:14 -0800191 treeObserver.addOnTouchModeChangeListener(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800192 }
193
194 @Override
195 protected void onDetachedFromWindow() {
196 super.onDetachedFromWindow();
197 final ViewTreeObserver treeObserver = getViewTreeObserver();
Gilles Debunne0e7d652d2011-02-22 15:26:14 -0800198 treeObserver.removeOnTouchModeChangeListener(this);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800199 }
200
201 /**
202 * {@inheritDoc}
203 */
204 public void onTouchModeChanged(boolean isInTouchMode) {
205 if (!isInTouchMode) {
206 // leaving touch mode.. if nothing has focus, let's give it to
207 // the indicator of the current tab
Romain Guy7237c562009-08-18 17:38:14 -0700208 if (mCurrentView != null && (!mCurrentView.hasFocus() || mCurrentView.isFocused())) {
Jack Veenstra53175142009-06-01 21:27:01 -0700209 mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211 }
212 }
213
214 /**
215 * Add a tab.
216 * @param tabSpec Specifies how to create the indicator and content.
217 */
218 public void addTab(TabSpec tabSpec) {
219
220 if (tabSpec.mIndicatorStrategy == null) {
221 throw new IllegalArgumentException("you must specify a way to create the tab indicator.");
222 }
223
224 if (tabSpec.mContentStrategy == null) {
225 throw new IllegalArgumentException("you must specify a way to create the tab content");
226 }
227 View tabIndicator = tabSpec.mIndicatorStrategy.createIndicatorView();
228 tabIndicator.setOnKeyListener(mTabKeyListener);
Jack Veenstra53175142009-06-01 21:27:01 -0700229
230 // If this is a custom view, then do not draw the bottom strips for
231 // the tab indicators.
232 if (tabSpec.mIndicatorStrategy instanceof ViewIndicatorStrategy) {
Romain Guy61c9d4b2010-03-01 14:12:10 -0800233 mTabWidget.setStripEnabled(false);
Jack Veenstra53175142009-06-01 21:27:01 -0700234 }
Gilles Debunne44c14732010-10-19 11:56:59 -0700235
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800236 mTabWidget.addView(tabIndicator);
237 mTabSpecs.add(tabSpec);
238
239 if (mCurrentTab == -1) {
240 setCurrentTab(0);
241 }
242 }
243
244
245 /**
246 * Removes all tabs from the tab widget associated with this tab host.
247 */
248 public void clearAllTabs() {
249 mTabWidget.removeAllViews();
250 initTabHost();
251 mTabContent.removeAllViews();
252 mTabSpecs.clear();
253 requestLayout();
254 invalidate();
255 }
256
257 public TabWidget getTabWidget() {
258 return mTabWidget;
259 }
260
261 public int getCurrentTab() {
262 return mCurrentTab;
263 }
264
265 public String getCurrentTabTag() {
266 if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
267 return mTabSpecs.get(mCurrentTab).getTag();
268 }
269 return null;
270 }
271
272 public View getCurrentTabView() {
273 if (mCurrentTab >= 0 && mCurrentTab < mTabSpecs.size()) {
Jack Veenstra53175142009-06-01 21:27:01 -0700274 return mTabWidget.getChildTabViewAt(mCurrentTab);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800275 }
276 return null;
277 }
278
279 public View getCurrentView() {
280 return mCurrentView;
281 }
282
283 public void setCurrentTabByTag(String tag) {
284 int i;
285 for (i = 0; i < mTabSpecs.size(); i++) {
286 if (mTabSpecs.get(i).getTag().equals(tag)) {
287 setCurrentTab(i);
288 break;
289 }
290 }
291 }
292
293 /**
294 * Get the FrameLayout which holds tab content
295 */
296 public FrameLayout getTabContentView() {
297 return mTabContent;
298 }
299
Roger Olsson54ed87c2011-01-24 11:19:11 +0100300 /**
301 * Get the location of the TabWidget.
302 *
303 * @return The TabWidget location.
304 */
305 private int getTabWidgetLocation() {
306 int location = TABWIDGET_LOCATION_TOP;
307
308 switch (mTabWidget.getOrientation()) {
309 case LinearLayout.VERTICAL:
310 location = (mTabContent.getLeft() < mTabWidget.getLeft()) ? TABWIDGET_LOCATION_RIGHT
311 : TABWIDGET_LOCATION_LEFT;
312 break;
313 case LinearLayout.HORIZONTAL:
314 default:
315 location = (mTabContent.getTop() < mTabWidget.getTop()) ? TABWIDGET_LOCATION_BOTTOM
316 : TABWIDGET_LOCATION_TOP;
317 break;
318 }
319 return location;
320 }
321
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800322 @Override
323 public boolean dispatchKeyEvent(KeyEvent event) {
324 final boolean handled = super.dispatchKeyEvent(event);
325
Roger Olsson54ed87c2011-01-24 11:19:11 +0100326 // unhandled key events change focus to tab indicator for embedded
327 // activities when there is nothing that will take focus from default
328 // focus searching
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800329 if (!handled
330 && (event.getAction() == KeyEvent.ACTION_DOWN)
Bjorn Bringertacdef592009-12-10 15:58:49 +0000331 && (mCurrentView != null)
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800332 && (mCurrentView.isRootNamespace())
Roger Olsson54ed87c2011-01-24 11:19:11 +0100333 && (mCurrentView.hasFocus())) {
334 int keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
335 int directionShouldChangeFocus = View.FOCUS_UP;
336 int soundEffect = SoundEffectConstants.NAVIGATION_UP;
337
338 switch (getTabWidgetLocation()) {
339 case TABWIDGET_LOCATION_LEFT:
340 keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_LEFT;
341 directionShouldChangeFocus = View.FOCUS_LEFT;
342 soundEffect = SoundEffectConstants.NAVIGATION_LEFT;
343 break;
344 case TABWIDGET_LOCATION_RIGHT:
345 keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_RIGHT;
346 directionShouldChangeFocus = View.FOCUS_RIGHT;
347 soundEffect = SoundEffectConstants.NAVIGATION_RIGHT;
348 break;
349 case TABWIDGET_LOCATION_BOTTOM:
350 keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_DOWN;
351 directionShouldChangeFocus = View.FOCUS_DOWN;
352 soundEffect = SoundEffectConstants.NAVIGATION_DOWN;
353 break;
354 case TABWIDGET_LOCATION_TOP:
355 default:
356 keyCodeShouldChangeFocus = KeyEvent.KEYCODE_DPAD_UP;
357 directionShouldChangeFocus = View.FOCUS_UP;
358 soundEffect = SoundEffectConstants.NAVIGATION_UP;
359 break;
360 }
361 if (event.getKeyCode() == keyCodeShouldChangeFocus
362 && mCurrentView.findFocus().focusSearch(directionShouldChangeFocus) == null) {
363 mTabWidget.getChildTabViewAt(mCurrentTab).requestFocus();
364 playSoundEffect(soundEffect);
365 return true;
366 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 }
Evan Millar3730bb12009-08-21 13:58:41 -0700368 return handled;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800369 }
370
371
372 @Override
373 public void dispatchWindowFocusChanged(boolean hasFocus) {
Bjorn Bringertacdef592009-12-10 15:58:49 +0000374 if (mCurrentView != null){
375 mCurrentView.dispatchWindowFocusChanged(hasFocus);
376 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 }
378
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -0800379 @Override
380 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
381 super.onInitializeAccessibilityEvent(event);
382 event.setClassName(TabHost.class.getName());
383 }
384
385 @Override
386 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
387 super.onInitializeAccessibilityNodeInfo(info);
388 info.setClassName(TabHost.class.getName());
389 }
390
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800391 public void setCurrentTab(int index) {
392 if (index < 0 || index >= mTabSpecs.size()) {
393 return;
394 }
395
396 if (index == mCurrentTab) {
397 return;
398 }
399
400 // notify old tab content
401 if (mCurrentTab != -1) {
402 mTabSpecs.get(mCurrentTab).mContentStrategy.tabClosed();
403 }
404
405 mCurrentTab = index;
406 final TabHost.TabSpec spec = mTabSpecs.get(index);
407
408 // Call the tab widget's focusCurrentTab(), instead of just
409 // selecting the tab.
410 mTabWidget.focusCurrentTab(mCurrentTab);
Evan Millar3730bb12009-08-21 13:58:41 -0700411
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800412 // tab content
413 mCurrentView = spec.mContentStrategy.getContentView();
414
415 if (mCurrentView.getParent() == null) {
416 mTabContent
417 .addView(
418 mCurrentView,
419 new ViewGroup.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -0800420 ViewGroup.LayoutParams.MATCH_PARENT,
421 ViewGroup.LayoutParams.MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800422 }
423
424 if (!mTabWidget.hasFocus()) {
425 // if the tab widget didn't take focus (likely because we're in touch mode)
426 // give the current tab content view a shot
427 mCurrentView.requestFocus();
428 }
429
430 //mTabContent.requestFocus(View.FOCUS_FORWARD);
431 invokeOnTabChangeListener();
432 }
433
434 /**
435 * Register a callback to be invoked when the selected state of any of the items
436 * in this list changes
437 * @param l
438 * The callback that will run
439 */
440 public void setOnTabChangedListener(OnTabChangeListener l) {
441 mOnTabChangeListener = l;
442 }
443
444 private void invokeOnTabChangeListener() {
445 if (mOnTabChangeListener != null) {
446 mOnTabChangeListener.onTabChanged(getCurrentTabTag());
447 }
448 }
449
450 /**
451 * Interface definition for a callback to be invoked when tab changed
452 */
453 public interface OnTabChangeListener {
454 void onTabChanged(String tabId);
455 }
456
457
458 /**
459 * Makes the content of a tab when it is selected. Use this if your tab
460 * content needs to be created on demand, i.e. you are not showing an
461 * existing view or starting an activity.
462 */
463 public interface TabContentFactory {
464 /**
465 * Callback to make the tab contents
Evan Millar3730bb12009-08-21 13:58:41 -0700466 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800467 * @param tag
468 * Which tab was selected.
Jack Veenstra982be3b2009-05-27 15:07:59 -0700469 * @return The view to display the contents of the selected tab.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 */
471 View createTabContent(String tag);
472 }
473
474
475 /**
Jack Veenstra982be3b2009-05-27 15:07:59 -0700476 * A tab has a tab indicator, content, and a tag that is used to keep
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800477 * track of it. This builder helps choose among these options.
478 *
479 * For the tab indicator, your choices are:
480 * 1) set a label
481 * 2) set a label and an icon
482 *
483 * For the tab content, your choices are:
484 * 1) the id of a {@link View}
485 * 2) a {@link TabContentFactory} that creates the {@link View} content.
486 * 3) an {@link Intent} that launches an {@link android.app.Activity}.
487 */
488 public class TabSpec {
489
490 private String mTag;
491
492 private IndicatorStrategy mIndicatorStrategy;
493 private ContentStrategy mContentStrategy;
494
495 private TabSpec(String tag) {
496 mTag = tag;
497 }
498
499 /**
500 * Specify a label as the tab indicator.
501 */
502 public TabSpec setIndicator(CharSequence label) {
503 mIndicatorStrategy = new LabelIndicatorStrategy(label);
504 return this;
505 }
506
507 /**
508 * Specify a label and icon as the tab indicator.
509 */
510 public TabSpec setIndicator(CharSequence label, Drawable icon) {
511 mIndicatorStrategy = new LabelAndIconIndicatorStrategy(label, icon);
512 return this;
513 }
514
515 /**
Jack Veenstra53175142009-06-01 21:27:01 -0700516 * Specify a view as the tab indicator.
517 */
518 public TabSpec setIndicator(View view) {
519 mIndicatorStrategy = new ViewIndicatorStrategy(view);
520 return this;
521 }
522
523 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800524 * Specify the id of the view that should be used as the content
525 * of the tab.
526 */
527 public TabSpec setContent(int viewId) {
528 mContentStrategy = new ViewIdContentStrategy(viewId);
529 return this;
530 }
531
532 /**
533 * Specify a {@link android.widget.TabHost.TabContentFactory} to use to
534 * create the content of the tab.
535 */
536 public TabSpec setContent(TabContentFactory contentFactory) {
537 mContentStrategy = new FactoryContentStrategy(mTag, contentFactory);
538 return this;
539 }
540
541 /**
542 * Specify an intent to use to launch an activity as the tab content.
543 */
544 public TabSpec setContent(Intent intent) {
545 mContentStrategy = new IntentContentStrategy(mTag, intent);
546 return this;
547 }
548
549
Jack Veenstra53175142009-06-01 21:27:01 -0700550 public String getTag() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 return mTag;
552 }
553 }
554
555 /**
556 * Specifies what you do to create a tab indicator.
557 */
558 private static interface IndicatorStrategy {
559
560 /**
561 * Return the view for the indicator.
562 */
563 View createIndicatorView();
564 }
565
566 /**
567 * Specifies what you do to manage the tab content.
568 */
569 private static interface ContentStrategy {
570
571 /**
572 * Return the content view. The view should may be cached locally.
573 */
574 View getContentView();
575
576 /**
577 * Perhaps do something when the tab associated with this content has
578 * been closed (i.e make it invisible, or remove it).
579 */
580 void tabClosed();
581 }
582
583 /**
584 * How to create a tab indicator that just has a label.
585 */
586 private class LabelIndicatorStrategy implements IndicatorStrategy {
587
588 private final CharSequence mLabel;
589
590 private LabelIndicatorStrategy(CharSequence label) {
591 mLabel = label;
592 }
593
594 public View createIndicatorView() {
Mike Cleron76097642009-09-25 17:53:56 -0700595 final Context context = getContext();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596 LayoutInflater inflater =
Mike Cleron76097642009-09-25 17:53:56 -0700597 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Gilles Debunne44c14732010-10-19 11:56:59 -0700598 View tabIndicator = inflater.inflate(mTabLayoutId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800599 mTabWidget, // tab widget is the parent
600 false); // no inflate params
601
602 final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
603 tv.setText(mLabel);
604
Mike Cleron76097642009-09-25 17:53:56 -0700605 if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
606 // Donut apps get old color scheme
607 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
608 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
609 }
Gilles Debunne44c14732010-10-19 11:56:59 -0700610
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 return tabIndicator;
612 }
613 }
614
615 /**
616 * How we create a tab indicator that has a label and an icon
617 */
618 private class LabelAndIconIndicatorStrategy implements IndicatorStrategy {
619
620 private final CharSequence mLabel;
621 private final Drawable mIcon;
622
623 private LabelAndIconIndicatorStrategy(CharSequence label, Drawable icon) {
624 mLabel = label;
625 mIcon = icon;
626 }
627
628 public View createIndicatorView() {
Mike Cleron76097642009-09-25 17:53:56 -0700629 final Context context = getContext();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 LayoutInflater inflater =
Mike Cleron76097642009-09-25 17:53:56 -0700631 (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
Gilles Debunne44c14732010-10-19 11:56:59 -0700632 View tabIndicator = inflater.inflate(mTabLayoutId,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 mTabWidget, // tab widget is the parent
634 false); // no inflate params
635
636 final TextView tv = (TextView) tabIndicator.findViewById(R.id.title);
Jeff Sharkey11f4a482011-08-08 21:05:40 -0700637 final ImageView iconView = (ImageView) tabIndicator.findViewById(R.id.icon);
638
639 // when icon is gone by default, we're in exclusive mode
640 final boolean exclusive = iconView.getVisibility() == View.GONE;
641 final boolean bindIcon = !exclusive || TextUtils.isEmpty(mLabel);
642
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800643 tv.setText(mLabel);
644
Jeff Sharkey11f4a482011-08-08 21:05:40 -0700645 if (bindIcon && mIcon != null) {
Gilles Debunne44c14732010-10-19 11:56:59 -0700646 iconView.setImageDrawable(mIcon);
647 iconView.setVisibility(VISIBLE);
648 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800649
Mike Cleron76097642009-09-25 17:53:56 -0700650 if (context.getApplicationInfo().targetSdkVersion <= Build.VERSION_CODES.DONUT) {
651 // Donut apps get old color scheme
652 tabIndicator.setBackgroundResource(R.drawable.tab_indicator_v4);
653 tv.setTextColor(context.getResources().getColorStateList(R.color.tab_indicator_text_v4));
654 }
Gilles Debunne44c14732010-10-19 11:56:59 -0700655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 return tabIndicator;
657 }
658 }
659
660 /**
Jack Veenstra53175142009-06-01 21:27:01 -0700661 * How to create a tab indicator by specifying a view.
662 */
663 private class ViewIndicatorStrategy implements IndicatorStrategy {
664
665 private final View mView;
666
667 private ViewIndicatorStrategy(View view) {
668 mView = view;
669 }
670
671 public View createIndicatorView() {
672 return mView;
673 }
674 }
675
676 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 * How to create the tab content via a view id.
678 */
679 private class ViewIdContentStrategy implements ContentStrategy {
680
681 private final View mView;
682
683 private ViewIdContentStrategy(int viewId) {
684 mView = mTabContent.findViewById(viewId);
685 if (mView != null) {
686 mView.setVisibility(View.GONE);
687 } else {
688 throw new RuntimeException("Could not create tab content because " +
689 "could not find view with id " + viewId);
690 }
691 }
692
693 public View getContentView() {
694 mView.setVisibility(View.VISIBLE);
695 return mView;
696 }
697
698 public void tabClosed() {
699 mView.setVisibility(View.GONE);
700 }
701 }
702
703 /**
704 * How tab content is managed using {@link TabContentFactory}.
705 */
706 private class FactoryContentStrategy implements ContentStrategy {
707 private View mTabContent;
708 private final CharSequence mTag;
709 private TabContentFactory mFactory;
710
711 public FactoryContentStrategy(CharSequence tag, TabContentFactory factory) {
712 mTag = tag;
713 mFactory = factory;
714 }
715
716 public View getContentView() {
717 if (mTabContent == null) {
718 mTabContent = mFactory.createTabContent(mTag.toString());
719 }
720 mTabContent.setVisibility(View.VISIBLE);
721 return mTabContent;
722 }
723
724 public void tabClosed() {
Gilles Debunnece2baf02010-02-10 15:19:14 -0800725 mTabContent.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800726 }
727 }
728
729 /**
730 * How tab content is managed via an {@link Intent}: the content view is the
731 * decorview of the launched activity.
732 */
733 private class IntentContentStrategy implements ContentStrategy {
734
735 private final String mTag;
736 private final Intent mIntent;
737
738 private View mLaunchedView;
739
740 private IntentContentStrategy(String tag, Intent intent) {
741 mTag = tag;
742 mIntent = intent;
743 }
744
745 public View getContentView() {
746 if (mLocalActivityManager == null) {
747 throw new IllegalStateException("Did you forget to call 'public void setup(LocalActivityManager activityGroup)'?");
748 }
749 final Window w = mLocalActivityManager.startActivity(
750 mTag, mIntent);
751 final View wd = w != null ? w.getDecorView() : null;
752 if (mLaunchedView != wd && mLaunchedView != null) {
753 if (mLaunchedView.getParent() != null) {
754 mTabContent.removeView(mLaunchedView);
755 }
756 }
757 mLaunchedView = wd;
Evan Millar3730bb12009-08-21 13:58:41 -0700758
Jack Veenstra982be3b2009-05-27 15:07:59 -0700759 // XXX Set FOCUS_AFTER_DESCENDANTS on embedded activities for now so they can get
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800760 // focus if none of their children have it. They need focus to be able to
761 // display menu items.
762 //
763 // Replace this with something better when Bug 628886 is fixed...
764 //
765 if (mLaunchedView != null) {
766 mLaunchedView.setVisibility(View.VISIBLE);
767 mLaunchedView.setFocusableInTouchMode(true);
768 ((ViewGroup) mLaunchedView).setDescendantFocusability(
769 FOCUS_AFTER_DESCENDANTS);
770 }
771 return mLaunchedView;
772 }
773
774 public void tabClosed() {
775 if (mLaunchedView != null) {
776 mLaunchedView.setVisibility(View.GONE);
777 }
778 }
779 }
780
781}