| /* |
| * 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 |
| } |
| } |
| |
| } |