com.actionbarsherlock.internal.ActionBarSherlockCompat.java Source code

Java tutorial

Introduction

Here is the source code for com.actionbarsherlock.internal.ActionBarSherlockCompat.java

Source

/*
50AH-code
=========
    
50 Android Hacks (http://manning.com/sessa/) book source code
    
    
Copyright (c) 2012 Manning
    
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
    
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
    
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
    
    
*/

package com.actionbarsherlock.internal;

import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static com.actionbarsherlock.internal.ResourcesCompat.getResources_getBoolean;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import org.xmlpull.v1.XmlPullParser;
import android.app.Activity;
import android.content.Context;
import android.content.pm.ActivityInfo;
import android.content.res.AssetManager;
import android.content.res.Configuration;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.content.res.XmlResourceParser;
import android.os.Bundle;
import android.util.AndroidRuntimeException;
import android.util.Log;
import android.util.TypedValue;
import android.view.ContextThemeWrapper;
import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.Window;
import android.view.accessibility.AccessibilityEvent;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.FrameLayout;
import android.widget.TextView;
import com.actionbarsherlock.ActionBarSherlock;
import com.actionbarsherlock.R;
import com.actionbarsherlock.app.ActionBar;
import com.actionbarsherlock.internal.app.ActionBarImpl;
import com.actionbarsherlock.internal.view.StandaloneActionMode;
import com.actionbarsherlock.internal.view.menu.ActionMenuPresenter;
import com.actionbarsherlock.internal.view.menu.MenuBuilder;
import com.actionbarsherlock.internal.view.menu.MenuItemImpl;
import com.actionbarsherlock.internal.view.menu.MenuPresenter;
import com.actionbarsherlock.internal.widget.ActionBarContainer;
import com.actionbarsherlock.internal.widget.ActionBarContextView;
import com.actionbarsherlock.internal.widget.ActionBarView;
import com.actionbarsherlock.internal.widget.IcsProgressBar;
import com.actionbarsherlock.view.ActionMode;
import com.actionbarsherlock.view.Menu;
import com.actionbarsherlock.view.MenuItem;

@ActionBarSherlock.Implementation(api = 7)
public class ActionBarSherlockCompat extends ActionBarSherlock
        implements MenuBuilder.Callback, com.actionbarsherlock.view.Window.Callback, MenuPresenter.Callback,
        android.view.MenuItem.OnMenuItemClickListener {
    /** Window features which are enabled by default. */
    protected static final int DEFAULT_FEATURES = 0;

    public ActionBarSherlockCompat(Activity activity, int flags) {
        super(activity, flags);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Properties
    ///////////////////////////////////////////////////////////////////////////

    /** Whether or not the device has a dedicated menu key button. */
    private boolean mReserveOverflow;
    /** Lazy-load indicator for {@link #mReserveOverflow}. */
    private boolean mReserveOverflowSet = false;

    /** Current menu instance for managing action items. */
    private MenuBuilder mMenu;
    /** Map between native options items and sherlock items. */
    protected HashMap<android.view.MenuItem, MenuItemImpl> mNativeItemMap;
    /** Indication of a long-press on the hardware menu key. */
    private boolean mMenuKeyIsLongPress = false;

    /** Parent view of the window decoration (action bar, mode, etc.). */
    private ViewGroup mDecor;
    /** Parent view of the activity content. */
    private ViewGroup mContentParent;

    /** Whether or not the title is stable and can be displayed. */
    private boolean mIsTitleReady = false;
    /** Whether or not the parent activity has been destroyed. */
    private boolean mIsDestroyed = false;

    /* Emulate PanelFeatureState */
    private boolean mClosingActionMenu;
    private boolean mMenuIsPrepared;
    private boolean mMenuRefreshContent;
    private Bundle mMenuFrozenActionViewState;

    /** Implementation which backs the action bar interface API. */
    private ActionBarImpl aActionBar;
    /** Main action bar view which displays the core content. */
    private ActionBarView wActionBar;
    /** Relevant window and action bar features flags. */
    private int mFeatures = DEFAULT_FEATURES;
    /** Relevant user interface option flags. */
    private int mUiOptions = 0;

    /** Decor indeterminate progress indicator. */
    private IcsProgressBar mCircularProgressBar;
    /** Decor progress indicator. */
    private IcsProgressBar mHorizontalProgressBar;

    /** Current displayed context action bar, if any. */
    private ActionMode mActionMode;
    /** Parent view in which the context action bar is displayed. */
    private ActionBarContextView mActionModeView;

    /** Title view used with dialogs. */
    private TextView mTitleView;
    /** Current activity title. */
    private CharSequence mTitle = null;
    /** Whether or not this "activity" is floating (i.e., a dialog) */
    private boolean mIsFloating;

    ///////////////////////////////////////////////////////////////////////////
    // Instance methods
    ///////////////////////////////////////////////////////////////////////////

    @Override
    public ActionBar getActionBar() {
        if (DEBUG)
            Log.d(TAG, "[getActionBar]");

        initActionBar();
        return aActionBar;
    }

    private void initActionBar() {
        if (DEBUG)
            Log.d(TAG, "[initActionBar]");

        // Initializing the window decor can change window feature flags.
        // Make sure that we have the correct set before performing the test below.
        if (mDecor == null) {
            installDecor();
        }

        if ((aActionBar != null) || !hasFeature(Window.FEATURE_ACTION_BAR) || hasFeature(Window.FEATURE_NO_TITLE)
                || mActivity.isChild()) {
            return;
        }

        aActionBar = new ActionBarImpl(mActivity, mFeatures);

        if (!mIsDelegate) {
            //We may never get another chance to set the title
            wActionBar.setWindowTitle(mActivity.getTitle());
        }
    }

    @Override
    protected Context getThemedContext() {
        return aActionBar.getThemedContext();
    }

    @Override
    public void setTitle(CharSequence title) {
        if (DEBUG)
            Log.d(TAG, "[setTitle] title: " + title);

        dispatchTitleChanged(title, 0);
    }

    @Override
    public ActionMode startActionMode(ActionMode.Callback callback) {
        if (DEBUG)
            Log.d(TAG, "[startActionMode] callback: " + callback);

        if (mActionMode != null) {
            mActionMode.finish();
        }

        final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
        ActionMode mode = null;

        //Emulate Activity's onWindowStartingActionMode:
        initActionBar();
        if (aActionBar != null) {
            mode = aActionBar.startActionMode(wrappedCallback);
        }

        if (mode != null) {
            mActionMode = mode;
        } else {
            if (mActionModeView == null) {
                ViewStub stub = (ViewStub) mDecor.findViewById(R.id.abs__action_mode_bar_stub);
                if (stub != null) {
                    mActionModeView = (ActionBarContextView) stub.inflate();
                }
            }
            if (mActionModeView != null) {
                mActionModeView.killMode();
                mode = new StandaloneActionMode(mActivity, mActionModeView, wrappedCallback, true);
                if (callback.onCreateActionMode(mode, mode.getMenu())) {
                    mode.invalidate();
                    mActionModeView.initForMode(mode);
                    mActionModeView.setVisibility(View.VISIBLE);
                    mActionMode = mode;
                    mActionModeView.sendAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
                } else {
                    mActionMode = null;
                }
            }
        }
        if (mActionMode != null && mActivity instanceof OnActionModeStartedListener) {
            ((OnActionModeStartedListener) mActivity).onActionModeStarted(mActionMode);
        }
        return mActionMode;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Lifecycle and interaction callbacks for delegation
    ///////////////////////////////////////////////////////////////////////////

    @Override
    public void dispatchConfigurationChanged(Configuration newConfig) {
        if (DEBUG)
            Log.d(TAG, "[dispatchConfigurationChanged] newConfig: " + newConfig);

        if (aActionBar != null) {
            aActionBar.onConfigurationChanged(newConfig);
        }
    }

    @Override
    public void dispatchPostResume() {
        if (DEBUG)
            Log.d(TAG, "[dispatchPostResume]");

        if (aActionBar != null) {
            aActionBar.setShowHideAnimationEnabled(true);
        }
    }

    @Override
    public void dispatchPause() {
        if (DEBUG)
            Log.d(TAG, "[dispatchPause]");

        if (wActionBar != null && wActionBar.isOverflowMenuShowing()) {
            wActionBar.hideOverflowMenu();
        }
    }

    @Override
    public void dispatchStop() {
        if (DEBUG)
            Log.d(TAG, "[dispatchStop]");

        if (aActionBar != null) {
            aActionBar.setShowHideAnimationEnabled(false);
        }
    }

    @Override
    public void dispatchInvalidateOptionsMenu() {
        if (DEBUG)
            Log.d(TAG, "[dispatchInvalidateOptionsMenu]");

        Bundle savedActionViewStates = null;
        if (mMenu != null) {
            savedActionViewStates = new Bundle();
            mMenu.saveActionViewStates(savedActionViewStates);
            if (savedActionViewStates.size() > 0) {
                mMenuFrozenActionViewState = savedActionViewStates;
            }
            // This will be started again when the panel is prepared.
            mMenu.stopDispatchingItemsChanged();
            mMenu.clear();
        }
        mMenuRefreshContent = true;

        // Prepare the options panel if we have an action bar
        if (wActionBar != null) {
            mMenuIsPrepared = false;
            preparePanel();
        }
    }

    @Override
    public boolean dispatchOpenOptionsMenu() {
        if (DEBUG)
            Log.d(TAG, "[dispatchOpenOptionsMenu]");

        if (!isReservingOverflow()) {
            return false;
        }

        return wActionBar.showOverflowMenu();
    }

    @Override
    public boolean dispatchCloseOptionsMenu() {
        if (DEBUG)
            Log.d(TAG, "[dispatchCloseOptionsMenu]");

        if (!isReservingOverflow()) {
            return false;
        }

        return wActionBar.hideOverflowMenu();
    }

    @Override
    public void dispatchPostCreate(Bundle savedInstanceState) {
        if (DEBUG)
            Log.d(TAG, "[dispatchOnPostCreate]");

        if (mIsDelegate) {
            mIsTitleReady = true;
        }

        if (mDecor == null) {
            initActionBar();
        }
    }

    @Override
    public boolean dispatchCreateOptionsMenu(android.view.Menu menu) {
        if (DEBUG) {
            Log.d(TAG, "[dispatchCreateOptionsMenu] android.view.Menu: " + menu);
            Log.d(TAG, "[dispatchCreateOptionsMenu] returning true");
        }
        return true;
    }

    @Override
    public boolean dispatchPrepareOptionsMenu(android.view.Menu menu) {
        if (DEBUG)
            Log.d(TAG, "[dispatchPrepareOptionsMenu] android.view.Menu: " + menu);

        if (mActionMode != null) {
            return false;
        }

        mMenuIsPrepared = false;
        if (!preparePanel()) {
            return false;
        }

        if (isReservingOverflow()) {
            return false;
        }

        if (mNativeItemMap == null) {
            mNativeItemMap = new HashMap<android.view.MenuItem, MenuItemImpl>();
        } else {
            mNativeItemMap.clear();
        }

        if (mMenu == null) {
            return false;
        }

        boolean result = mMenu.bindNativeOverflow(menu, this, mNativeItemMap);
        if (DEBUG)
            Log.d(TAG, "[dispatchPrepareOptionsMenu] returning " + result);
        return result;
    }

    @Override
    public boolean dispatchOptionsItemSelected(android.view.MenuItem item) {
        throw new IllegalStateException("Native callback invoked. Create a test case and report!");
    }

    @Override
    public boolean dispatchMenuOpened(int featureId, android.view.Menu menu) {
        if (DEBUG)
            Log.d(TAG, "[dispatchMenuOpened] featureId: " + featureId + ", menu: " + menu);

        if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
            if (aActionBar != null) {
                aActionBar.dispatchMenuVisibilityChanged(true);
            }
            return true;
        }

        return false;
    }

    @Override
    public void dispatchPanelClosed(int featureId, android.view.Menu menu) {
        if (DEBUG)
            Log.d(TAG, "[dispatchPanelClosed] featureId: " + featureId + ", menu: " + menu);

        if (featureId == Window.FEATURE_ACTION_BAR || featureId == Window.FEATURE_OPTIONS_PANEL) {
            if (aActionBar != null) {
                aActionBar.dispatchMenuVisibilityChanged(false);
            }
        }
    }

    @Override
    public void dispatchTitleChanged(CharSequence title, int color) {
        if (DEBUG)
            Log.d(TAG, "[dispatchTitleChanged] title: " + title + ", color: " + color);

        if (!mIsDelegate || mIsTitleReady) {
            if (mTitleView != null) {
                mTitleView.setText(title);
            } else if (wActionBar != null) {
                wActionBar.setWindowTitle(title);
            }
        }

        mTitle = title;
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (DEBUG)
            Log.d(TAG, "[dispatchKeyEvent] event: " + event);

        final int keyCode = event.getKeyCode();

        // Not handled by the view hierarchy, does the action bar want it
        // to cancel out of something special?
        if (keyCode == KeyEvent.KEYCODE_BACK) {
            final int action = event.getAction();
            // Back cancels action modes first.
            if (mActionMode != null) {
                if (action == KeyEvent.ACTION_UP) {
                    mActionMode.finish();
                }
                if (DEBUG)
                    Log.d(TAG, "[dispatchKeyEvent] returning true");
                return true;
            }

            // Next collapse any expanded action views.
            if (wActionBar != null && wActionBar.hasExpandedActionView()) {
                if (action == KeyEvent.ACTION_UP) {
                    wActionBar.collapseActionView();
                }
                if (DEBUG)
                    Log.d(TAG, "[dispatchKeyEvent] returning true");
                return true;
            }
        }

        boolean result = false;
        if (keyCode == KeyEvent.KEYCODE_MENU && isReservingOverflow()) {
            if (event.getAction() == KeyEvent.ACTION_DOWN && event.isLongPress()) {
                mMenuKeyIsLongPress = true;
            } else if (event.getAction() == KeyEvent.ACTION_UP) {
                if (!mMenuKeyIsLongPress) {
                    if (mActionMode == null && wActionBar != null) {
                        if (wActionBar.isOverflowMenuShowing()) {
                            wActionBar.hideOverflowMenu();
                        } else {
                            wActionBar.showOverflowMenu();
                        }
                    }
                    result = true;
                }
                mMenuKeyIsLongPress = false;
            }
        }

        if (DEBUG)
            Log.d(TAG, "[dispatchKeyEvent] returning " + result);
        return result;
    }

    @Override
    public void dispatchDestroy() {
        mIsDestroyed = true;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Menu callback lifecycle and creation
    ///////////////////////////////////////////////////////////////////////////

    private boolean preparePanel() {
        // Already prepared (isPrepared will be reset to false later)
        if (mMenuIsPrepared) {
            return true;
        }

        // Init the panel state's menu--return false if init failed
        if (mMenu == null || mMenuRefreshContent) {
            if (mMenu == null) {
                if (!initializePanelMenu() || (mMenu == null)) {
                    return false;
                }
            }

            if (wActionBar != null) {
                wActionBar.setMenu(mMenu, this);
            }

            // Call callback, and return if it doesn't want to display menu.

            // Creating the panel menu will involve a lot of manipulation;
            // don't dispatch change events to presenters until we're done.
            mMenu.stopDispatchingItemsChanged();
            if (!callbackCreateOptionsMenu(mMenu)) {
                // Ditch the menu created above
                mMenu = null;

                if (wActionBar != null) {
                    // Don't show it in the action bar either
                    wActionBar.setMenu(null, this);
                }

                return false;
            }

            mMenuRefreshContent = false;
        }

        // Callback and return if the callback does not want to show the menu

        // Preparing the panel menu can involve a lot of manipulation;
        // don't dispatch change events to presenters until we're done.
        mMenu.stopDispatchingItemsChanged();

        // Restore action view state before we prepare. This gives apps
        // an opportunity to override frozen/restored state in onPrepare.
        if (mMenuFrozenActionViewState != null) {
            mMenu.restoreActionViewStates(mMenuFrozenActionViewState);
            mMenuFrozenActionViewState = null;
        }

        if (!callbackPrepareOptionsMenu(mMenu)) {
            if (wActionBar != null) {
                // The app didn't want to show the menu for now but it still exists.
                // Clear it out of the action bar.
                wActionBar.setMenu(null, this);
            }
            mMenu.startDispatchingItemsChanged();
            return false;
        }

        // Set the proper keymap
        KeyCharacterMap kmap = KeyCharacterMap.load(KeyCharacterMap.VIRTUAL_KEYBOARD);
        mMenu.setQwertyMode(kmap.getKeyboardType() != KeyCharacterMap.NUMERIC);
        mMenu.startDispatchingItemsChanged();

        // Set other state
        mMenuIsPrepared = true;

        return true;
    }

    public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
        return callbackOptionsItemSelected(item);
    }

    public void onMenuModeChange(MenuBuilder menu) {
        reopenMenu(true);
    }

    private void reopenMenu(boolean toggleMenuMode) {
        if (wActionBar != null && wActionBar.isOverflowReserved()) {
            if (!wActionBar.isOverflowMenuShowing() || !toggleMenuMode) {
                if (wActionBar.getVisibility() == View.VISIBLE) {
                    if (callbackPrepareOptionsMenu(mMenu)) {
                        wActionBar.showOverflowMenu();
                    }
                }
            } else {
                wActionBar.hideOverflowMenu();
            }
            return;
        }
    }

    private boolean initializePanelMenu() {
        Context context = mActivity;//getContext();

        // If we have an action bar, initialize the menu with a context themed for it.
        if (wActionBar != null) {
            TypedValue outValue = new TypedValue();
            Resources.Theme currentTheme = context.getTheme();
            currentTheme.resolveAttribute(R.attr.actionBarWidgetTheme, outValue, true);
            final int targetThemeRes = outValue.resourceId;

            if (targetThemeRes != 0 /*&& context.getThemeResId() != targetThemeRes*/) {
                context = new ContextThemeWrapper(context, targetThemeRes);
            }
        }

        mMenu = new MenuBuilder(context);
        mMenu.setCallback(this);

        return true;
    }

    void checkCloseActionMenu(Menu menu) {
        if (mClosingActionMenu) {
            return;
        }

        mClosingActionMenu = true;
        wActionBar.dismissPopupMenus();
        //Callback cb = getCallback();
        //if (cb != null && !isDestroyed()) {
        //    cb.onPanelClosed(FEATURE_ACTION_BAR, menu);
        //}
        mClosingActionMenu = false;
    }

    @Override
    public boolean onOpenSubMenu(MenuBuilder subMenu) {
        return true;
    }

    @Override
    public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
        checkCloseActionMenu(menu);
    }

    @Override
    public boolean onMenuItemClick(android.view.MenuItem item) {
        if (DEBUG)
            Log.d(TAG, "[mNativeItemListener.onMenuItemClick] item: " + item);

        final MenuItemImpl sherlockItem = mNativeItemMap.get(item);
        if (sherlockItem != null) {
            sherlockItem.invoke();
        } else {
            Log.e(TAG, "Options item \"" + item + "\" not found in mapping");
        }

        return true; //Do not allow continuation of native handling
    }

    @Override
    public boolean onMenuItemSelected(int featureId, MenuItem item) {
        return callbackOptionsItemSelected(item);
    }

    ///////////////////////////////////////////////////////////////////////////
    // Progress bar interaction and internal handling
    ///////////////////////////////////////////////////////////////////////////

    @Override
    public void setProgressBarVisibility(boolean visible) {
        if (DEBUG)
            Log.d(TAG, "[setProgressBarVisibility] visible: " + visible);

        setFeatureInt(Window.FEATURE_PROGRESS,
                visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
    }

    @Override
    public void setProgressBarIndeterminateVisibility(boolean visible) {
        if (DEBUG)
            Log.d(TAG, "[setProgressBarIndeterminateVisibility] visible: " + visible);

        setFeatureInt(Window.FEATURE_INDETERMINATE_PROGRESS,
                visible ? Window.PROGRESS_VISIBILITY_ON : Window.PROGRESS_VISIBILITY_OFF);
    }

    @Override
    public void setProgressBarIndeterminate(boolean indeterminate) {
        if (DEBUG)
            Log.d(TAG, "[setProgressBarIndeterminate] indeterminate: " + indeterminate);

        setFeatureInt(Window.FEATURE_PROGRESS,
                indeterminate ? Window.PROGRESS_INDETERMINATE_ON : Window.PROGRESS_INDETERMINATE_OFF);
    }

    @Override
    public void setProgress(int progress) {
        if (DEBUG)
            Log.d(TAG, "[setProgress] progress: " + progress);

        setFeatureInt(Window.FEATURE_PROGRESS, progress + Window.PROGRESS_START);
    }

    @Override
    public void setSecondaryProgress(int secondaryProgress) {
        if (DEBUG)
            Log.d(TAG, "[setSecondaryProgress] secondaryProgress: " + secondaryProgress);

        setFeatureInt(Window.FEATURE_PROGRESS, secondaryProgress + Window.PROGRESS_SECONDARY_START);
    }

    private void setFeatureInt(int featureId, int value) {
        updateInt(featureId, value, false);
    }

    private void updateInt(int featureId, int value, boolean fromResume) {
        // Do nothing if the decor is not yet installed... an update will
        // need to be forced when we eventually become active.
        if (mContentParent == null) {
            return;
        }

        final int featureMask = 1 << featureId;

        if ((getFeatures() & featureMask) == 0 && !fromResume) {
            return;
        }

        onIntChanged(featureId, value);
    }

    private void onIntChanged(int featureId, int value) {
        if (featureId == Window.FEATURE_PROGRESS || featureId == Window.FEATURE_INDETERMINATE_PROGRESS) {
            updateProgressBars(value);
        }
    }

    private void updateProgressBars(int value) {
        IcsProgressBar circularProgressBar = getCircularProgressBar(true);
        IcsProgressBar horizontalProgressBar = getHorizontalProgressBar(true);

        final int features = mFeatures;//getLocalFeatures();
        if (value == Window.PROGRESS_VISIBILITY_ON) {
            if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
                int level = horizontalProgressBar.getProgress();
                int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ? View.VISIBLE
                        : View.INVISIBLE;
                horizontalProgressBar.setVisibility(visibility);
            }
            if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
                circularProgressBar.setVisibility(View.VISIBLE);
            }
        } else if (value == Window.PROGRESS_VISIBILITY_OFF) {
            if ((features & (1 << Window.FEATURE_PROGRESS)) != 0) {
                horizontalProgressBar.setVisibility(View.GONE);
            }
            if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0) {
                circularProgressBar.setVisibility(View.GONE);
            }
        } else if (value == Window.PROGRESS_INDETERMINATE_ON) {
            horizontalProgressBar.setIndeterminate(true);
        } else if (value == Window.PROGRESS_INDETERMINATE_OFF) {
            horizontalProgressBar.setIndeterminate(false);
        } else if (Window.PROGRESS_START <= value && value <= Window.PROGRESS_END) {
            // We want to set the progress value before testing for visibility
            // so that when the progress bar becomes visible again, it has the
            // correct level.
            horizontalProgressBar.setProgress(value - Window.PROGRESS_START);

            if (value < Window.PROGRESS_END) {
                showProgressBars(horizontalProgressBar, circularProgressBar);
            } else {
                hideProgressBars(horizontalProgressBar, circularProgressBar);
            }
        } else if (Window.PROGRESS_SECONDARY_START <= value && value <= Window.PROGRESS_SECONDARY_END) {
            horizontalProgressBar.setSecondaryProgress(value - Window.PROGRESS_SECONDARY_START);

            showProgressBars(horizontalProgressBar, circularProgressBar);
        }
    }

    private void showProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
        final int features = mFeatures;//getLocalFeatures();
        if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0
                && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
            spinnyProgressBar.setVisibility(View.VISIBLE);
        }
        // Only show the progress bars if the primary progress is not complete
        if ((features & (1 << Window.FEATURE_PROGRESS)) != 0 && horizontalProgressBar.getProgress() < 10000) {
            horizontalProgressBar.setVisibility(View.VISIBLE);
        }
    }

    private void hideProgressBars(IcsProgressBar horizontalProgressBar, IcsProgressBar spinnyProgressBar) {
        final int features = mFeatures;//getLocalFeatures();
        Animation anim = AnimationUtils.loadAnimation(mActivity, android.R.anim.fade_out);
        anim.setDuration(1000);
        if ((features & (1 << Window.FEATURE_INDETERMINATE_PROGRESS)) != 0
                && spinnyProgressBar.getVisibility() == View.VISIBLE) {
            spinnyProgressBar.startAnimation(anim);
            spinnyProgressBar.setVisibility(View.INVISIBLE);
        }
        if ((features & (1 << Window.FEATURE_PROGRESS)) != 0
                && horizontalProgressBar.getVisibility() == View.VISIBLE) {
            horizontalProgressBar.startAnimation(anim);
            horizontalProgressBar.setVisibility(View.INVISIBLE);
        }
    }

    private IcsProgressBar getCircularProgressBar(boolean shouldInstallDecor) {
        if (mCircularProgressBar != null) {
            return mCircularProgressBar;
        }
        if (mContentParent == null && shouldInstallDecor) {
            installDecor();
        }
        mCircularProgressBar = (IcsProgressBar) mDecor.findViewById(R.id.abs__progress_circular);
        if (mCircularProgressBar != null) {
            mCircularProgressBar.setVisibility(View.INVISIBLE);
        }
        return mCircularProgressBar;
    }

    private IcsProgressBar getHorizontalProgressBar(boolean shouldInstallDecor) {
        if (mHorizontalProgressBar != null) {
            return mHorizontalProgressBar;
        }
        if (mContentParent == null && shouldInstallDecor) {
            installDecor();
        }
        mHorizontalProgressBar = (IcsProgressBar) mDecor.findViewById(R.id.abs__progress_horizontal);
        if (mHorizontalProgressBar != null) {
            mHorizontalProgressBar.setVisibility(View.INVISIBLE);
        }
        return mHorizontalProgressBar;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Feature management and content interaction and creation
    ///////////////////////////////////////////////////////////////////////////

    private int getFeatures() {
        if (DEBUG)
            Log.d(TAG, "[getFeatures] returning " + mFeatures);

        return mFeatures;
    }

    @Override
    public boolean hasFeature(int featureId) {
        if (DEBUG)
            Log.d(TAG, "[hasFeature] featureId: " + featureId);

        boolean result = (mFeatures & (1 << featureId)) != 0;
        if (DEBUG)
            Log.d(TAG, "[hasFeature] returning " + result);
        return result;
    }

    @Override
    public boolean requestFeature(int featureId) {
        if (DEBUG)
            Log.d(TAG, "[requestFeature] featureId: " + featureId);

        if (mContentParent != null) {
            throw new AndroidRuntimeException("requestFeature() must be called before adding content");
        }

        switch (featureId) {
        case Window.FEATURE_ACTION_BAR:
        case Window.FEATURE_ACTION_BAR_OVERLAY:
        case Window.FEATURE_ACTION_MODE_OVERLAY:
        case Window.FEATURE_INDETERMINATE_PROGRESS:
        case Window.FEATURE_NO_TITLE:
        case Window.FEATURE_PROGRESS:
            mFeatures |= (1 << featureId);
            return true;

        default:
            return false;
        }
    }

    @Override
    public void setUiOptions(int uiOptions) {
        if (DEBUG)
            Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions);

        mUiOptions = uiOptions;
    }

    @Override
    public void setUiOptions(int uiOptions, int mask) {
        if (DEBUG)
            Log.d(TAG, "[setUiOptions] uiOptions: " + uiOptions + ", mask: " + mask);

        mUiOptions = (mUiOptions & ~mask) | (uiOptions & mask);
    }

    @Override
    public void setContentView(int layoutResId) {
        if (DEBUG)
            Log.d(TAG, "[setContentView] layoutResId: " + layoutResId);

        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mActivity.getLayoutInflater().inflate(layoutResId, mContentParent);

        android.view.Window.Callback callback = mActivity.getWindow().getCallback();
        if (callback != null) {
            callback.onContentChanged();
        }

        initActionBar();
    }

    @Override
    public void setContentView(View view, ViewGroup.LayoutParams params) {
        if (DEBUG)
            Log.d(TAG, "[setContentView] view: " + view + ", params: " + params);

        if (mContentParent == null) {
            installDecor();
        } else {
            mContentParent.removeAllViews();
        }
        mContentParent.addView(view, params);

        android.view.Window.Callback callback = mActivity.getWindow().getCallback();
        if (callback != null) {
            callback.onContentChanged();
        }

        initActionBar();
    }

    @Override
    public void addContentView(View view, ViewGroup.LayoutParams params) {
        if (DEBUG)
            Log.d(TAG, "[addContentView] view: " + view + ", params: " + params);

        if (mContentParent == null) {
            installDecor();
        }
        mContentParent.addView(view, params);

        initActionBar();
    }

    private void installDecor() {
        if (DEBUG)
            Log.d(TAG, "[installDecor]");

        if (mDecor == null) {
            mDecor = (ViewGroup) mActivity.getWindow().getDecorView().findViewById(android.R.id.content);
        }
        if (mContentParent == null) {
            //Since we are not operating at the window level we need to take
            //into account the fact that the true decor may have already been
            //initialized and had content attached to it. If that is the case,
            //copy over its children to our new content container.
            List<View> views = null;
            if (mDecor.getChildCount() > 0) {
                views = new ArrayList<View>(1); //Usually there's only one child
                for (int i = 0, children = mDecor.getChildCount(); i < children; i++) {
                    View child = mDecor.getChildAt(0);
                    mDecor.removeView(child);
                    views.add(child);
                }
            }

            mContentParent = generateLayout();

            //Copy over the old children. See above for explanation.
            if (views != null) {
                for (View child : views) {
                    mContentParent.addView(child);
                }
            }

            mTitleView = (TextView) mDecor.findViewById(android.R.id.title);
            if (mTitleView != null) {
                if (hasFeature(Window.FEATURE_NO_TITLE)) {
                    mTitleView.setVisibility(View.GONE);
                    if (mContentParent instanceof FrameLayout) {
                        ((FrameLayout) mContentParent).setForeground(null);
                    }
                } else {
                    mTitleView.setText(mTitle);
                }
            } else {
                wActionBar = (ActionBarView) mDecor.findViewById(R.id.abs__action_bar);
                if (wActionBar != null) {
                    wActionBar.setWindowCallback(this);
                    if (wActionBar.getTitle() == null) {
                        wActionBar.setWindowTitle(mActivity.getTitle());
                    }
                    if (hasFeature(Window.FEATURE_PROGRESS)) {
                        wActionBar.initProgress();
                    }
                    if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
                        wActionBar.initIndeterminateProgress();
                    }

                    //Since we don't require onCreate dispatching, parse for uiOptions here
                    int uiOptions = loadUiOptionsFromManifest(mActivity);
                    if (uiOptions != 0) {
                        mUiOptions = uiOptions;
                    }

                    boolean splitActionBar = false;
                    final boolean splitWhenNarrow = (mUiOptions
                            & ActivityInfo.UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW) != 0;
                    if (splitWhenNarrow) {
                        splitActionBar = getResources_getBoolean(mActivity, R.bool.abs__split_action_bar_is_narrow);
                    } else {
                        splitActionBar = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme)
                                .getBoolean(R.styleable.SherlockTheme_windowSplitActionBar, false);
                    }
                    final ActionBarContainer splitView = (ActionBarContainer) mDecor
                            .findViewById(R.id.abs__split_action_bar);
                    if (splitView != null) {
                        wActionBar.setSplitView(splitView);
                        wActionBar.setSplitActionBar(splitActionBar);
                        wActionBar.setSplitWhenNarrow(splitWhenNarrow);

                        mActionModeView = (ActionBarContextView) mDecor.findViewById(R.id.abs__action_context_bar);
                        mActionModeView.setSplitView(splitView);
                        mActionModeView.setSplitActionBar(splitActionBar);
                        mActionModeView.setSplitWhenNarrow(splitWhenNarrow);
                    } else if (splitActionBar) {
                        Log.e(TAG, "Requested split action bar with incompatible window decor! Ignoring request.");
                    }

                    // Post the panel invalidate for later; avoid application onCreateOptionsMenu
                    // being called in the middle of onCreate or similar.
                    mDecor.post(new Runnable() {
                        @Override
                        public void run() {
                            //Invalidate if the panel menu hasn't been created before this.
                            if (!mIsDestroyed && !mActivity.isFinishing() && mMenu == null) {
                                dispatchInvalidateOptionsMenu();
                            }
                        }
                    });
                }
            }
        }
    }

    private ViewGroup generateLayout() {
        if (DEBUG)
            Log.d(TAG, "[generateLayout]");

        // Apply data from current theme.

        TypedArray a = mActivity.getTheme().obtainStyledAttributes(R.styleable.SherlockTheme);

        mIsFloating = a.getBoolean(R.styleable.SherlockTheme_android_windowIsFloating, false);

        if (!a.hasValue(R.styleable.SherlockTheme_windowActionBar)) {
            throw new IllegalStateException(
                    "You must use Theme.Sherlock, Theme.Sherlock.Light, Theme.Sherlock.Light.DarkActionBar, or a derivative.");
        }

        if (a.getBoolean(R.styleable.SherlockTheme_windowNoTitle, false)) {
            requestFeature(Window.FEATURE_NO_TITLE);
        } else if (a.getBoolean(R.styleable.SherlockTheme_windowActionBar, false)) {
            // Don't allow an action bar if there is no title.
            requestFeature(Window.FEATURE_ACTION_BAR);
        }

        if (a.getBoolean(R.styleable.SherlockTheme_windowActionBarOverlay, false)) {
            requestFeature(Window.FEATURE_ACTION_BAR_OVERLAY);
        }

        if (a.getBoolean(R.styleable.SherlockTheme_windowActionModeOverlay, false)) {
            requestFeature(Window.FEATURE_ACTION_MODE_OVERLAY);
        }

        a.recycle();

        int layoutResource;
        if (!hasFeature(Window.FEATURE_NO_TITLE)) {
            if (mIsFloating) {
                //Trash original dialog LinearLayout
                mDecor = (ViewGroup) mDecor.getParent();
                mDecor.removeAllViews();

                layoutResource = R.layout.abs__dialog_title_holo;
            } else {
                if (hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY)) {
                    layoutResource = R.layout.abs__screen_action_bar_overlay;
                } else {
                    layoutResource = R.layout.abs__screen_action_bar;
                }
            }
        } else if (hasFeature(Window.FEATURE_ACTION_MODE_OVERLAY) && !hasFeature(Window.FEATURE_NO_TITLE)) {
            layoutResource = R.layout.abs__screen_simple_overlay_action_mode;
        } else {
            layoutResource = R.layout.abs__screen_simple;
        }

        if (DEBUG)
            Log.d(TAG, "[generateLayout] using screen XML " + mActivity.getResources().getString(layoutResource));
        View in = mActivity.getLayoutInflater().inflate(layoutResource, null);
        mDecor.addView(in, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));

        ViewGroup contentParent = (ViewGroup) mDecor.findViewById(R.id.abs__content);
        if (contentParent == null) {
            throw new RuntimeException("Couldn't find content container view");
        }

        //Make our new child the true content view (for fragments). VERY VOLATILE!
        mDecor.setId(View.NO_ID);
        contentParent.setId(android.R.id.content);

        if (hasFeature(Window.FEATURE_INDETERMINATE_PROGRESS)) {
            IcsProgressBar progress = getCircularProgressBar(false);
            if (progress != null) {
                progress.setIndeterminate(true);
            }
        }

        return contentParent;
    }

    ///////////////////////////////////////////////////////////////////////////
    // Miscellaneous
    ///////////////////////////////////////////////////////////////////////////

    /**
     * Determine whether or not the device has a dedicated menu key.
     *
     * @return {@code true} if native menu key is present.
     */
    private boolean isReservingOverflow() {
        if (!mReserveOverflowSet) {
            mReserveOverflow = ActionMenuPresenter.reserveOverflow(mActivity);
            mReserveOverflowSet = true;
        }
        return mReserveOverflow;
    }

    private static int loadUiOptionsFromManifest(Activity activity) {
        int uiOptions = 0;
        try {
            final String thisPackage = activity.getClass().getName();
            if (DEBUG)
                Log.i(TAG, "Parsing AndroidManifest.xml for " + thisPackage);

            final String packageName = activity.getApplicationInfo().packageName;
            final AssetManager am = activity.createPackageContext(packageName, 0).getAssets();
            final XmlResourceParser xml = am.openXmlResourceParser("AndroidManifest.xml");

            int eventType = xml.getEventType();
            while (eventType != XmlPullParser.END_DOCUMENT) {
                if (eventType == XmlPullParser.START_TAG) {
                    String name = xml.getName();

                    if ("application".equals(name)) {
                        //Check if the <application> has the attribute
                        if (DEBUG)
                            Log.d(TAG, "Got <application>");

                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
                            if (DEBUG)
                                Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));

                            if ("uiOptions".equals(xml.getAttributeName(i))) {
                                uiOptions = xml.getAttributeIntValue(i, 0);
                                break; //out of for loop
                            }
                        }
                    } else if ("activity".equals(name)) {
                        //Check if the <activity> is us and has the attribute
                        if (DEBUG)
                            Log.d(TAG, "Got <activity>");
                        Integer activityUiOptions = null;
                        String activityPackage = null;
                        boolean isOurActivity = false;

                        for (int i = xml.getAttributeCount() - 1; i >= 0; i--) {
                            if (DEBUG)
                                Log.d(TAG, xml.getAttributeName(i) + ": " + xml.getAttributeValue(i));

                            //We need both uiOptions and name attributes
                            String attrName = xml.getAttributeName(i);
                            if ("uiOptions".equals(attrName)) {
                                activityUiOptions = xml.getAttributeIntValue(i, 0);
                            } else if ("name".equals(attrName)) {
                                activityPackage = cleanActivityName(packageName, xml.getAttributeValue(i));
                                if (!thisPackage.equals(activityPackage)) {
                                    break; //out of for loop
                                }
                                isOurActivity = true;
                            }

                            //Make sure we have both attributes before processing
                            if ((activityUiOptions != null) && (activityPackage != null)) {
                                //Our activity, uiOptions specified, override with our value
                                uiOptions = activityUiOptions.intValue();
                            }
                        }
                        if (isOurActivity) {
                            //If we matched our activity but it had no logo don't
                            //do any more processing of the manifest
                            break;
                        }
                    }
                }
                eventType = xml.nextToken();
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
        if (DEBUG)
            Log.i(TAG, "Returning " + Integer.toHexString(uiOptions));
        return uiOptions;
    }

    public static String cleanActivityName(String manifestPackage, String activityName) {
        if (activityName.charAt(0) == '.') {
            //Relative activity name (e.g., android:name=".ui.SomeClass")
            return manifestPackage + activityName;
        }
        if (activityName.indexOf('.', 1) == -1) {
            //Unqualified activity name (e.g., android:name="SomeClass")
            return manifestPackage + "." + activityName;
        }
        //Fully-qualified activity name (e.g., "com.my.package.SomeClass")
        return activityName;
    }

    /**
     * Clears out internal reference when the action mode is destroyed.
     */
    private class ActionModeCallbackWrapper implements ActionMode.Callback {
        private final ActionMode.Callback mWrapped;

        public ActionModeCallbackWrapper(ActionMode.Callback wrapped) {
            mWrapped = wrapped;
        }

        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            return mWrapped.onCreateActionMode(mode, menu);
        }

        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return mWrapped.onPrepareActionMode(mode, menu);
        }

        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
            return mWrapped.onActionItemClicked(mode, item);
        }

        public void onDestroyActionMode(ActionMode mode) {
            mWrapped.onDestroyActionMode(mode);
            if (mActionModeView != null) {
                mActionModeView.setVisibility(View.GONE);
                mActionModeView.removeAllViews();
            }
            if (mActivity instanceof OnActionModeFinishedListener) {
                ((OnActionModeFinishedListener) mActivity).onActionModeFinished(mActionMode);
            }
            mActionMode = null;
        }
    }
}