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