/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License */ package com.android.systemui.statusbar.phone; import android.content.Context; import android.util.AttributeSet; import android.view.MotionEvent; import android.view.View; import android.widget.ScrollView; /** * A scroll view which can be observed for scroll change events. */ public class ObservableScrollView extends ScrollView { private Listener mListener; private int mLastOverscrollAmount; private boolean mTouchEnabled = true; private boolean mHandlingTouchEvent; private float mLastX; private float mLastY; private boolean mBlockFlinging; private boolean mTouchCancelled; public ObservableScrollView(Context context, AttributeSet attrs) { super(context, attrs); } public void setListener(Listener listener) { mListener = listener; } public void setTouchEnabled(boolean touchEnabled) { mTouchEnabled = touchEnabled; } public boolean isScrolledToBottom() { return getScrollY() == getMaxScrollY(); } public boolean isHandlingTouchEvent() { return mHandlingTouchEvent; } private int getMaxScrollY() { int scrollRange = 0; if (getChildCount() > 0) { View child = getChildAt(0); scrollRange = Math.max(0, child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop)); } return scrollRange; } @Override public boolean onTouchEvent(MotionEvent ev) { mHandlingTouchEvent = true; mLastX = ev.getX(); mLastY = ev.getY(); boolean result = super.onTouchEvent(ev); mHandlingTouchEvent = false; return result; } @Override public boolean onInterceptTouchEvent(MotionEvent ev) { mHandlingTouchEvent = true; mLastX = ev.getX(); mLastY = ev.getY(); boolean result = super.onInterceptTouchEvent(ev); mHandlingTouchEvent = false; return result; } @Override public boolean dispatchTouchEvent(MotionEvent ev) { if (ev.getAction() == MotionEvent.ACTION_DOWN) { if (!mTouchEnabled) { mTouchCancelled = true; return false; } mTouchCancelled = false; } else if (mTouchCancelled) { return false; } else if (!mTouchEnabled) { MotionEvent cancel = MotionEvent.obtain(ev); cancel.setAction(MotionEvent.ACTION_CANCEL); super.dispatchTouchEvent(cancel); cancel.recycle(); mTouchCancelled = true; return false; } return super.dispatchTouchEvent(ev); } @Override protected void onScrollChanged(int l, int t, int oldl, int oldt) { super.onScrollChanged(l, t, oldl, oldt); if (mListener != null) { mListener.onScrollChanged(); } } @Override protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent) { mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY()); return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, maxOverScrollX, maxOverScrollY, isTouchEvent); } public void setBlockFlinging(boolean blockFlinging) { mBlockFlinging = blockFlinging; } @Override public void fling(int velocityY) { if (!mBlockFlinging) { super.fling(velocityY); } } @Override protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); if (mListener != null && mLastOverscrollAmount > 0) { mListener.onOverscrolled(mLastX, mLastY, mLastOverscrollAmount); } } public interface Listener { void onScrollChanged(); void onOverscrolled(float lastX, float lastY, int amount); } }