1e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira/* 2e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Copyright (C) 2013 The Android Open Source Project 3e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * 4e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Licensed under the Apache License, Version 2.0 (the "License"); 5e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * you may not use this file except in compliance with the License. 6e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * You may obtain a copy of the License at 7e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * 8e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * http://www.apache.org/licenses/LICENSE-2.0 9e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * 10e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Unless required by applicable law or agreed to in writing, software 11e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * distributed under the License is distributed on an "AS IS" BASIS, 12e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * See the License for the specific language governing permissions and 14e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * limitations under the License. 15e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 16e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 17e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereirapackage android.support.v4.widget; 18e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 19e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.content.Context; 20e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.content.res.Resources; 216611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereiraimport android.content.res.TypedArray; 2270acb0c19be3831a2080e4f902324de16bfbf62eTor Norbyeimport android.support.annotation.ColorInt; 2370acb0c19be3831a2080e4f902324de16bfbf62eTor Norbyeimport android.support.annotation.ColorRes; 2471e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikasimport android.support.annotation.VisibleForTesting; 25272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banesimport android.support.v4.view.MotionEventCompat; 26359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banesimport android.support.v4.view.NestedScrollingChild; 27359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banesimport android.support.v4.view.NestedScrollingChildHelper; 28da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereiraimport android.support.v4.view.NestedScrollingParent; 29da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereiraimport android.support.v4.view.NestedScrollingParentHelper; 30e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.support.v4.view.ViewCompat; 31e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.util.AttributeSet; 32e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.util.DisplayMetrics; 33272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banesimport android.util.Log; 34e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.MotionEvent; 35e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.View; 36e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.ViewConfiguration; 37e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.ViewGroup; 38e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.animation.Animation; 39e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.animation.Animation.AnimationListener; 40e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.animation.DecelerateInterpolator; 41e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereiraimport android.view.animation.Transformation; 42f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpiniimport android.widget.AbsListView; 43e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 44e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira/** 45e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * The SwipeRefreshLayout should be used whenever the user can refresh the 46e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * contents of a view via a vertical swipe gesture. The activity that 47e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * instantiates this view should add an OnRefreshListener to be notified 48e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout 49e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * will notify the listener each and every time the gesture is completed again; 50e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * the listener is responsible for correctly determining when to actually 51e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * initiate a refresh of its content. If the listener determines there should 52e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * not be a refresh, it must call setRefreshing(false) to cancel any visual 53e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * indication of a refresh. If an activity wishes to show just the progress 54689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * animation, it should call setRefreshing(true). To disable the gesture and 55689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * progress animation, call setEnabled(false) on the view. 56689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * <p> 57689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * This layout should be made the parent of the view that will be refreshed as a 58ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn * result of the gesture and can only support one direct child. This view will 59ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn * also be made the target of the gesture and will be forced to match both the 60e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * width and the height supplied in this layout. The SwipeRefreshLayout does not 61e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * provide accessibility events; instead, a menu item must be provided to allow 62689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * refresh of the content wherever this gesture is used. 63689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * </p> 64e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 65359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banespublic class SwipeRefreshLayout extends ViewGroup implements NestedScrollingParent, 66359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes NestedScrollingChild { 67689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // Maps to ProgressBar.Large style 68689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public static final int LARGE = MaterialProgressDrawable.LARGE; 69689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // Maps to ProgressBar default style 70689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public static final int DEFAULT = MaterialProgressDrawable.DEFAULT; 71689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 7271e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikas @VisibleForTesting 7371e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikas static final int CIRCLE_DIAMETER = 40; 7471e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikas @VisibleForTesting 7571e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikas static final int CIRCLE_DIAMETER_LARGE = 56; 7671e4b67c03aac59452e02a4152efafcf6e94ee7fAurimas Liutikas 77272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private static final String LOG_TAG = SwipeRefreshLayout.class.getSimpleName(); 78272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 79d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira private static final int MAX_ALPHA = 255; 80dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final int STARTING_PROGRESS_ALPHA = (int) (.3f * MAX_ALPHA); 81d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira 82e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private static final float DECELERATE_INTERPOLATION_FACTOR = 2f; 83272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private static final int INVALID_POINTER = -1; 84689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private static final float DRAG_RATE = .5f; 85dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 86dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Max amount of circle that can be filled by progress during swipe gesture, 87dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // where 1.0 is a full circle 88dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final float MAX_PROGRESS_ANGLE = .8f; 89dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 90dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final int SCALE_DOWN_DURATION = 150; 91dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 92dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final int ALPHA_ANIMATION_DURATION = 300; 93dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 94dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final int ANIMATE_TO_TRIGGER_DURATION = 200; 95dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 96dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private static final int ANIMATE_TO_START_DURATION = 200; 97689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 98689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // Default background for the progress spinner 99689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private static final int CIRCLE_BG_LIGHT = 0xFFFAFAFA; 1000f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira // Default offset in dips from the top of the view to where the progress spinner should stop 101689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private static final int DEFAULT_CIRCLE_TARGET = 64; 102e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 103689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private View mTarget; // the target of the gesture 104e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private OnRefreshListener mListener; 105e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private boolean mRefreshing = false; 106e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private int mTouchSlop; 107dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private float mTotalDragDistance = -1; 108fc60a78760589b76087e2973add98d0aa35d50abChris Banes 109da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // If nested scrolling is enabled, the total amount that needed to be 110da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // consumed by this as the nested scrolling parent is used in place of the 111da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // overscroll determined by MOVE events in the onTouch handler 112da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira private float mTotalUnconsumed; 113359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes private final NestedScrollingParentHelper mNestedScrollingParentHelper; 114359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes private final NestedScrollingChildHelper mNestedScrollingChildHelper; 115359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes private final int[] mParentScrollConsumed = new int[2]; 116fc60a78760589b76087e2973add98d0aa35d50abChris Banes private final int[] mParentOffsetInWindow = new int[2]; 117fc60a78760589b76087e2973add98d0aa35d50abChris Banes private boolean mNestedScrollInProgress; 118359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 119e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private int mMediumAnimationDuration; 120e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private int mCurrentTargetOffsetTop; 121cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira // Whether or not the starting offset has been determined. 122cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira private boolean mOriginalOffsetCalculated = false; 123272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 124272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private float mInitialMotionY; 1259c6db363573da735f9ecf15e68e2c2c375acc657Mindy Pereira private float mInitialDownY; 126272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private boolean mIsBeingDragged; 127272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private int mActivePointerId = INVALID_POINTER; 128689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // Whether this item is scaled up rather than clipped 129689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private boolean mScale; 130272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 131e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira // Target is returning to its start offset because it was cancelled or a 132e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira // refresh was triggered. 133e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private boolean mReturningToStart; 134e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private final DecelerateInterpolator mDecelerateInterpolator; 1356611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira private static final int[] LAYOUT_ATTRS = new int[] { 1366611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira android.R.attr.enabled 1376611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira }; 138e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 139689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private CircleImageView mCircleView; 1404e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira private int mCircleViewIndex = -1; 141689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 142689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira protected int mFrom; 143689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 1445e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira private float mStartingScale; 1455e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira 146689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira protected int mOriginalOffsetTop; 147689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 148689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private MaterialProgressDrawable mProgress; 149689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 150689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private Animation mScaleAnimation; 15112ffe36178df269e0c2d3b33f7de360e74c63f71Mindy Pereira 152689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private Animation mScaleDownAnimation; 153689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 154dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private Animation mAlphaStartAnimation; 155dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 156dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private Animation mAlphaMaxAnimation; 157689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 1585e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira private Animation mScaleDownToStartAnimation; 1595e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira 160dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private float mSpinnerFinalOffset; 161689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 162689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private boolean mNotify; 163689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 164689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private int mCircleWidth; 165689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 166689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private int mCircleHeight; 167689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 168cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira // Whether the client has set a custom starting position; 169cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira private boolean mUsingCustomStart; 170cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira 171689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private Animation.AnimationListener mRefreshListener = new Animation.AnimationListener() { 172e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 173689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void onAnimationStart(Animation animation) { 174e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 175e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 176e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 177689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void onAnimationRepeat(Animation animation) { 178e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 179e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 180e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 181e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void onAnimationEnd(Animation animation) { 182689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (mRefreshing) { 183dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Make sure the progress view is fully visible 184dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mProgress.setAlpha(MAX_ALPHA); 185689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mProgress.start(); 186689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (mNotify) { 187689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (mListener != null) { 188689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mListener.onRefresh(); 189689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 190689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 191fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas mCurrentTargetOffsetTop = mCircleView.getTop(); 192689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } else { 193fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas reset(); 194689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 195e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 196e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira }; 197e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 198fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas private void reset() { 199fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas mCircleView.clearAnimation(); 200fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas mProgress.stop(); 201fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas mCircleView.setVisibility(View.GONE); 202fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas setColorViewAlpha(MAX_ALPHA); 203fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas // Return the circle to its start position 204fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas if (mScale) { 205fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas setAnimationProgress(0 /* animation complete and view is hidden */); 206fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas } else { 207fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCurrentTargetOffsetTop, 208fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas true /* requires update */); 209fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas } 210fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas mCurrentTargetOffsetTop = mCircleView.getTop(); 211fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas } 212fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas 213fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas @Override 214fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas protected void onDetachedFromWindow() { 215fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas super.onDetachedFromWindow(); 216fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas reset(); 217fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas } 218fe8329efc8f69a64a03fee24b9ceee1aa2474711Aurimas Liutikas 219d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira private void setColorViewAlpha(int targetAlpha) { 220d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira mCircleView.getBackground().setAlpha(targetAlpha); 221d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira mProgress.setAlpha(targetAlpha); 222d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira } 223d0b65a16762b554ee6531844b021e3e0975cbf18Mindy Pereira 224689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira /** 225689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * The refresh indicator starting and resting position is always positioned 226689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * near the top of the refreshing content. This position is a consistent 227689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * location, but can be adjusted in either direction based on whether or not 228689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * there is a toolbar or actionbar present. 229689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 230689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param scale Set to true if there is no view at a higher z-order than 231689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * where the progress spinner is set to appear. 232689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param start The offset in pixels from the top of this view at which the 233689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * progress spinner should appear. 234689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param end The offset in pixels from the top of this view at which the 235689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * progress spinner should come to rest after a successful swipe 236689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * gesture. 237689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira */ 238689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void setProgressViewOffset(boolean scale, int start, int end) { 239689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mScale = scale; 240689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.setVisibility(View.GONE); 241689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mOriginalOffsetTop = mCurrentTargetOffsetTop = start; 242dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mSpinnerFinalOffset = end; 243cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira mUsingCustomStart = true; 244689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.invalidate(); 245689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 246e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 247689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira /** 248dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * The refresh indicator resting position is always positioned near the top 249dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * of the refreshing content. This position is a consistent location, but 250dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * can be adjusted in either direction based on whether or not there is a 251dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * toolbar or actionbar present. 252dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * 253dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * @param scale Set to true if there is no view at a higher z-order than 254dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * where the progress spinner is set to appear. 255dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * @param end The offset in pixels from the top of this view at which the 256dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * progress spinner should come to rest after a successful swipe 257dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * gesture. 258dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira */ 259dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira public void setProgressViewEndTarget(boolean scale, int end) { 260dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mSpinnerFinalOffset = end; 261dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mScale = scale; 262dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mCircleView.invalidate(); 263dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 264dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 265dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira /** 266dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * One of DEFAULT, or LARGE. 267689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira */ 268689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void setSize(int size) { 269dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (size != MaterialProgressDrawable.LARGE && size != MaterialProgressDrawable.DEFAULT) { 270689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira return; 271e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 272689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira final DisplayMetrics metrics = getResources().getDisplayMetrics(); 273689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (size == MaterialProgressDrawable.LARGE) { 274689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER_LARGE * metrics.density); 275689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } else { 276689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleHeight = mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density); 277e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 278dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira // force the bounds of the progress circle inside the circle view to 279dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira // update by setting it to null before updating its size and then 280dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira // re-setting it 281dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mCircleView.setImageDrawable(null); 282689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mProgress.updateSizes(size); 283dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mCircleView.setImageDrawable(mProgress); 284689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 285e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 286e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 287e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Simple constructor to use when creating a SwipeRefreshLayout from code. 288689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 289e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * @param context 290e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 291e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public SwipeRefreshLayout(Context context) { 292e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira this(context, null); 293e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 294e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 295e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 296e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Constructor that is called when inflating SwipeRefreshLayout from XML. 297689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 298e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * @param context 2996611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira * @param attrs 300e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 3016611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira public SwipeRefreshLayout(Context context, AttributeSet attrs) { 3026611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira super(context, attrs); 303e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 304e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 305e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 306e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mMediumAnimationDuration = getResources().getInteger( 307e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira android.R.integer.config_mediumAnimTime); 308e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 309e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira setWillNotDraw(false); 310e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mDecelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR); 3116611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira 3126611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS); 3136611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira setEnabled(a.getBoolean(0, true)); 3146611d8cf18999a874e37245e9ecf269e0e69846bMindy Pereira a.recycle(); 315e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 316689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira final DisplayMetrics metrics = getResources().getDisplayMetrics(); 317689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleWidth = (int) (CIRCLE_DIAMETER * metrics.density); 318689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleHeight = (int) (CIRCLE_DIAMETER * metrics.density); 319689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 3200f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira createProgressView(); 3210f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira ViewCompat.setChildrenDrawingOrderEnabled(this, true); 322dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira // the absolute offset has to take into account that the circle starts at an offset 323dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mSpinnerFinalOffset = DEFAULT_CIRCLE_TARGET * metrics.density; 324dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mTotalDragDistance = mSpinnerFinalOffset; 325da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mNestedScrollingParentHelper = new NestedScrollingParentHelper(this); 326359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 327359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes mNestedScrollingChildHelper = new NestedScrollingChildHelper(this); 328359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes setNestedScrollingEnabled(true); 329e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 330e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 3314e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira protected int getChildDrawingOrder(int childCount, int i) { 3324e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira if (mCircleViewIndex < 0) { 3334e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira return i; 3344e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira } else if (i == childCount - 1) { 3354e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira // Draw the selected child last 3364e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira return mCircleViewIndex; 3374e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira } else if (i >= mCircleViewIndex) { 3384e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira // Move the children after the selected child earlier one 3394e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira return i + 1; 3404e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira } else { 3414e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira // Keep the children before the selected child the same 3424e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira return i; 3430f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 3440f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 3450f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira 3460f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira private void createProgressView() { 34796dfc170940d2f7218317242115eda1273863966Mindy Pereira mCircleView = new CircleImageView(getContext(), CIRCLE_BG_LIGHT, CIRCLE_DIAMETER/2); 348689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mProgress = new MaterialProgressDrawable(getContext(), this); 349dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira mProgress.setBackgroundColor(CIRCLE_BG_LIGHT); 350689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.setImageDrawable(mProgress); 351e51e32e8c7ce74a19e1ad69c75f0b6d5dadb313cMindy Pereira mCircleView.setVisibility(View.GONE); 3520f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira addView(mCircleView); 353e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 354e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 355e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 356e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Set the listener to be notified when a refresh is triggered via the swipe 357e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * gesture. 358e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 359e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void setOnRefreshListener(OnRefreshListener listener) { 360e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mListener = listener; 361e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 362e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 363e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 364dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * Pre API 11, alpha is used to make the progress circle appear instead of scale. 365dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira */ 366dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private boolean isAlphaUsedForScale() { 367dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return android.os.Build.VERSION.SDK_INT < 11; 368dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 369dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 370dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira /** 371e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Notify the widget that refresh state has changed. Do not call this when 372e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * refresh is triggered by a swipe gesture. 373e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * 374e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * @param refreshing Whether or not the view should show refresh progress. 375e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 376e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void setRefreshing(boolean refreshing) { 377689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (refreshing && mRefreshing != refreshing) { 378689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // scale and show 379689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mRefreshing = refreshing; 3805e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira int endTarget = 0; 3815e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (!mUsingCustomStart) { 3825e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira endTarget = (int) (mSpinnerFinalOffset + mOriginalOffsetTop); 3835e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } else { 3845e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira endTarget = (int) mSpinnerFinalOffset; 3855e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 3865e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira setTargetOffsetTopAndBottom(endTarget - mCurrentTargetOffsetTop, 387689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira true /* requires update */); 388689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mNotify = false; 389689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira startScaleUpAnimation(mRefreshListener); 390689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } else { 391689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira setRefreshing(refreshing, false /* notify */); 392689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 393689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 394689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 395689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void startScaleUpAnimation(AnimationListener listener) { 396689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.setVisibility(View.VISIBLE); 397dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (android.os.Build.VERSION.SDK_INT >= 11) { 398dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Pre API 11, alpha is used in place of scale up to show the 399dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // progress circle appearing. 400dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Don't adjust the alpha during appearance otherwise. 401dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mProgress.setAlpha(MAX_ALPHA); 402dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 403689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mScaleAnimation = new Animation() { 404689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira @Override 405689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 40674437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira setAnimationProgress(interpolatedTime); 407689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 408689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira }; 409689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mScaleAnimation.setDuration(mMediumAnimationDuration); 410689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (listener != null) { 411689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.setAnimationListener(listener); 412689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 413c27bc76d7eac50363e6cd063ae41996833f52ebeMindy Pereira mCircleView.clearAnimation(); 414689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.startAnimation(mScaleAnimation); 415689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 416689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 41774437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira /** 41874437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira * Pre API 11, this does an alpha animation. 41974437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira * @param progress 42074437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira */ 42174437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira private void setAnimationProgress(float progress) { 422dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (isAlphaUsedForScale()) { 42374437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira setColorViewAlpha((int) (progress * MAX_ALPHA)); 42474437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira } else { 42574437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira ViewCompat.setScaleX(mCircleView, progress); 42674437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira ViewCompat.setScaleY(mCircleView, progress); 42774437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira } 42874437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira } 42974437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira 430689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void setRefreshing(boolean refreshing, final boolean notify) { 431e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira if (mRefreshing != refreshing) { 432689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mNotify = notify; 433e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira ensureTarget(); 434e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mRefreshing = refreshing; 435e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira if (mRefreshing) { 436689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira animateOffsetToCorrectPosition(mCurrentTargetOffsetTop, mRefreshListener); 437e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } else { 438689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira startScaleDownAnimation(mRefreshListener); 439e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 440e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 441e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 442e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 443689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void startScaleDownAnimation(Animation.AnimationListener listener) { 444689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mScaleDownAnimation = new Animation() { 445689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira @Override 446689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 44774437a01478b39230bb5b4657b74480ede53ae94Mindy Pereira setAnimationProgress(1 - interpolatedTime); 448689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 449689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira }; 450dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mScaleDownAnimation.setDuration(SCALE_DOWN_DURATION); 451e51e32e8c7ce74a19e1ad69c75f0b6d5dadb313cMindy Pereira mCircleView.setAnimationListener(listener); 452c27bc76d7eac50363e6cd063ae41996833f52ebeMindy Pereira mCircleView.clearAnimation(); 453689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.startAnimation(mScaleDownAnimation); 454689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 455689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 456dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private void startProgressAlphaStartAnimation() { 457dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mAlphaStartAnimation = startAlphaAnimation(mProgress.getAlpha(), STARTING_PROGRESS_ALPHA); 458dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 459dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 460dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private void startProgressAlphaMaxAnimation() { 461dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mAlphaMaxAnimation = startAlphaAnimation(mProgress.getAlpha(), MAX_ALPHA); 462dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 463dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 464dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private Animation startAlphaAnimation(final int startingAlpha, final int endingAlpha) { 465dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Pre API 11, alpha is used in place of scale. Don't also use it to 466dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // show the trigger point. 467dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (mScale && isAlphaUsedForScale()) { 468dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return null; 469dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 470dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira Animation alpha = new Animation() { 471dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira @Override 472dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 473dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mProgress 474dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira .setAlpha((int) (startingAlpha+ ((endingAlpha - startingAlpha) 475dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira * interpolatedTime))); 476dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 477dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira }; 478dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira alpha.setDuration(ALPHA_ANIMATION_DURATION); 479dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira // Clear out the previous animation listeners. 480dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mCircleView.setAnimationListener(null); 481dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mCircleView.clearAnimation(); 482dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mCircleView.startAnimation(alpha); 483dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return alpha; 484dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 485dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 486d08402a890f1ac4d3de832a9a781cbc5ebde804fMindy Pereira /** 487d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira * @deprecated Use {@link #setProgressBackgroundColorSchemeResource(int)} 488d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira */ 489d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira @Deprecated 490d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira public void setProgressBackgroundColor(int colorRes) { 491d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira setProgressBackgroundColorSchemeResource(colorRes); 492d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira } 493d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira 494d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira /** 495d08402a890f1ac4d3de832a9a781cbc5ebde804fMindy Pereira * Set the background color of the progress spinner disc. 496d08402a890f1ac4d3de832a9a781cbc5ebde804fMindy Pereira * 497d08402a890f1ac4d3de832a9a781cbc5ebde804fMindy Pereira * @param colorRes Resource id of the color. 498d08402a890f1ac4d3de832a9a781cbc5ebde804fMindy Pereira */ 49970acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye public void setProgressBackgroundColorSchemeResource(@ColorRes int colorRes) { 500d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira setProgressBackgroundColorSchemeColor(getResources().getColor(colorRes)); 501d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira } 502d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira 503d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira /** 504d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira * Set the background color of the progress spinner disc. 505d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira * 506d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira * @param color 507d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira */ 50870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye public void setProgressBackgroundColorSchemeColor(@ColorInt int color) { 509d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira mCircleView.setBackgroundColor(color); 510d9df810a8e9ac28148c853249f0951329053c784Mindy Pereira mProgress.setBackgroundColor(color); 511689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 512689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 513e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 514689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @deprecated Use {@link #setColorSchemeResources(int...)} 5155f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes */ 5165f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes @Deprecated 51770acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye public void setColorScheme(@ColorInt int... colors) { 518689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira setColorSchemeResources(colors); 5195f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes } 5205f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes 5215f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes /** 522689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * Set the color resources used in the progress animation from color resources. 5235f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes * The first color will also be the color of the bar that grows in response 5245f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes * to a user swipe gesture. 525689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 526689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param colorResIds 5275f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes */ 52870acb0c19be3831a2080e4f902324de16bfbf62eTor Norbye public void setColorSchemeResources(@ColorRes int... colorResIds) { 5295f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes final Resources res = getResources(); 530689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira int[] colorRes = new int[colorResIds.length]; 531689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira for (int i = 0; i < colorResIds.length; i++) { 532689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira colorRes[i] = res.getColor(colorResIds[i]); 533689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 534689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira setColorSchemeColors(colorRes); 5355f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes } 5365f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes 5375f7fb42c9ff8a1e43a5462d19b9fcd9741f6895bChris Banes /** 538689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * Set the colors used in the progress animation. The first 539689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * color will also be the color of the bar that grows in response to a user 540689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * swipe gesture. 541689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 542689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param colors 543e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 544cb9a4f5c5c208c5525b8f3cceb0a8e714cb8f724Aurimas Liutikas public void setColorSchemeColors(@ColorInt int... colors) { 545e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira ensureTarget(); 546689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mProgress.setColorSchemeColors(colors); 547e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 548e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 549e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 550e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * @return Whether the SwipeRefreshWidget is actively showing refresh 551e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * progress. 552e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 553e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public boolean isRefreshing() { 554e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira return mRefreshing; 555e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 556e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 557e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira private void ensureTarget() { 558689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // Don't bother getting the parent height if the parent hasn't been laid 559689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira // out yet. 560e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira if (mTarget == null) { 5610f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira for (int i = 0; i < getChildCount(); i++) { 5620f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira View child = getChildAt(i); 5630f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira if (!child.equals(mCircleView)) { 5640f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira mTarget = child; 5650f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira break; 5660f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 5670f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 568e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 569e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 570e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 571689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira /** 572689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * Set the distance to trigger a sync in dips 573689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * 574689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira * @param distance 575689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira */ 576689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void setDistanceToTriggerSync(int distance) { 577dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mTotalDragDistance = distance; 578e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 579e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 580e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 581e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 582689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira final int width = getMeasuredWidth(); 583e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira final int height = getMeasuredHeight(); 584e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira if (getChildCount() == 0) { 585e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira return; 586e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 5870f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira if (mTarget == null) { 5880f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira ensureTarget(); 5890f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 5900f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira if (mTarget == null) { 5910f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira return; 5920f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 5930f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira final View child = mTarget; 594e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira final int childLeft = getPaddingLeft(); 595689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira final int childTop = getPaddingTop(); 596e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira final int childWidth = width - getPaddingLeft() - getPaddingRight(); 597e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira final int childHeight = height - getPaddingTop() - getPaddingBottom(); 598e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight); 599c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira int circleWidth = mCircleView.getMeasuredWidth(); 600c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira int circleHeight = mCircleView.getMeasuredHeight(); 601c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira mCircleView.layout((width / 2 - circleWidth / 2), mCurrentTargetOffsetTop, 602c39bdbe9d24491caf92194229393ff58c16aed7fMindy Pereira (width / 2 + circleWidth / 2), mCurrentTargetOffsetTop + circleHeight); 603e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 604e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 605e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 606e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 607e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira super.onMeasure(widthMeasureSpec, heightMeasureSpec); 6080f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira if (mTarget == null) { 6090f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira ensureTarget(); 610e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 6110f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira if (mTarget == null) { 6120f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira return; 6130f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira } 6140f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira mTarget.measure(MeasureSpec.makeMeasureSpec( 6150f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira getMeasuredWidth() - getPaddingLeft() - getPaddingRight(), 6160f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec( 6170f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira getMeasuredHeight() - getPaddingTop() - getPaddingBottom(), MeasureSpec.EXACTLY)); 6180f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira mCircleView.measure(MeasureSpec.makeMeasureSpec(mCircleWidth, MeasureSpec.EXACTLY), 6190f3c6c1be783cfb67a2d11a1a027de0847949064Mindy Pereira MeasureSpec.makeMeasureSpec(mCircleHeight, MeasureSpec.EXACTLY)); 620cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira if (!mUsingCustomStart && !mOriginalOffsetCalculated) { 621cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira mOriginalOffsetCalculated = true; 622cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira mCurrentTargetOffsetTop = mOriginalOffsetTop = -mCircleView.getMeasuredHeight(); 623cdf346714ac98eb4f40acf33f4328eb68edfb2efMindy Pereira } 6244e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira mCircleViewIndex = -1; 6254e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira // Get the index of the circleview. 6264e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira for (int index = 0; index < getChildCount(); index++) { 6274e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira if (getChildAt(index) == mCircleView) { 6284e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira mCircleViewIndex = index; 6294e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira break; 6304e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira } 6314e987010ab5116581826eb40aece85a46968cfa1Mindy Pereira } 632e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 633e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 634f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini /** 635062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira * Get the diameter of the progress circle that is displayed as part of the 636062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira * swipe to refresh layout. This is not valid until a measure pass has 637062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira * completed. 638062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira * 639062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira * @return Diameter in pixels of the progress circle view. 640062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira */ 641062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira public int getProgressCircleDiameter() { 642062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira return mCircleView != null ?mCircleView.getMeasuredHeight() : 0; 643062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira } 644062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira 645062bc5d81cac509fcd2feacf164e0bf4c009b2c7Mindy Pereira /** 646f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini * @return Whether it is possible for the child view of this layout to 647f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini * scroll up. Override this if the child view is a custom view. 648f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini */ 649f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini public boolean canChildScrollUp() { 650f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini if (android.os.Build.VERSION.SDK_INT < 14) { 651ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn if (mTarget instanceof AbsListView) { 652ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn final AbsListView absListView = (AbsListView) mTarget; 653f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini return absListView.getChildCount() > 0 654f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0) 655ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn .getTop() < absListView.getPaddingTop()); 656f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini } else { 657f9b9ce76405345744bd57482d21c35a657836f87Yigit Boyar return ViewCompat.canScrollVertically(mTarget, -1) || mTarget.getScrollY() > 0; 658f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini } 659f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini } else { 660ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn return ViewCompat.canScrollVertically(mTarget, -1); 661f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini } 662f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini } 663f25dedc8624a17b43426781516ac88e8ff01d5bdMindy DelliCarpini 664e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 665e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public boolean onInterceptTouchEvent(MotionEvent ev) { 666e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira ensureTarget(); 667272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 668272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final int action = MotionEventCompat.getActionMasked(ev); 669272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 670272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { 671e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira mReturningToStart = false; 672e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 673272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 674fc60a78760589b76087e2973add98d0aa35d50abChris Banes if (!isEnabled() || mReturningToStart || canChildScrollUp() 675fc60a78760589b76087e2973add98d0aa35d50abChris Banes || mRefreshing || mNestedScrollInProgress) { 676272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes // Fail fast if we're not in a state where a swipe is possible 677272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 678272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 679272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 680272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes switch (action) { 681272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEvent.ACTION_DOWN: 682689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira setTargetOffsetTopAndBottom(mOriginalOffsetTop - mCircleView.getTop(), true); 683272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 684272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mIsBeingDragged = false; 6859c6db363573da735f9ecf15e68e2c2c375acc657Mindy Pereira final float initialDownY = getMotionEventY(ev, mActivePointerId); 6869c6db363573da735f9ecf15e68e2c2c375acc657Mindy Pereira if (initialDownY == -1) { 687dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return false; 688dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 6899c6db363573da735f9ecf15e68e2c2c375acc657Mindy Pereira mInitialDownY = initialDownY; 69071ea2e2a57d48a75c0086964727931d8edf710d1Mindy Pereira break; 691272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 692272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEvent.ACTION_MOVE: 693272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (mActivePointerId == INVALID_POINTER) { 694272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes Log.e(LOG_TAG, "Got ACTION_MOVE event but don't have an active pointer id."); 695272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 696272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 697272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 698dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira final float y = getMotionEventY(ev, mActivePointerId); 699dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (y == -1) { 700272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 701272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 7029c6db363573da735f9ecf15e68e2c2c375acc657Mindy Pereira final float yDiff = y - mInitialDownY; 703dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (yDiff > mTouchSlop && !mIsBeingDragged) { 70471ea2e2a57d48a75c0086964727931d8edf710d1Mindy Pereira mInitialMotionY = mInitialDownY + mTouchSlop; 705272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mIsBeingDragged = true; 706dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mProgress.setAlpha(STARTING_PROGRESS_ALPHA); 707272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 708272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes break; 709272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 710272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEventCompat.ACTION_POINTER_UP: 711272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes onSecondaryPointerUp(ev); 712272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes break; 713272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 714272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEvent.ACTION_UP: 715272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEvent.ACTION_CANCEL: 716ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn mIsBeingDragged = false; 717ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn mActivePointerId = INVALID_POINTER; 718272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes break; 719e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 720272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 721272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return mIsBeingDragged; 722e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 723e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 724dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private float getMotionEventY(MotionEvent ev, int activePointerId) { 725dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira final int index = MotionEventCompat.findPointerIndex(ev, activePointerId); 726dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (index < 0) { 727dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return -1; 728dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 729dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return MotionEventCompat.getY(ev, index); 730dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 731dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 732e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 733e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void requestDisallowInterceptTouchEvent(boolean b) { 734da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // if this is a List < L or another view that doesn't support nested 735da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // scrolling, ignore this request so that the vertical scroll event 736da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // isn't stolen 737da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if ((android.os.Build.VERSION.SDK_INT < 21 && mTarget instanceof AbsListView) 738da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira || (mTarget != null && !ViewCompat.isNestedScrollingEnabled(mTarget))) { 739da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // Nope. 740da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } else { 741da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira super.requestDisallowInterceptTouchEvent(b); 742da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 743da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 744da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 745359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes // NestedScrollingParent 746359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 747da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 748da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public boolean onStartNestedScroll(View child, View target, int nestedScrollAxes) { 7495eeb3073886ae76705086c83a841f466b40280bbChris Banes return isEnabled() && !mReturningToStart && !mRefreshing 750fc60a78760589b76087e2973add98d0aa35d50abChris Banes && (nestedScrollAxes & ViewCompat.SCROLL_AXIS_VERTICAL) != 0; 751da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 752da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 753da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 754da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onNestedScrollAccepted(View child, View target, int axes) { 755da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // Reset the counter of how much leftover scroll needs to be consumed. 756da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mNestedScrollingParentHelper.onNestedScrollAccepted(child, target, axes); 757fc60a78760589b76087e2973add98d0aa35d50abChris Banes // Dispatch up to the nested parent 758fc60a78760589b76087e2973add98d0aa35d50abChris Banes startNestedScroll(axes & ViewCompat.SCROLL_AXIS_VERTICAL); 759da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mTotalUnconsumed = 0; 760fc60a78760589b76087e2973add98d0aa35d50abChris Banes mNestedScrollInProgress = true; 761da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 762da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 763da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 764da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onNestedPreScroll(View target, int dx, int dy, int[] consumed) { 765da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // If we are in the middle of consuming, a scroll, then we want to move the spinner back up 766da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // before allowing the list to scroll 767da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (dy > 0 && mTotalUnconsumed > 0) { 768da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (dy > mTotalUnconsumed) { 769da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira consumed[1] = dy - (int) mTotalUnconsumed; 770da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mTotalUnconsumed = 0; 771da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } else { 772da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mTotalUnconsumed -= dy; 773da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira consumed[1] = dy; 774da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 775da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira moveSpinner(mTotalUnconsumed); 776da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 777359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 77833eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira // If a client layout is using a custom start position for the circle 77933eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira // view, they mean to hide it again before scrolling the child view 78033eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira // If we get back to mTotalUnconsumed == 0 and there is more to go, hide 78133eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira // the circle so it isn't exposed if its blocking content is moved 78233eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira if (mUsingCustomStart && dy > 0 && mTotalUnconsumed == 0 78333eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira && Math.abs(dy - consumed[1]) > 0) { 78433eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira mCircleView.setVisibility(View.GONE); 78533eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira } 78633eba8caa44bd2f92123c15afd8a020ffef059aaMindy Pereira 787359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes // Now let our nested parent consume the leftovers 788359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes final int[] parentConsumed = mParentScrollConsumed; 789359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes if (dispatchNestedPreScroll(dx - consumed[0], dy - consumed[1], parentConsumed, null)) { 790359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes consumed[0] += parentConsumed[0]; 791359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes consumed[1] += parentConsumed[1]; 792359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 793da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 794da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 795da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 796da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public int getNestedScrollAxes() { 797da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira return mNestedScrollingParentHelper.getNestedScrollAxes(); 798da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 799da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 800da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 801da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onStopNestedScroll(View target) { 802da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mNestedScrollingParentHelper.onStopNestedScroll(target); 803fc60a78760589b76087e2973add98d0aa35d50abChris Banes mNestedScrollInProgress = false; 804da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // Finish the spinner for nested scrolling if we ever consumed any 805da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // unconsumed nested scroll 806da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (mTotalUnconsumed > 0) { 807da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira finishSpinner(mTotalUnconsumed); 808da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mTotalUnconsumed = 0; 809da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 810359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes // Dispatch up our nested parent 811359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes stopNestedScroll(); 812da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 813da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 814da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 815fc60a78760589b76087e2973add98d0aa35d50abChris Banes public void onNestedScroll(final View target, final int dxConsumed, final int dyConsumed, 816fc60a78760589b76087e2973add98d0aa35d50abChris Banes final int dxUnconsumed, final int dyUnconsumed) { 817fc60a78760589b76087e2973add98d0aa35d50abChris Banes // Dispatch up to the nested parent first 818fc60a78760589b76087e2973add98d0aa35d50abChris Banes dispatchNestedScroll(dxConsumed, dyConsumed, dxUnconsumed, dyUnconsumed, 819fc60a78760589b76087e2973add98d0aa35d50abChris Banes mParentOffsetInWindow); 820fc60a78760589b76087e2973add98d0aa35d50abChris Banes 821fc60a78760589b76087e2973add98d0aa35d50abChris Banes // This is a bit of a hack. Nested scrolling works from the bottom up, and as we are 822fc60a78760589b76087e2973add98d0aa35d50abChris Banes // sometimes between two nested scrolling views, we need a way to be able to know when any 823fc60a78760589b76087e2973add98d0aa35d50abChris Banes // nested scrolling parent has stopped handling events. We do that by using the 824fc60a78760589b76087e2973add98d0aa35d50abChris Banes // 'offset in window 'functionality to see if we have been moved from the event. 825fc60a78760589b76087e2973add98d0aa35d50abChris Banes // This is a decent indication of whether we should take over the event stream or not. 826fc60a78760589b76087e2973add98d0aa35d50abChris Banes final int dy = dyUnconsumed + mParentOffsetInWindow[1]; 8275eeb3073886ae76705086c83a841f466b40280bbChris Banes if (dy < 0 && !canChildScrollUp()) { 828fc60a78760589b76087e2973add98d0aa35d50abChris Banes mTotalUnconsumed += Math.abs(dy); 829da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira moveSpinner(mTotalUnconsumed); 830da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 831da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 832da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 833359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes // NestedScrollingChild 834da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 835da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 836359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public void setNestedScrollingEnabled(boolean enabled) { 837359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes mNestedScrollingChildHelper.setNestedScrollingEnabled(enabled); 838359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 839359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 840359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 841359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean isNestedScrollingEnabled() { 842359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.isNestedScrollingEnabled(); 843359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 844359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 845359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 846359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean startNestedScroll(int axes) { 847359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.startNestedScroll(axes); 848359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 849359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 850359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 851359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public void stopNestedScroll() { 852359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes mNestedScrollingChildHelper.stopNestedScroll(); 853359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 854359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 855359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 856359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean hasNestedScrollingParent() { 857359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.hasNestedScrollingParent(); 858359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 859359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 860359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 861359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean dispatchNestedScroll(int dxConsumed, int dyConsumed, int dxUnconsumed, 862359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes int dyUnconsumed, int[] offsetInWindow) { 863359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.dispatchNestedScroll(dxConsumed, dyConsumed, 864359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes dxUnconsumed, dyUnconsumed, offsetInWindow); 865359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 866359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 867359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 868359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean dispatchNestedPreScroll(int dx, int dy, int[] consumed, int[] offsetInWindow) { 869359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.dispatchNestedPreScroll(dx, dy, consumed, offsetInWindow); 870359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 871359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 872359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 873acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira public boolean onNestedPreFling(View target, float velocityX, 874acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira float velocityY) { 875acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira return dispatchNestedPreFling(velocityX, velocityY); 876acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira } 877acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira 878acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira @Override 879acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira public boolean onNestedFling(View target, float velocityX, float velocityY, 880acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira boolean consumed) { 881acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira return dispatchNestedFling(velocityX, velocityY, consumed); 882acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira } 883acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira 884acefb3dda2f5b7e2e1a6c046ad1420876c5b4b38Mindy Pereira @Override 885359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean dispatchNestedFling(float velocityX, float velocityY, boolean consumed) { 886359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.dispatchNestedFling(velocityX, velocityY, consumed); 887359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 888359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 889359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes @Override 890359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes public boolean dispatchNestedPreFling(float velocityX, float velocityY) { 891359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes return mNestedScrollingChildHelper.dispatchNestedPreFling(velocityX, velocityY); 892359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes } 893359e30274ccef5f95130ac09a7bd593d8e18bb92Chris Banes 894dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira private boolean isAnimationRunning(Animation animation) { 895dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira return animation != null && animation.hasStarted() && !animation.hasEnded(); 896dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira } 897dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira 898da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira private void moveSpinner(float overscrollTop) { 899da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mProgress.showArrow(true); 900da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float originalDragPercent = overscrollTop / mTotalDragDistance; 901da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 902da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float dragPercent = Math.min(1f, Math.abs(originalDragPercent)); 903da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float adjustedPercent = (float) Math.max(dragPercent - .4, 0) * 5 / 3; 904da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float extraOS = Math.abs(overscrollTop) - mTotalDragDistance; 905da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float slingshotDist = mUsingCustomStart ? mSpinnerFinalOffset - mOriginalOffsetTop 906da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira : mSpinnerFinalOffset; 907da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float tensionSlingshotPercent = Math.max(0, Math.min(extraOS, slingshotDist * 2) 908da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira / slingshotDist); 909da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float tensionPercent = (float) ((tensionSlingshotPercent / 4) - Math.pow( 910da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira (tensionSlingshotPercent / 4), 2)) * 2f; 911da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float extraMove = (slingshotDist) * tensionPercent * 2; 912da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 913da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira int targetY = mOriginalOffsetTop + (int) ((slingshotDist * dragPercent) + extraMove); 914da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // where 1.0f is a full circle 915da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (mCircleView.getVisibility() != View.VISIBLE) { 916da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mCircleView.setVisibility(View.VISIBLE); 917da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 918da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (!mScale) { 919da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira ViewCompat.setScaleX(mCircleView, 1f); 920da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira ViewCompat.setScaleY(mCircleView, 1f); 921da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 922f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas 923f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas if (mScale) { 924f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas setAnimationProgress(Math.min(1f, overscrollTop / mTotalDragDistance)); 925f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas } 926da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (overscrollTop < mTotalDragDistance) { 927da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (mProgress.getAlpha() > STARTING_PROGRESS_ALPHA 928da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira && !isAnimationRunning(mAlphaStartAnimation)) { 929da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // Animate the alpha 930da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira startProgressAlphaStartAnimation(); 931da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 932da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } else { 933da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (mProgress.getAlpha() < MAX_ALPHA && !isAnimationRunning(mAlphaMaxAnimation)) { 934da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // Animate the alpha 935da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira startProgressAlphaMaxAnimation(); 936da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 937da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 938f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas float strokeStart = adjustedPercent * .8f; 939f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas mProgress.setStartEndTrim(0f, Math.min(MAX_PROGRESS_ANGLE, strokeStart)); 940f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas mProgress.setArrowScale(Math.min(1f, adjustedPercent)); 941f629b8e6d20e9b5ae373bfad904a3c8932a82ca6Aurimas Liutikas 942da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira float rotation = (-0.25f + .4f * adjustedPercent + tensionPercent * 2) * .5f; 943da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mProgress.setProgressRotation(rotation); 944da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira setTargetOffsetTopAndBottom(targetY - mCurrentTargetOffsetTop, true /* requires update */); 945da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 946da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 947da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira private void finishSpinner(float overscrollTop) { 948da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (overscrollTop > mTotalDragDistance) { 949da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira setRefreshing(true, true /* notify */); 950da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } else { 951da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira // cancel refresh 952da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mRefreshing = false; 953da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mProgress.setStartEndTrim(0f, 0f); 954da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira Animation.AnimationListener listener = null; 955da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (!mScale) { 956da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira listener = new Animation.AnimationListener() { 957da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 958da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 959da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onAnimationStart(Animation animation) { 960da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 961da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 962da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 963da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onAnimationEnd(Animation animation) { 964da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (!mScale) { 965da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira startScaleDownAnimation(null); 966da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 967da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 968da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 969da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira @Override 970da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira public void onAnimationRepeat(Animation animation) { 971da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 972da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 973da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira }; 974da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 975da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira animateOffsetToStartPosition(mCurrentTargetOffsetTop, listener); 976da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira mProgress.showArrow(false); 977da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 978da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira } 979da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira 980e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira @Override 981272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes public boolean onTouchEvent(MotionEvent ev) { 982272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final int action = MotionEventCompat.getActionMasked(ev); 983a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira int pointerIndex = -1; 984272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 985272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (mReturningToStart && action == MotionEvent.ACTION_DOWN) { 986272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mReturningToStart = false; 987272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 988272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 989fc60a78760589b76087e2973add98d0aa35d50abChris Banes if (!isEnabled() || mReturningToStart || canChildScrollUp() || mNestedScrollInProgress) { 990272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes // Fail fast if we're not in a state where a swipe is possible 991272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 992272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 993272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 994e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira switch (action) { 995e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira case MotionEvent.ACTION_DOWN: 996272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mActivePointerId = MotionEventCompat.getPointerId(ev, 0); 997272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mIsBeingDragged = false; 998e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira break; 999272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1000dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira case MotionEvent.ACTION_MOVE: { 1001a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 1002272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (pointerIndex < 0) { 1003272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes Log.e(LOG_TAG, "Got ACTION_MOVE event but have an invalid active pointer id."); 1004272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 1005272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 1006272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1007272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final float y = MotionEventCompat.getY(ev, pointerIndex); 1008dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; 1009272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (mIsBeingDragged) { 1010da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira if (overscrollTop > 0) { 1011da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira moveSpinner(overscrollTop); 1012272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } else { 1013da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira return false; 1014e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1015e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1016e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira break; 1017dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira } 1018272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEventCompat.ACTION_POINTER_DOWN: { 1019a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira pointerIndex = MotionEventCompat.getActionIndex(ev); 1020a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira if (pointerIndex < 0) { 1021a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira Log.e(LOG_TAG, "Got ACTION_POINTER_DOWN event but have an invalid action index."); 1022a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira return false; 1023a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira } 1024a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira mActivePointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 1025272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes break; 1026272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 1027272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1028272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes case MotionEventCompat.ACTION_POINTER_UP: 1029272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes onSecondaryPointerUp(ev); 1030272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes break; 1031272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1032a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira case MotionEvent.ACTION_UP: { 1033a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira pointerIndex = MotionEventCompat.findPointerIndex(ev, mActivePointerId); 1034a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira if (pointerIndex < 0) { 1035a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira Log.e(LOG_TAG, "Got ACTION_UP event but don't have an active pointer id."); 103607a4db40e79aae23694b205f99b013ee2e4f2307Mindy Pereira return false; 103707a4db40e79aae23694b205f99b013ee2e4f2307Mindy Pereira } 1038a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira 1039dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira final float y = MotionEventCompat.getY(ev, pointerIndex); 1040dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira final float overscrollTop = (y - mInitialMotionY) * DRAG_RATE; 1041ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn mIsBeingDragged = false; 1042da1b0a00654a315d645b941cf3a4cd2a746fe405Mindy Pereira finishSpinner(overscrollTop); 1043ca9e92293ff32b4a8a15513b0a7afe42c127cdb7Andrew Flynn mActivePointerId = INVALID_POINTER; 1044272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return false; 1045dd27d55a5152f86c07758067e587bdd5b719abafMindy Pereira } 1046a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira case MotionEvent.ACTION_CANCEL: 1047a8ba5574e2cb80191fadb0d7963483133b55a2feMindy Pereira return false; 1048e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1049272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1050272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes return true; 1051e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1052e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 1053689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void animateOffsetToCorrectPosition(int from, AnimationListener listener) { 1054689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mFrom = from; 1055689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mAnimateToCorrectPosition.reset(); 1056dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira mAnimateToCorrectPosition.setDuration(ANIMATE_TO_TRIGGER_DURATION); 1057689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mAnimateToCorrectPosition.setInterpolator(mDecelerateInterpolator); 1058689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira if (listener != null) { 1059689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.setAnimationListener(listener); 1060689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 1061c27bc76d7eac50363e6cd063ae41996833f52ebeMindy Pereira mCircleView.clearAnimation(); 1062689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.startAnimation(mAnimateToCorrectPosition); 1063e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1064e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 1065689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void animateOffsetToStartPosition(int from, AnimationListener listener) { 10665e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (mScale) { 10675e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira // Scale the item back down 10685e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira startScaleDownReturnToStartAnimation(from, listener); 10695e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } else { 10705e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mFrom = from; 10715e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mAnimateToStartPosition.reset(); 10725e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mAnimateToStartPosition.setDuration(ANIMATE_TO_START_DURATION); 10735e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mAnimateToStartPosition.setInterpolator(mDecelerateInterpolator); 10745e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (listener != null) { 10755e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.setAnimationListener(listener); 10765e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 10775e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.clearAnimation(); 10785e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.startAnimation(mAnimateToStartPosition); 1079e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1080e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1081e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 1082689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private final Animation mAnimateToCorrectPosition = new Animation() { 1083689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira @Override 1084689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 1085689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira int targetTop = 0; 10865e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira int endTarget = 0; 10875e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (!mUsingCustomStart) { 10885e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira endTarget = (int) (mSpinnerFinalOffset - Math.abs(mOriginalOffsetTop)); 10895e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } else { 10905e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira endTarget = (int) mSpinnerFinalOffset; 10915e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 1092dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira targetTop = (mFrom + (int) ((endTarget - mFrom) * interpolatedTime)); 1093689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira int offset = targetTop - mCircleView.getTop(); 1094689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira setTargetOffsetTopAndBottom(offset, false /* requires update */); 10953fba72ed900aa288256bdf29427df39753964ff0Mindy Pereira mProgress.setArrowScale(1 - interpolatedTime); 1096689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 1097689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira }; 109812ffe36178df269e0c2d3b33f7de360e74c63f71Mindy Pereira 10995e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira private void moveToStart(float interpolatedTime) { 11005e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira int targetTop = 0; 11015e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira targetTop = (mFrom + (int) ((mOriginalOffsetTop - mFrom) * interpolatedTime)); 11025e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira int offset = targetTop - mCircleView.getTop(); 11035e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira setTargetOffsetTopAndBottom(offset, false /* requires update */); 11045e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 11055e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira 1106689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private final Animation mAnimateToStartPosition = new Animation() { 1107689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira @Override 1108689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 11095e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira moveToStart(interpolatedTime); 1110689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 1111689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira }; 1112689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira 11135e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira private void startScaleDownReturnToStartAnimation(int from, 11145e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira Animation.AnimationListener listener) { 11155e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mFrom = from; 11165e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (isAlphaUsedForScale()) { 11175e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mStartingScale = mProgress.getAlpha(); 11185e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } else { 11195e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mStartingScale = ViewCompat.getScaleX(mCircleView); 11205e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 11215e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mScaleDownToStartAnimation = new Animation() { 11225e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira @Override 11235e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira public void applyTransformation(float interpolatedTime, Transformation t) { 11245e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira float targetScale = (mStartingScale + (-mStartingScale * interpolatedTime)); 11255e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira setAnimationProgress(targetScale); 11265e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira moveToStart(interpolatedTime); 11275e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 11285e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira }; 11295e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mScaleDownToStartAnimation.setDuration(SCALE_DOWN_DURATION); 11305e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira if (listener != null) { 11315e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.setAnimationListener(listener); 11325e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 11335e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.clearAnimation(); 11345e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira mCircleView.startAnimation(mScaleDownToStartAnimation); 11355e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira } 11365e31e2ad5b0c1f62b93a4e554d16f8b9d28b5a15Mindy Pereira 1137689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira private void setTargetOffsetTopAndBottom(int offset, boolean requiresUpdate) { 1138689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.bringToFront(); 1139689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCircleView.offsetTopAndBottom(offset); 1140689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira mCurrentTargetOffsetTop = mCircleView.getTop(); 1141dc943d6192dab40df68b6b583c84e64045ea1bc8Mindy Pereira if (requiresUpdate && android.os.Build.VERSION.SDK_INT < 11) { 11420b7c53c6ee75c8473bcefd0dff9c6e3ed6c20410Mindy Pereira invalidate(); 1143689ab224266f62aefb35ef0b80af6edf66eb8972Mindy Pereira } 1144e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1145e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira 1146272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes private void onSecondaryPointerUp(MotionEvent ev) { 1147272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final int pointerIndex = MotionEventCompat.getActionIndex(ev); 1148272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final int pointerId = MotionEventCompat.getPointerId(ev, pointerIndex); 1149272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes if (pointerId == mActivePointerId) { 1150272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes // This was our active pointer going up. Choose a new 1151272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes // active pointer and adjust accordingly. 1152272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes final int newPointerIndex = pointerIndex == 0 ? 1 : 0; 1153272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes mActivePointerId = MotionEventCompat.getPointerId(ev, newPointerIndex); 1154272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 1155272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes } 1156272acf0aa6bc9bf0d744b68a5b8b092c2c3e46d4Chris Banes 1157e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira /** 1158e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * Classes that wish to be notified when the swipe gesture correctly 1159e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira * triggers a refresh should implement this interface. 1160e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira */ 1161e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public interface OnRefreshListener { 1162e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira public void onRefresh(); 1163e9a361cf082bf8fbe908d1abfdc327209ec01d82Mindy Pereira } 1164241d15309b21a8857de2bda0afaf06668bdd348eMindy Pereira} 1165