blob: ef353f940461f913b422881b8d5a25dca5e0d4cf [file] [log] [blame]
Winson Chung88f33452012-02-23 15:23:44 -08001/*
2 * Copyright (C) 2012 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
Daniel Sandler325dc232013-06-05 22:57:57 -040017package com.android.launcher3;
Winson Chung88f33452012-02-23 15:23:44 -080018
Sunny Goyal17feee82020-03-24 13:55:15 -070019import android.view.MotionEvent;
Winson Chung88f33452012-02-23 15:23:44 -080020import android.view.View;
Tony2ca999c2018-09-24 17:24:51 -040021import android.view.ViewConfiguration;
Winson Chung88f33452012-02-23 15:23:44 -080022
Sunny Goyal17feee82020-03-24 13:55:15 -070023/**
24 * Utility class to handle tripper long press on a view with custom timeout and stylus event
25 */
Winson Chung88f33452012-02-23 15:23:44 -080026public class CheckLongPressHelper {
Winson Chung7501adf2015-06-02 11:24:28 -070027
Tony2ca999c2018-09-24 17:24:51 -040028 public static final float DEFAULT_LONG_PRESS_TIMEOUT_FACTOR = 0.75f;
Tony Wickham1bce7fd2016-04-28 17:39:03 -070029
Sunny Goyal17feee82020-03-24 13:55:15 -070030 private final View mView;
31 private final View.OnLongClickListener mListener;
32 private final float mSlop;
Winson Chung88f33452012-02-23 15:23:44 -080033
Sunny Goyal17feee82020-03-24 13:55:15 -070034 private float mLongPressTimeoutFactor = DEFAULT_LONG_PRESS_TIMEOUT_FACTOR;
35
36 private boolean mHasPerformedLongPress;
37
38 private Runnable mPendingCheckForLongPress;
Winson Chung88f33452012-02-23 15:23:44 -080039
40 public CheckLongPressHelper(View v) {
Sunny Goyal17feee82020-03-24 13:55:15 -070041 this(v, null);
Winson Chung88f33452012-02-23 15:23:44 -080042 }
43
Winson Chung6b276142015-05-13 15:44:26 -070044 public CheckLongPressHelper(View v, View.OnLongClickListener listener) {
45 mView = v;
46 mListener = listener;
Sunny Goyal17feee82020-03-24 13:55:15 -070047 mSlop = ViewConfiguration.get(mView.getContext()).getScaledTouchSlop();
48 }
49
50 /**
51 * Handles the touch event on a view
52 *
53 * @see View#onTouchEvent(MotionEvent)
54 */
55 public void onTouchEvent(MotionEvent ev) {
56 switch (ev.getAction()) {
57 case MotionEvent.ACTION_DOWN: {
58 // Just in case the previous long press hasn't been cleared, we make sure to
59 // start fresh on touch down.
60 cancelLongPress();
61
62 postCheckForLongPress();
63 if (isStylusButtonPressed(ev)) {
64 triggerLongPress();
65 }
66 break;
67 }
68 case MotionEvent.ACTION_CANCEL:
69 case MotionEvent.ACTION_UP:
70 cancelLongPress();
71 break;
72 case MotionEvent.ACTION_MOVE:
73 if (!Utilities.pointInView(mView, ev.getX(), ev.getY(), mSlop)) {
74 cancelLongPress();
75 } else if (mPendingCheckForLongPress != null && isStylusButtonPressed(ev)) {
76 // Only trigger long press if it has not been cancelled before
77 triggerLongPress();
78 }
79 break;
80 }
Winson Chung6b276142015-05-13 15:44:26 -070081 }
82
Winson Chung7501adf2015-06-02 11:24:28 -070083 /**
84 * Overrides the default long press timeout.
85 */
Tony2ca999c2018-09-24 17:24:51 -040086 public void setLongPressTimeoutFactor(float longPressTimeoutFactor) {
87 mLongPressTimeoutFactor = longPressTimeoutFactor;
Winson Chung7501adf2015-06-02 11:24:28 -070088 }
89
Sunny Goyal17feee82020-03-24 13:55:15 -070090 private void postCheckForLongPress() {
Winson Chung88f33452012-02-23 15:23:44 -080091 mHasPerformedLongPress = false;
92
93 if (mPendingCheckForLongPress == null) {
Sunny Goyal17feee82020-03-24 13:55:15 -070094 mPendingCheckForLongPress = this::triggerLongPress;
Winson Chung88f33452012-02-23 15:23:44 -080095 }
Tony2ca999c2018-09-24 17:24:51 -040096 mView.postDelayed(mPendingCheckForLongPress,
97 (long) (ViewConfiguration.getLongPressTimeout() * mLongPressTimeoutFactor));
Winson Chung88f33452012-02-23 15:23:44 -080098 }
99
Sunny Goyal17feee82020-03-24 13:55:15 -0700100 /**
101 * Cancels any pending long press
102 */
Winson Chung88f33452012-02-23 15:23:44 -0800103 public void cancelLongPress() {
104 mHasPerformedLongPress = false;
Sunny Goyal17feee82020-03-24 13:55:15 -0700105 clearCallbacks();
106 }
107
108 /**
109 * Returns true if long press has been performed in the current touch gesture
110 */
111 public boolean hasPerformedLongPress() {
112 return mHasPerformedLongPress;
113 }
114
115 private void triggerLongPress() {
116 if ((mView.getParent() != null) && mView.hasWindowFocus() && !mHasPerformedLongPress) {
117 boolean handled;
118 if (mListener != null) {
119 handled = mListener.onLongClick(mView);
120 } else {
121 handled = mView.performLongClick();
122 }
123 if (handled) {
124 mView.setPressed(false);
125 mHasPerformedLongPress = true;
126 }
127 clearCallbacks();
128 }
129 }
130
131 private void clearCallbacks() {
Winson Chung88f33452012-02-23 15:23:44 -0800132 if (mPendingCheckForLongPress != null) {
Winson Chung88f33452012-02-23 15:23:44 -0800133 mView.removeCallbacks(mPendingCheckForLongPress);
134 mPendingCheckForLongPress = null;
135 }
136 }
137
Sunny Goyal17feee82020-03-24 13:55:15 -0700138
139 /**
140 * Identifies if the provided {@link MotionEvent} is a stylus with the primary stylus button
141 * pressed.
142 *
143 * @param event The event to check.
144 * @return Whether a stylus button press occurred.
145 */
146 private static boolean isStylusButtonPressed(MotionEvent event) {
147 return event.getToolType(0) == MotionEvent.TOOL_TYPE_STYLUS
148 && event.isButtonPressed(MotionEvent.BUTTON_SECONDARY);
Winson Chung88f33452012-02-23 15:23:44 -0800149 }
150}