content: /*
   * Copyright (C) 2010 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.app;
  
  import android.animation.LayoutTransition;
  import android.app.FragmentManager.BackStackEntry;
  import android.content.Context;
  import android.content.res.TypedArray;
  import android.util.AttributeSet;
  import android.view.Gravity;
  import android.view.LayoutInflater;
  import android.view.View;
  import android.view.ViewGroup;
  import android.widget.LinearLayout;
  import android.widget.TextView;
  
  /**
   * Helper class for showing "bread crumbs" representing the fragment
   * stack in an activity.  This is intended to be used with
   * {@link ActionBar#setCustomView(View)
   * ActionBar.setCustomView(View)} to place the bread crumbs in
   * the action bar.
   *
   * <p>The default style for this view is
   * {@link android.R.style#Widget_FragmentBreadCrumbs}.
   *
   * @deprecated This widget is no longer supported.
   */
  @Deprecated
  public class FragmentBreadCrumbs extends ViewGroup
          implements FragmentManager.OnBackStackChangedListener {
      Activity mActivity;
      LayoutInflater mInflater;
      LinearLayout mContainer;
      int mMaxVisible = -1;
  
      // Hahah
      BackStackRecord mTopEntry;
      BackStackRecord mParentEntry;
  
      /** Listener to inform when a parent entry is clicked */
      private OnClickListener mParentClickListener;
  
      private OnBreadCrumbClickListener mOnBreadCrumbClickListener;
  
      private int mGravity;
      private int mLayoutResId;
      private int mTextColor;
  
      private static final int DEFAULT_GRAVITY = Gravity.START | Gravity.CENTER_VERTICAL;
  
      /**
       * Interface to intercept clicks on the bread crumbs.
       *
       * @deprecated This widget is no longer supported.
       */
      @Deprecated
      public interface OnBreadCrumbClickListener {
          /**
           * Called when a bread crumb is clicked.
           * 
           * @param backStack The BackStackEntry whose bread crumb was clicked.
           * May be null, if this bread crumb is for the root of the back stack.
           * @param flags Additional information about the entry.  Currently
           * always 0.
           * 
           * @return Return true to consume this click.  Return to false to allow
           * the default action (popping back stack to this entry) to occur.
           */
          public boolean onBreadCrumbClick(BackStackEntry backStack, int flags);
      }
      
      public FragmentBreadCrumbs(Context context) {
          this(context, null);
      }
  
      public FragmentBreadCrumbs(Context context, AttributeSet attrs) {
          this(context, attrs, com.android.internal.R.attr.fragmentBreadCrumbsStyle);
      }
  
      public FragmentBreadCrumbs(Context context, AttributeSet attrs, int defStyleAttr) {
          this(context, attrs, defStyleAttr, 0);
      }
  
      /**
       * @hide
       */
      public FragmentBreadCrumbs(
              Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
          super(context, attrs, defStyleAttr, defStyleRes);
  
          final TypedArray a = context.obtainStyledAttributes(attrs,
                  com.android.internal.R.styleable.FragmentBreadCrumbs, defStyleAttr, defStyleRes);
  
          mGravity = a.getInt(com.android.internal.R.styleable.FragmentBreadCrumbs_gravity,
                  DEFAULT_GRAVITY);
          mLayoutResId = a.getResourceId(
                  com.android.internal.R.styleable.FragmentBreadCrumbs_itemLayout,
                  com.android.internal.R.layout.fragment_bread_crumb_item);
          mTextColor = a.getColor(
                  com.android.internal.R.styleable.FragmentBreadCrumbs_itemColor,
                  0);
  
          a.recycle();
      }
  
      /**
       * Attach the bread crumbs to their activity.  This must be called once
       * when creating the bread crumbs.
       */
      public void setActivity(Activity a) {
          mActivity = a;
          mInflater = (LayoutInflater)a.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
          mContainer = (LinearLayout)mInflater.inflate(
                  com.android.internal.R.layout.fragment_bread_crumbs,
                  this, false);
          addView(mContainer);
          a.getFragmentManager().addOnBackStackChangedListener(this);
          updateCrumbs();
          setLayoutTransition(new LayoutTransition());
      }
  
      /**
       * The maximum number of breadcrumbs to show. Older fragment headers will be hidden from view.
       * @param visibleCrumbs the number of visible breadcrumbs. This should be greater than zero.
       */
      public void setMaxVisible(int visibleCrumbs) {
          if (visibleCrumbs < 1) {
              throw new IllegalArgumentException("visibleCrumbs must be greater than zero");
          }
          mMaxVisible = visibleCrumbs;
      }
  
      /**
       * Inserts an optional parent entry at the first position in the breadcrumbs. Selecting this
       * entry will result in a call to the specified listener's 
       * {@link android.view.View.OnClickListener#onClick(View)}
       * method.
       *
       * @param title the title for the parent entry
       * @param shortTitle the short title for the parent entry
       * @param listener the {@link android.view.View.OnClickListener} to be called when clicked.
       * A null will result in no action being taken when the parent entry is clicked.
       */
      public void setParentTitle(CharSequence title, CharSequence shortTitle,
              OnClickListener listener) {
          mParentEntry = createBackStackEntry(title, shortTitle);
          mParentClickListener = listener;
          updateCrumbs();
      }
  
      /**
       * Sets a listener for clicks on the bread crumbs.  This will be called before
       * the default click action is performed.
       * 
       * @param listener The new listener to set.  Replaces any existing listener.
       */
      public void setOnBreadCrumbClickListener(OnBreadCrumbClickListener listener) {
          mOnBreadCrumbClickListener = listener;
      }
      
      private BackStackRecord createBackStackEntry(CharSequence title, CharSequence shortTitle) {
          if (title == null) return null;
  
          final BackStackRecord entry = new BackStackRecord(
                  (FragmentManagerImpl) mActivity.getFragmentManager());
          entry.setBreadCrumbTitle(title);
          entry.setBreadCrumbShortTitle(shortTitle);
          return entry;
      }
  
      /**
       * Set a custom title for the bread crumbs.  This will be the first entry
       * shown at the left, representing the root of the bread crumbs.  If the
       * title is null, it will not be shown.
       */
      public void setTitle(CharSequence title, CharSequence shortTitle) {
          mTopEntry = createBackStackEntry(title, shortTitle);
          updateCrumbs();
      }
  
      @Override
      protected void onLayout(boolean changed, int l, int t, int r, int b) {
          // Eventually we should implement our own layout of the views, rather than relying on
          // a single linear layout.
          final int childCount = getChildCount();
          if (childCount == 0) {
              return;
          }
  
          final View child = getChildAt(0);
  
          final int childTop = mPaddingTop;
          final int childBottom = mPaddingTop + child.getMeasuredHeight() - mPaddingBottom;
  
          int childLeft;
          int childRight;
  
          final int layoutDirection = getLayoutDirection();
          final int horizontalGravity = mGravity & Gravity.RELATIVE_HORIZONTAL_GRAVITY_MASK;
          switch (Gravity.getAbsoluteGravity(horizontalGravity, layoutDirection)) {
              case Gravity.RIGHT:
                  childRight = mRight - mLeft - mPaddingRight;
                  childLeft = childRight - child.getMeasuredWidth();
                  break;
  
              case Gravity.CENTER_HORIZONTAL:
                  childLeft = mPaddingLeft + (mRight - mLeft - child.getMeasuredWidth()) / 2;
                  childRight = childLeft + child.getMeasuredWidth();
                  break;
  
              case Gravity.LEFT:
              default:
                  childLeft = mPaddingLeft;
                  childRight = childLeft + child.getMeasuredWidth();
                  break;
          }
  
          if (childLeft < mPaddingLeft) {
              childLeft = mPaddingLeft;
          }
  
          if (childRight > mRight - mLeft - mPaddingRight) {
              childRight = mRight - mLeft - mPaddingRight;
          }
  
          child.layout(childLeft, childTop, childRight, childBottom);
      }
  
      @Override
      protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
          final int count = getChildCount();
  
          int maxHeight = 0;
          int maxWidth = 0;
          int measuredChildState = 0;
  
          // Find rightmost and bottom-most child
          for (int i = 0; i < count; i++) {
              final View child = getChildAt(i);
              if (child.getVisibility() != GONE) {
                  measureChild(child, widthMeasureSpec, heightMeasureSpec);
                  maxWidth = Math.max(maxWidth, child.getMeasuredWidth());
                  maxHeight = Math.max(maxHeight, child.getMeasuredHeight());
                  measuredChildState = combineMeasuredStates(measuredChildState,
                          child.getMeasuredState());
              }
          }
  
          // Account for padding too
          maxWidth += mPaddingLeft + mPaddingRight;
          maxHeight += mPaddingTop + mPaddingBottom;
  
          // Check against our minimum height and width
          maxHeight = Math.max(maxHeight, getSuggestedMinimumHeight());
          maxWidth = Math.max(maxWidth, getSuggestedMinimumWidth());
  
          setMeasuredDimension(resolveSizeAndState(maxWidth, widthMeasureSpec, measuredChildState),
                  resolveSizeAndState(maxHeight, heightMeasureSpec,
                          measuredChildState<<MEASURED_HEIGHT_STATE_SHIFT));
      }
  
      @Override
      public void onBackStackChanged() {
          updateCrumbs();
      }
  
      /**
       * Returns the number of entries before the backstack, including the title of the current
       * fragment and any custom parent title that was set.
       */
      private int getPreEntryCount() {
          return (mTopEntry != null ? 1 : 0) + (mParentEntry != null ? 1 : 0);
      }
  
      /**
       * Returns the pre-entry corresponding to the index. If there is a parent and a top entry
       * set, parent has an index of zero and top entry has an index of 1. Returns null if the
       * specified index doesn't exist or is null.
       * @param index should not be more than {@link #getPreEntryCount()} - 1
       */
      private BackStackEntry getPreEntry(int index) {
          // If there's a parent entry, then return that for zero'th item, else top entry.
          if (mParentEntry != null) {
              return index == 0 ? mParentEntry : mTopEntry;
          } else {
              return mTopEntry;
          }
      }
  
      void updateCrumbs() {
          FragmentManager fm = mActivity.getFragmentManager();
          int numEntries = fm.getBackStackEntryCount();
          int numPreEntries = getPreEntryCount();
          int numViews = mContainer.getChildCount();
          for (int i = 0; i < numEntries + numPreEntries; i++) {
              BackStackEntry bse = i < numPreEntries
                      ? getPreEntry(i)
                      : fm.getBackStackEntryAt(i - numPreEntries);
              if (i < numViews) {
                  View v = mContainer.getChildAt(i);
                  Object tag = v.getTag();
                  if (tag != bse) {
                      for (int j = i; j < numViews; j++) {
                          mContainer.removeViewAt(i);
                      }
                      numViews = i;
                  }
              }
              if (i >= numViews) {
                  final View item = mInflater.inflate(mLayoutResId, this, false);
                  final TextView text = (TextView) item.findViewById(com.android.internal.R.id.title);
                  text.setText(bse.getBreadCrumbTitle());
                  text.setTag(bse);
                  text.setTextColor(mTextColor);
                  if (i == 0) {
                      item.findViewById(com.android.internal.R.id.left_icon).setVisibility(View.GONE);
                  }
                  mContainer.addView(item);
                  text.setOnClickListener(mOnClickListener);
              }
          }
          int viewI = numEntries + numPreEntries;
          numViews = mContainer.getChildCount();
          while (numViews > viewI) {
              mContainer.removeViewAt(numViews - 1);
              numViews--;
          }
          // Adjust the visibility and availability of the bread crumbs and divider
          for (int i = 0; i < numViews; i++) {
              final View child = mContainer.getChildAt(i);
              // Disable the last one
              child.findViewById(com.android.internal.R.id.title).setEnabled(i < numViews - 1);
              if (mMaxVisible > 0) {
                  // Make only the last mMaxVisible crumbs visible
                  child.setVisibility(i < numViews - mMaxVisible ? View.GONE : View.VISIBLE);
                  final View leftIcon = child.findViewById(com.android.internal.R.id.left_icon);
                  // Remove the divider for all but the last mMaxVisible - 1
                  leftIcon.setVisibility(i > numViews - mMaxVisible && i != 0 ? View.VISIBLE
                          : View.GONE);
              }
          }
      }
  
      private OnClickListener mOnClickListener = new OnClickListener() {
          public void onClick(View v) {
              if (v.getTag() instanceof BackStackEntry) {
                  BackStackEntry bse = (BackStackEntry) v.getTag();
                  if (bse == mParentEntry) {
                      if (mParentClickListener != null) {
                          mParentClickListener.onClick(v);
                      }
                  } else {
                      if (mOnBreadCrumbClickListener != null) {
                          if (mOnBreadCrumbClickListener.onBreadCrumbClick(
                                  bse == mTopEntry ? null : bse, 0)) {
                              return;
                          }
                      }
                      if (bse == mTopEntry) {
                          // Pop everything off the back stack.
                          mActivity.getFragmentManager().popBackStack();
                      } else {
                          mActivity.getFragmentManager().popBackStack(bse.getId(), 0);
                      }
                  }
              }
          }
      };
  }
  