1bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal/* 2bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * Copyright (C) 2016 The Android Open Source Project 3bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * 4bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * Licensed under the Apache License, Version 2.0 (the "License"); 5bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * you may not use this file except in compliance with the License. 6bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * You may obtain a copy of the License at 7bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * 8bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * http://www.apache.org/licenses/LICENSE-2.0 9bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * 10bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * Unless required by applicable law or agreed to in writing, software 11bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * distributed under the License is distributed on an "AS IS" BASIS, 12bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * See the License for the specific language governing permissions and 14bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * limitations under the License. 15bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal */ 16bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalpackage com.android.launcher3.util; 17bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 18bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalimport android.graphics.Rect; 19bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalimport android.graphics.RectF; 20bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalimport android.view.MotionEvent; 21bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalimport android.view.TouchDelegate; 22bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalimport android.view.View; 23bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 24bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal/** 25bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * This class differs from the framework {@link TouchDelegate} in that it transforms the 26bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * coordinates of the motion event to the provided bounds. 27bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * 28bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * You can also modify the bounds post construction. Since the bounds are available during layout, 29bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * this avoids new object creation during every layout. 30bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal */ 31bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyalpublic class TransformingTouchDelegate extends TouchDelegate { 32bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal private static final Rect sTempRect = new Rect(); 33bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 34bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal private final RectF mBounds; 35bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 361cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal private final RectF mTouchCheckBounds; 371cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal private float mTouchExtension; 381cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal private boolean mWasTouchOutsideBounds; 391cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal 40bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal private View mDelegateView; 41bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal private boolean mDelegateTargeted; 42bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 43bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal public TransformingTouchDelegate(View delegateView) { 44bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal super(sTempRect, delegateView); 45bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 46bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal mDelegateView = delegateView; 47bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal mBounds = new RectF(); 481cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mTouchCheckBounds = new RectF(); 49bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 50bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 51bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal public void setBounds(int left, int top, int right, int bottom) { 52bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal mBounds.set(left, top, right, bottom); 531cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal updateTouchBounds(); 541cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal } 551cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal 561cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal public void extendTouchBounds(float extension) { 571cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mTouchExtension = extension; 581cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal updateTouchBounds(); 591cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal } 601cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal 611cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal private void updateTouchBounds() { 621cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mTouchCheckBounds.set(mBounds); 631cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mTouchCheckBounds.inset(-mTouchExtension, -mTouchExtension); 64bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 65bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 66bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal public void setDelegateView(View view) { 67bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal mDelegateView = view; 68bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 69bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal 70bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal /** 71bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * Will forward touch events to the delegate view if the event is within the bounds 72bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * specified in the constructor. 73bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * 74bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * @param event The touch event to forward 75bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal * @return True if the event was forwarded to the delegate, false otherwise. 76bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal */ 77bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal public boolean onTouchEvent(MotionEvent event) { 78bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal boolean sendToDelegate = false; 79bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal switch (event.getAction()) { 80bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal case MotionEvent.ACTION_DOWN: 811cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mDelegateTargeted = mTouchCheckBounds.contains(event.getX(), event.getY()); 82bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal if (mDelegateTargeted) { 831cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal mWasTouchOutsideBounds = !mBounds.contains(event.getX(), event.getY()); 84bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal sendToDelegate = true; 85bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 86bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal break; 87bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal case MotionEvent.ACTION_MOVE: 88bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal sendToDelegate = mDelegateTargeted; 89bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal break; 90bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal case MotionEvent.ACTION_UP: 91bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal case MotionEvent.ACTION_CANCEL: 92bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal sendToDelegate = mDelegateTargeted; 93bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal mDelegateTargeted = false; 94bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal break; 95bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 96bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal boolean handled = false; 97bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal if (sendToDelegate) { 981cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal float x = event.getX(); 991cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal float y = event.getY(); 1001cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal if (mWasTouchOutsideBounds) { 1011cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal event.setLocation(mBounds.centerX(), mBounds.centerY()); 1021cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal } else { 1031cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal event.offsetLocation(-mBounds.left, -mBounds.top); 1041cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal } 105bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal handled = mDelegateView.dispatchTouchEvent(event); 1061cc48e1d0c0bd30bc59cc37c4d49ee78b01424f0Sunny Goyal event.setLocation(x, y); 107bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 108bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal return handled; 109bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal } 110bf547ff6f0011378ef3f9caa81f6825ba0fd48deSunny Goyal} 111