1/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5 * use this file except in compliance with the License. You may obtain a copy of
6 * the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations under
14 * the License.
15 */
16
17package com.android.browser;
18
19import com.android.browser.TabBar.TabView;
20
21import android.animation.ObjectAnimator;
22import android.content.Context;
23import android.util.AttributeSet;
24import android.view.View;
25import android.widget.HorizontalScrollView;
26import android.widget.LinearLayout;
27
28/**
29 * custom view for displaying tabs in the tabbed title bar
30 */
31public class TabScrollView extends HorizontalScrollView {
32
33    private LinearLayout mContentView;
34    private int mSelected;
35    private int mAnimationDuration;
36    private int mTabOverlap;
37
38    /**
39     * @param context
40     * @param attrs
41     * @param defStyle
42     */
43    public TabScrollView(Context context, AttributeSet attrs, int defStyle) {
44        super(context, attrs, defStyle);
45        init(context);
46    }
47
48    /**
49     * @param context
50     * @param attrs
51     */
52    public TabScrollView(Context context, AttributeSet attrs) {
53        super(context, attrs);
54        init(context);
55    }
56
57    /**
58     * @param context
59     */
60    public TabScrollView(Context context) {
61        super(context);
62        init(context);
63    }
64
65    private void init(Context ctx) {
66        mAnimationDuration = ctx.getResources().getInteger(
67                R.integer.tab_animation_duration);
68        mTabOverlap = (int) ctx.getResources().getDimension(R.dimen.tab_overlap);
69        setHorizontalScrollBarEnabled(false);
70        setOverScrollMode(OVER_SCROLL_NEVER);
71        mContentView = new TabLayout(ctx);
72        mContentView.setOrientation(LinearLayout.HORIZONTAL);
73        mContentView.setLayoutParams(
74                new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
75        mContentView.setPadding(
76                (int) ctx.getResources().getDimension(R.dimen.tab_first_padding_left),
77                0, 0, 0);
78        addView(mContentView);
79        mSelected = -1;
80        // prevent ProGuard from removing the property methods
81        setScroll(getScroll());
82    }
83
84    @Override
85    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
86        super.onLayout(changed, left, top, right, bottom);
87        ensureChildVisible(getSelectedTab());
88    }
89
90    // in case of a configuration change, adjust tab width
91    protected void updateLayout() {
92        final int count = mContentView.getChildCount();
93        for (int i = 0; i < count; i++) {
94            final TabView tv = (TabView) mContentView.getChildAt(i);
95            tv.updateLayoutParams();
96        }
97        ensureChildVisible(getSelectedTab());
98    }
99
100    void setSelectedTab(int position) {
101        View v = getSelectedTab();
102        if (v != null) {
103            v.setActivated(false);
104        }
105        mSelected = position;
106        v = getSelectedTab();
107        if (v != null) {
108            v.setActivated(true);
109        }
110        requestLayout();
111    }
112
113    int getChildIndex(View v) {
114        return mContentView.indexOfChild(v);
115    }
116
117    View getSelectedTab() {
118        if ((mSelected >= 0) && (mSelected < mContentView.getChildCount())) {
119            return mContentView.getChildAt(mSelected);
120        } else {
121            return null;
122        }
123    }
124
125    void clearTabs() {
126        mContentView.removeAllViews();
127    }
128
129    void addTab(View tab) {
130        mContentView.addView(tab);
131        tab.setActivated(false);
132    }
133
134    void removeTab(View tab) {
135        int ix = mContentView.indexOfChild(tab);
136        if (ix == mSelected) {
137            mSelected = -1;
138        } else if (ix < mSelected) {
139            mSelected--;
140        }
141        mContentView.removeView(tab);
142    }
143
144    private void ensureChildVisible(View child) {
145        if (child != null) {
146            int childl = child.getLeft();
147            int childr = childl + child.getWidth();
148            int viewl = getScrollX();
149            int viewr = viewl + getWidth();
150            if (childl < viewl) {
151                // need scrolling to left
152                animateScroll(childl);
153            } else if (childr > viewr) {
154                // need scrolling to right
155                animateScroll(childr - viewr + viewl);
156            }
157        }
158    }
159
160// TODO: These animations are broken and don't work correctly, removing for now
161//       as animateOut is actually causing issues
162//    private void animateIn(View tab) {
163//        ObjectAnimator animator = ObjectAnimator.ofInt(tab, "TranslationX", 500, 0);
164//        animator.setDuration(mAnimationDuration);
165//        animator.start();
166//    }
167//
168//    private void animateOut(final View tab) {
169//        ObjectAnimator animator = ObjectAnimator.ofInt(
170//                tab, "TranslationX", 0, getScrollX() - tab.getRight());
171//        animator.setDuration(mAnimationDuration);
172//        animator.addListener(new AnimatorListenerAdapter() {
173//            @Override
174//            public void onAnimationEnd(Animator animation) {
175//                mContentView.removeView(tab);
176//            }
177//        });
178//        animator.setInterpolator(new AccelerateInterpolator());
179//        animator.start();
180//    }
181
182    private void animateScroll(int newscroll) {
183        ObjectAnimator animator = ObjectAnimator.ofInt(this, "scroll", getScrollX(), newscroll);
184        animator.setDuration(mAnimationDuration);
185        animator.start();
186    }
187
188    /**
189     * required for animation
190     */
191    public void setScroll(int newscroll) {
192        scrollTo(newscroll, getScrollY());
193    }
194
195    /**
196     * required for animation
197     */
198    public int getScroll() {
199        return getScrollX();
200    }
201
202    @Override
203    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
204        super.onScrollChanged(l, t, oldl, oldt);
205
206        // TabViews base their drawing based on their absolute position within the
207        // window. When hardware accelerated, we need to recreate their display list
208        // when they scroll
209        if (isHardwareAccelerated()) {
210            int count = mContentView.getChildCount();
211            for (int i = 0; i < count; i++) {
212                mContentView.getChildAt(i).invalidate();
213            }
214        }
215    }
216
217    class TabLayout extends LinearLayout {
218
219        public TabLayout(Context context) {
220            super(context);
221            setChildrenDrawingOrderEnabled(true);
222        }
223
224        @Override
225        protected void onMeasure(int hspec, int vspec) {
226            super.onMeasure(hspec, vspec);
227            int w = getMeasuredWidth();
228            w -= Math.max(0, mContentView.getChildCount() - 1) * mTabOverlap;
229            setMeasuredDimension(w, getMeasuredHeight());
230        }
231
232        @Override
233        protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
234            super.onLayout(changed, left, top, right, bottom);
235            if (getChildCount() > 1) {
236                int nextLeft = getChildAt(0).getRight() - mTabOverlap;
237                for (int i = 1; i < getChildCount(); i++) {
238                    View tab = getChildAt(i);
239                    int w = tab.getRight() - tab.getLeft();
240                    tab.layout(nextLeft, tab.getTop(), nextLeft + w, tab.getBottom());
241                    nextLeft += w - mTabOverlap;
242                }
243            }
244        }
245
246        @Override
247        protected int getChildDrawingOrder(int count, int i) {
248            int next = -1;
249            if ((i == (count - 1)) && (mSelected >= 0) && (mSelected < count)) {
250                next = mSelected;
251            } else {
252                next = count - i - 1;
253                if (next <= mSelected && next > 0) {
254                    next--;
255                }
256            }
257            return next;
258        }
259
260    }
261
262}
263