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