Merge commit '42b2e5ca519766e37ce6941ba4faecc9691cc403' into upstream
[debian/openrocket] / android-libraries / ActionBarSherlock / src / com / actionbarsherlock / internal / widget / ScrollingTabContainerView.java
diff --git a/android-libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java b/android-libraries/ActionBarSherlock/src/com/actionbarsherlock/internal/widget/ScrollingTabContainerView.java
new file mode 100644 (file)
index 0000000..1a532e0
--- /dev/null
@@ -0,0 +1,545 @@
+/*
+ * Copyright (C) 2011 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 com.actionbarsherlock.internal.widget;
+
+import android.content.Context;
+import android.content.res.Configuration;
+import android.content.res.TypedArray;
+import android.graphics.drawable.Drawable;
+import android.text.TextUtils.TruncateAt;
+import android.util.AttributeSet;
+import android.view.Gravity;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.view.ViewParent;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
+import android.widget.BaseAdapter;
+import android.widget.ImageView;
+import android.widget.LinearLayout;
+import android.widget.ListView;
+import com.actionbarsherlock.R;
+import com.actionbarsherlock.app.ActionBar;
+import com.actionbarsherlock.internal.nineoldandroids.animation.Animator;
+import com.actionbarsherlock.internal.nineoldandroids.animation.ObjectAnimator;
+import com.actionbarsherlock.internal.nineoldandroids.widget.NineHorizontalScrollView;
+
+/**
+ * This widget implements the dynamic action bar tab behavior that can change
+ * across different configurations or circumstances.
+ */
+public class ScrollingTabContainerView extends NineHorizontalScrollView
+        implements IcsAdapterView.OnItemSelectedListener {
+    //UNUSED private static final String TAG = "ScrollingTabContainerView";
+    Runnable mTabSelector;
+    private TabClickListener mTabClickListener;
+
+    private IcsLinearLayout mTabLayout;
+    private IcsSpinner mTabSpinner;
+    private boolean mAllowCollapse;
+
+    private LayoutInflater mInflater;
+
+    int mMaxTabWidth;
+    private int mContentHeight;
+    private int mSelectedTabIndex;
+
+    protected Animator mVisibilityAnim;
+    protected final VisibilityAnimListener mVisAnimListener = new VisibilityAnimListener();
+
+    private static final /*Time*/Interpolator sAlphaInterpolator = new DecelerateInterpolator();
+
+    private static final int FADE_DURATION = 200;
+
+    public ScrollingTabContainerView(Context context) {
+        super(context);
+        setHorizontalScrollBarEnabled(false);
+
+        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+                R.attr.actionBarStyle, 0);
+        setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+        a.recycle();
+
+        mInflater = LayoutInflater.from(context);
+
+        mTabLayout = createTabLayout();
+        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+    }
+
+    @Override
+    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+        final int widthMode = MeasureSpec.getMode(widthMeasureSpec);
+        final boolean lockedExpanded = widthMode == MeasureSpec.EXACTLY;
+        setFillViewport(lockedExpanded);
+
+        final int childCount = mTabLayout.getChildCount();
+        if (childCount > 1 &&
+                (widthMode == MeasureSpec.EXACTLY || widthMode == MeasureSpec.AT_MOST)) {
+            if (childCount > 2) {
+                mMaxTabWidth = (int) (MeasureSpec.getSize(widthMeasureSpec) * 0.4f);
+            } else {
+                mMaxTabWidth = MeasureSpec.getSize(widthMeasureSpec) / 2;
+            }
+        } else {
+            mMaxTabWidth = -1;
+        }
+
+        heightMeasureSpec = MeasureSpec.makeMeasureSpec(mContentHeight, MeasureSpec.EXACTLY);
+
+        final boolean canCollapse = !lockedExpanded && mAllowCollapse;
+
+        if (canCollapse) {
+            // See if we should expand
+            mTabLayout.measure(MeasureSpec.UNSPECIFIED, heightMeasureSpec);
+            if (mTabLayout.getMeasuredWidth() > MeasureSpec.getSize(widthMeasureSpec)) {
+                performCollapse();
+            } else {
+                performExpand();
+            }
+        } else {
+            performExpand();
+        }
+
+        final int oldWidth = getMeasuredWidth();
+        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+        final int newWidth = getMeasuredWidth();
+
+        if (lockedExpanded && oldWidth != newWidth) {
+            // Recenter the tab display if we're at a new (scrollable) size.
+            setTabSelected(mSelectedTabIndex);
+        }
+    }
+
+    /**
+     * Indicates whether this view is collapsed into a dropdown menu instead
+     * of traditional tabs.
+     * @return true if showing as a spinner
+     */
+    private boolean isCollapsed() {
+        return mTabSpinner != null && mTabSpinner.getParent() == this;
+    }
+
+    public void setAllowCollapse(boolean allowCollapse) {
+        mAllowCollapse = allowCollapse;
+    }
+
+    private void performCollapse() {
+        if (isCollapsed()) return;
+
+        if (mTabSpinner == null) {
+            mTabSpinner = createSpinner();
+        }
+        removeView(mTabLayout);
+        addView(mTabSpinner, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        if (mTabSpinner.getAdapter() == null) {
+            mTabSpinner.setAdapter(new TabAdapter());
+        }
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+            mTabSelector = null;
+        }
+        mTabSpinner.setSelection(mSelectedTabIndex);
+    }
+
+    private boolean performExpand() {
+        if (!isCollapsed()) return false;
+
+        removeView(mTabSpinner);
+        addView(mTabLayout, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
+                ViewGroup.LayoutParams.MATCH_PARENT));
+        setTabSelected(mTabSpinner.getSelectedItemPosition());
+        return false;
+    }
+
+    public void setTabSelected(int position) {
+        mSelectedTabIndex = position;
+        final int tabCount = mTabLayout.getChildCount();
+        for (int i = 0; i < tabCount; i++) {
+            final View child = mTabLayout.getChildAt(i);
+            final boolean isSelected = i == position;
+            child.setSelected(isSelected);
+            if (isSelected) {
+                animateToTab(position);
+            }
+        }
+    }
+
+    public void setContentHeight(int contentHeight) {
+        mContentHeight = contentHeight;
+        requestLayout();
+    }
+
+    private IcsLinearLayout createTabLayout() {
+        final IcsLinearLayout tabLayout = (IcsLinearLayout) LayoutInflater.from(getContext())
+                .inflate(R.layout.abs__action_bar_tab_bar_view, null);
+        tabLayout.setLayoutParams(new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
+        return tabLayout;
+    }
+
+    private IcsSpinner createSpinner() {
+        final IcsSpinner spinner = new IcsSpinner(getContext(), null,
+                R.attr.actionDropDownStyle);
+        spinner.setLayoutParams(new LinearLayout.LayoutParams(
+                LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.MATCH_PARENT));
+        spinner.setOnItemSelectedListener(this);
+        return spinner;
+    }
+
+    @Override
+    protected void onConfigurationChanged(Configuration newConfig) {
+        super.onConfigurationChanged(newConfig);
+
+        // Action bar can change size on configuration changes.
+        // Reread the desired height from the theme-specified style.
+        TypedArray a = getContext().obtainStyledAttributes(null, R.styleable.SherlockActionBar,
+                R.attr.actionBarStyle, 0);
+        setContentHeight(a.getLayoutDimension(R.styleable.SherlockActionBar_height, 0));
+        a.recycle();
+    }
+
+    public void animateToVisibility(int visibility) {
+        if (mVisibilityAnim != null) {
+            mVisibilityAnim.cancel();
+        }
+        if (visibility == VISIBLE) {
+            if (getVisibility() != VISIBLE) {
+                setAlpha(0);
+            }
+            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 1);
+            anim.setDuration(FADE_DURATION);
+            anim.setInterpolator(sAlphaInterpolator);
+
+            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+            anim.start();
+        } else {
+            ObjectAnimator anim = ObjectAnimator.ofFloat(this, "alpha", 0);
+            anim.setDuration(FADE_DURATION);
+            anim.setInterpolator(sAlphaInterpolator);
+
+            anim.addListener(mVisAnimListener.withFinalVisibility(visibility));
+            anim.start();
+        }
+    }
+
+    public void animateToTab(final int position) {
+        final View tabView = mTabLayout.getChildAt(position);
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+        }
+        mTabSelector = new Runnable() {
+            public void run() {
+                final int scrollPos = tabView.getLeft() - (getWidth() - tabView.getWidth()) / 2;
+                smoothScrollTo(scrollPos, 0);
+                mTabSelector = null;
+            }
+        };
+        post(mTabSelector);
+    }
+
+    @Override
+    public void onAttachedToWindow() {
+        super.onAttachedToWindow();
+        if (mTabSelector != null) {
+            // Re-post the selector we saved
+            post(mTabSelector);
+        }
+    }
+
+    @Override
+    public void onDetachedFromWindow() {
+        super.onDetachedFromWindow();
+        if (mTabSelector != null) {
+            removeCallbacks(mTabSelector);
+        }
+    }
+
+    private TabView createTabView(ActionBar.Tab tab, boolean forAdapter) {
+        //Workaround for not being able to pass a defStyle on pre-3.0
+        final TabView tabView = (TabView)mInflater.inflate(R.layout.abs__action_bar_tab, null);
+        tabView.init(this, tab, forAdapter);
+
+        if (forAdapter) {
+            tabView.setBackgroundDrawable(null);
+            tabView.setLayoutParams(new ListView.LayoutParams(ListView.LayoutParams.MATCH_PARENT,
+                    mContentHeight));
+        } else {
+            tabView.setFocusable(true);
+
+            if (mTabClickListener == null) {
+                mTabClickListener = new TabClickListener();
+            }
+            tabView.setOnClickListener(mTabClickListener);
+        }
+        return tabView;
+    }
+
+    public void addTab(ActionBar.Tab tab, boolean setSelected) {
+        TabView tabView = createTabView(tab, false);
+        mTabLayout.addView(tabView, new IcsLinearLayout.LayoutParams(0,
+                LayoutParams.MATCH_PARENT, 1));
+        if (mTabSpinner != null) {
+            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+        }
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+        if (mAllowCollapse) {
+            requestLayout();
+        }
+    }
+
+    public void addTab(ActionBar.Tab tab, int position, boolean setSelected) {
+        final TabView tabView = createTabView(tab, false);
+        mTabLayout.addView(tabView, position, new IcsLinearLayout.LayoutParams(
+                0, LayoutParams.MATCH_PARENT, 1));
+        if (mTabSpinner != null) {
+            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+        }
+        if (setSelected) {
+            tabView.setSelected(true);
+        }
+        if (mAllowCollapse) {
+            requestLayout();
+        }
+    }
+
+    public void updateTab(int position) {
+        ((TabView) mTabLayout.getChildAt(position)).update();
+        if (mTabSpinner != null) {
+            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+        }
+        if (mAllowCollapse) {
+            requestLayout();
+        }
+    }
+
+    public void removeTabAt(int position) {
+        mTabLayout.removeViewAt(position);
+        if (mTabSpinner != null) {
+            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+        }
+        if (mAllowCollapse) {
+            requestLayout();
+        }
+    }
+
+    public void removeAllTabs() {
+        mTabLayout.removeAllViews();
+        if (mTabSpinner != null) {
+            ((TabAdapter) mTabSpinner.getAdapter()).notifyDataSetChanged();
+        }
+        if (mAllowCollapse) {
+            requestLayout();
+        }
+    }
+
+    @Override
+    public void onItemSelected(IcsAdapterView<?> parent, View view, int position, long id) {
+        TabView tabView = (TabView) view;
+        tabView.getTab().select();
+    }
+
+    @Override
+    public void onNothingSelected(IcsAdapterView<?> parent) {
+    }
+
+    public static class TabView extends LinearLayout {
+        private ScrollingTabContainerView mParent;
+        private ActionBar.Tab mTab;
+        private CapitalizingTextView mTextView;
+        private ImageView mIconView;
+        private View mCustomView;
+
+        public TabView(Context context, AttributeSet attrs) {
+            //TODO super(context, null, R.attr.actionBarTabStyle);
+            super(context, attrs);
+        }
+
+        public void init(ScrollingTabContainerView parent, ActionBar.Tab tab, boolean forList) {
+            mParent = parent;
+            mTab = tab;
+
+            if (forList) {
+                setGravity(Gravity.LEFT | Gravity.CENTER_VERTICAL);
+            }
+
+            update();
+        }
+
+        public void bindTab(ActionBar.Tab tab) {
+            mTab = tab;
+            update();
+        }
+
+        @Override
+        public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
+            super.onMeasure(widthMeasureSpec, heightMeasureSpec);
+
+            // Re-measure if we went beyond our maximum size.
+            if (mParent.mMaxTabWidth > 0 && getMeasuredWidth() > mParent.mMaxTabWidth) {
+                super.onMeasure(MeasureSpec.makeMeasureSpec(mParent.mMaxTabWidth, MeasureSpec.EXACTLY),
+                        heightMeasureSpec);
+            }
+        }
+
+        public void update() {
+            final ActionBar.Tab tab = mTab;
+            final View custom = tab.getCustomView();
+            if (custom != null) {
+                final ViewParent customParent = custom.getParent();
+                if (customParent != this) {
+                    if (customParent != null) ((ViewGroup) customParent).removeView(custom);
+                    addView(custom);
+                }
+                mCustomView = custom;
+                if (mTextView != null) mTextView.setVisibility(GONE);
+                if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+            } else {
+                if (mCustomView != null) {
+                    removeView(mCustomView);
+                    mCustomView = null;
+                }
+
+                final Drawable icon = tab.getIcon();
+                final CharSequence text = tab.getText();
+
+                if (icon != null) {
+                    if (mIconView == null) {
+                        ImageView iconView = new ImageView(getContext());
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        iconView.setLayoutParams(lp);
+                        addView(iconView, 0);
+                        mIconView = iconView;
+                    }
+                    mIconView.setImageDrawable(icon);
+                    mIconView.setVisibility(VISIBLE);
+                } else if (mIconView != null) {
+                    mIconView.setVisibility(GONE);
+                    mIconView.setImageDrawable(null);
+                }
+
+                if (text != null) {
+                    if (mTextView == null) {
+                        CapitalizingTextView textView = new CapitalizingTextView(getContext(), null,
+                                R.attr.actionBarTabTextStyle);
+                        textView.setEllipsize(TruncateAt.END);
+                        LayoutParams lp = new LayoutParams(LayoutParams.WRAP_CONTENT,
+                                LayoutParams.WRAP_CONTENT);
+                        lp.gravity = Gravity.CENTER_VERTICAL;
+                        textView.setLayoutParams(lp);
+                        addView(textView);
+                        mTextView = textView;
+                    }
+                    mTextView.setTextCompat(text);
+                    mTextView.setVisibility(VISIBLE);
+                } else if (mTextView != null) {
+                    mTextView.setVisibility(GONE);
+                    mTextView.setText(null);
+                }
+
+                if (mIconView != null) {
+                    mIconView.setContentDescription(tab.getContentDescription());
+                }
+            }
+        }
+
+        public ActionBar.Tab getTab() {
+            return mTab;
+        }
+    }
+
+    private class TabAdapter extends BaseAdapter {
+        @Override
+        public int getCount() {
+            return mTabLayout.getChildCount();
+        }
+
+        @Override
+        public Object getItem(int position) {
+            return ((TabView) mTabLayout.getChildAt(position)).getTab();
+        }
+
+        @Override
+        public long getItemId(int position) {
+            return position;
+        }
+
+        @Override
+        public View getView(int position, View convertView, ViewGroup parent) {
+            if (convertView == null) {
+                convertView = createTabView((ActionBar.Tab) getItem(position), true);
+            } else {
+                ((TabView) convertView).bindTab((ActionBar.Tab) getItem(position));
+            }
+            return convertView;
+        }
+    }
+
+    private class TabClickListener implements OnClickListener {
+        public void onClick(View view) {
+            TabView tabView = (TabView) view;
+            tabView.getTab().select();
+            final int tabCount = mTabLayout.getChildCount();
+            for (int i = 0; i < tabCount; i++) {
+                final View child = mTabLayout.getChildAt(i);
+                child.setSelected(child == view);
+            }
+        }
+    }
+
+    protected class VisibilityAnimListener implements Animator.AnimatorListener {
+        private boolean mCanceled = false;
+        private int mFinalVisibility;
+
+        public VisibilityAnimListener withFinalVisibility(int visibility) {
+            mFinalVisibility = visibility;
+            return this;
+        }
+
+        @Override
+        public void onAnimationStart(Animator animation) {
+            setVisibility(VISIBLE);
+            mVisibilityAnim = animation;
+            mCanceled = false;
+        }
+
+        @Override
+        public void onAnimationEnd(Animator animation) {
+            if (mCanceled) return;
+
+            mVisibilityAnim = null;
+            setVisibility(mFinalVisibility);
+        }
+
+        @Override
+        public void onAnimationCancel(Animator animation) {
+            mCanceled = true;
+        }
+
+        @Override
+        public void onAnimationRepeat(Animator animation) {
+        }
+    }
+}