1 /* 2 * Copyright (C) 2022 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.wm.shell.bubbles; 18 19 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_BUBBLES; 20 import static com.android.wm.shell.bubbles.BubbleDebugConfig.TAG_WITH_CLASS_NAME; 21 import static com.android.wm.shell.protolog.ShellProtoLogGroup.WM_SHELL_BUBBLES; 22 23 import android.content.Context; 24 import android.graphics.PointF; 25 import android.view.MotionEvent; 26 import android.view.VelocityTracker; 27 import android.view.ViewConfiguration; 28 29 import androidx.annotation.Nullable; 30 31 import com.android.internal.protolog.common.ProtoLog; 32 33 /** 34 * Handles {@link MotionEvent}s for bubbles that begin in the nav bar area 35 */ 36 class BubblesNavBarMotionEventHandler { 37 private static final String TAG = 38 TAG_WITH_CLASS_NAME ? "BubblesNavBarMotionEventHandler" : TAG_BUBBLES; 39 private static final int VELOCITY_UNITS = 1000; 40 41 private final Runnable mOnInterceptTouch; 42 private final MotionEventListener mMotionEventListener; 43 private final int mTouchSlop; 44 private final BubblePositioner mPositioner; 45 private final PointF mTouchDown = new PointF(); 46 private boolean mTrackingTouches; 47 private boolean mInterceptingTouches; 48 @Nullable 49 private VelocityTracker mVelocityTracker; 50 BubblesNavBarMotionEventHandler(Context context, BubblePositioner positioner, Runnable onInterceptTouch, MotionEventListener motionEventListener)51 BubblesNavBarMotionEventHandler(Context context, BubblePositioner positioner, 52 Runnable onInterceptTouch, MotionEventListener motionEventListener) { 53 mTouchSlop = ViewConfiguration.get(context).getScaledTouchSlop(); 54 mPositioner = positioner; 55 mOnInterceptTouch = onInterceptTouch; 56 mMotionEventListener = motionEventListener; 57 } 58 59 /** 60 * Handle {@link MotionEvent} and forward it to {@code motionEventListener} defined in 61 * constructor 62 * 63 * @return {@code true} if this {@link MotionEvent} is handled (it started in the gesture area) 64 */ onMotionEvent(MotionEvent motionEvent)65 public boolean onMotionEvent(MotionEvent motionEvent) { 66 float dx = motionEvent.getX() - mTouchDown.x; 67 float dy = motionEvent.getY() - mTouchDown.y; 68 69 switch (motionEvent.getAction()) { 70 case MotionEvent.ACTION_DOWN: 71 if (isInGestureRegion(motionEvent)) { 72 mTouchDown.set(motionEvent.getX(), motionEvent.getY()); 73 mMotionEventListener.onDown(motionEvent.getX(), motionEvent.getY()); 74 mTrackingTouches = true; 75 return true; 76 } 77 break; 78 case MotionEvent.ACTION_MOVE: 79 if (mTrackingTouches) { 80 if (!mInterceptingTouches && Math.hypot(dx, dy) > mTouchSlop) { 81 mInterceptingTouches = true; 82 mOnInterceptTouch.run(); 83 } 84 if (mInterceptingTouches) { 85 getVelocityTracker().addMovement(motionEvent); 86 mMotionEventListener.onMove(dx, dy); 87 } 88 return true; 89 } 90 break; 91 case MotionEvent.ACTION_CANCEL: 92 if (mTrackingTouches) { 93 mMotionEventListener.onCancel(); 94 finishTracking(); 95 return true; 96 } 97 break; 98 case MotionEvent.ACTION_UP: 99 if (mTrackingTouches) { 100 if (mInterceptingTouches) { 101 getVelocityTracker().computeCurrentVelocity(VELOCITY_UNITS); 102 mMotionEventListener.onUp(getVelocityTracker().getXVelocity(), 103 getVelocityTracker().getYVelocity()); 104 } 105 finishTracking(); 106 return true; 107 } 108 break; 109 } 110 return false; 111 } 112 isInGestureRegion(MotionEvent ev)113 private boolean isInGestureRegion(MotionEvent ev) { 114 // Only handles touch events beginning in navigation bar system gesture zone 115 if (mPositioner.getNavBarGestureZone().contains((int) ev.getX(), (int) ev.getY())) { 116 ProtoLog.d(WM_SHELL_BUBBLES, "handling touch x=%d y=%d navBarGestureZone=%s", 117 (int) ev.getX(), (int) ev.getY(), mPositioner.getNavBarGestureZone()); 118 return true; 119 } 120 return false; 121 } 122 getVelocityTracker()123 private VelocityTracker getVelocityTracker() { 124 if (mVelocityTracker == null) { 125 mVelocityTracker = VelocityTracker.obtain(); 126 } 127 return mVelocityTracker; 128 } 129 finishTracking()130 private void finishTracking() { 131 mTouchDown.set(0, 0); 132 mTrackingTouches = false; 133 mInterceptingTouches = false; 134 if (mVelocityTracker != null) { 135 mVelocityTracker.recycle(); 136 mVelocityTracker = null; 137 } 138 } 139 140 /** 141 * Callback for receiving {@link MotionEvent} updates 142 */ 143 interface MotionEventListener { 144 /** 145 * Touch down action. 146 * 147 * @param x x coordinate 148 * @param y y coordinate 149 */ onDown(float x, float y)150 void onDown(float x, float y); 151 152 /** 153 * Move action. 154 * Reports distance from point reported in {@link #onDown(float, float)} 155 * 156 * @param dx distance moved on x-axis from starting point, in pixels 157 * @param dy distance moved on y-axis from starting point, in pixels 158 */ onMove(float dx, float dy)159 void onMove(float dx, float dy); 160 161 /** 162 * Touch up action. 163 * 164 * @param velX velocity of the move action on x axis 165 * @param velY velocity of the move actin on y axis 166 */ onUp(float velX, float velY)167 void onUp(float velX, float velY); 168 169 /** 170 * Motion action was cancelled. 171 */ onCancel()172 void onCancel(); 173 } 174 } 175