Loading src/com/android/contacts/detail/ContactDetailFragmentCarousel.java +24 −24 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen private View mDetailFragmentView; private View mUpdatesFragmentView; private final Handler mHandler = new Handler(); private boolean mScrollToCurrentPage = false; public ContactDetailFragmentCarousel(Context context) { this(context, null); Loading Loading @@ -144,6 +144,28 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen resolveSize(screenHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (mScrollToCurrentPage) { mScrollToCurrentPage = false; // Use scrollTo() instead of smoothScrollTo() to prevent a visible flicker to the user scrollTo(mCurrentPage == ABOUT_PAGE ? 0 : mAllowedHorizontalScrollLength, 0); updateTouchInterceptors(); } } /** * Set the current page that should be restored when the view is first laid out. */ public void restoreCurrentPage(int pageIndex) { setCurrentPage(pageIndex); // It is only possible to scroll the view after onMeasure() has been called (where the // allowed horizontal scroll length is determined). Hence, set a flag that will be read // in onLayout() after the children and this view have finished being laid out. mScrollToCurrentPage = true; } /** * Set the current page. This auto-scrolls the carousel to the current page and dims out * the non-selected page. Loading Loading @@ -183,31 +205,13 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen mEnableSwipe = enable; if (mUpdatesFragmentView != null) { mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE); mScrollToCurrentPage = true; requestLayout(); invalidate(); } // This method could have been called before the view has been measured (i.e. // immediately after a rotation), so snap to edge only after the view is ready. postRunnableToSnapToEdge(); } } /** * Snap to the currently selected page only once all the view setup and measurement has * completed (i.e. we need to know the allowed horizontal scroll width in order to * snap to the correct page). */ private void postRunnableToSnapToEdge() { mHandler.post(new Runnable() { @Override public void run() { if (isAttachedToWindow() && mAboutFragment != null && mUpdatesFragment != null) { snapToEdge(); } } }); } public int getCurrentPage() { return mCurrentPage; } Loading Loading @@ -302,8 +306,4 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen } return false; } private boolean isAttachedToWindow() { return getWindowToken() != null; } } src/com/android/contacts/detail/ContactDetailLayoutController.java +41 −4 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import com.android.contacts.NfcHandler; import com.android.contacts.R; import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.app.Activity; import android.app.FragmentManager; Loading Loading @@ -77,6 +79,7 @@ public class ContactDetailLayoutController { private ContactLoader.Result mContactData; private boolean mTabCarouselIsAnimating; private boolean mContactHasUpdates; private LayoutMode mLayoutMode; Loading Loading @@ -176,6 +179,7 @@ public class ContactDetailLayoutController { } mTabCarousel.setListener(mTabCarouselListener); mTabCarousel.restoreCurrentTab(currentPageIndex); mDetailFragment.setVerticalScrollListener( new VerticalScrollListener(TAB_INDEX_DETAIL)); mUpdatesFragment.setVerticalScrollListener( Loading Loading @@ -211,7 +215,7 @@ public class ContactDetailLayoutController { mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView); mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment); mFragmentCarousel.setCurrentPage(currentPageIndex); mFragmentCarousel.restoreCurrentPage(currentPageIndex); break; } } Loading Loading @@ -239,6 +243,7 @@ public class ContactDetailLayoutController { public void showEmptyState() { switch (mLayoutMode) { case FRAGMENT_CAROUSEL: { mFragmentCarousel.setCurrentPage(0); mFragmentCarousel.enableSwipe(false); mDetailFragment.showEmptyState(); break; Loading Loading @@ -323,6 +328,7 @@ public class ContactDetailLayoutController { break; case FRAGMENT_CAROUSEL: { // Disable swipe so only the detail fragment shows mFragmentCarousel.setCurrentPage(0); mFragmentCarousel.enableSwipe(false); break; } Loading Loading @@ -449,12 +455,14 @@ public class ContactDetailLayoutController { mTabCarousel, "y", desiredValue).setDuration(75); mTabCarouselAnimator.setInterpolator(AnimationUtils.loadInterpolator( mActivity, android.R.anim.accelerate_decelerate_interpolator)); mTabCarouselAnimator.addListener(mTabCarouselAnimatorListener); } private void cancelTabCarouselAnimator() { if (mTabCarouselAnimator != null) { mTabCarouselAnimator.cancel(); mTabCarouselAnimator = null; mTabCarouselIsAnimating = false; } } }; Loading @@ -478,6 +486,34 @@ public class ContactDetailLayoutController { } } /** * This listener keeps track of whether the tab carousel animation is currently going on or not, * in order to prevent other simultaneous changes to the Y position of the tab carousel which * can cause flicker. */ private final AnimatorListener mTabCarouselAnimatorListener = new AnimatorListener() { @Override public void onAnimationCancel(Animator animation) { mTabCarouselIsAnimating = false; } @Override public void onAnimationEnd(Animator animation) { mTabCarouselIsAnimating = false; } @Override public void onAnimationRepeat(Animator animation) { mTabCarouselIsAnimating = true; } @Override public void onAnimationStart(Animator animation) { mTabCarouselIsAnimating = true; } }; private final ContactDetailTabCarousel.Listener mTabCarouselListener = new ContactDetailTabCarousel.Listener() { Loading Loading @@ -529,10 +565,11 @@ public class ContactDetailLayoutController { int totalItemCount) { int currentPageIndex = mViewPager.getCurrentItem(); // Don't move the carousel if: 1) the contact does not have social updates because then // tab carousel must not be visible, 2) if the view pager is still being scrolled, or // 3) if the current page being viewed is not this one. // tab carousel must not be visible, 2) if the view pager is still being scrolled, // 3) if the current page being viewed is not this one, or 4) if the tab carousel // is already being animated vertically. if (!mContactHasUpdates || mViewPagerState != ViewPager.SCROLL_STATE_IDLE || mPageIndex != currentPageIndex) { mPageIndex != currentPageIndex || mTabCarouselIsAnimating) { return; } // If the FIRST item is not visible on the screen, then the carousel must be pinned Loading src/com/android/contacts/detail/ContactDetailTabCarousel.java +23 −9 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On private int mTabDisplayLabelHeight; private boolean mScrollToCurrentTab = false; private int mLastScrollPosition; private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE; Loading Loading @@ -103,12 +104,7 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update); mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates)); // TODO: We can't always assume the "about" page will be the current page. mAboutTab.showSelectedState(); mAboutTab.setAlphaLayerValue(0); mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener); mUpdatesTab.setAlphaLayerValue(MAX_ALPHA); mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener); // Retrieve the photo view for the "about" tab Loading Loading @@ -144,6 +140,15 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On resolveSize(tabHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (mScrollToCurrentTab) { mScrollToCurrentTab = false; scrollTo(mCurrentTab == TAB_INDEX_ABOUT ? 0 : mAllowedHorizontalScrollLength, 0); } } private final OnClickListener mAboutTabTouchInterceptListener = new OnClickListener() { @Override public void onClick(View v) { Loading Loading @@ -173,6 +178,17 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On updateAlphaLayers(); } /** * Set the current tab that should be restored when the view is first laid out. */ public void restoreCurrentTab(int position) { setCurrentTab(position); // It is only possible to scroll the view after onMeasure() has been called (where the // allowed horizontal scroll length is determined). Hence, set a flag that will be read // in onLayout() after the children and this view have finished being laid out. mScrollToCurrentTab = true; } /** * Restore the Y position of this view to the last manually requested value. This can be done * after the parent has been re-laid out again, where this view's position could have been Loading Loading @@ -225,8 +241,6 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On * Updates the tab selection. */ public void setCurrentTab(int position) { // TODO: Handle device rotation (saving and restoring state of the selected tab) // This will take more work because there is no tab carousel in phone landscape switch (position) { case TAB_INDEX_ABOUT: mAboutTab.showSelectedState(); Loading Loading @@ -270,10 +284,10 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mListener.onTouchDown(); return false; return true; case MotionEvent.ACTION_UP: mListener.onTouchUp(); return false; return true; } return super.onTouchEvent(event); } Loading Loading
src/com/android/contacts/detail/ContactDetailFragmentCarousel.java +24 −24 Original line number Diff line number Diff line Loading @@ -88,7 +88,7 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen private View mDetailFragmentView; private View mUpdatesFragmentView; private final Handler mHandler = new Handler(); private boolean mScrollToCurrentPage = false; public ContactDetailFragmentCarousel(Context context) { this(context, null); Loading Loading @@ -144,6 +144,28 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen resolveSize(screenHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (mScrollToCurrentPage) { mScrollToCurrentPage = false; // Use scrollTo() instead of smoothScrollTo() to prevent a visible flicker to the user scrollTo(mCurrentPage == ABOUT_PAGE ? 0 : mAllowedHorizontalScrollLength, 0); updateTouchInterceptors(); } } /** * Set the current page that should be restored when the view is first laid out. */ public void restoreCurrentPage(int pageIndex) { setCurrentPage(pageIndex); // It is only possible to scroll the view after onMeasure() has been called (where the // allowed horizontal scroll length is determined). Hence, set a flag that will be read // in onLayout() after the children and this view have finished being laid out. mScrollToCurrentPage = true; } /** * Set the current page. This auto-scrolls the carousel to the current page and dims out * the non-selected page. Loading Loading @@ -183,31 +205,13 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen mEnableSwipe = enable; if (mUpdatesFragmentView != null) { mUpdatesFragmentView.setVisibility(enable ? View.VISIBLE : View.GONE); mScrollToCurrentPage = true; requestLayout(); invalidate(); } // This method could have been called before the view has been measured (i.e. // immediately after a rotation), so snap to edge only after the view is ready. postRunnableToSnapToEdge(); } } /** * Snap to the currently selected page only once all the view setup and measurement has * completed (i.e. we need to know the allowed horizontal scroll width in order to * snap to the correct page). */ private void postRunnableToSnapToEdge() { mHandler.post(new Runnable() { @Override public void run() { if (isAttachedToWindow() && mAboutFragment != null && mUpdatesFragment != null) { snapToEdge(); } } }); } public int getCurrentPage() { return mCurrentPage; } Loading Loading @@ -302,8 +306,4 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen } return false; } private boolean isAttachedToWindow() { return getWindowToken() != null; } }
src/com/android/contacts/detail/ContactDetailLayoutController.java +41 −4 Original line number Diff line number Diff line Loading @@ -21,6 +21,8 @@ import com.android.contacts.NfcHandler; import com.android.contacts.R; import com.android.contacts.activities.ContactDetailActivity.FragmentKeyListener; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.ObjectAnimator; import android.app.Activity; import android.app.FragmentManager; Loading Loading @@ -77,6 +79,7 @@ public class ContactDetailLayoutController { private ContactLoader.Result mContactData; private boolean mTabCarouselIsAnimating; private boolean mContactHasUpdates; private LayoutMode mLayoutMode; Loading Loading @@ -176,6 +179,7 @@ public class ContactDetailLayoutController { } mTabCarousel.setListener(mTabCarouselListener); mTabCarousel.restoreCurrentTab(currentPageIndex); mDetailFragment.setVerticalScrollListener( new VerticalScrollListener(TAB_INDEX_DETAIL)); mUpdatesFragment.setVerticalScrollListener( Loading Loading @@ -211,7 +215,7 @@ public class ContactDetailLayoutController { mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView); mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment); mFragmentCarousel.setCurrentPage(currentPageIndex); mFragmentCarousel.restoreCurrentPage(currentPageIndex); break; } } Loading Loading @@ -239,6 +243,7 @@ public class ContactDetailLayoutController { public void showEmptyState() { switch (mLayoutMode) { case FRAGMENT_CAROUSEL: { mFragmentCarousel.setCurrentPage(0); mFragmentCarousel.enableSwipe(false); mDetailFragment.showEmptyState(); break; Loading Loading @@ -323,6 +328,7 @@ public class ContactDetailLayoutController { break; case FRAGMENT_CAROUSEL: { // Disable swipe so only the detail fragment shows mFragmentCarousel.setCurrentPage(0); mFragmentCarousel.enableSwipe(false); break; } Loading Loading @@ -449,12 +455,14 @@ public class ContactDetailLayoutController { mTabCarousel, "y", desiredValue).setDuration(75); mTabCarouselAnimator.setInterpolator(AnimationUtils.loadInterpolator( mActivity, android.R.anim.accelerate_decelerate_interpolator)); mTabCarouselAnimator.addListener(mTabCarouselAnimatorListener); } private void cancelTabCarouselAnimator() { if (mTabCarouselAnimator != null) { mTabCarouselAnimator.cancel(); mTabCarouselAnimator = null; mTabCarouselIsAnimating = false; } } }; Loading @@ -478,6 +486,34 @@ public class ContactDetailLayoutController { } } /** * This listener keeps track of whether the tab carousel animation is currently going on or not, * in order to prevent other simultaneous changes to the Y position of the tab carousel which * can cause flicker. */ private final AnimatorListener mTabCarouselAnimatorListener = new AnimatorListener() { @Override public void onAnimationCancel(Animator animation) { mTabCarouselIsAnimating = false; } @Override public void onAnimationEnd(Animator animation) { mTabCarouselIsAnimating = false; } @Override public void onAnimationRepeat(Animator animation) { mTabCarouselIsAnimating = true; } @Override public void onAnimationStart(Animator animation) { mTabCarouselIsAnimating = true; } }; private final ContactDetailTabCarousel.Listener mTabCarouselListener = new ContactDetailTabCarousel.Listener() { Loading Loading @@ -529,10 +565,11 @@ public class ContactDetailLayoutController { int totalItemCount) { int currentPageIndex = mViewPager.getCurrentItem(); // Don't move the carousel if: 1) the contact does not have social updates because then // tab carousel must not be visible, 2) if the view pager is still being scrolled, or // 3) if the current page being viewed is not this one. // tab carousel must not be visible, 2) if the view pager is still being scrolled, // 3) if the current page being viewed is not this one, or 4) if the tab carousel // is already being animated vertically. if (!mContactHasUpdates || mViewPagerState != ViewPager.SCROLL_STATE_IDLE || mPageIndex != currentPageIndex) { mPageIndex != currentPageIndex || mTabCarouselIsAnimating) { return; } // If the FIRST item is not visible on the screen, then the carousel must be pinned Loading
src/com/android/contacts/detail/ContactDetailTabCarousel.java +23 −9 Original line number Diff line number Diff line Loading @@ -63,6 +63,7 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On private int mTabDisplayLabelHeight; private boolean mScrollToCurrentTab = false; private int mLastScrollPosition; private int mAllowedHorizontalScrollLength = Integer.MIN_VALUE; Loading Loading @@ -103,12 +104,7 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On mUpdatesTab = (CarouselTab) findViewById(R.id.tab_update); mUpdatesTab.setLabel(mContext.getString(R.string.contactDetailUpdates)); // TODO: We can't always assume the "about" page will be the current page. mAboutTab.showSelectedState(); mAboutTab.setAlphaLayerValue(0); mAboutTab.enableTouchInterceptor(mAboutTabTouchInterceptListener); mUpdatesTab.setAlphaLayerValue(MAX_ALPHA); mUpdatesTab.enableTouchInterceptor(mUpdatesTabTouchInterceptListener); // Retrieve the photo view for the "about" tab Loading Loading @@ -144,6 +140,15 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On resolveSize(tabHeight, heightMeasureSpec)); } @Override protected void onLayout(boolean changed, int l, int t, int r, int b) { super.onLayout(changed, l, t, r, b); if (mScrollToCurrentTab) { mScrollToCurrentTab = false; scrollTo(mCurrentTab == TAB_INDEX_ABOUT ? 0 : mAllowedHorizontalScrollLength, 0); } } private final OnClickListener mAboutTabTouchInterceptListener = new OnClickListener() { @Override public void onClick(View v) { Loading Loading @@ -173,6 +178,17 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On updateAlphaLayers(); } /** * Set the current tab that should be restored when the view is first laid out. */ public void restoreCurrentTab(int position) { setCurrentTab(position); // It is only possible to scroll the view after onMeasure() has been called (where the // allowed horizontal scroll length is determined). Hence, set a flag that will be read // in onLayout() after the children and this view have finished being laid out. mScrollToCurrentTab = true; } /** * Restore the Y position of this view to the last manually requested value. This can be done * after the parent has been re-laid out again, where this view's position could have been Loading Loading @@ -225,8 +241,6 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On * Updates the tab selection. */ public void setCurrentTab(int position) { // TODO: Handle device rotation (saving and restoring state of the selected tab) // This will take more work because there is no tab carousel in phone landscape switch (position) { case TAB_INDEX_ABOUT: mAboutTab.showSelectedState(); Loading Loading @@ -270,10 +284,10 @@ public class ContactDetailTabCarousel extends HorizontalScrollView implements On switch (event.getAction()) { case MotionEvent.ACTION_DOWN: mListener.onTouchDown(); return false; return true; case MotionEvent.ACTION_UP: mListener.onTouchUp(); return false; return true; } return super.onTouchEvent(event); } Loading