1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17 package com.android.systemui.statusbar.phone; 18 19 import android.content.Context; 20 import android.util.AttributeSet; 21 import android.view.MotionEvent; 22 import android.view.View; 23 import android.widget.ScrollView; 24 25 /** 26 * A scroll view which can be observed for scroll change events. 27 */ 28 public class ObservableScrollView extends ScrollView { 29 30 private Listener mListener; 31 private int mLastOverscrollAmount; 32 private boolean mTouchEnabled = true; 33 private boolean mHandlingTouchEvent; 34 private float mLastX; 35 private float mLastY; 36 private boolean mBlockFlinging; 37 private boolean mTouchCancelled; 38 ObservableScrollView(Context context, AttributeSet attrs)39 public ObservableScrollView(Context context, AttributeSet attrs) { 40 super(context, attrs); 41 } 42 setListener(Listener listener)43 public void setListener(Listener listener) { 44 mListener = listener; 45 } 46 setTouchEnabled(boolean touchEnabled)47 public void setTouchEnabled(boolean touchEnabled) { 48 mTouchEnabled = touchEnabled; 49 } 50 isScrolledToBottom()51 public boolean isScrolledToBottom() { 52 return getScrollY() == getMaxScrollY(); 53 } 54 isHandlingTouchEvent()55 public boolean isHandlingTouchEvent() { 56 return mHandlingTouchEvent; 57 } 58 getMaxScrollY()59 private int getMaxScrollY() { 60 int scrollRange = 0; 61 if (getChildCount() > 0) { 62 View child = getChildAt(0); 63 scrollRange = Math.max(0, 64 child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop)); 65 } 66 return scrollRange; 67 } 68 69 @Override onTouchEvent(MotionEvent ev)70 public boolean onTouchEvent(MotionEvent ev) { 71 mHandlingTouchEvent = true; 72 mLastX = ev.getX(); 73 mLastY = ev.getY(); 74 boolean result = super.onTouchEvent(ev); 75 mHandlingTouchEvent = false; 76 return result; 77 } 78 79 @Override onInterceptTouchEvent(MotionEvent ev)80 public boolean onInterceptTouchEvent(MotionEvent ev) { 81 mHandlingTouchEvent = true; 82 mLastX = ev.getX(); 83 mLastY = ev.getY(); 84 boolean result = super.onInterceptTouchEvent(ev); 85 mHandlingTouchEvent = false; 86 return result; 87 } 88 89 @Override dispatchTouchEvent(MotionEvent ev)90 public boolean dispatchTouchEvent(MotionEvent ev) { 91 if (ev.getAction() == MotionEvent.ACTION_DOWN) { 92 if (!mTouchEnabled) { 93 mTouchCancelled = true; 94 return false; 95 } 96 mTouchCancelled = false; 97 } else if (mTouchCancelled) { 98 return false; 99 } else if (!mTouchEnabled) { 100 MotionEvent cancel = MotionEvent.obtain(ev); 101 cancel.setAction(MotionEvent.ACTION_CANCEL); 102 super.dispatchTouchEvent(ev); 103 cancel.recycle(); 104 mTouchCancelled = true; 105 return false; 106 } 107 return super.dispatchTouchEvent(ev); 108 } 109 110 @Override onScrollChanged(int l, int t, int oldl, int oldt)111 protected void onScrollChanged(int l, int t, int oldl, int oldt) { 112 super.onScrollChanged(l, t, oldl, oldt); 113 if (mListener != null) { 114 mListener.onScrollChanged(); 115 } 116 } 117 118 @Override overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, boolean isTouchEvent)119 protected boolean overScrollBy(int deltaX, int deltaY, int scrollX, int scrollY, 120 int scrollRangeX, int scrollRangeY, int maxOverScrollX, int maxOverScrollY, 121 boolean isTouchEvent) { 122 mLastOverscrollAmount = Math.max(0, scrollY + deltaY - getMaxScrollY()); 123 return super.overScrollBy(deltaX, deltaY, scrollX, scrollY, scrollRangeX, scrollRangeY, 124 maxOverScrollX, maxOverScrollY, isTouchEvent); 125 } 126 setBlockFlinging(boolean blockFlinging)127 public void setBlockFlinging(boolean blockFlinging) { 128 mBlockFlinging = blockFlinging; 129 } 130 131 @Override fling(int velocityY)132 public void fling(int velocityY) { 133 if (!mBlockFlinging) { 134 super.fling(velocityY); 135 } 136 } 137 138 @Override onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY)139 protected void onOverScrolled(int scrollX, int scrollY, boolean clampedX, boolean clampedY) { 140 super.onOverScrolled(scrollX, scrollY, clampedX, clampedY); 141 if (mListener != null && mLastOverscrollAmount > 0) { 142 mListener.onOverscrolled(mLastX, mLastY, mLastOverscrollAmount); 143 } 144 } 145 146 public interface Listener { onScrollChanged()147 void onScrollChanged(); onOverscrolled(float lastX, float lastY, int amount)148 void onOverscrolled(float lastX, float lastY, int amount); 149 } 150 } 151