1 package com
.actionbarsherlock
.internal
;
3 import static android
.view
.ViewGroup
.LayoutParams
.MATCH_PARENT
;
4 import static com
.actionbarsherlock
.internal
.ResourcesCompat
.getResources_getBoolean
;
5 import java
.util
.ArrayList
;
6 import java
.util
.HashMap
;
8 import org
.xmlpull
.v1
.XmlPullParser
;
9 import android
.app
.Activity
;
10 import android
.content
.Context
;
11 import android
.content
.pm
.ActivityInfo
;
12 import android
.content
.res
.AssetManager
;
13 import android
.content
.res
.Configuration
;
14 import android
.content
.res
.Resources
;
15 import android
.content
.res
.TypedArray
;
16 import android
.content
.res
.XmlResourceParser
;
17 import android
.os
.Bundle
;
18 import android
.util
.AndroidRuntimeException
;
19 import android
.util
.Log
;
20 import android
.util
.TypedValue
;
21 import android
.view
.ContextThemeWrapper
;
22 import android
.view
.KeyCharacterMap
;
23 import android
.view
.KeyEvent
;
24 import android
.view
.View
;
25 import android
.view
.ViewGroup
;
26 import android
.view
.ViewStub
;
27 import android
.view
.Window
;
28 import android
.view
.accessibility
.AccessibilityEvent
;
29 import android
.view
.animation
.Animation
;
30 import android
.view
.animation
.AnimationUtils
;
31 import android
.widget
.FrameLayout
;
32 import android
.widget
.TextView
;
33 import com
.actionbarsherlock
.ActionBarSherlock
;
34 import com
.actionbarsherlock
.R
;
35 import com
.actionbarsherlock
.app
.ActionBar
;
36 import com
.actionbarsherlock
.internal
.app
.ActionBarImpl
;
37 import com
.actionbarsherlock
.internal
.view
.StandaloneActionMode
;
38 import com
.actionbarsherlock
.internal
.view
.menu
.ActionMenuPresenter
;
39 import com
.actionbarsherlock
.internal
.view
.menu
.MenuBuilder
;
40 import com
.actionbarsherlock
.internal
.view
.menu
.MenuItemImpl
;
41 import com
.actionbarsherlock
.internal
.view
.menu
.MenuPresenter
;
42 import com
.actionbarsherlock
.internal
.widget
.ActionBarContainer
;
43 import com
.actionbarsherlock
.internal
.widget
.ActionBarContextView
;
44 import com
.actionbarsherlock
.internal
.widget
.ActionBarView
;
45 import com
.actionbarsherlock
.internal
.widget
.IcsProgressBar
;
46 import com
.actionbarsherlock
.view
.ActionMode
;
47 import com
.actionbarsherlock
.view
.Menu
;
48 import com
.actionbarsherlock
.view
.MenuItem
;
50 @ActionBarSherlock.Implementation(api
= 7)
51 public class ActionBarSherlockCompat
extends ActionBarSherlock
implements MenuBuilder
.Callback
, com
.actionbarsherlock
.view
.Window
.Callback
, MenuPresenter
.Callback
, android
.view
.MenuItem
.OnMenuItemClickListener
{
52 /** Window features which are enabled by default. */
53 protected static final int DEFAULT_FEATURES
= 0;
55 static private final String PANELS_TAG
= "sherlock:Panels";
57 public ActionBarSherlockCompat(Activity activity
, int flags
) {
58 super(activity
, flags
);
62 ///////////////////////////////////////////////////////////////////////////
64 ///////////////////////////////////////////////////////////////////////////
66 /** Whether or not the device has a dedicated menu key button. */
67 private boolean mReserveOverflow
;
68 /** Lazy-load indicator for {@link #mReserveOverflow}. */
69 private boolean mReserveOverflowSet
= false
;
71 /** Current menu instance for managing action items. */
72 private MenuBuilder mMenu
;
73 /** Map between native options items and sherlock items. */
74 protected HashMap
<android
.view
.MenuItem
, MenuItemImpl
> mNativeItemMap
;
76 /** Parent view of the window decoration (action bar, mode, etc.). */
77 private ViewGroup mDecor
;
78 /** Parent view of the activity content. */
79 private ViewGroup mContentParent
;
81 /** Whether or not the title is stable and can be displayed. */
82 private boolean mIsTitleReady
= false
;
83 /** Whether or not the parent activity has been destroyed. */
84 private boolean mIsDestroyed
= false
;
86 /* Emulate PanelFeatureState */
87 private boolean mClosingActionMenu
;
88 private boolean mMenuIsPrepared
;
89 private boolean mMenuRefreshContent
;
90 private Bundle mMenuFrozenActionViewState
;
92 /** Implementation which backs the action bar interface API. */
93 private ActionBarImpl aActionBar
;
94 /** Main action bar view which displays the core content. */
95 private ActionBarView wActionBar
;
96 /** Relevant window and action bar features flags. */
97 private int mFeatures
= DEFAULT_FEATURES
;
98 /** Relevant user interface option flags. */
99 private int mUiOptions
= 0;
101 /** Decor indeterminate progress indicator. */
102 private IcsProgressBar mCircularProgressBar
;
103 /** Decor progress indicator. */
104 private IcsProgressBar mHorizontalProgressBar
;
106 /** Current displayed context action bar, if any. */
107 private ActionMode mActionMode
;
108 /** Parent view in which the context action bar is displayed. */
109 private ActionBarContextView mActionModeView
;
111 /** Title view used with dialogs. */
112 private TextView mTitleView
;
113 /** Current activity title. */
114 private CharSequence mTitle
= null
;
115 /** Whether or not this "activity" is floating (i.e., a dialog) */
116 private boolean mIsFloating
;
120 ///////////////////////////////////////////////////////////////////////////
122 ///////////////////////////////////////////////////////////////////////////
125 public ActionBar
getActionBar() {
126 if (DEBUG
) Log
.d(TAG
, "[getActionBar]");
132 private void initActionBar() {
133 if (DEBUG
) Log
.d(TAG
, "[initActionBar]");
135 // Initializing the window decor can change window feature flags.
136 // Make sure that we have the correct set before performing the test below.
137 if (mDecor
== null
) {
141 if ((aActionBar
!= null
) || !hasFeature(Window
.FEATURE_ACTION_BAR
) || hasFeature(Window
.FEATURE_NO_TITLE
) || mActivity
.isChild()) {
145 aActionBar
= new ActionBarImpl(mActivity
, mFeatures
);
148 //We may never get another chance to set the title
149 wActionBar
.setWindowTitle(mActivity
.getTitle());
154 protected Context
getThemedContext() {
155 return aActionBar
.getThemedContext();
159 public void setTitle(CharSequence title
) {
160 if (DEBUG
) Log
.d(TAG
, "[setTitle] title: " + title
);
162 dispatchTitleChanged(title
, 0);
166 public ActionMode
startActionMode(ActionMode
.Callback callback
) {
167 if (DEBUG
) Log
.d(TAG
, "[startActionMode] callback: " + callback
);
169 if (mActionMode
!= null
) {
170 mActionMode
.finish();
173 final ActionMode
.Callback wrappedCallback
= new ActionModeCallbackWrapper(callback
);
174 ActionMode mode
= null
;
176 //Emulate Activity's onWindowStartingActionMode:
178 if (aActionBar
!= null
) {
179 mode
= aActionBar
.startActionMode(wrappedCallback
);
185 if (mActionModeView
== null
) {
186 ViewStub stub
= (ViewStub
)mDecor
.findViewById(R
.id
.abs__action_mode_bar_stub
);
188 mActionModeView
= (ActionBarContextView
)stub
.inflate();
191 if (mActionModeView
!= null
) {
192 mActionModeView
.killMode();
193 mode
= new StandaloneActionMode(mActivity
, mActionModeView
, wrappedCallback
, true
);
194 if (callback
.onCreateActionMode(mode
, mode
.getMenu())) {
196 mActionModeView
.initForMode(mode
);
197 mActionModeView
.setVisibility(View
.VISIBLE
);
199 mActionModeView
.sendAccessibilityEvent(AccessibilityEvent
.TYPE_WINDOW_STATE_CHANGED
);
205 if (mActionMode
!= null
&& mActivity
instanceof OnActionModeStartedListener
) {
206 ((OnActionModeStartedListener
)mActivity
).onActionModeStarted(mActionMode
);
212 ///////////////////////////////////////////////////////////////////////////
213 // Lifecycle and interaction callbacks for delegation
214 ///////////////////////////////////////////////////////////////////////////
217 public void dispatchConfigurationChanged(Configuration newConfig
) {
218 if (DEBUG
) Log
.d(TAG
, "[dispatchConfigurationChanged] newConfig: " + newConfig
);
220 if (aActionBar
!= null
) {
221 aActionBar
.onConfigurationChanged(newConfig
);
226 public void dispatchPostResume() {
227 if (DEBUG
) Log
.d(TAG
, "[dispatchPostResume]");
229 if (aActionBar
!= null
) {
230 aActionBar
.setShowHideAnimationEnabled(true
);
235 public void dispatchPause() {
236 if (DEBUG
) Log
.d(TAG
, "[dispatchPause]");
238 if (wActionBar
!= null
&& wActionBar
.isOverflowMenuShowing()) {
239 wActionBar
.hideOverflowMenu();
244 public void dispatchStop() {
245 if (DEBUG
) Log
.d(TAG
, "[dispatchStop]");
247 if (aActionBar
!= null
) {
248 aActionBar
.setShowHideAnimationEnabled(false
);
253 public void dispatchInvalidateOptionsMenu() {
254 if (DEBUG
) Log
.d(TAG
, "[dispatchInvalidateOptionsMenu]");
256 Bundle savedActionViewStates
= null
;
258 savedActionViewStates
= new Bundle();
259 mMenu
.saveActionViewStates(savedActionViewStates
);
260 if (savedActionViewStates
.size() > 0) {
261 mMenuFrozenActionViewState
= savedActionViewStates
;
263 // This will be started again when the panel is prepared.
264 mMenu
.stopDispatchingItemsChanged();
267 mMenuRefreshContent
= true
;
269 // Prepare the options panel if we have an action bar
270 if (wActionBar
!= null
) {
271 mMenuIsPrepared
= false
;
277 public boolean dispatchOpenOptionsMenu() {
278 if (DEBUG
) Log
.d(TAG
, "[dispatchOpenOptionsMenu]");
280 if (!isReservingOverflow()) {
284 return wActionBar
.showOverflowMenu();
288 public boolean dispatchCloseOptionsMenu() {
289 if (DEBUG
) Log
.d(TAG
, "[dispatchCloseOptionsMenu]");
291 if (!isReservingOverflow()) {
295 if (wActionBar
!= null
) {
296 return wActionBar
.hideOverflowMenu();
302 public void dispatchPostCreate(Bundle savedInstanceState
) {
303 if (DEBUG
) Log
.d(TAG
, "[dispatchOnPostCreate]");
306 mIsTitleReady
= true
;
309 if (mDecor
== null
) {
315 public boolean dispatchCreateOptionsMenu(android
.view
.Menu menu
) {
317 Log
.d(TAG
, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu
);
318 Log
.d(TAG
, "[dispatchCreateOptionsMenu] returning true");
324 public boolean dispatchPrepareOptionsMenu(android
.view
.Menu menu
) {
325 if (DEBUG
) Log
.d(TAG
, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu
);
327 if (mActionMode
!= null
) {
331 mMenuIsPrepared
= false
;
332 if (!preparePanel()) {
336 if (isReservingOverflow()) {
340 if (mNativeItemMap
== null
) {
341 mNativeItemMap
= new HashMap
<android
.view
.MenuItem
, MenuItemImpl
>();
343 mNativeItemMap
.clear();
350 boolean result
= mMenu
.bindNativeOverflow(menu
, this, mNativeItemMap
);
351 if (DEBUG
) Log
.d(TAG
, "[dispatchPrepareOptionsMenu] returning " + result
);
356 public boolean dispatchOptionsItemSelected(android
.view
.MenuItem item
) {
357 throw new IllegalStateException("Native callback invoked. Create a test case and report!");
361 public boolean dispatchMenuOpened(int featureId
, android
.view
.Menu menu
) {
362 if (DEBUG
) Log
.d(TAG
, "[dispatchMenuOpened] featureId: " + featureId
+ ", menu: " + menu
);
364 if (featureId
== Window
.FEATURE_ACTION_BAR
|| featureId
== Window
.FEATURE_OPTIONS_PANEL
) {
365 if (aActionBar
!= null
) {
366 aActionBar
.dispatchMenuVisibilityChanged(true
);
375 public void dispatchPanelClosed(int featureId
, android
.view
.Menu menu
){
376 if (DEBUG
) Log
.d(TAG
, "[dispatchPanelClosed] featureId: " + featureId
+ ", menu: " + menu
);
378 if (featureId
== Window
.FEATURE_ACTION_BAR
|| featureId
== Window
.FEATURE_OPTIONS_PANEL
) {
379 if (aActionBar
!= null
) {
380 aActionBar
.dispatchMenuVisibilityChanged(false
);
386 public void dispatchTitleChanged(CharSequence title
, int color
) {
387 if (DEBUG
) Log
.d(TAG
, "[dispatchTitleChanged] title: " + title
+ ", color: " + color
);
389 if (!mIsDelegate
|| mIsTitleReady
) {
390 if (mTitleView
!= null
) {
391 mTitleView
.setText(title
);
392 } else if (wActionBar
!= null
) {
393 wActionBar
.setWindowTitle(title
);
401 public boolean dispatchKeyEvent(KeyEvent event
) {
402 if (DEBUG
) Log
.d(TAG
, "[dispatchKeyEvent] event: " + event
);
404 final int keyCode
= event
.getKeyCode();
406 // Not handled by the view hierarchy, does the action bar want it
407 // to cancel out of something special?
408 if (keyCode
== KeyEvent
.KEYCODE_BACK
) {
409 final int action
= event
.getAction();
410 // Back cancels action modes first.
411 if (mActionMode
!= null
) {
412 if (action
== KeyEvent
.ACTION_UP
) {
413 mActionMode
.finish();
415 if (DEBUG
) Log
.d(TAG
, "[dispatchKeyEvent] returning true");
419 // Next collapse any expanded action views.
420 if (wActionBar
!= null
&& wActionBar
.hasExpandedActionView()) {
421 if (action
== KeyEvent
.ACTION_UP
) {
422 wActionBar
.collapseActionView();
424 if (DEBUG
) Log
.d(TAG
, "[dispatchKeyEvent] returning true");
429 if (DEBUG
) Log
.d(TAG
, "[dispatchKeyEvent] returning false");
434 public void dispatchDestroy() {
439 public void dispatchSaveInstanceState(Bundle outState
) {
441 mMenuFrozenActionViewState
= new Bundle();
442 mMenu
.saveActionViewStates(mMenuFrozenActionViewState
);
444 outState
.putParcelable(PANELS_TAG
, mMenuFrozenActionViewState
);
448 public void dispatchRestoreInstanceState(Bundle savedInstanceState
) {
449 mMenuFrozenActionViewState
= savedInstanceState
.getParcelable(PANELS_TAG
);
452 ///////////////////////////////////////////////////////////////////////////
453 // Menu callback lifecycle and creation
454 ///////////////////////////////////////////////////////////////////////////
456 private boolean preparePanel() {
457 // Already prepared (isPrepared will be reset to false later)
458 if (mMenuIsPrepared
) {
462 // Init the panel state's menu--return false if init failed
463 if (mMenu
== null
|| mMenuRefreshContent
) {
465 if (!initializePanelMenu() || (mMenu
== null
)) {
470 if (wActionBar
!= null
) {
471 wActionBar
.setMenu(mMenu
, this);
474 // Call callback, and return if it doesn't want to display menu.
476 // Creating the panel menu will involve a lot of manipulation;
477 // don't dispatch change events to presenters until we're done.
478 mMenu
.stopDispatchingItemsChanged();
479 if (!callbackCreateOptionsMenu(mMenu
)) {
480 // Ditch the menu created above
483 if (wActionBar
!= null
) {
484 // Don't show it in the action bar either
485 wActionBar
.setMenu(null
, this);
491 mMenuRefreshContent
= false
;
494 // Callback and return if the callback does not want to show the menu
496 // Preparing the panel menu can involve a lot of manipulation;
497 // don't dispatch change events to presenters until we're done.
498 mMenu
.stopDispatchingItemsChanged();
500 // Restore action view state before we prepare. This gives apps
501 // an opportunity to override frozen/restored state in onPrepare.
502 if (mMenuFrozenActionViewState
!= null
) {
503 mMenu
.restoreActionViewStates(mMenuFrozenActionViewState
);
504 mMenuFrozenActionViewState
= null
;
507 if (!callbackPrepareOptionsMenu(mMenu
)) {
508 if (wActionBar
!= null
) {
509 // The app didn't want to show the menu for now but it still exists.
510 // Clear it out of the action bar.
511 wActionBar
.setMenu(null
, this);
513 mMenu
.startDispatchingItemsChanged();
517 // Set the proper keymap
518 KeyCharacterMap kmap
= KeyCharacterMap
.load(KeyCharacterMap
.VIRTUAL_KEYBOARD
);
519 mMenu
.setQwertyMode(kmap
.getKeyboardType() != KeyCharacterMap
.NUMERIC
);
520 mMenu
.startDispatchingItemsChanged();
523 mMenuIsPrepared
= true
;
528 public boolean onMenuItemSelected(MenuBuilder menu
, MenuItem item
) {
529 return callbackOptionsItemSelected(item
);
532 public void onMenuModeChange(MenuBuilder menu
) {
536 private void reopenMenu(boolean toggleMenuMode
) {
537 if (wActionBar
!= null
&& wActionBar
.isOverflowReserved()) {
538 if (!wActionBar
.isOverflowMenuShowing() || !toggleMenuMode
) {
539 if (wActionBar
.getVisibility() == View
.VISIBLE
) {
540 if (callbackPrepareOptionsMenu(mMenu
)) {
541 wActionBar
.showOverflowMenu();
545 wActionBar
.hideOverflowMenu();
551 private boolean initializePanelMenu() {
552 Context context
= mActivity
;//getContext();
554 // If we have an action bar, initialize the menu with a context themed for it.
555 if (wActionBar
!= null
) {
556 TypedValue outValue
= new TypedValue();
557 Resources
.Theme currentTheme
= context
.getTheme();
558 currentTheme
.resolveAttribute(R
.attr
.actionBarWidgetTheme
,
560 final int targetThemeRes
= outValue
.resourceId
;
562 if (targetThemeRes
!= 0 /*&& context.getThemeResId() != targetThemeRes*/) {
563 context
= new ContextThemeWrapper(context
, targetThemeRes
);
567 mMenu
= new MenuBuilder(context
);
568 mMenu
.setCallback(this);
573 void checkCloseActionMenu(Menu menu
) {
574 if (mClosingActionMenu
) {
578 mClosingActionMenu
= true
;
579 wActionBar
.dismissPopupMenus();
580 //Callback cb = getCallback();
581 //if (cb != null && !isDestroyed()) {
582 // cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
584 mClosingActionMenu
= false
;
588 public boolean onOpenSubMenu(MenuBuilder subMenu
) {
593 public void onCloseMenu(MenuBuilder menu
, boolean allMenusAreClosing
) {
594 checkCloseActionMenu(menu
);
598 public boolean onMenuItemClick(android
.view
.MenuItem item
) {
599 if (DEBUG
) Log
.d(TAG
, "[mNativeItemListener.onMenuItemClick] item: " + item
);
601 final MenuItemImpl sherlockItem
= mNativeItemMap
.get(item
);
602 if (sherlockItem
!= null
) {
603 sherlockItem
.invoke();
605 Log
.e(TAG
, "Options item \"" + item
+ "\" not found in mapping");
608 return true
; //Do not allow continuation of native handling
612 public boolean onMenuItemSelected(int featureId
, MenuItem item
) {
613 return callbackOptionsItemSelected(item
);
617 ///////////////////////////////////////////////////////////////////////////
618 // Progress bar interaction and internal handling
619 ///////////////////////////////////////////////////////////////////////////
622 public void setProgressBarVisibility(boolean visible
) {
623 if (DEBUG
) Log
.d(TAG
, "[setProgressBarVisibility] visible: " + visible
);
625 setFeatureInt(Window
.FEATURE_PROGRESS
, visible ? Window
.PROGRESS_VISIBILITY_ON
:
626 Window
.PROGRESS_VISIBILITY_OFF
);
630 public void setProgressBarIndeterminateVisibility(boolean visible
) {
631 if (DEBUG
) Log
.d(TAG
, "[setProgressBarIndeterminateVisibility] visible: " + visible
);
633 setFeatureInt(Window
.FEATURE_INDETERMINATE_PROGRESS
,
634 visible ? Window
.PROGRESS_VISIBILITY_ON
: Window
.PROGRESS_VISIBILITY_OFF
);
638 public void setProgressBarIndeterminate(boolean indeterminate
) {
639 if (DEBUG
) Log
.d(TAG
, "[setProgressBarIndeterminate] indeterminate: " + indeterminate
);
641 setFeatureInt(Window
.FEATURE_PROGRESS
,
642 indeterminate ? Window
.PROGRESS_INDETERMINATE_ON
: Window
.PROGRESS_INDETERMINATE_OFF
);
646 public void setProgress(int progress
) {
647 if (DEBUG
) Log
.d(TAG
, "[setProgress] progress: " + progress
);
649 setFeatureInt(Window
.FEATURE_PROGRESS
, progress
+ Window
.PROGRESS_START
);
653 public void setSecondaryProgress(int secondaryProgress
) {
654 if (DEBUG
) Log
.d(TAG
, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress
);
656 setFeatureInt(Window
.FEATURE_PROGRESS
,
657 secondaryProgress
+ Window
.PROGRESS_SECONDARY_START
);
660 private void setFeatureInt(int featureId
, int value
) {
661 updateInt(featureId
, value
, false
);
664 private void updateInt(int featureId
, int value
, boolean fromResume
) {
665 // Do nothing if the decor is not yet installed... an update will
666 // need to be forced when we eventually become active.
667 if (mContentParent
== null
) {
671 final int featureMask
= 1 << featureId
;
673 if ((getFeatures() & featureMask
) == 0 && !fromResume
) {
677 onIntChanged(featureId
, value
);
680 private void onIntChanged(int featureId
, int value
) {
681 if (featureId
== Window
.FEATURE_PROGRESS
|| featureId
== Window
.FEATURE_INDETERMINATE_PROGRESS
) {
682 updateProgressBars(value
);
686 private void updateProgressBars(int value
) {
687 IcsProgressBar circularProgressBar
= getCircularProgressBar(true
);
688 IcsProgressBar horizontalProgressBar
= getHorizontalProgressBar(true
);
690 final int features
= mFeatures
;//getLocalFeatures();
691 if (value
== Window
.PROGRESS_VISIBILITY_ON
) {
692 if ((features
& (1 << Window
.FEATURE_PROGRESS
)) != 0) {
693 int level
= horizontalProgressBar
.getProgress();
694 int visibility
= (horizontalProgressBar
.isIndeterminate() || level
< 10000) ?
695 View
.VISIBLE
: View
.INVISIBLE
;
696 horizontalProgressBar
.setVisibility(visibility
);
698 if ((features
& (1 << Window
.FEATURE_INDETERMINATE_PROGRESS
)) != 0) {
699 circularProgressBar
.setVisibility(View
.VISIBLE
);
701 } else if (value
== Window
.PROGRESS_VISIBILITY_OFF
) {
702 if ((features
& (1 << Window
.FEATURE_PROGRESS
)) != 0) {
703 horizontalProgressBar
.setVisibility(View
.GONE
);
705 if ((features
& (1 << Window
.FEATURE_INDETERMINATE_PROGRESS
)) != 0) {
706 circularProgressBar
.setVisibility(View
.GONE
);
708 } else if (value
== Window
.PROGRESS_INDETERMINATE_ON
) {
709 horizontalProgressBar
.setIndeterminate(true
);
710 } else if (value
== Window
.PROGRESS_INDETERMINATE_OFF
) {
711 horizontalProgressBar
.setIndeterminate(false
);
712 } else if (Window
.PROGRESS_START
<= value
&& value
<= Window
.PROGRESS_END
) {
713 // We want to set the progress value before testing for visibility
714 // so that when the progress bar becomes visible again, it has the
716 horizontalProgressBar
.setProgress(value
- Window
.PROGRESS_START
);
718 if (value
< Window
.PROGRESS_END
) {
719 showProgressBars(horizontalProgressBar
, circularProgressBar
);
721 hideProgressBars(horizontalProgressBar
, circularProgressBar
);
723 } else if (Window
.PROGRESS_SECONDARY_START
<= value
&& value
<= Window
.PROGRESS_SECONDARY_END
) {
724 horizontalProgressBar
.setSecondaryProgress(value
- Window
.PROGRESS_SECONDARY_START
);
726 showProgressBars(horizontalProgressBar
, circularProgressBar
);
730 private void showProgressBars(IcsProgressBar horizontalProgressBar
, IcsProgressBar spinnyProgressBar
) {
731 final int features
= mFeatures
;//getLocalFeatures();
732 if ((features
& (1 << Window
.FEATURE_INDETERMINATE_PROGRESS
)) != 0 &&
733 spinnyProgressBar
.getVisibility() == View
.INVISIBLE
) {
734 spinnyProgressBar
.setVisibility(View
.VISIBLE
);
736 // Only show the progress bars if the primary progress is not complete
737 if ((features
& (1 << Window
.FEATURE_PROGRESS
)) != 0 &&
738 horizontalProgressBar
.getProgress() < 10000) {
739 horizontalProgressBar
.setVisibility(View
.VISIBLE
);
743 private void hideProgressBars(IcsProgressBar horizontalProgressBar
, IcsProgressBar spinnyProgressBar
) {
744 final int features
= mFeatures
;//getLocalFeatures();
745 Animation anim
= AnimationUtils
.loadAnimation(mActivity
, android
.R
.anim
.fade_out
);
746 anim
.setDuration(1000);
747 if ((features
& (1 << Window
.FEATURE_INDETERMINATE_PROGRESS
)) != 0 &&
748 spinnyProgressBar
.getVisibility() == View
.VISIBLE
) {
749 spinnyProgressBar
.startAnimation(anim
);
750 spinnyProgressBar
.setVisibility(View
.INVISIBLE
);
752 if ((features
& (1 << Window
.FEATURE_PROGRESS
)) != 0 &&
753 horizontalProgressBar
.getVisibility() == View
.VISIBLE
) {
754 horizontalProgressBar
.startAnimation(anim
);
755 horizontalProgressBar
.setVisibility(View
.INVISIBLE
);
759 private IcsProgressBar
getCircularProgressBar(boolean shouldInstallDecor
) {
760 if (mCircularProgressBar
!= null
) {
761 return mCircularProgressBar
;
763 if (mContentParent
== null
&& shouldInstallDecor
) {
766 mCircularProgressBar
= (IcsProgressBar
)mDecor
.findViewById(R
.id
.abs__progress_circular
);
767 if (mCircularProgressBar
!= null
) {
768 mCircularProgressBar
.setVisibility(View
.INVISIBLE
);
770 return mCircularProgressBar
;
773 private IcsProgressBar
getHorizontalProgressBar(boolean shouldInstallDecor
) {
774 if (mHorizontalProgressBar
!= null
) {
775 return mHorizontalProgressBar
;
777 if (mContentParent
== null
&& shouldInstallDecor
) {
780 mHorizontalProgressBar
= (IcsProgressBar
)mDecor
.findViewById(R
.id
.abs__progress_horizontal
);
781 if (mHorizontalProgressBar
!= null
) {
782 mHorizontalProgressBar
.setVisibility(View
.INVISIBLE
);
784 return mHorizontalProgressBar
;
788 ///////////////////////////////////////////////////////////////////////////
789 // Feature management and content interaction and creation
790 ///////////////////////////////////////////////////////////////////////////
792 private int getFeatures() {
793 if (DEBUG
) Log
.d(TAG
, "[getFeatures] returning " + mFeatures
);
799 public boolean hasFeature(int featureId
) {
800 if (DEBUG
) Log
.d(TAG
, "[hasFeature] featureId: " + featureId
);
802 boolean result
= (mFeatures
& (1 << featureId
)) != 0;
803 if (DEBUG
) Log
.d(TAG
, "[hasFeature] returning " + result
);
808 public boolean requestFeature(int featureId
) {
809 if (DEBUG
) Log
.d(TAG
, "[requestFeature] featureId: " + featureId
);
811 if (mContentParent
!= null
) {
812 throw new AndroidRuntimeException("requestFeature() must be called before adding content");
816 case Window
.FEATURE_ACTION_BAR
:
817 case Window
.FEATURE_ACTION_BAR_OVERLAY
:
818 case Window
.FEATURE_ACTION_MODE_OVERLAY
:
819 case Window
.FEATURE_INDETERMINATE_PROGRESS
:
820 case Window
.FEATURE_NO_TITLE
:
821 case Window
.FEATURE_PROGRESS
:
822 mFeatures
|= (1 << featureId
);
831 public void setUiOptions(int uiOptions
) {
832 if (DEBUG
) Log
.d(TAG
, "[setUiOptions] uiOptions: " + uiOptions
);
834 mUiOptions
= uiOptions
;
838 public void setUiOptions(int uiOptions
, int mask
) {
839 if (DEBUG
) Log
.d(TAG
, "[setUiOptions] uiOptions: " + uiOptions
+ ", mask: " + mask
);
841 mUiOptions
= (mUiOptions
& ~mask
) | (uiOptions
& mask
);
845 public void setContentView(int layoutResId
) {
846 if (DEBUG
) Log
.d(TAG
, "[setContentView] layoutResId: " + layoutResId
);
848 if (mContentParent
== null
) {
851 mContentParent
.removeAllViews();
853 mActivity
.getLayoutInflater().inflate(layoutResId
, mContentParent
);
855 android
.view
.Window
.Callback callback
= mActivity
.getWindow().getCallback();
856 if (callback
!= null
) {
857 callback
.onContentChanged();
864 public void setContentView(View view
, ViewGroup
.LayoutParams params
) {
865 if (DEBUG
) Log
.d(TAG
, "[setContentView] view: " + view
+ ", params: " + params
);
867 if (mContentParent
== null
) {
870 mContentParent
.removeAllViews();
872 mContentParent
.addView(view
, params
);
874 android
.view
.Window
.Callback callback
= mActivity
.getWindow().getCallback();
875 if (callback
!= null
) {
876 callback
.onContentChanged();
883 public void addContentView(View view
, ViewGroup
.LayoutParams params
) {
884 if (DEBUG
) Log
.d(TAG
, "[addContentView] view: " + view
+ ", params: " + params
);
886 if (mContentParent
== null
) {
889 mContentParent
.addView(view
, params
);
894 private void installDecor() {
895 if (DEBUG
) Log
.d(TAG
, "[installDecor]");
897 if (mDecor
== null
) {
898 mDecor
= (ViewGroup
)mActivity
.getWindow().getDecorView().findViewById(android
.R
.id
.content
);
900 if (mContentParent
== null
) {
901 //Since we are not operating at the window level we need to take
902 //into account the fact that the true decor may have already been
903 //initialized and had content attached to it. If that is the case,
904 //copy over its children to our new content container.
905 List
<View
> views
= null
;
906 if (mDecor
.getChildCount() > 0) {
907 views
= new ArrayList
<View
>(1); //Usually there's only one child
908 for (int i
= 0, children
= mDecor
.getChildCount(); i
< children
; i
++) {
909 View child
= mDecor
.getChildAt(0);
910 mDecor
.removeView(child
);
915 mContentParent
= generateLayout();
917 //Copy over the old children. See above for explanation.
919 for (View child
: views
) {
920 mContentParent
.addView(child
);
924 mTitleView
= (TextView
)mDecor
.findViewById(android
.R
.id
.title
);
925 if (mTitleView
!= null
) {
926 if (hasFeature(Window
.FEATURE_NO_TITLE
)) {
927 mTitleView
.setVisibility(View
.GONE
);
928 if (mContentParent
instanceof FrameLayout
) {
929 ((FrameLayout
)mContentParent
).setForeground(null
);
932 mTitleView
.setText(mTitle
);
935 wActionBar
= (ActionBarView
)mDecor
.findViewById(R
.id
.abs__action_bar
);
936 if (wActionBar
!= null
) {
937 wActionBar
.setWindowCallback(this);
938 if (wActionBar
.getTitle() == null
) {
939 wActionBar
.setWindowTitle(mActivity
.getTitle());
941 if (hasFeature(Window
.FEATURE_PROGRESS
)) {
942 wActionBar
.initProgress();
944 if (hasFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
)) {
945 wActionBar
.initIndeterminateProgress();
948 //Since we don't require onCreate dispatching, parse for uiOptions here
949 int uiOptions
= loadUiOptionsFromManifest(mActivity
);
950 if (uiOptions
!= 0) {
951 mUiOptions
= uiOptions
;
954 boolean splitActionBar
= false
;
955 final boolean splitWhenNarrow
= (mUiOptions
& ActivityInfo
.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW
) != 0;
956 if (splitWhenNarrow
) {
957 splitActionBar
= getResources_getBoolean(mActivity
, R
.bool
.abs__split_action_bar_is_narrow
);
959 splitActionBar
= mActivity
.getTheme()
960 .obtainStyledAttributes(R
.styleable
.SherlockTheme
)
961 .getBoolean(R
.styleable
.SherlockTheme_windowSplitActionBar
, false
);
963 final ActionBarContainer splitView
= (ActionBarContainer
)mDecor
.findViewById(R
.id
.abs__split_action_bar
);
964 if (splitView
!= null
) {
965 wActionBar
.setSplitView(splitView
);
966 wActionBar
.setSplitActionBar(splitActionBar
);
967 wActionBar
.setSplitWhenNarrow(splitWhenNarrow
);
969 mActionModeView
= (ActionBarContextView
)mDecor
.findViewById(R
.id
.abs__action_context_bar
);
970 mActionModeView
.setSplitView(splitView
);
971 mActionModeView
.setSplitActionBar(splitActionBar
);
972 mActionModeView
.setSplitWhenNarrow(splitWhenNarrow
);
973 } else if (splitActionBar
) {
974 Log
.e(TAG
, "Requested split action bar with incompatible window decor! Ignoring request.");
977 // Post the panel invalidate for later; avoid application onCreateOptionsMenu
978 // being called in the middle of onCreate or similar.
979 mDecor
.post(new Runnable() {
982 //Invalidate if the panel menu hasn't been created before this.
983 if (!mIsDestroyed
&& !mActivity
.isFinishing() && mMenu
== null
) {
984 dispatchInvalidateOptionsMenu();
993 private ViewGroup
generateLayout() {
994 if (DEBUG
) Log
.d(TAG
, "[generateLayout]");
996 // Apply data from current theme.
998 TypedArray a
= mActivity
.getTheme().obtainStyledAttributes(R
.styleable
.SherlockTheme
);
1000 mIsFloating
= a
.getBoolean(R
.styleable
.SherlockTheme_android_windowIsFloating
, false
);
1002 if (!a
.hasValue(R
.styleable
.SherlockTheme_windowActionBar
)) {
1003 throw new IllegalStateException("You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative.");
1006 if (a
.getBoolean(R
.styleable
.SherlockTheme_windowNoTitle
, false
)) {
1007 requestFeature(Window
.FEATURE_NO_TITLE
);
1008 } else if (a
.getBoolean(R
.styleable
.SherlockTheme_windowActionBar
, false
)) {
1009 // Don't allow an action bar if there is no title.
1010 requestFeature(Window
.FEATURE_ACTION_BAR
);
1013 if (a
.getBoolean(R
.styleable
.SherlockTheme_windowActionBarOverlay
, false
)) {
1014 requestFeature(Window
.FEATURE_ACTION_BAR_OVERLAY
);
1017 if (a
.getBoolean(R
.styleable
.SherlockTheme_windowActionModeOverlay
, false
)) {
1018 requestFeature(Window
.FEATURE_ACTION_MODE_OVERLAY
);
1024 if (!hasFeature(Window
.FEATURE_NO_TITLE
)) {
1026 //Trash original dialog LinearLayout
1027 mDecor
= (ViewGroup
)mDecor
.getParent();
1028 mDecor
.removeAllViews();
1030 layoutResource
= R
.layout
.abs__dialog_title_holo
;
1032 if (hasFeature(Window
.FEATURE_ACTION_BAR_OVERLAY
)) {
1033 layoutResource
= R
.layout
.abs__screen_action_bar_overlay
;
1035 layoutResource
= R
.layout
.abs__screen_action_bar
;
1038 } else if (hasFeature(Window
.FEATURE_ACTION_MODE_OVERLAY
) && !hasFeature(Window
.FEATURE_NO_TITLE
)) {
1039 layoutResource
= R
.layout
.abs__screen_simple_overlay_action_mode
;
1041 layoutResource
= R
.layout
.abs__screen_simple
;
1044 if (DEBUG
) Log
.d(TAG
, "[generateLayout] using screen XML " + mActivity
.getResources().getString(layoutResource
));
1045 View
in = mActivity
.getLayoutInflater().inflate(layoutResource
, null
);
1046 mDecor
.addView(in, new ViewGroup
.LayoutParams(MATCH_PARENT
, MATCH_PARENT
));
1048 ViewGroup contentParent
= (ViewGroup
)mDecor
.findViewById(R
.id
.abs__content
);
1049 if (contentParent
== null
) {
1050 throw new RuntimeException("Couldn't find content container view");
1053 //Make our new child the true content view (for fragments). VERY VOLATILE!
1054 mDecor
.setId(View
.NO_ID
);
1055 contentParent
.setId(android
.R
.id
.content
);
1057 if (hasFeature(Window
.FEATURE_INDETERMINATE_PROGRESS
)) {
1058 IcsProgressBar progress
= getCircularProgressBar(false
);
1059 if (progress
!= null
) {
1060 progress
.setIndeterminate(true
);
1064 return contentParent
;
1068 ///////////////////////////////////////////////////////////////////////////
1070 ///////////////////////////////////////////////////////////////////////////
1073 * Determine whether or not the device has a dedicated menu key.
1075 * @return {@code true} if native menu key is present.
1077 private boolean isReservingOverflow() {
1078 if (!mReserveOverflowSet
) {
1079 mReserveOverflow
= ActionMenuPresenter
.reserveOverflow(mActivity
);
1080 mReserveOverflowSet
= true
;
1082 return mReserveOverflow
;
1085 private static int loadUiOptionsFromManifest(Activity activity
) {
1088 final String thisPackage
= activity
.getClass().getName();
1089 if (DEBUG
) Log
.i(TAG
, "Parsing AndroidManifest.xml for " + thisPackage
);
1091 final String packageName
= activity
.getApplicationInfo().packageName
;
1092 final AssetManager am
= activity
.createPackageContext(packageName
, 0).getAssets();
1093 final XmlResourceParser xml
= am
.openXmlResourceParser("AndroidManifest.xml");
1095 int eventType
= xml
.getEventType();
1096 while (eventType
!= XmlPullParser
.END_DOCUMENT
) {
1097 if (eventType
== XmlPullParser
.START_TAG
) {
1098 String name
= xml
.getName();
1100 if ("application".equals(name
)) {
1101 //Check if the <application> has the attribute
1102 if (DEBUG
) Log
.d(TAG
, "Got <application>");
1104 for (int i
= xml
.getAttributeCount() - 1; i
>= 0; i
--) {
1105 if (DEBUG
) Log
.d(TAG
, xml
.getAttributeName(i
) + ": " + xml
.getAttributeValue(i
));
1107 if ("uiOptions".equals(xml
.getAttributeName(i
))) {
1108 uiOptions
= xml
.getAttributeIntValue(i
, 0);
1109 break; //out of for loop
1112 } else if ("activity".equals(name
)) {
1113 //Check if the <activity> is us and has the attribute
1114 if (DEBUG
) Log
.d(TAG
, "Got <activity>");
1115 Integer activityUiOptions
= null
;
1116 String activityPackage
= null
;
1117 boolean isOurActivity
= false
;
1119 for (int i
= xml
.getAttributeCount() - 1; i
>= 0; i
--) {
1120 if (DEBUG
) Log
.d(TAG
, xml
.getAttributeName(i
) + ": " + xml
.getAttributeValue(i
));
1122 //We need both uiOptions and name attributes
1123 String attrName
= xml
.getAttributeName(i
);
1124 if ("uiOptions".equals(attrName
)) {
1125 activityUiOptions
= xml
.getAttributeIntValue(i
, 0);
1126 } else if ("name".equals(attrName
)) {
1127 activityPackage
= cleanActivityName(packageName
, xml
.getAttributeValue(i
));
1128 if (!thisPackage
.equals(activityPackage
)) {
1129 break; //out of for loop
1131 isOurActivity
= true
;
1134 //Make sure we have both attributes before processing
1135 if ((activityUiOptions
!= null
) && (activityPackage
!= null
)) {
1136 //Our activity, uiOptions specified, override with our value
1137 uiOptions
= activityUiOptions
.intValue();
1140 if (isOurActivity
) {
1141 //If we matched our activity but it had no logo don't
1142 //do any more processing of the manifest
1147 eventType
= xml
.nextToken();
1149 } catch (Exception e
) {
1150 e
.printStackTrace();
1152 if (DEBUG
) Log
.i(TAG
, "Returning " + Integer
.toHexString(uiOptions
));
1156 public static String
cleanActivityName(String manifestPackage
, String activityName
) {
1157 if (activityName
.charAt(0) == '.') {
1158 //Relative activity name (e.g., android:name=".ui.SomeClass")
1159 return manifestPackage
+ activityName
;
1161 if (activityName
.indexOf('.', 1) == -1) {
1162 //Unqualified activity name (e.g., android:name="SomeClass")
1163 return manifestPackage
+ "." + activityName
;
1165 //Fully-qualified activity name (e.g., "com.my.package.SomeClass")
1166 return activityName
;
1170 * Clears out internal reference when the action mode is destroyed.
1172 private class ActionModeCallbackWrapper
implements ActionMode
.Callback
{
1173 private final ActionMode
.Callback mWrapped
;
1175 public ActionModeCallbackWrapper(ActionMode
.Callback wrapped
) {
1179 public boolean onCreateActionMode(ActionMode mode
, Menu menu
) {
1180 return mWrapped
.onCreateActionMode(mode
, menu
);
1183 public boolean onPrepareActionMode(ActionMode mode
, Menu menu
) {
1184 return mWrapped
.onPrepareActionMode(mode
, menu
);
1187 public boolean onActionItemClicked(ActionMode mode
, MenuItem item
) {
1188 return mWrapped
.onActionItemClicked(mode
, item
);
1191 public void onDestroyActionMode(ActionMode mode
) {
1192 mWrapped
.onDestroyActionMode(mode
);
1193 if (mActionModeView
!= null
) {
1194 mActionModeView
.setVisibility(View
.GONE
);
1195 mActionModeView
.removeAllViews();
1197 if (mActivity
instanceof OnActionModeFinishedListener
) {
1198 ((OnActionModeFinishedListener
)mActivity
).onActionModeFinished(mActionMode
);