18f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan/*
28f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * Copyright (C) 2011 The Android Open Source Project
38f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan *
48f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * Licensed under the Apache License, Version 2.0 (the "License");
58f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * you may not use this file except in compliance with the License.
68f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * You may obtain a copy of the License at
78f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan *
88f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan *      http://www.apache.org/licenses/LICENSE-2.0
98f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan *
108f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * Unless required by applicable law or agreed to in writing, software
118f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * distributed under the License is distributed on an "AS IS" BASIS,
128f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
138f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * See the License for the specific language governing permissions and
148f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * limitations under the License.
158f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan */
168f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
178f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanpackage com.android.contacts.detail;
188f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
198f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.content.Context;
208f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.util.AttributeSet;
218f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.view.LayoutInflater;
228f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.view.MotionEvent;
238f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.view.View;
248f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.view.View.OnTouchListener;
25e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport android.view.ViewPropertyAnimator;
268f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanimport android.widget.HorizontalScrollView;
278f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
28e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.R;
29e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Chengimport com.android.contacts.widget.FrameLayoutWithOverlay;
30e0b2f1e2d01d1ac52ba207dc7ce76971d853298eChiao Cheng
318f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan/**
328f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * This is a horizontally scrolling carousel with 2 fragments: one to see info about the contact and
338f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan * one to see updates from the contact. Depending on the scroll position and user selection of which
3413f6f26cb8b2f78f0b92dfc1d102222c13217300Maurice Chu * fragment to currently view, the touch interceptors over each fragment are configured accordingly.
358f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan */
368f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuanpublic class ContactDetailFragmentCarousel extends HorizontalScrollView implements OnTouchListener {
378f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
388f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private static final String TAG = ContactDetailFragmentCarousel.class.getSimpleName();
398f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
40db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    /**
41db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     * Number of pixels that this view can be scrolled horizontally.
42db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     */
438f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE;
44db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan
45db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    /**
46db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     * Minimum X scroll position that must be surpassed (if the user is on the "about" page of the
47db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     * contact card), in order for this view to automatically snap to the "updates" page.
48db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     */
498f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int mLowerThreshold = Integer.MIN_VALUE;
50db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan
51db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    /**
52db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     * Maximum X scroll position (if the user is on the "updates" page of the contact card), below
53db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     * which this view will automatically snap to the "about" page.
54db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan     */
558f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int mUpperThreshold = Integer.MIN_VALUE;
568f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
5751f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    /**
5851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * Minimum width of a fragment (if there is more than 1 fragment in the carousel, then this is
5951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * the width of one of the fragments).
6051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     */
6151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    private int mMinFragmentWidth = Integer.MIN_VALUE;
6251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
6351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    /**
6451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * Fragment width (if there are 1+ fragments in the carousel) as defined as a fraction of the
6551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * screen width.
6651f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     */
6751f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    private static final float FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION = 0.85f;
6851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
698f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private static final int ABOUT_PAGE = 0;
708f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private static final int UPDATES_PAGE = 1;
718f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
7251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    private static final int MAX_FRAGMENT_VIEW_COUNT = 2;
7351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
7451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    private boolean mEnableSwipe;
7551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
768f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int mCurrentPage = ABOUT_PAGE;
778f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int mLastScrollPosition;
788f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
797edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus    private FrameLayoutWithOverlay mAboutFragment;
807edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus    private FrameLayoutWithOverlay mUpdatesFragment;
818f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
828f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    public ContactDetailFragmentCarousel(Context context) {
838f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        this(context, null);
848f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
858f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
868f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    public ContactDetailFragmentCarousel(Context context, AttributeSet attrs) {
878f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        this(context, attrs, 0);
888f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
898f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
908f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    public ContactDetailFragmentCarousel(Context context, AttributeSet attrs, int defStyle) {
918f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        super(context, attrs, defStyle);
928f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
938f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        final LayoutInflater inflater =
948f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan                (LayoutInflater) mContext.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
958f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        inflater.inflate(R.layout.contact_detail_fragment_carousel, this);
968f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
978f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        setOnTouchListener(this);
988f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
998f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
100db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    @Override
101db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
10251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        int screenWidth = MeasureSpec.getSize(widthMeasureSpec);
10351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        int screenHeight = MeasureSpec.getSize(heightMeasureSpec);
104db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan
105db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan        // Take the width of this view as the width of the screen and compute necessary thresholds.
106db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan        // Only do this computation 1x.
107db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan        if (mAllowedHorizontalScrollLength == Integer.MIN_VALUE) {
10851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            mMinFragmentWidth = (int) (FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION * screenWidth);
10951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            mAllowedHorizontalScrollLength = (MAX_FRAGMENT_VIEW_COUNT * mMinFragmentWidth) -
11051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                    screenWidth;
11151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            mLowerThreshold = (screenWidth - mMinFragmentWidth) / MAX_FRAGMENT_VIEW_COUNT;
112db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan            mUpperThreshold = mAllowedHorizontalScrollLength - mLowerThreshold;
113db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan        }
11451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
11551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        if (getChildCount() > 0) {
11651f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            View child = getChildAt(0);
11751f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            // If we enable swipe, then the {@link LinearLayout} child width must be the sum of the
11851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            // width of all its children fragments.
119a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            // Or the current page may already be set to something other than the first.  If so,
120a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            // it also means there are multiple child fragments.
121988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng            if (mEnableSwipe || mCurrentPage == 1 ||
122988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    (mCurrentPage == 0 && getLayoutDirection() == View.LAYOUT_DIRECTION_RTL)) {
12351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                child.measure(MeasureSpec.makeMeasureSpec(
12451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                        mMinFragmentWidth * MAX_FRAGMENT_VIEW_COUNT, MeasureSpec.EXACTLY),
12551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                        MeasureSpec.makeMeasureSpec(screenHeight, MeasureSpec.EXACTLY));
12651f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            } else {
12751f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                // Otherwise, the {@link LinearLayout} child width will just be the screen width
12851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                // because it will only have 1 child fragment.
12951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                child.measure(MeasureSpec.makeMeasureSpec(screenWidth, MeasureSpec.EXACTLY),
13051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                        MeasureSpec.makeMeasureSpec(screenHeight, MeasureSpec.EXACTLY));
13151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            }
13251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        }
13351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
13451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        setMeasuredDimension(
13551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                resolveSize(screenWidth, widthMeasureSpec),
13651f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan                resolveSize(screenHeight, heightMeasureSpec));
137db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    }
138db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan
13951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    /**
140ccf3b6bb9cba613eb31163c7ab97be71307234d1Katherine Kuan     * Set the current page. This dims out the non-selected page but doesn't do any scrolling of
141ccf3b6bb9cba613eb31163c7ab97be71307234d1Katherine Kuan     * the carousel.
14251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     */
143db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan    public void setCurrentPage(int pageIndex) {
14451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        mCurrentPage = pageIndex;
14525594d6db384d27641b402cddf23d44818e1cd10Katherine Kuan
1467edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        updateTouchInterceptors();
14751f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    }
14851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan
14951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    /**
1507edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus     * Set the view containers for the detail and updates fragment.
15151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     */
1527edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus    public void setFragmentViews(FrameLayoutWithOverlay about, FrameLayoutWithOverlay updates) {
1537edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        mAboutFragment = about;
1547edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        mUpdatesFragment = updates;
15584edfd9a76657a653491faac53b5976adf9fd2cbJosh Gargus
1567edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        mAboutFragment.setOverlayOnClickListener(mAboutFragTouchInterceptListener);
1577edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        mUpdatesFragment.setOverlayOnClickListener(mUpdatesFragTouchInterceptListener);
15851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    }
159db0d8669cd1ffaa45827edb65b2b0eecb27561f5Katherine Kuan
16051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    /**
16151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * Enable swiping if the detail and update fragments should be showing. Otherwise disable
16251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     * swiping if only the detail fragment should be showing.
16351f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan     */
16451f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan    public void enableSwipe(boolean enable) {
16551f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        if (mEnableSwipe != enable) {
16651f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            mEnableSwipe = enable;
1677edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus            if (mUpdatesFragment != null) {
1687edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus                mUpdatesFragment.setVisibility(enable ? View.VISIBLE : View.GONE);
169a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng                snapToEdge();
170ccf3b6bb9cba613eb31163c7ab97be71307234d1Katherine Kuan                updateTouchInterceptors();
17151f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            }
17251f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        }
17325594d6db384d27641b402cddf23d44818e1cd10Katherine Kuan    }
17425594d6db384d27641b402cddf23d44818e1cd10Katherine Kuan
1750b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu    /**
1760b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu     * Reset the fragment carousel to show the about page.
1770b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu     */
1780b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu    public void reset() {
1790b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu        if (mCurrentPage != ABOUT_PAGE) {
1800b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu            mCurrentPage = ABOUT_PAGE;
181a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            snapToEdgeSmooth();
1820b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu        }
1830b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu    }
1840b398b3d178d1fe8fd3ebfb9396e36d1228b53c6Maurice Chu
1858f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    public int getCurrentPage() {
1868f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        return mCurrentPage;
1878f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
1888f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
1898f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private final OnClickListener mAboutFragTouchInterceptListener = new OnClickListener() {
1908f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        @Override
1918f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        public void onClick(View v) {
1928f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            mCurrentPage = ABOUT_PAGE;
193a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            snapToEdgeSmooth();
1948f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        }
1958f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    };
1968f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
1978f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private final OnClickListener mUpdatesFragTouchInterceptListener = new OnClickListener() {
1988f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        @Override
1998f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        public void onClick(View v) {
2008f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            mCurrentPage = UPDATES_PAGE;
201a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            snapToEdgeSmooth();
2028f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        }
2038f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    };
2048f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
2058f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private void updateTouchInterceptors() {
2067edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        // Disable the touch-interceptor on the selected page, and enable it on the other.
2077edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        if (mAboutFragment != null) {
2087edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus            mAboutFragment.setOverlayClickable(mCurrentPage != ABOUT_PAGE);
2097edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        }
2107edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        if (mUpdatesFragment != null) {
2117edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus            mUpdatesFragment.setOverlayClickable(mCurrentPage != UPDATES_PAGE);
2128f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        }
2138f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
2148f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
2158f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    @Override
2168f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    protected void onScrollChanged(int l, int t, int oldl, int oldt) {
2178f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        super.onScrollChanged(l, t, oldl, oldt);
21851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        if (!mEnableSwipe) {
21951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            return;
22051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        }
2212a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann        mLastScrollPosition = l;
2228f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
2238f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
224a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    /**
225a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng     * Used to set initial scroll offset.  Not smooth.
226a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng     */
2278f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private void snapToEdge() {
228a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng        setScrollX(calculateHorizontalOffset());
2298f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        updateTouchInterceptors();
2308f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
2318f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
2328f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    /**
233a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng     * Smooth version of snapToEdge().
234a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng     */
235a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    private void snapToEdgeSmooth() {
236a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng        smoothScrollTo(calculateHorizontalOffset(), 0);
237a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng        updateTouchInterceptors();
238a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    }
239a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng
240a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    private int calculateHorizontalOffset() {
241988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng        int offset;
242988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng        if (getLayoutDirection() == View.LAYOUT_DIRECTION_RTL) {
243988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng            offset = (mCurrentPage == ABOUT_PAGE) ? mAllowedHorizontalScrollLength : 0;
244988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng        } else {
245988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng            offset = (mCurrentPage == ABOUT_PAGE) ? 0 : mAllowedHorizontalScrollLength;
246988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng        }
247988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng        return offset;
248a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    }
249a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng
250a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng    /**
2518f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan     * Returns the desired page we should scroll to based on the current X scroll position and the
2528f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan     * current page.
2538f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan     */
2548f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    private int getDesiredPage() {
2558f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        switch (mCurrentPage) {
2568f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            case ABOUT_PAGE:
257988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                if (getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
258988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    // If the user is on the "about" page, and the scroll position exceeds the lower
259988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    // threshold, then we should switch to the "updates" page.
260988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    return (mLastScrollPosition > mLowerThreshold) ? UPDATES_PAGE : ABOUT_PAGE;
261988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                } else {
262988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    return (mLastScrollPosition < mUpperThreshold) ? UPDATES_PAGE : ABOUT_PAGE;
263988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                }
2648f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            case UPDATES_PAGE:
265988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                if (getLayoutDirection() == View.LAYOUT_DIRECTION_LTR) {
266988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    // If the user is on the "updates" page, and the scroll position goes below the
267988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    // upper threshold, then we should switch to the "about" page.
268988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    return (mLastScrollPosition < mUpperThreshold) ? ABOUT_PAGE : UPDATES_PAGE;
269988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                } else {
270988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                    return (mLastScrollPosition > mLowerThreshold) ? ABOUT_PAGE : UPDATES_PAGE;
271988c2266116b705b43bbf643ca16773a989ade35Chiao Cheng                }
2728f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        }
2738f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        throw new IllegalStateException("Invalid current page " + mCurrentPage);
2748f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
2758f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan
2768f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    @Override
2778f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    public boolean onTouch(View v, MotionEvent event) {
27851f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        if (!mEnableSwipe) {
27951f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan            return false;
28051f1071a1dc91dace0de73be1c5fbba4f091f054Katherine Kuan        }
2818f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        if (event.getAction() == MotionEvent.ACTION_UP) {
2828f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            mCurrentPage = getDesiredPage();
283a5b52c36b9817a48bcb155862255462ef1bae4caChiao Cheng            snapToEdgeSmooth();
2848f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan            return true;
2858f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        }
2868f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan        return false;
2878f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan    }
2882a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann
2892a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann    /**
2902a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann     * Starts an "appear" animation by moving in the "Updates" from the right.
2912a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann     */
2922a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann    public void animateAppear() {
2932a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann        final int x = Math.round((1.0f - FRAGMENT_WIDTH_SCREEN_WIDTH_FRACTION) * getWidth());
2947edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        mUpdatesFragment.setTranslationX(x);
2957edad9dd95a411cc5ed69815e5f0be8a5d1e8b19Josh Gargus        final ViewPropertyAnimator animator = mUpdatesFragment.animate();
2962a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann        animator.translationX(0.0f);
2972a45e35ab3273c8901d9df3671e51614dc0250c6Daniel Lehmann    }
2988f0f3343e4363a5e1f11b35392c8393f263aaa41Katherine Kuan}
299