1 /* 2 * Copyright (C) 2016 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file 5 * except in compliance with the License. You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software distributed under the 10 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 11 * KIND, either express or implied. See the License for the specific language governing 12 * permissions and limitations under the License. 13 */ 14 15 package com.android.systemui.qs; 16 17 import android.content.Context; 18 import android.util.AttributeSet; 19 import android.view.MotionEvent; 20 import android.view.View; 21 import android.view.ViewConfiguration; 22 import android.view.ViewParent; 23 import android.widget.ScrollView; 24 25 /** 26 * ScrollView that disallows intercepting for touches that can cause scrolling. 27 */ 28 public class NonInterceptingScrollView extends ScrollView { 29 30 private final int mTouchSlop; 31 private float mDownY; 32 private boolean mScrollEnabled = true; 33 NonInterceptingScrollView(Context context, AttributeSet attrs)34 public NonInterceptingScrollView(Context context, AttributeSet attrs) { 35 super(context, attrs); 36 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 37 } 38 39 @Override onTouchEvent(MotionEvent ev)40 public boolean onTouchEvent(MotionEvent ev) { 41 int action = ev.getActionMasked(); 42 switch (action) { 43 case MotionEvent.ACTION_DOWN: 44 if (canScrollVertically(1)) { 45 // If we can scroll down, make sure we're not intercepted by the parent 46 final ViewParent parent = getParent(); 47 if (parent != null) { 48 parent.requestDisallowInterceptTouchEvent(true); 49 } 50 } else if (!canScrollVertically(-1)) { 51 // Don't pass on the touch to the view, because scrolling will unconditionally 52 // disallow interception even if we can't scroll. 53 // if a user can't scroll at all, we should never listen to the touch. 54 return false; 55 } 56 break; 57 } 58 return super.onTouchEvent(ev); 59 } 60 61 @Override onInterceptTouchEvent(MotionEvent ev)62 public boolean onInterceptTouchEvent(MotionEvent ev) { 63 // If there's a touch on this view and we can scroll down, we don't want to be intercepted 64 int action = ev.getActionMasked(); 65 switch (action) { 66 case MotionEvent.ACTION_DOWN: 67 // If we can scroll down, make sure non of our parents intercepts us. 68 if (canScrollVertically(1)) { 69 final ViewParent parent = getParent(); 70 if (parent != null) { 71 parent.requestDisallowInterceptTouchEvent(true); 72 } 73 } 74 mDownY = ev.getY(); 75 break; 76 case MotionEvent.ACTION_MOVE: { 77 final int y = (int) ev.getY(); 78 final float yDiff = y - mDownY; 79 if (yDiff < -mTouchSlop && !canScrollVertically(1)) { 80 // Don't intercept touches that are overscrolling. 81 return false; 82 } 83 break; 84 } 85 } 86 return super.onInterceptTouchEvent(ev); 87 } 88 89 @Override canScrollVertically(int direction)90 public boolean canScrollVertically(int direction) { 91 return mScrollEnabled && super.canScrollVertically(direction); 92 } 93 94 @Override canScrollHorizontally(int direction)95 public boolean canScrollHorizontally(int direction) { 96 return mScrollEnabled && super.canScrollHorizontally(direction); 97 } 98 getScrollRange()99 public int getScrollRange() { 100 int scrollRange = 0; 101 if (getChildCount() > 0) { 102 View child = getChildAt(0); 103 scrollRange = Math.max(0, 104 child.getHeight() - (getHeight() - mPaddingBottom - mPaddingTop)); 105 } 106 return scrollRange; 107 } 108 109 /** 110 * Enable scrolling for this view. Needed because the view might be clipped but still intercepts 111 * touches on the lockscreen. 112 */ setScrollingEnabled(boolean enabled)113 public void setScrollingEnabled(boolean enabled) { 114 mScrollEnabled = enabled; 115 } 116 } 117