1193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa/*
2193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * Copyright (C) 2012 The Android Open Source Project
3193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa *
4193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * Licensed under the Apache License, Version 2.0 (the "License");
5193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * you may not use this file except in compliance with the License.
6193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * You may obtain a copy of the License at
7193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa *
8193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa *      http://www.apache.org/licenses/LICENSE-2.0
9193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa *
10193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * Unless required by applicable law or agreed to in writing, software
11193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * distributed under the License is distributed on an "AS IS" BASIS,
12193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * See the License for the specific language governing permissions and
14193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * limitations under the License.
15193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa */
16193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
17193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawapackage com.android.contacts.dialpad;
18193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
19193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawaimport android.content.Context;
20a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanvimport android.graphics.Rect;
21193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawaimport android.util.AttributeSet;
22193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawaimport android.view.MotionEvent;
2369827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawaimport android.view.View;
24a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanvimport android.view.accessibility.AccessibilityManager;
25193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawaimport android.widget.ImageButton;
26193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
27193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa/**
28193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa * Custom {@link ImageButton} for dialpad buttons.
29a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv * <p>
30a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv * This class implements lift-to-type interaction when touch exploration is
31a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv * enabled.
32193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa */
33193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawapublic class DialpadImageButton extends ImageButton {
34a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    /** Accessibility manager instance used to check touch exploration state. */
35a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    private AccessibilityManager mAccessibilityManager;
36a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
37a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    /** Bounds used to filter HOVER_EXIT events. */
38a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    private Rect mHoverBounds = new Rect();
39a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
4069827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    public interface OnPressedListener {
4169827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa        public void onPressed(View view, boolean pressed);
4269827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    }
4369827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa
4469827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    private OnPressedListener mOnPressedListener;
4569827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa
4669827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    public void setOnPressedListener(OnPressedListener onPressedListener) {
4769827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa        mOnPressedListener = onPressedListener;
4869827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    }
49193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
50193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    public DialpadImageButton(Context context, AttributeSet attrs) {
51193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa        super(context, attrs);
52a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        initForAccessibility(context);
53193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    }
54193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
55193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    public DialpadImageButton(Context context, AttributeSet attrs, int defStyle) {
56193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa        super(context, attrs, defStyle);
57a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        initForAccessibility(context);
58a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    }
59a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
60a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    private void initForAccessibility(Context context) {
61a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
62a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                Context.ACCESSIBILITY_SERVICE);
63193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    }
64193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa
65193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    @Override
6669827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa    public void setPressed(boolean pressed) {
6769827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa        super.setPressed(pressed);
6869827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa        if (mOnPressedListener != null) {
6969827088e19e7e02930ca9e017d9933761a46728Daisuke Miyakawa            mOnPressedListener.onPressed(this, pressed);
70193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa        }
71193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa    }
72a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
73a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    @Override
74a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    public void onSizeChanged(int w, int h, int oldw, int oldh) {
75a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        super.onSizeChanged(w, h, oldw, oldh);
76a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
77a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        mHoverBounds.left = getPaddingLeft();
78a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        mHoverBounds.right = w - getPaddingRight();
79a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        mHoverBounds.top = getPaddingTop();
80a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        mHoverBounds.bottom = h - getPaddingBottom();
81a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    }
82a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
83a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    @Override
84a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    public boolean performClick() {
85a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        // When accessibility is on, simulate press and release to preserve the
86a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        // semantic meaning of performClick(). Required for Braille support.
87a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        if (mAccessibilityManager.isEnabled()) {
88a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            // Checking the press state prevents double activation.
89a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            if (!isPressed()) {
90a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                setPressed(true);
91a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                setPressed(false);
92a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            }
93a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
94a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            return true;
95a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        }
96a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
97a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        return super.performClick();
98a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    }
99a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
100a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    @Override
101a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    public boolean onHoverEvent(MotionEvent event) {
102a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        // When touch exploration is turned on, lifting a finger while inside
103a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        // the button's hover target bounds should perform a click action.
104a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        if (mAccessibilityManager.isEnabled()
105a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                && mAccessibilityManager.isTouchExplorationEnabled()) {
106a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            switch (event.getActionMasked()) {
107a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                case MotionEvent.ACTION_HOVER_ENTER:
108a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    // Lift-to-type temporarily disables double-tap activation.
109a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    setClickable(false);
110a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    break;
111a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                case MotionEvent.ACTION_HOVER_EXIT:
112a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    if (mHoverBounds.contains((int) event.getX(), (int) event.getY())) {
113a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                        performClick();
114a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    }
115a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    setClickable(true);
116a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv                    break;
117a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv            }
118a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        }
119a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv
120a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv        return super.onHoverEvent(event);
121a0d0552348b6b0269fdc92e5638ef6bba1bb74bealanv    }
122193fc1c37de959a31469a2b7e1eb3b29c4f87377Daisuke Miyakawa}
123