1083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon/*
2083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * Copyright (C) 2012 The Android Open Source Project
3083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon *
4083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * Licensed under the Apache License, Version 2.0 (the "License");
5083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * you may not use this file except in compliance with the License.
6083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * You may obtain a copy of the License at
7083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon *
8083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon *      http://www.apache.org/licenses/LICENSE-2.0
9083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon *
10083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * Unless required by applicable law or agreed to in writing, software
11083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * distributed under the License is distributed on an "AS IS" BASIS,
12083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * See the License for the specific language governing permissions and
14083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * limitations under the License.
15083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon */
16083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
17083c494c07681cdb826a1f2958d2143cf694de82Santos Cordonpackage com.android.incallui;
18083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
19083c494c07681cdb826a1f2958d2143cf694de82Santos Cordonimport android.view.MotionEvent;
20083c494c07681cdb826a1f2958d2143cf694de82Santos Cordonimport android.view.View;
21083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
22083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon/**
23083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * OnTouchListener used to shrink the "hit target" of some onscreen buttons.
24083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon *
25083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * We do this for a few specific buttons which are vulnerable to
26083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * "false touches" because either (1) they're near the edge of the
27083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * screen and might be unintentionally touched while holding the
28083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * device in your hand, (2) they're in the upper corners and might
29083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * be touched by the user's ear before the prox sensor has a chance to
30083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon * kick in, or (3) they are close to other buttons.
31083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon */
32083c494c07681cdb826a1f2958d2143cf694de82Santos Cordonpublic class SmallerHitTargetTouchListener implements View.OnTouchListener {
33083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    private static final String TAG = "SmallerHitTargetTouchListener";
34083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
35083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    /**
36083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     * Edge dimensions where a touch does not register an action (in DIP).
37083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     */
38083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    private static final int HIT_TARGET_EDGE_IGNORE_DP_X = 30;
39083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    private static final int HIT_TARGET_EDGE_IGNORE_DP_Y = 10;
40083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    private static final int HIT_TARGET_MIN_SIZE_DP_X = HIT_TARGET_EDGE_IGNORE_DP_X * 3;
41083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    private static final int HIT_TARGET_MIN_SIZE_DP_Y = HIT_TARGET_EDGE_IGNORE_DP_Y * 3;
42083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
43083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    // True if the most recent DOWN event was a "hit".
44083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    boolean mDownEventHit;
45083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
46083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    /**
47083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     * Called when a touch event is dispatched to a view. This allows listeners to
48083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     * get a chance to respond before the target view.
49083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     *
50083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     * @return True if the listener has consumed the event, false otherwise.
51083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     *         (In other words, we return true when the touch is *outside*
52083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     *         the "smaller hit target", which will prevent the actual
53083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     *         button from handling these events.)
54083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon     */
55083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    @Override
56083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    public boolean onTouch(View v, MotionEvent event) {
57083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon        // if (DBG) log("SmallerHitTargetTouchListener: " + v + ", event " + event);
58083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
59083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon        if (event.getAction() == MotionEvent.ACTION_DOWN) {
60083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // Note that event.getX() and event.getY() are already
61083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // translated into the View's coordinates.  (In other words,
62083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // "0,0" is a touch on the upper-left-most corner of the view.)
63083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int touchX = (int) event.getX();
64083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int touchY = (int) event.getY();
65083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
66083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int viewWidth = v.getWidth();
67083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int viewHeight = v.getHeight();
68083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
69083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final float pixelDensity = v.getResources().getDisplayMetrics().density;
70083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int targetMinSizeX = (int) (HIT_TARGET_MIN_SIZE_DP_X * pixelDensity);
71083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int targetMinSizeY = (int) (HIT_TARGET_MIN_SIZE_DP_Y * pixelDensity);
72083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
73083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            int edgeIgnoreX = (int) (HIT_TARGET_EDGE_IGNORE_DP_X * pixelDensity);
74083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            int edgeIgnoreY = (int) (HIT_TARGET_EDGE_IGNORE_DP_Y * pixelDensity);
75083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
76083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // If we are dealing with smaller buttons where the dead zone defined by
77083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // HIT_TARGET_EDGE_IGNORE_DP_[X|Y] is too large.
78083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            if (viewWidth < targetMinSizeX || viewHeight < targetMinSizeY) {
79083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // This really should not happen given our two use cases (as of this writing)
80083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // in the call edge button and secondary calling card. However, we leave
81083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // this is as a precautionary measure.
82083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                Log.w(TAG, "onTouch: view is too small for SmallerHitTargetTouchListener");
83083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                edgeIgnoreX = 0;
84083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                edgeIgnoreY = 0;
85083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            }
86083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
87083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int minTouchX = edgeIgnoreX;
88083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int maxTouchX = viewWidth - edgeIgnoreX;
89083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int minTouchY = edgeIgnoreY;
90083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            final int maxTouchY = viewHeight - edgeIgnoreY;
91083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon
92083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            if (touchX < minTouchX || touchX > maxTouchX ||
93083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                    touchY < minTouchY || touchY > maxTouchY) {
94083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // Missed!
95083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // if (DBG) log("  -> MISSED!");
96083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                mDownEventHit = false;
97083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                return true;  // Consume this event; don't let the button see it
98083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            } else {
99083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // Hit!
100083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                // if (DBG) log("  -> HIT!");
101083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                mDownEventHit = true;
102083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon                return false;  // Let this event through to the actual button
103083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            }
104083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon        } else {
105083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // This is a MOVE, UP or CANCEL event.
106083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            //
107083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // We only do the "smaller hit target" check on DOWN events.
108083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // For the subsequent MOVE/UP/CANCEL events, we let them
109083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // through to the actual button IFF the previous DOWN event
110083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            // got through to the actual button (i.e. it was a "hit".)
111083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon            return !mDownEventHit;
112083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon        }
113083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon    }
114083c494c07681cdb826a1f2958d2143cf694de82Santos Cordon}
115