blob: 101aea43ef1034f88ee901ab6a6bbb503d50d609 [file] [log] [blame]
/*
* Copyright (C) 2013 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package android.support.v7.app;
import android.content.Context;
import android.content.res.Configuration;
import android.content.res.TypedArray;
import android.graphics.drawable.Drawable;
import android.support.v4.app.ActionBarDrawerToggle;
import android.support.v4.view.WindowCompat;
import android.support.v7.appcompat.R;
import android.support.v7.internal.view.menu.ListMenuPresenter;
import android.support.v7.internal.view.menu.MenuBuilder;
import android.support.v7.internal.view.menu.MenuPresenter;
import android.support.v7.internal.view.menu.MenuView;
import android.support.v7.internal.view.menu.MenuWrapperFactory;
import android.support.v7.internal.widget.ActionBarContainer;
import android.support.v7.internal.widget.ActionBarContextView;
import android.support.v7.internal.widget.ActionBarView;
import android.support.v7.internal.widget.ProgressBarICS;
import android.support.v7.view.ActionMode;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.FrameLayout;
class ActionBarActivityDelegateBase extends ActionBarActivityDelegate implements
MenuPresenter.Callback, MenuBuilder.Callback {
private static final String TAG = "ActionBarActivityDelegateBase";
private static final int[] ACTION_BAR_DRAWABLE_TOGGLE_ATTRS = new int[] {
R.attr.homeAsUpIndicator
};
private ActionBarView mActionBarView;
private ListMenuPresenter mListMenuPresenter;
private MenuBuilder mMenu;
private ActionMode mActionMode;
// true if we have installed a window sub-decor layout.
private boolean mSubDecorInstalled;
// Used to keep track of Progress Bar Window features
private boolean mFeatureProgress, mFeatureIndeterminateProgress;
private boolean mInvalidateMenuPosted;
private final Runnable mInvalidateMenuRunnable = new Runnable() {
@Override
public void run() {
final MenuBuilder menu = createMenu();
if (mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu) &&
mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu)) {
setMenu(menu);
} else {
setMenu(null);
}
mInvalidateMenuPosted = false;
}
};
ActionBarActivityDelegateBase(ActionBarActivity activity) {
super(activity);
}
@Override
public ActionBar createSupportActionBar() {
ensureSubDecor();
return new ActionBarImplBase(mActivity, mActivity);
}
@Override
public void onConfigurationChanged(Configuration newConfig) {
// If this is called before sub-decor is installed, ActionBar will not
// be properly initialized.
if (mHasActionBar && mSubDecorInstalled) {
// Note: The action bar will need to access
// view changes from superclass.
ActionBarImplBase actionBar = (ActionBarImplBase) getSupportActionBar();
actionBar.onConfigurationChanged(newConfig);
}
}
@Override
public void onStop() {
ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
if (ab != null) {
ab.setShowHideAnimationEnabled(false);
}
}
@Override
public void onPostResume() {
ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
if (ab != null) {
ab.setShowHideAnimationEnabled(true);
}
}
@Override
public void setContentView(View v) {
ensureSubDecor();
if (mHasActionBar) {
final ViewGroup contentParent =
(ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
contentParent.removeAllViews();
contentParent.addView(v);
} else {
mActivity.superSetContentView(v);
}
}
@Override
public void setContentView(int resId) {
ensureSubDecor();
if (mHasActionBar) {
final ViewGroup contentParent =
(ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
contentParent.removeAllViews();
final LayoutInflater inflater = mActivity.getLayoutInflater();
inflater.inflate(resId, contentParent);
} else {
mActivity.superSetContentView(resId);
}
}
@Override
public void setContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
if (mHasActionBar) {
final ViewGroup contentParent =
(ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
contentParent.removeAllViews();
contentParent.addView(v, lp);
} else {
mActivity.superSetContentView(v, lp);
}
}
@Override
public void addContentView(View v, ViewGroup.LayoutParams lp) {
ensureSubDecor();
if (mHasActionBar) {
final ViewGroup contentParent =
(ViewGroup) mActivity.findViewById(R.id.action_bar_activity_content);
contentParent.addView(v, lp);
} else {
mActivity.superSetContentView(v, lp);
}
}
final void ensureSubDecor() {
if (mHasActionBar && !mSubDecorInstalled) {
if (mOverlayActionBar) {
mActivity.superSetContentView(R.layout.abc_action_bar_decor_overlay);
} else {
mActivity.superSetContentView(R.layout.abc_action_bar_decor);
}
mActionBarView = (ActionBarView) mActivity.findViewById(R.id.action_bar);
mActionBarView.setWindowCallback(mActivity);
/**
* Progress Bars
*/
if (mFeatureProgress) {
mActionBarView.initProgress();
}
if (mFeatureIndeterminateProgress) {
mActionBarView.initIndeterminateProgress();
}
/**
* Split Action Bar
*/
boolean splitWhenNarrow = UIOPTION_SPLIT_ACTION_BAR_WHEN_NARROW
.equals(getUiOptionsFromMetadata());
boolean splitActionBar;
if (splitWhenNarrow) {
splitActionBar = mActivity.getResources()
.getBoolean(R.bool.abc_split_action_bar_is_narrow);
} else {
TypedArray a = mActivity.obtainStyledAttributes(R.styleable.ActionBarWindow);
splitActionBar = a
.getBoolean(R.styleable.ActionBarWindow_windowSplitActionBar, false);
a.recycle();
}
final ActionBarContainer splitView = (ActionBarContainer) mActivity.findViewById(
R.id.split_action_bar);
if (splitView != null) {
mActionBarView.setSplitView(splitView);
mActionBarView.setSplitActionBar(splitActionBar);
mActionBarView.setSplitWhenNarrow(splitWhenNarrow);
final ActionBarContextView cab = (ActionBarContextView) mActivity.findViewById(
R.id.action_context_bar);
cab.setSplitView(splitView);
cab.setSplitActionBar(splitActionBar);
cab.setSplitWhenNarrow(splitWhenNarrow);
}
mSubDecorInstalled = true;
supportInvalidateOptionsMenu();
}
}
@Override
public boolean supportRequestWindowFeature(int featureId) {
switch (featureId) {
case WindowCompat.FEATURE_ACTION_BAR:
mHasActionBar = true;
return true;
case WindowCompat.FEATURE_ACTION_BAR_OVERLAY:
mOverlayActionBar = true;
return true;
case Window.FEATURE_PROGRESS:
mFeatureProgress = true;
return true;
case Window.FEATURE_INDETERMINATE_PROGRESS:
mFeatureIndeterminateProgress = true;
return true;
default:
return mActivity.requestWindowFeature(featureId);
}
}
@Override
public void setTitle(CharSequence title) {
ActionBar ab = getSupportActionBar();
if (ab != null) {
ab.setTitle(title);
}
}
@Override
public View onCreatePanelView(int featureId) {
View createdPanelView = null;
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
boolean show = true;
MenuBuilder menu = mMenu;
if (mActionMode == null) {
// We only want to dispatch Activity/Fragment menu calls if there isn't
// currently an action mode
if (menu == null) {
// We don't have a menu created, so create one
menu = createMenu();
setMenu(menu);
// Make sure we're not dispatching item changes to presenters
menu.stopDispatchingItemsChanged();
// Dispatch onCreateSupportOptionsMenu
show = mActivity.superOnCreatePanelMenu(Window.FEATURE_OPTIONS_PANEL, menu);
}
if (show) {
// Make sure we're not dispatching item changes to presenters
menu.stopDispatchingItemsChanged();
// Dispatch onPrepareSupportOptionsMenu
show = mActivity.superOnPreparePanel(Window.FEATURE_OPTIONS_PANEL, null, menu);
}
}
if (show) {
createdPanelView = (View) getListMenuView(mActivity, this);
// Allow menu to start dispatching changes to presenters
menu.startDispatchingItemsChanged();
} else {
// If the menu isn't being shown, we no longer need it
setMenu(null);
}
}
return createdPanelView;
}
@Override
public boolean onCreatePanelMenu(int featureId, Menu menu) {
if (featureId != Window.FEATURE_OPTIONS_PANEL) {
return mActivity.superOnCreatePanelMenu(featureId, menu);
}
return false;
}
@Override
public boolean onPreparePanel(int featureId, View view, Menu menu) {
if (featureId != Window.FEATURE_OPTIONS_PANEL) {
return mActivity.superOnPreparePanel(featureId, view, menu);
}
return false;
}
@Override
public boolean onMenuItemSelected(int featureId, MenuItem item) {
if (featureId == Window.FEATURE_OPTIONS_PANEL) {
item = MenuWrapperFactory.createMenuItemWrapper(item);
}
return mActivity.superOnMenuItemSelected(featureId, item);
}
@Override
public boolean onMenuItemSelected(MenuBuilder menu, MenuItem item) {
return mActivity.onMenuItemSelected(Window.FEATURE_OPTIONS_PANEL, item);
}
@Override
public void onMenuModeChange(MenuBuilder menu) {
reopenMenu(menu, true);
}
@Override
public void onCloseMenu(MenuBuilder menu, boolean allMenusAreClosing) {
mActivity.closeOptionsMenu();
}
@Override
public boolean onOpenSubMenu(MenuBuilder subMenu) {
return false;
}
@Override
public ActionMode startSupportActionMode(ActionMode.Callback callback) {
if (callback == null) {
throw new IllegalArgumentException("ActionMode callback can not be null.");
}
if (mActionMode != null) {
mActionMode.finish();
}
final ActionMode.Callback wrappedCallback = new ActionModeCallbackWrapper(callback);
ActionBarImplBase ab = (ActionBarImplBase) getSupportActionBar();
if (ab != null) {
mActionMode = ab.startActionMode(wrappedCallback);
}
if (mActionMode != null) {
mActivity.onSupportActionModeStarted(mActionMode);
}
return mActionMode;
}
@Override
public void supportInvalidateOptionsMenu() {
if (!mInvalidateMenuPosted) {
mInvalidateMenuPosted = true;
mActivity.getWindow().getDecorView().post(mInvalidateMenuRunnable);
}
}
private MenuBuilder createMenu() {
MenuBuilder menu = new MenuBuilder(getActionBarThemedContext());
menu.setCallback(this);
return menu;
}
private void reopenMenu(MenuBuilder menu, boolean toggleMenuMode) {
if (mActionBarView != null && mActionBarView.isOverflowReserved()) {
if (!mActionBarView.isOverflowMenuShowing() || !toggleMenuMode) {
if (mActionBarView.getVisibility() == View.VISIBLE) {
mActionBarView.showOverflowMenu();
}
} else {
mActionBarView.hideOverflowMenu();
}
return;
}
menu.close();
}
private MenuView getListMenuView(Context context, MenuPresenter.Callback cb) {
if (mMenu == null) {
return null;
}
if (mListMenuPresenter == null) {
TypedArray a = context.obtainStyledAttributes(R.styleable.Theme);
final int listPresenterTheme = a.getResourceId(
R.styleable.Theme_panelMenuListTheme,
R.style.Theme_AppCompat_CompactMenu);
a.recycle();
mListMenuPresenter = new ListMenuPresenter(
R.layout.abc_list_menu_item_layout, listPresenterTheme);
mListMenuPresenter.setCallback(cb);
mMenu.addMenuPresenter(mListMenuPresenter);
} else {
// Make sure we update the ListView
mListMenuPresenter.updateMenuView(false);
}
return mListMenuPresenter.getMenuView(new FrameLayout(context));
}
private void setMenu(MenuBuilder menu) {
if (menu == mMenu) {
return;
}
if (mMenu != null) {
mMenu.removeMenuPresenter(mListMenuPresenter);
}
mMenu = menu;
if (menu != null && mListMenuPresenter != null) {
// Only update list menu if there isn't an action mode menu
menu.addMenuPresenter(mListMenuPresenter);
}
if (mActionBarView != null) {
mActionBarView.setMenu(menu, this);
}
}
@Override
public boolean onBackPressed() {
// Back cancels action modes first.
if (mActionMode != null) {
mActionMode.finish();
return true;
}
// Next collapse any expanded action views.
if (mActionBarView != null && mActionBarView.hasExpandedActionView()) {
mActionBarView.collapseActionView();
return true;
}
return false;
}
@Override
void setSupportProgressBarVisibility(boolean visible) {
updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
Window.PROGRESS_VISIBILITY_OFF);
}
@Override
void setSupportProgressBarIndeterminateVisibility(boolean visible) {
updateProgressBars(visible ? Window.PROGRESS_VISIBILITY_ON :
Window.PROGRESS_VISIBILITY_OFF);
}
@Override
void setSupportProgressBarIndeterminate(boolean indeterminate) {
updateProgressBars(indeterminate ? Window.PROGRESS_INDETERMINATE_ON
: Window.PROGRESS_INDETERMINATE_OFF);
}
@Override
void setSupportProgress(int progress) {
updateProgressBars(Window.PROGRESS_START + progress);
}
@Override
ActionBarDrawerToggle.Delegate getDrawerToggleDelegate() {
return new ActionBarDrawableToggleImpl();
}
/**
* Progress Bar function. Mostly extracted from PhoneWindow.java
*/
private void updateProgressBars(int value) {
ProgressBarICS circularProgressBar = getCircularProgressBar();
ProgressBarICS horizontalProgressBar = getHorizontalProgressBar();
if (value == Window.PROGRESS_VISIBILITY_ON) {
if (mFeatureProgress) {
int level = horizontalProgressBar.getProgress();
int visibility = (horizontalProgressBar.isIndeterminate() || level < 10000) ?
View.VISIBLE : View.INVISIBLE;
horizontalProgressBar.setVisibility(visibility);
}
if (mFeatureIndeterminateProgress) {
circularProgressBar.setVisibility(View.VISIBLE);
}
} else if (value == Window.PROGRESS_VISIBILITY_OFF) {
if (mFeatureProgress) {
horizontalProgressBar.setVisibility(View.GONE);
}
if (mFeatureIndeterminateProgress) {
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);
}
}
}
private void showProgressBars(ProgressBarICS horizontalProgressBar,
ProgressBarICS spinnyProgressBar) {
if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.INVISIBLE) {
spinnyProgressBar.setVisibility(View.VISIBLE);
}
// Only show the progress bars if the primary progress is not complete
if (mFeatureProgress && horizontalProgressBar.getProgress() < 10000) {
horizontalProgressBar.setVisibility(View.VISIBLE);
}
}
private void hideProgressBars(ProgressBarICS horizontalProgressBar,
ProgressBarICS spinnyProgressBar) {
if (mFeatureIndeterminateProgress && spinnyProgressBar.getVisibility() == View.VISIBLE) {
spinnyProgressBar.setVisibility(View.INVISIBLE);
}
if (mFeatureProgress && horizontalProgressBar.getVisibility() == View.VISIBLE) {
horizontalProgressBar.setVisibility(View.INVISIBLE);
}
}
private ProgressBarICS getCircularProgressBar() {
ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_circular);
if (pb != null) {
pb.setVisibility(View.INVISIBLE);
}
return pb;
}
private ProgressBarICS getHorizontalProgressBar() {
ProgressBarICS pb = (ProgressBarICS) mActionBarView.findViewById(R.id.progress_horizontal);
if (pb != null) {
pb.setVisibility(View.INVISIBLE);
}
return pb;
}
/**
* Clears out internal reference when the action mode is destroyed.
*/
private class ActionModeCallbackWrapper implements ActionMode.Callback {
private 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);
mActivity.onSupportActionModeFinished(mode);
mActionMode = null;
}
}
private class ActionBarDrawableToggleImpl
implements ActionBarDrawerToggle.Delegate {
@Override
public Drawable getThemeUpIndicator() {
final TypedArray a = mActivity.obtainStyledAttributes(ACTION_BAR_DRAWABLE_TOGGLE_ATTRS);
final Drawable result = a.getDrawable(0);
a.recycle();
return result;
}
@Override
public void setActionBarUpIndicator(Drawable upDrawable, int contentDescRes) {
if (mActionBarView != null) {
mActionBarView.setHomeAsUpIndicator(upDrawable);
}
}
@Override
public void setActionBarDescription(int contentDescRes) {
// No support for setting Action Bar content description
}
}
}