/* * Copyright (C) 2011 Google Inc. * * 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 com.example.google.tv.leftnavbar; import android.app.ActionBar; import android.app.Activity; import android.app.Dialog; import android.content.Context; import android.graphics.drawable.Drawable; import android.view.ActionMode; import android.view.LayoutInflater; import android.view.View; import android.view.ViewGroup; import android.view.ViewGroup.MarginLayoutParams; import android.view.Window; import android.widget.SpinnerAdapter; /** * TV-specific implementation of the ActionBar API, using the title bar and a * left navigation panel. */ public class LeftNavBar extends ActionBar { /** * Always show an expanded version of the Left Navigation bar. * * @see #setDisplayOptions(int) * @see #setDisplayOptions(int, int) * @hide */ public static final int DISPLAY_ALWAYS_EXPANDED = 0x20; /** * Use the app logo when the Left Navigation bar is expanded. * * @see #setDisplayOptions(int) * @see #setDisplayOptions(int, int) * @hide */ public static final int DISPLAY_USE_LOGO_WHEN_EXPANDED = 0x80; /** * Show an indeterminate progress indicator. * * @see #setDisplayOptions(int) * @see #setDisplayOptions(int, int) * @hide */ public static final int DISPLAY_SHOW_INDETERMINATE_PROGRESS = 0x100; /** * Display option for the Left Navigation bar to automatically expand when * certain ev ents happen. This includes gaining focus but could also * include other events, such as mouse hover. * * @see #setDisplayOptions(int) * @see #setDisplayOptions(int, int) */ public static final int DISPLAY_AUTO_EXPAND = 0x40; /** * Display options applied by default. */ public static final int DEFAULT_DISPLAY_OPTIONS = ActionBar.DISPLAY_SHOW_HOME | ActionBar.DISPLAY_USE_LOGO | ActionBar.DISPLAY_SHOW_TITLE | LeftNavBar.DISPLAY_ALWAYS_EXPANDED; private Context mContext; private boolean mIsOverlay; private TitleBarView mTitleBar; private LeftNavView mLeftNav; private View mContent; public LeftNavBar(Activity activity) { initialize(activity.getWindow(), activity); } public LeftNavBar(Dialog dialog) { initialize(dialog.getWindow(), dialog.getContext()); } private void initialize(Window window, Context context) { View decor = window.getDecorView(); ViewGroup group = (ViewGroup) window.getDecorView(); LayoutInflater inflater = (LayoutInflater) decor.getContext().getSystemService( Context.LAYOUT_INFLATER_SERVICE); inflater.inflate(R.layout.lib_title_container,group,true); inflater.inflate(R.layout.lib_left_nav,group, true); mContext = decor.getContext(); mIsOverlay = window.hasFeature(Window.FEATURE_ACTION_BAR_OVERLAY); mTitleBar = (TitleBarView) decor.findViewById(R.id.title_container); mLeftNav = (LeftNavView) decor.findViewById(R.id.left_nav); mContent = group.getChildAt(0); if (mTitleBar == null || mLeftNav == null) { throw new IllegalStateException( getClass().getSimpleName() + ": incompatible window decor!"); } setDisplayOptions(DEFAULT_DISPLAY_OPTIONS); showOptionsMenu(true); } private void updateWindowLayout(boolean animated) { updateTitleBar(animated); setLeftMargin(mTitleBar, mLeftNav.getApparentWidth(true) ); if (!mIsOverlay) { setLeftMargin(mContent, mLeftNav.getApparentWidth(false)); setTopMargin(mContent, mTitleBar.getApparentHeight()); } } private void updateTitleBar(boolean animated) { int options = getDisplayOptions(); boolean titleVisible = has(options, DISPLAY_SHOW_TITLE); boolean progressVisible = has(options, DISPLAY_SHOW_INDETERMINATE_PROGRESS); boolean horizontalProgressVisible = mTitleBar.isHorizontalProgressVisible(); mTitleBar.setVisible( isShowing() && (titleVisible || progressVisible || horizontalProgressVisible), animated); mTitleBar.setProgressVisible(progressVisible); } private void setLeftMargin(View view, int margin) { MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); params.leftMargin = margin; view.setLayoutParams(params); } private void setTopMargin(View view, int margin) { MarginLayoutParams params = (MarginLayoutParams) view.getLayoutParams(); params.topMargin = margin; view.setLayoutParams(params); } // ---------------------------------------------------------------------------------------------- // Visibility. @Override public void show() { setVisible(true); } @Override public void hide() { setVisible(false); } private void setVisible(boolean visible) { boolean shouldAnimate = mIsOverlay; if (mLeftNav.setVisible(visible, shouldAnimate)) { updateWindowLayout(shouldAnimate); } } @Override public boolean isShowing() { return mLeftNav.isVisible(); } // ---------------------------------------------------------------------------------------------- // Title / subtitle. @Override public void setTitle(CharSequence title) { mTitleBar.setTitle(title); } @Override public void setTitle(int resId) { setTitle(mContext.getString(resId)); } @Override public CharSequence getTitle() { return mTitleBar.getTitle(); } @Override public void setSubtitle(CharSequence subtitle) { mTitleBar.setSubtitle(subtitle); } @Override public void setSubtitle(int resId) { setSubtitle(mContext.getString(resId)); } @Override public CharSequence getSubtitle() { return mTitleBar.getSubtitle(); } // ---------------------------------------------------------------------------------------------- // Tabs. @Override public Tab newTab() { return new TabImpl(mContext) { @Override public void select() { selectTab(this); } }; } /** * Ensures the given tab is a valid object. */ private TabImpl convertTab(Tab tab) { if (tab == null) { return null; } if (!(tab instanceof TabImpl)) { throw new IllegalArgumentException("Invalid tab object."); } return (TabImpl) tab; } @Override public void addTab(Tab tab) { addTab(tab, TabDisplay.LAST_POSITION); } @Override public void addTab(Tab tab, boolean setSelected) { addTab(tab, TabDisplay.LAST_POSITION, setSelected); } @Override public void addTab(Tab tab, int position) { addTab(tab, position, getTabCount() == 0); } @Override public void addTab(Tab tab, int position, boolean setSelected) { mLeftNav.getTabs().add(convertTab(tab), position, setSelected); } @Override public Tab getSelectedTab() { return mLeftNav.getTabs().getSelected(); } @Override public Tab getTabAt(int index) { return mLeftNav.getTabs().get(index); } @Override public int getTabCount() { return mLeftNav.getTabs().getCount(); } @Override public void removeAllTabs() { mLeftNav.getTabs().removeAll(); } @Override public void removeTab(Tab tab) { mLeftNav.getTabs().remove(convertTab(tab)); } @Override public void removeTabAt(int position) { mLeftNav.getTabs().remove(position); } @Override public void selectTab(Tab tab) { mLeftNav.getTabs().select(convertTab(tab)); } // ---------------------------------------------------------------------------------------------- // Navigation modes. @Override public int getNavigationItemCount() { switch (getNavigationMode()) { case NAVIGATION_MODE_TABS: return getTabCount(); case NAVIGATION_MODE_LIST: return mLeftNav.getSpinner().getCount(); default: throw new IllegalStateException( "No count available for mode: " + getNavigationMode()); } } @Override public int getNavigationMode() { return mLeftNav.getNavigationMode(); } @Override public int getSelectedNavigationIndex() { switch (getNavigationMode()) { case NAVIGATION_MODE_TABS: Tab selected = getSelectedTab(); return selected != null ? selected.getPosition() : -1; case NAVIGATION_MODE_LIST: return mLeftNav.getSpinner().getSelected(); default: throw new IllegalStateException( "No selection available for mode: " + getNavigationMode()); } } @Override public void setListNavigationCallbacks(SpinnerAdapter adapter, OnNavigationListener callback) { mLeftNav.getSpinner().setContent(adapter, callback); } @Override public void setNavigationMode(int mode) { mLeftNav.setNavigationMode(mode); } @Override public void setSelectedNavigationItem(int position) { switch (getNavigationMode()) { case NAVIGATION_MODE_TABS: selectTab(getTabAt(position)); break; case NAVIGATION_MODE_LIST: mLeftNav.getSpinner().setSelected(position); break; default: throw new IllegalStateException( "Cannot set selection on mode: " + getNavigationMode()); } } // ---------------------------------------------------------------------------------------------- // Display options. @Override public int getDisplayOptions() { return mLeftNav.getDisplayOptions(); } private static boolean has(int changes, int option) { return (changes & option) != 0; } @Override public void setDisplayOptions(int options) { int changes = mLeftNav.setDisplayOptions(options); if (has(changes, DISPLAY_ALWAYS_EXPANDED) || has(changes, DISPLAY_AUTO_EXPAND) || has(changes, DISPLAY_SHOW_TITLE) || has(changes, DISPLAY_SHOW_INDETERMINATE_PROGRESS)) { updateWindowLayout(false); } } @Override public void setDisplayOptions(int options, int mask) { int current = getDisplayOptions(); int updated = ((options & mask) | (current & ~mask)); setDisplayOptions(updated); } @Override public void setDisplayHomeAsUpEnabled(boolean showHomeAsUp) { setDisplayOptions(showHomeAsUp ? DISPLAY_HOME_AS_UP : 0, DISPLAY_HOME_AS_UP); } @Override public void setDisplayShowCustomEnabled(boolean showCustom) { setDisplayOptions(showCustom ? DISPLAY_SHOW_CUSTOM : 0, DISPLAY_SHOW_CUSTOM); } @Override public void setDisplayShowHomeEnabled(boolean showHome) { setDisplayOptions(showHome ? DISPLAY_SHOW_HOME : 0, DISPLAY_SHOW_HOME); } @Override public void setDisplayShowTitleEnabled(boolean showTitle) { setDisplayOptions(showTitle ? DISPLAY_SHOW_TITLE : 0, DISPLAY_SHOW_TITLE); } @Override public void setDisplayUseLogoEnabled(boolean useLogo) { setDisplayOptions(useLogo ? DISPLAY_USE_LOGO : 0, DISPLAY_USE_LOGO); } /** * Sets the horizontal progress indicator to the given value. */ public void setShowHorizontalProgress(int value) { mTitleBar.setHorizontalProgress(value); updateWindowLayout(false); } // ---------------------------------------------------------------------------------------------- // Custom view. @Override public View getCustomView() { return mLeftNav.getCustomView(); } @Override public void setCustomView(View view) { mLeftNav.setCustomView(view); } @Override public void setCustomView(View view, LayoutParams layoutParams) { view.setLayoutParams(layoutParams); setCustomView(view); } @Override public void setCustomView(int resId) { setCustomView(LayoutInflater.from(mContext).inflate(resId, mLeftNav, false)); } // ---------------------------------------------------------------------------------------------- // Miscellaneous. @Override public void addOnMenuVisibilityListener(OnMenuVisibilityListener listener) { // No use for that, apps should use the regular options menu API. } @Override public void removeOnMenuVisibilityListener(OnMenuVisibilityListener listener) { // No use for that, apps should use the regular options menu API. } @Override public void setBackgroundDrawable(Drawable d) { mLeftNav.setBackgroundDrawable(d); } @Override public int getHeight() { // The height is completely irrelevant in our case. // Returning the left nav's width as this is what apps may want to // adjust to. return mLeftNav.getApparentWidth(true /* * ignore the fact that it may be * hidden */); } // ---------------------------------------------------------------------------------------------- // Copies of ActionBarImpl methods. // This is to ensure this implementation can be easily swapped in. public void setShowHideAnimationEnabled(boolean enabled) { mLeftNav.setAnimationsEnabled(enabled); mTitleBar.setAnimationsEnabled(enabled); } public void dispatchMenuVisibilityChanged(boolean visible) { } public ActionMode startActionMode(ActionMode.Callback callback) { return null; } // Extra methods public void showOptionsMenu(boolean show) { mLeftNav.showOptionsMenu(show); } public void setOnClickHomeListener(View.OnClickListener listener) { mLeftNav.setOnClickHomeListener(listener); } }