Added ActionBarSherlock v4.0.3 library for use in the android application. ActionBar...
[debian/openrocket] / android-libraries / ActionBarSherlock / src / com / actionbarsherlock / internal / ActionBarSherlockCompat.java
1 package com.actionbarsherlock.internal;
2
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;
7 import java.util.List;
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;
49
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;
54
55
56     public ActionBarSherlockCompat(Activity activity, int flags) {
57         super(activity, flags);
58     }
59
60
61     ///////////////////////////////////////////////////////////////////////////
62     // Properties
63     ///////////////////////////////////////////////////////////////////////////
64
65     /** Whether or not the device has a dedicated menu key button. */
66     private boolean mReserveOverflow;
67     /** Lazy-load indicator for {@link #mReserveOverflow}. */
68     private boolean mReserveOverflowSet = false;
69
70     /** Current menu instance for managing action items. */
71     private MenuBuilder mMenu;
72     /** Map between native options items and sherlock items. */
73     protected HashMap<android.view.MenuItem, MenuItemImpl> mNativeItemMap;
74     /** Indication of a long-press on the hardware menu key. */
75     private boolean mMenuKeyIsLongPress = false;
76
77     /** Parent view of the window decoration (action bar, mode, etc.). */
78     private ViewGroup mDecor;
79     /** Parent view of the activity content. */
80     private ViewGroup mContentParent;
81
82     /** Whether or not the title is stable and can be displayed. */
83     private boolean mIsTitleReady = false;
84
85     /* Emulate PanelFeatureState */
86     private boolean mClosingActionMenu;
87     private boolean mMenuIsPrepared;
88     private boolean mMenuRefreshContent;
89     private Bundle mMenuFrozenActionViewState;
90
91     /** Implementation which backs the action bar interface API. */
92     private ActionBarImpl aActionBar;
93     /** Main action bar view which displays the core content. */
94     private ActionBarView wActionBar;
95     /** Relevant window and action bar features flags. */
96     private int mFeatures = DEFAULT_FEATURES;
97     /** Relevant user interface option flags. */
98     private int mUiOptions = 0;
99
100     /** Decor indeterminate progress indicator. */
101     private IcsProgressBar mCircularProgressBar;
102     /** Decor progress indicator. */
103     private IcsProgressBar mHorizontalProgressBar;
104
105     /** Current displayed context action bar, if any. */
106     private ActionMode mActionMode;
107     /** Parent view in which the context action bar is displayed. */
108     private ActionBarContextView mActionModeView;
109
110     /** Title view used with dialogs. */
111     private TextView mTitleView;
112     /** Current activity title. */
113     private CharSequence mTitle = null;
114     /** Whether or not this "activity" is floating (i.e., a dialog) */
115     private boolean mIsFloating;
116
117
118
119     ///////////////////////////////////////////////////////////////////////////
120     // Instance methods
121     ///////////////////////////////////////////////////////////////////////////
122
123     @Override
124     public ActionBar getActionBar() {
125         if (DEBUG) Log.d(TAG, "[getActionBar]");
126
127         initActionBar();
128         return aActionBar;
129     }
130
131     private void initActionBar() {
132         if (DEBUG) Log.d(TAG, "[initActionBar]");
133
134         // Initializing the window decor can change window feature flags.
135         // Make sure that we have the correct set before performing the test below.
136         if (mDecor == null) {
137             installDecor();
138         }
139
140         if ((aActionBar != null) || !hasFeature(Window.FEATURE_ACTION_BAR) || hasFeature(Window.FEATURE_NO_TITLE) || mActivity.isChild()) {
141             return;
142         }
143
144         aActionBar = new ActionBarImpl(mActivity, mFeatures);
145
146         if (!mIsDelegate) {
147             //We may never get another chance to set the title
148             wActionBar.setWindowTitle(mActivity.getTitle());
149         }
150     }
151
152     @Override
153     protected Context getThemedContext() {
154         return aActionBar.getThemedContext();
155     }
156
157     @Override
158     public void setTitle(CharSequence title) {
159         if (DEBUG) Log.d(TAG, "[setTitle] title: " + title);
160
161         dispatchTitleChanged(title, 0);
162     }
163
164     @Override
165     public ActionMode startActionMode(ActionMode.Callback callback) {
166         if (DEBUG) Log.d(TAG, "[startActionMode] callback: " + callback);
167
168         if (mActionMode != null) {
169             mActionMode.finish();
170         }
171
172         final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
173         ActionMode mode = null;
174
175         //Emulate Activity's onWindowStartingActionMode:
176         initActionBar();
177         if (aActionBar != null) {
178             mode = aActionBar.startActionMode(wrappedCallback);
179         }
180
181         if (mode != null) {
182             mActionMode = mode;
183         } else {
184             if (mActionModeView == null) {
185                 ViewStub stub = (ViewStub)mDecor.findViewById(R.id.abs__action_mode_bar_stub);
186                 if (stub != null) {
187                     mActionModeView = (ActionBarContextView)stub.inflate();
188                 }
189             }
190             if (mActionModeView != null) {
191                 mActionModeView.killMode();
192                 mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, true);
193                 if (callback.onCreateActionMode(mode, mode.getMenu())) {
194                     mode.invalidate();
195                     mActionModeView.initForMode(mode);
196                     mActionModeView.setVisibility(View.VISIBLE);
197                     mActionMode = mode;
198                     mActionModeView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
199                 } else {
200                     mActionMode = null;
201                 }
202             }
203         }
204         if (mActionMode != null && mActivity instanceof OnActionModeStartedListener) {
205             ((OnActionModeStartedListener)mActivity).onActionModeStarted(mActionMode);
206         }
207         return mActionMode;
208     }
209
210
211     ///////////////////////////////////////////////////////////////////////////
212     // Lifecycle and interaction callbacks for delegation
213     ///////////////////////////////////////////////////////////////////////////
214
215     @Override
216     public void dispatchConfigurationChanged(Configuration newConfig) {
217         if (DEBUG) Log.d(TAG, "[dispatchConfigurationChanged] newConfig: " + newConfig);
218
219         if (aActionBar != null) {
220             aActionBar.onConfigurationChanged(newConfig);
221         }
222     }
223
224     @Override
225     public void dispatchPostResume() {
226         if (DEBUG) Log.d(TAG, "[dispatchPostResume]");
227
228         if (aActionBar != null) {
229             aActionBar.setShowHideAnimationEnabled(true);
230         }
231     }
232
233     @Override
234     public void dispatchPause() {
235         if (DEBUG) Log.d(TAG, "[dispatchPause]");
236
237         if (wActionBar != null && wActionBar.isOverflowMenuShowing()) {
238             wActionBar.hideOverflowMenu();
239         }
240     }
241
242     @Override
243     public void dispatchStop() {
244         if (DEBUG) Log.d(TAG, "[dispatchStop]");
245
246         if (aActionBar != null) {
247             aActionBar.setShowHideAnimationEnabled(false);
248         }
249     }
250
251     @Override
252     public void dispatchInvalidateOptionsMenu() {
253         if (DEBUG) Log.d(TAG, "[dispatchInvalidateOptionsMenu]");
254
255         Bundle savedActionViewStates = null;
256         if (mMenu != null) {
257             savedActionViewStates = new Bundle();
258             mMenu.saveActionViewStates(savedActionViewStates);
259             if (savedActionViewStates.size() > 0) {
260                 mMenuFrozenActionViewState = savedActionViewStates;
261             }
262             // This will be started again when the panel is prepared.
263             mMenu.stopDispatchingItemsChanged();
264             mMenu.clear();
265         }
266         mMenuRefreshContent = true;
267
268         // Prepare the options panel if we have an action bar
269         if (wActionBar != null) {
270             mMenuIsPrepared = false;
271             preparePanel();
272         }
273     }
274
275     @Override
276     public boolean dispatchOpenOptionsMenu() {
277         if (DEBUG) Log.d(TAG, "[dispatchOpenOptionsMenu]");
278
279         if (!isReservingOverflow()) {
280             return false;
281         }
282
283         return wActionBar.showOverflowMenu();
284     }
285
286     @Override
287     public boolean dispatchCloseOptionsMenu() {
288         if (DEBUG) Log.d(TAG, "[dispatchCloseOptionsMenu]");
289
290         if (!isReservingOverflow()) {
291             return false;
292         }
293
294         return wActionBar.hideOverflowMenu();
295     }
296
297     @Override
298     public void dispatchPostCreate(Bundle savedInstanceState) {
299         if (DEBUG) Log.d(TAG, "[dispatchOnPostCreate]");
300
301         if (mIsDelegate) {
302             mIsTitleReady = true;
303         }
304
305         if (mDecor == null) {
306             initActionBar();
307         }
308     }
309
310     @Override
311     public boolean dispatchCreateOptionsMenu(android.view.Menu menu) {
312         if (DEBUG) {
313             Log.d(TAG, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu);
314             Log.d(TAG, "[dispatchCreateOptionsMenu] returning true");
315         }
316         return true;
317     }
318
319     @Override
320     public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) {
321         if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu);
322
323         if (mActionMode != null) {
324             return false;
325         }
326
327         mMenuIsPrepared = false;
328         if (!preparePanel()) {
329             return false;
330         }
331
332         if (isReservingOverflow()) {
333             return false;
334         }
335
336         if (mNativeItemMap == null) {
337             mNativeItemMap = new HashMap<android.view.MenuItem, MenuItemImpl>();
338         } else {
339             mNativeItemMap.clear();
340         }
341
342         if (mMenu == null) {
343             return false;
344         }
345
346         boolean result = mMenu.bindNativeOverflow(menu, this, mNativeItemMap);
347         if (DEBUG) Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result);
348         return result;
349     }
350
351     @Override
352     public boolean dispatchOptionsItemSelected(android.view.MenuItem item) {
353         throw new IllegalStateException("Native callback invoked. Create a test case and report!");
354     }
355
356     @Override
357     public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
358         if (DEBUG) Log.d(TAG, "[dispatchMenuOpened] featureId: " + featureId + ", menu: " + menu);
359
360         if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
361             if (aActionBar != null) {
362                 aActionBar.dispatchMenuVisibilityChanged(true);
363             }
364             return true;
365         }
366
367         return false;
368     }
369
370     @Override
371     public void dispatchPanelClosed(int featureId, android.view.Menu menu){
372         if (DEBUG) Log.d(TAG, "[dispatchPanelClosed] featureId: " + featureId + ", menu: " + menu);
373
374         if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
375             if (aActionBar != null) {
376                 aActionBar.dispatchMenuVisibilityChanged(false);
377             }
378         }
379     }
380
381     @Override
382     public void dispatchTitleChanged(CharSequence title, int color) {
383         if (DEBUG) Log.d(TAG, "[dispatchTitleChanged] title: " + title + ", color: " + color);
384
385         if (!mIsDelegate || mIsTitleReady) {
386             if (mTitleView != null) {
387                 mTitleView.setText(title);
388             } else if (wActionBar != null) {
389                 wActionBar.setWindowTitle(title);
390             }
391         }
392
393         mTitle = title;
394     }
395
396     @Override
397     public boolean dispatchKeyEvent(KeyEvent event) {
398         if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] event: " + event);
399
400         final int keyCode = event.getKeyCode();
401
402         // Not handled by the view hierarchy, does the action bar want it
403         // to cancel out of something special?
404         if (keyCode == KeyEvent.KEYCODE_BACK) {
405             final int action = event.getAction();
406             // Back cancels action modes first.
407             if (mActionMode != null) {
408                 if (action == KeyEvent.ACTION_UP) {
409                     mActionMode.finish();
410                 }
411                 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
412                 return true;
413             }
414
415             // Next collapse any expanded action views.
416             if (aActionBar != null && wActionBar.hasExpandedActionView()) {
417                 if (action == KeyEvent.ACTION_UP) {
418                     wActionBar.collapseActionView();
419                 }
420                 if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning true");
421                 return true;
422             }
423         }
424
425         boolean result = false;
426         if (keyCode == KeyEvent.KEYCODE_MENU && isReservingOverflow()) {
427             if (event.getAction() == KeyEvent.ACTION_DOWN && event.isLongPress()) {
428                 mMenuKeyIsLongPress = true;
429             } else if (event.getAction() == KeyEvent.ACTION_UP) {
430                 if (!mMenuKeyIsLongPress) {
431                     if (mActionMode == null) {
432                         if (wActionBar.isOverflowMenuShowing()) {
433                             wActionBar.hideOverflowMenu();
434                         } else {
435                             wActionBar.showOverflowMenu();
436                         }
437                     }
438                     result = true;
439                 }
440                 mMenuKeyIsLongPress = false;
441             }
442         }
443
444         if (DEBUG) Log.d(TAG, "[dispatchKeyEvent] returning " + result);
445         return result;
446     }
447
448
449     ///////////////////////////////////////////////////////////////////////////
450     // Menu callback lifecycle and creation
451     ///////////////////////////////////////////////////////////////////////////
452
453     private boolean preparePanel() {
454         // Already prepared (isPrepared will be reset to false later)
455         if (mMenuIsPrepared) {
456             return true;
457         }
458
459         // Init the panel state's menu--return false if init failed
460         if (mMenu == null || mMenuRefreshContent) {
461             if (mMenu == null) {
462                 if (!initializePanelMenu() || (mMenu == null)) {
463                     return false;
464                 }
465             }
466
467             if (wActionBar != null) {
468                 wActionBar.setMenu(mMenu, this);
469             }
470
471             // Call callback, and return if it doesn't want to display menu.
472
473             // Creating the panel menu will involve a lot of manipulation;
474             // don't dispatch change events to presenters until we're done.
475             mMenu.stopDispatchingItemsChanged();
476             if (!callbackCreateOptionsMenu(mMenu)) {
477                 // Ditch the menu created above
478                 mMenu = null;
479
480                 if (wActionBar != null) {
481                     // Don't show it in the action bar either
482                     wActionBar.setMenu(null, this);
483                 }
484
485                 return false;
486             }
487
488             mMenuRefreshContent = false;
489         }
490
491         // Callback and return if the callback does not want to show the menu
492
493         // Preparing the panel menu can involve a lot of manipulation;
494         // don't dispatch change events to presenters until we're done.
495         mMenu.stopDispatchingItemsChanged();
496
497         // Restore action view state before we prepare. This gives apps
498         // an opportunity to override frozen/restored state in onPrepare.
499         if (mMenuFrozenActionViewState != null) {
500             mMenu.restoreActionViewStates(mMenuFrozenActionViewState);
501             mMenuFrozenActionViewState = null;
502         }
503
504         if (!callbackPrepareOptionsMenu(mMenu)) {
505             if (wActionBar != null) {
506                 // The app didn't want to show the menu for now but it still exists.
507                 // Clear it out of the action bar.
508                 wActionBar.setMenu(null, this);
509             }
510             mMenu.startDispatchingItemsChanged();
511             return false;
512         }
513
514         // Set the proper keymap
515         KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
516         mMenu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
517         mMenu.startDispatchingItemsChanged();
518
519         // Set other state
520         mMenuIsPrepared = true;
521
522         return true;
523     }
524
525     public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
526         return callbackOptionsItemSelected(item);
527     }
528
529     public void onMenuModeChange(MenuBuilder menu) {
530         reopenMenu(true);
531     }
532
533     private void reopenMenu(boolean toggleMenuMode) {
534         if (wActionBar != null && wActionBar.isOverflowReserved()) {
535             if (!wActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
536                 if (wActionBar.getVisibility() == View.VISIBLE) {
537                     if (callbackPrepareOptionsMenu(mMenu)) {
538                         wActionBar.showOverflowMenu();
539                     }
540                 }
541             } else {
542                 wActionBar.hideOverflowMenu();
543             }
544             return;
545         }
546     }
547
548     private boolean initializePanelMenu() {
549         Context context = mActivity;//getContext();
550
551         // If we have an action bar, initialize the menu with a context themed for it.
552         if (wActionBar != null) {
553             TypedValue outValue = new TypedValue();
554             Resources.Theme currentTheme = context.getTheme();
555             currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme,
556                     outValue, true);
557             final int targetThemeRes = outValue.resourceId;
558
559             if (targetThemeRes != 0 /*&& context.getThemeResId() != targetThemeRes*/) {
560                 context = new ContextThemeWrapper(context, targetThemeRes);
561             }
562         }
563
564         mMenu = new MenuBuilder(context);
565         mMenu.setCallback(this);
566
567         return true;
568     }
569
570     void checkCloseActionMenu(Menu menu) {
571         if (mClosingActionMenu) {
572             return;
573         }
574
575         mClosingActionMenu = true;
576         wActionBar.dismissPopupMenus();
577         //Callback cb = getCallback();
578         //if (cb != null && !isDestroyed()) {
579         //    cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
580         //}
581         mClosingActionMenu = false;
582     }
583
584     @Override
585     public boolean onOpenSubMenu(MenuBuilder subMenu) {
586         return true;
587     }
588
589     @Override
590     public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
591         checkCloseActionMenu(menu);
592     }
593
594     @Override
595     public boolean onMenuItemClick(android.view.MenuItem item) {
596         if (DEBUG) Log.d(TAG, "[mNativeItemListener.onMenuItemClick] item: " + item);
597
598         final MenuItemImpl sherlockItem = mNativeItemMap.get(item);
599         if (sherlockItem != null) {
600             sherlockItem.invoke();
601         } else {
602             Log.e(TAG, "Options item \"" + item + "\" not found in mapping");
603         }
604
605         return true; //Do not allow continuation of native handling
606     }
607
608     @Override
609     public boolean onMenuItemSelected(int featureId, MenuItem item) {
610         return callbackOptionsItemSelected(item);
611     }
612
613
614     ///////////////////////////////////////////////////////////////////////////
615     // Progress bar interaction and internal handling
616     ///////////////////////////////////////////////////////////////////////////
617
618     @Override
619     public void setProgressBarVisibility(boolean visible) {
620         if (DEBUG) Log.d(TAG, "[setProgressBarVisibility] visible: " + visible);
621
622         setFeatureInt(Window.FEATURE_PROGRESS, visible ? Window.PROGRESS_VISIBILITY_ON :
623             Window.PROGRESS_VISIBILITY_OFF);
624     }
625
626     @Override
627     public void setProgressBarIndeterminateVisibility(boolean visible) {
628         if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible);
629
630         setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
631                 visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
632     }
633
634     @Override
635     public void setProgressBarIndeterminate(boolean indeterminate) {
636         if (DEBUG) Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate);
637
638         setFeatureInt(Window.FEATURE_PROGRESS,
639                 indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF);
640     }
641
642     @Override
643     public void setProgress(int progress) {
644         if (DEBUG) Log.d(TAG, "[setProgress] progress: " + progress);
645
646         setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
647     }
648
649     @Override
650     public void setSecondaryProgress(int secondaryProgress) {
651         if (DEBUG) Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress);
652
653         setFeatureInt(Window.FEATURE_PROGRESS,
654                 secondaryProgress + Window.PROGRESS_SECONDARY_START);
655     }
656
657     private void setFeatureInt(int featureId, int value) {
658         updateInt(featureId, value, false);
659     }
660
661     private void updateInt(int featureId, int value, boolean fromResume) {
662         // Do nothing if the decor is not yet installed... an update will
663         // need to be forced when we eventually become active.
664         if (mContentParent == null) {
665             return;
666         }
667
668         final int featureMask = 1 << featureId;
669
670         if ((getFeatures() & featureMask) == 0 && !fromResume) {
671             return;
672         }
673
674         onIntChanged(featureId, value);
675     }
676
677     private void onIntChanged(int featureId, int value) {
678         if (featureId == Window.FEATURE_PROGRESS || featureId == Window.FEATURE_INDETERMINATE_PROGRESS) {
679             updateProgressBars(value);
680         }
681     }
682
683     private void updateProgressBars(int value) {
684         IcsProgressBar circularProgressBar = getCircularProgressBar(true);
685         IcsProgressBar horizontalProgressBar = getHorizontalProgressBar(true);
686
687         final int features = mFeatures;//getLocalFeatures();
688         if (value == Window.PROGRESS_VISIBILITY_ON) {
689             if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
690                 int level = horizontalProgressBar.getProgress();
691                 int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
692                         View.VISIBLE : View.INVISIBLE;
693                 horizontalProgressBar.setVisibility(visibility);
694             }
695             if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
696                 circularProgressBar.setVisibility(View.VISIBLE);
697             }
698         } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
699             if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
700                 horizontalProgressBar.setVisibility(View.GONE);
701             }
702             if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
703                 circularProgressBar.setVisibility(View.GONE);
704             }
705         } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
706             horizontalProgressBar.setIndeterminate(true);
707         } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
708             horizontalProgressBar.setIndeterminate(false);
709         } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
710             // We want to set the progress value before testing for visibility
711             // so that when the progress bar becomes visible again, it has the
712             // correct level.
713             horizontalProgressBar.setProgress(value - Window.PROGRESS_START);
714
715             if (value < Window.PROGRESS_END) {
716                 showProgressBars(horizontalProgressBar, circularProgressBar);
717             } else {
718                 hideProgressBars(horizontalProgressBar, circularProgressBar);
719             }
720         } else if (Window.PROGRESS_SECONDARY_START <= value && value <= Window.PROGRESS_SECONDARY_END) {
721             horizontalProgressBar.setSecondaryProgress(value - Window.PROGRESS_SECONDARY_START);
722
723             showProgressBars(horizontalProgressBar, circularProgressBar);
724         }
725     }
726
727     private void showProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
728         final int features = mFeatures;//getLocalFeatures();
729         if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
730                 spinnyProgressBar.getVisibility() == View.INVISIBLE) {
731             spinnyProgressBar.setVisibility(View.VISIBLE);
732         }
733         // Only show the progress bars if the primary progress is not complete
734         if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
735                 horizontalProgressBar.getProgress() < 10000) {
736             horizontalProgressBar.setVisibility(View.VISIBLE);
737         }
738     }
739
740     private void hideProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
741         final int features = mFeatures;//getLocalFeatures();
742         Animation anim = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out);
743         anim.setDuration(1000);
744         if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0 &&
745                 spinnyProgressBar.getVisibility() == View.VISIBLE) {
746             spinnyProgressBar.startAnimation(anim);
747             spinnyProgressBar.setVisibility(View.INVISIBLE);
748         }
749         if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 &&
750                 horizontalProgressBar.getVisibility() == View.VISIBLE) {
751             horizontalProgressBar.startAnimation(anim);
752             horizontalProgressBar.setVisibility(View.INVISIBLE);
753         }
754     }
755
756     private IcsProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
757         if (mCircularProgressBar != null) {
758             return mCircularProgressBar;
759         }
760         if (mContentParent == null && shouldInstallDecor) {
761             installDecor();
762         }
763         mCircularProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_circular);
764         if (mCircularProgressBar != null) {
765             mCircularProgressBar.setVisibility(View.INVISIBLE);
766         }
767         return mCircularProgressBar;
768     }
769
770     private IcsProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
771         if (mHorizontalProgressBar != null) {
772             return mHorizontalProgressBar;
773         }
774         if (mContentParent == null && shouldInstallDecor) {
775             installDecor();
776         }
777         mHorizontalProgressBar = (IcsProgressBar)mDecor.findViewById(R.id.abs__progress_horizontal);
778         if (mHorizontalProgressBar != null) {
779             mHorizontalProgressBar.setVisibility(View.INVISIBLE);
780         }
781         return mHorizontalProgressBar;
782     }
783
784
785     ///////////////////////////////////////////////////////////////////////////
786     // Feature management and content interaction and creation
787     ///////////////////////////////////////////////////////////////////////////
788
789     private int getFeatures() {
790         if (DEBUG) Log.d(TAG, "[getFeatures] returning " + mFeatures);
791
792         return mFeatures;
793     }
794
795     @Override
796     public boolean hasFeature(int featureId) {
797         if (DEBUG) Log.d(TAG, "[hasFeature] featureId: " + featureId);
798
799         boolean result = (mFeatures & (1 << featureId)) != 0;
800         if (DEBUG) Log.d(TAG, "[hasFeature] returning " + result);
801         return result;
802     }
803
804     @Override
805     public boolean requestFeature(int featureId) {
806         if (DEBUG) Log.d(TAG, "[requestFeature] featureId: " + featureId);
807
808         if (mContentParent != null) {
809             throw new AndroidRuntimeException("requestFeature() must be called before adding content");
810         }
811
812         switch (featureId) {
813             case Window.FEATURE_ACTION_BAR:
814             case Window.FEATURE_ACTION_BAR_OVERLAY:
815             case Window.FEATURE_ACTION_MODE_OVERLAY:
816             case Window.FEATURE_INDETERMINATE_PROGRESS:
817             case Window.FEATURE_NO_TITLE:
818             case Window.FEATURE_PROGRESS:
819                 mFeatures |= (1 << featureId);
820                 return true;
821
822             default:
823                 return false;
824         }
825     }
826
827     @Override
828     public void setUiOptions(int uiOptions) {
829         if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions);
830
831         mUiOptions = uiOptions;
832     }
833
834     @Override
835     public void setUiOptions(int uiOptions, int mask) {
836         if (DEBUG) Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask);
837
838         mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);
839     }
840
841     @Override
842     public void setContentView(int layoutResId) {
843         if (DEBUG) Log.d(TAG, "[setContentView] layoutResId: " + layoutResId);
844
845         if (mContentParent == null) {
846             installDecor();
847         } else {
848             mContentParent.removeAllViews();
849         }
850         mActivity.getLayoutInflater().inflate(layoutResId, mContentParent);
851
852         android.view.Window.Callback callback = mActivity.getWindow().getCallback();
853         if (callback != null) {
854             callback.onContentChanged();
855         }
856
857         initActionBar();
858     }
859
860     @Override
861     public void setContentView(View view, ViewGroup.LayoutParams params) {
862         if (DEBUG) Log.d(TAG, "[setContentView] view: " + view + ", params: " + params);
863
864         if (mContentParent == null) {
865             installDecor();
866         } else {
867             mContentParent.removeAllViews();
868         }
869         mContentParent.addView(view, params);
870
871         android.view.Window.Callback callback = mActivity.getWindow().getCallback();
872         if (callback != null) {
873             callback.onContentChanged();
874         }
875
876         initActionBar();
877     }
878
879     @Override
880     public void addContentView(View view, ViewGroup.LayoutParams params) {
881         if (DEBUG) Log.d(TAG, "[addContentView] view: " + view + ", params: " + params);
882
883         if (mContentParent == null) {
884             installDecor();
885         }
886         mContentParent.addView(view, params);
887
888         initActionBar();
889     }
890
891     private void installDecor() {
892         if (DEBUG) Log.d(TAG, "[installDecor]");
893
894         if (mDecor == null) {
895             mDecor = (ViewGroup)mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
896         }
897         if (mContentParent == null) {
898             //Since we are not operating at the window level we need to take
899             //into account the fact that the true decor may have already been
900             //initialized and had content attached to it. If that is the case,
901             //copy over its children to our new content container.
902             List<View> views = null;
903             if (mDecor.getChildCount() > 0) {
904                 views = new ArrayList<View>(1); //Usually there's only one child
905                 for (int i = 0, children = mDecor.getChildCount(); i < children; i++) {
906                     View child = mDecor.getChildAt(0);
907                     mDecor.removeView(child);
908                     views.add(child);
909                 }
910             }
911
912             mContentParent = generateLayout();
913
914             //Copy over the old children. See above for explanation.
915             if (views != null) {
916                 for (View child : views) {
917                     mContentParent.addView(child);
918                 }
919             }
920
921             mTitleView = (TextView)mDecor.findViewById(android.R.id.title);
922             if (mTitleView != null) {
923                 if (hasFeature(Window.FEATURE_NO_TITLE)) {
924                     mTitleView.setVisibility(View.GONE);
925                     if (mContentParent instanceof FrameLayout) {
926                         ((FrameLayout)mContentParent).setForeground(null);
927                     }
928                 } else {
929                     mTitleView.setText(mTitle);
930                 }
931             } else {
932                 wActionBar = (ActionBarView)mDecor.findViewById(R.id.abs__action_bar);
933                 if (wActionBar != null) {
934                     wActionBar.setWindowCallback(this);
935                     if (wActionBar.getTitle() == null) {
936                         wActionBar.setWindowTitle(mActivity.getTitle());
937                     }
938                     if (hasFeature(Window.FEATURE_PROGRESS)) {
939                         wActionBar.initProgress();
940                     }
941                     if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
942                         wActionBar.initIndeterminateProgress();
943                     }
944
945                     //Since we don't require onCreate dispatching, parse for uiOptions here
946                     int uiOptions = loadUiOptionsFromManifest(mActivity);
947                     if (uiOptions != 0) {
948                         mUiOptions = uiOptions;
949                     }
950
951                     boolean splitActionBar = false;
952                     final boolean splitWhenNarrow = (mUiOptions & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
953                     if (splitWhenNarrow) {
954                         splitActionBar = getResources_getBoolean(mActivity, R.bool.abs__split_action_bar_is_narrow);
955                     } else {
956                         splitActionBar = mActivity.getTheme()
957                                 .obtainStyledAttributes(R.styleable.SherlockTheme)
958                                 .getBoolean(R.styleable.SherlockTheme_windowSplitActionBar, false);
959                     }
960                     final ActionBarContainer splitView = (ActionBarContainer)mDecor.findViewById(R.id.abs__split_action_bar);
961                     if (splitView != null) {
962                         wActionBar.setSplitView(splitView);
963                         wActionBar.setSplitActionBar(splitActionBar);
964                         wActionBar.setSplitWhenNarrow(splitWhenNarrow);
965
966                         mActionModeView = (ActionBarContextView)mDecor.findViewById(R.id.abs__action_context_bar);
967                         mActionModeView.setSplitView(splitView);
968                         mActionModeView.setSplitActionBar(splitActionBar);
969                         mActionModeView.setSplitWhenNarrow(splitWhenNarrow);
970                     } else if (splitActionBar) {
971                         Log.e(TAG, "Requested split action bar with incompatible window decor! Ignoring request.");
972                     }
973
974                     // Post the panel invalidate for later; avoid application onCreateOptionsMenu
975                     // being called in the middle of onCreate or similar.
976                     mDecor.post(new Runnable() {
977                         @Override
978                         public void run() {
979                             //Invalidate if the panel menu hasn't been created before this.
980                             if (!mActivity.isFinishing() && mMenu == null) {
981                                 dispatchInvalidateOptionsMenu();
982                             }
983                         }
984                     });
985                 }
986             }
987         }
988     }
989
990     private ViewGroup generateLayout() {
991         if (DEBUG) Log.d(TAG, "[generateLayout]");
992
993         // Apply data from current theme.
994
995         TypedArray a = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme);
996
997         mIsFloating = a.getBoolean(R.styleable.SherlockTheme_android_windowIsFloating, false);
998
999         if (!a.hasValue(R.styleable.SherlockTheme_windowActionBar)) {
1000             throw new IllegalStateException("You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative.");
1001         }
1002
1003         if (a.getBoolean(R.styleable.SherlockTheme_windowNoTitle, false)) {
1004             requestFeature(Window.FEATURE_NO_TITLE);
1005         } else if (a.getBoolean(R.styleable.SherlockTheme_windowActionBar, false)) {
1006             // Don't allow an action bar if there is no title.
1007             requestFeature(Window.FEATURE_ACTION_BAR);
1008         }
1009
1010         if (a.getBoolean(R.styleable.SherlockTheme_windowActionBarOverlay, false)) {
1011             requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
1012         }
1013
1014         if (a.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false)) {
1015             requestFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
1016         }
1017
1018         a.recycle();
1019
1020         int layoutResource;
1021         if (!hasFeature(Window.FEATURE_NO_TITLE)) {
1022             if (mIsFloating) {
1023                 //Trash original dialog LinearLayout
1024                 mDecor = (ViewGroup)mDecor.getParent();
1025                 mDecor.removeAllViews();
1026
1027                 layoutResource = R.layout.abs__dialog_title_holo;
1028             } else {
1029                 if (hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
1030                     layoutResource = R.layout.abs__screen_action_bar_overlay;
1031                 } else {
1032                     layoutResource = R.layout.abs__screen_action_bar;
1033                 }
1034             }
1035         } else if (hasFeature(Window.FEATURE_ACTION_MODE_OVERLAY) && !hasFeature(Window.FEATURE_NO_TITLE)) {
1036             layoutResource = R.layout.abs__screen_simple_overlay_action_mode;
1037         } else {
1038             layoutResource = R.layout.abs__screen_simple;
1039         }
1040
1041         if (DEBUG) Log.d(TAG, "[generateLayout] using screen XML " + mActivity.getResources().getString(layoutResource));
1042         View in = mActivity.getLayoutInflater().inflate(layoutResource, null);
1043         mDecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1044
1045         ViewGroup contentParent = (ViewGroup)mDecor.findViewById(R.id.abs__content);
1046         if (contentParent == null) {
1047             throw new RuntimeException("Couldn't find content container view");
1048         }
1049
1050         //Make our new child the true content view (for fragments). VERY VOLATILE!
1051         mDecor.setId(View.NO_ID);
1052         contentParent.setId(android.R.id.content);
1053
1054         if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
1055             IcsProgressBar progress = getCircularProgressBar(false);
1056             if (progress != null) {
1057                 progress.setIndeterminate(true);
1058             }
1059         }
1060
1061         return contentParent;
1062     }
1063
1064
1065     ///////////////////////////////////////////////////////////////////////////
1066     // Miscellaneous
1067     ///////////////////////////////////////////////////////////////////////////
1068
1069     /**
1070      * Determine whether or not the device has a dedicated menu key.
1071      *
1072      * @return {@code true} if native menu key is present.
1073      */
1074     private boolean isReservingOverflow() {
1075         if (!mReserveOverflowSet) {
1076             mReserveOverflow = ActionMenuPresenter.reserveOverflow(mActivity);
1077             mReserveOverflowSet = true;
1078         }
1079         return mReserveOverflow;
1080     }
1081
1082     private static int loadUiOptionsFromManifest(Activity activity) {
1083         int uiOptions = 0;
1084         try {
1085             final String thisPackage = activity.getClass().getName();
1086             if (DEBUG) Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage);
1087
1088             final String packageName = activity.getApplicationInfo().packageName;
1089             final AssetManager am = activity.createPackageContext(packageName, 0).getAssets();
1090             final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");
1091
1092             int eventType = xml.getEventType();
1093             while (eventType != XmlPullParser.END_DOCUMENT) {
1094                 if (eventType == XmlPullParser.START_TAG) {
1095                     String name = xml.getName();
1096
1097                     if ("application".equals(name)) {
1098                         //Check if the <application> has the attribute
1099                         if (DEBUG) Log.d(TAG, "Got <application>");
1100
1101                         for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
1102                             if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
1103
1104                             if ("uiOptions".equals(xml.getAttributeName(i))) {
1105                                 uiOptions = xml.getAttributeIntValue(i, 0);
1106                                 break; //out of for loop
1107                             }
1108                         }
1109                     } else if ("activity".equals(name)) {
1110                         //Check if the <activity> is us and has the attribute
1111                         if (DEBUG) Log.d(TAG, "Got <activity>");
1112                         Integer activityUiOptions = null;
1113                         String activityPackage = null;
1114                         boolean isOurActivity = false;
1115
1116                         for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
1117                             if (DEBUG) Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));
1118
1119                             //We need both uiOptions and name attributes
1120                             String attrName = xml.getAttributeName(i);
1121                             if ("uiOptions".equals(attrName)) {
1122                                 activityUiOptions = xml.getAttributeIntValue(i, 0);
1123                             } else if ("name".equals(attrName)) {
1124                                 activityPackage = cleanActivityName(packageName, xml.getAttributeValue(i));
1125                                 if (!thisPackage.equals(activityPackage)) {
1126                                     break; //out of for loop
1127                                 }
1128                                 isOurActivity = true;
1129                             }
1130
1131                             //Make sure we have both attributes before processing
1132                             if ((activityUiOptions != null) && (activityPackage != null)) {
1133                                 //Our activity, uiOptions specified, override with our value
1134                                 uiOptions = activityUiOptions.intValue();
1135                             }
1136                         }
1137                         if (isOurActivity) {
1138                             //If we matched our activity but it had no logo don't
1139                             //do any more processing of the manifest
1140                             break;
1141                         }
1142                     }
1143                 }
1144                 eventType = xml.nextToken();
1145             }
1146         } catch (Exception e) {
1147             e.printStackTrace();
1148         }
1149         if (DEBUG) Log.i(TAG, "Returning " + Integer.toHexString(uiOptions));
1150         return uiOptions;
1151     }
1152
1153     public static String cleanActivityName(String manifestPackage, String activityName) {
1154         if (activityName.charAt(0) == '.') {
1155             //Relative activity name (e.g., android:name=".ui.SomeClass")
1156             return manifestPackage + activityName;
1157         }
1158         if (activityName.indexOf('.', 1) == -1) {
1159             //Unqualified activity name (e.g., android:name="SomeClass")
1160             return manifestPackage + "." + activityName;
1161         }
1162         //Fully-qualified activity name (e.g., "com.my.package.SomeClass")
1163         return activityName;
1164     }
1165
1166     /**
1167      * Clears out internal reference when the action mode is destroyed.
1168      */
1169     private class ActionModeCallbackWrapper implements ActionMode.Callback {
1170         private final ActionMode.Callback mWrapped;
1171
1172         public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
1173             mWrapped = wrapped;
1174         }
1175
1176         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1177             return mWrapped.onCreateActionMode(mode, menu);
1178         }
1179
1180         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1181             return mWrapped.onPrepareActionMode(mode, menu);
1182         }
1183
1184         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1185             return mWrapped.onActionItemClicked(mode, item);
1186         }
1187
1188         public void onDestroyActionMode(ActionMode mode) {
1189             mWrapped.onDestroyActionMode(mode);
1190             if (mActionModeView != null) {
1191                 mActionModeView.setVisibility(View.GONE);
1192                 mActionModeView.removeAllViews();
1193             }
1194             if (mActivity instanceof OnActionModeFinishedListener) {
1195                 ((OnActionModeFinishedListener)mActivity).onActionModeFinished(mActionMode);
1196             }
1197             mActionMode = null;
1198         }
1199     }
1200 }