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