Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit d340308d authored by Katherine Kuan's avatar Katherine Kuan Committed by Android (Google) Code Review
Browse files

Merge "Fix tab carousel flicker issues"

parents 3deb35da 226dcdf3
Loading
Loading
Loading
Loading
+24 −24
Original line number Diff line number Diff line
@@ -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);
@@ -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.
@@ -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;
    }
@@ -302,8 +306,4 @@ public class ContactDetailFragmentCarousel extends HorizontalScrollView implemen
        }
        return false;
    }

    private boolean isAttachedToWindow() {
        return getWindowToken() != null;
    }
}
+41 −4
Original line number Diff line number Diff line
@@ -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;
@@ -77,6 +79,7 @@ public class ContactDetailLayoutController {

    private ContactLoader.Result mContactData;

    private boolean mTabCarouselIsAnimating;
    private boolean mContactHasUpdates;

    private LayoutMode mLayoutMode;
@@ -176,6 +179,7 @@ public class ContactDetailLayoutController {
                }

                mTabCarousel.setListener(mTabCarouselListener);
                mTabCarousel.restoreCurrentTab(currentPageIndex);
                mDetailFragment.setVerticalScrollListener(
                        new VerticalScrollListener(TAB_INDEX_DETAIL));
                mUpdatesFragment.setVerticalScrollListener(
@@ -211,7 +215,7 @@ public class ContactDetailLayoutController {

                mFragmentCarousel.setFragmentViews(mDetailFragmentView, mUpdatesFragmentView);
                mFragmentCarousel.setFragments(mDetailFragment, mUpdatesFragment);
                mFragmentCarousel.setCurrentPage(currentPageIndex);
                mFragmentCarousel.restoreCurrentPage(currentPageIndex);
                break;
            }
        }
@@ -239,6 +243,7 @@ public class ContactDetailLayoutController {
    public void showEmptyState() {
        switch (mLayoutMode) {
            case FRAGMENT_CAROUSEL: {
                mFragmentCarousel.setCurrentPage(0);
                mFragmentCarousel.enableSwipe(false);
                mDetailFragment.showEmptyState();
                break;
@@ -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;
            }
@@ -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;
            }
        }
    };
@@ -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() {

@@ -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
+23 −9
Original line number Diff line number Diff line
@@ -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;
@@ -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
@@ -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) {
@@ -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
@@ -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();
@@ -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);
    }