33 | * This class implements lift-to-type interaction when touch exploration is 34 | * enabled. 35 | */ 36 | public class DialpadKeyButton extends FrameLayout { 37 | /** Accessibility manager instance used to check touch exploration state. */ 38 | private AccessibilityManager mAccessibilityManager; 39 | 40 | /** Bounds used to filter HOVER_EXIT events. */ 41 | private Rect mHoverBounds = new Rect(); 42 | 43 | public interface OnPressedListener { 44 | public void onPressed(View view, boolean pressed); 45 | } 46 | 47 | private OnPressedListener mOnPressedListener; 48 | 49 | public void setOnPressedListener(OnPressedListener onPressedListener) { 50 | mOnPressedListener = onPressedListener; 51 | } 52 | 53 | public DialpadKeyButton(Context context, AttributeSet attrs) { 54 | super(context, attrs); 55 | initForAccessibility(context); 56 | } 57 | 58 | public DialpadKeyButton(Context context, AttributeSet attrs, int defStyle) { 59 | super(context, attrs, defStyle); 60 | initForAccessibility(context); 61 | } 62 | 63 | private void initForAccessibility(Context context) { 64 | mAccessibilityManager = (AccessibilityManager) context.getSystemService( 65 | Context.ACCESSIBILITY_SERVICE); 66 | } 67 | 68 | @Override 69 | public void setPressed(boolean pressed) { 70 | super.setPressed(pressed); 71 | if (mOnPressedListener != null) { 72 | mOnPressedListener.onPressed(this, pressed); 73 | } 74 | } 75 | 76 | @Override 77 | public void onSizeChanged(int w, int h, int oldw, int oldh) { 78 | super.onSizeChanged(w, h, oldw, oldh); 79 | 80 | mHoverBounds.left = getPaddingLeft(); 81 | mHoverBounds.right = w - getPaddingRight(); 82 | mHoverBounds.top = getPaddingTop(); 83 | mHoverBounds.bottom = h - getPaddingBottom(); 84 | } 85 | 86 | @Override 87 | public boolean performAccessibilityAction(int action, Bundle arguments) { 88 | if (action == AccessibilityNodeInfo.ACTION_CLICK) { 89 | simulateClickForAccessibility(); 90 | return true; 91 | } 92 | 93 | return super.performAccessibilityAction(action, arguments); 94 | } 95 | 96 | @Override 97 | public boolean onHoverEvent(MotionEvent event) { 98 | // When touch exploration is turned on, lifting a finger while inside 99 | // the button's hover target bounds should perform a click action. 100 | if (mAccessibilityManager.isEnabled() 101 | && mAccessibilityManager.isTouchExplorationEnabled()) { 102 | switch (event.getActionMasked()) { 103 | case MotionEvent.ACTION_HOVER_ENTER: 104 | // Lift-to-type temporarily disables double-tap activation. 105 | setClickable(false); 106 | break; 107 | case MotionEvent.ACTION_HOVER_EXIT: 108 | if (mHoverBounds.contains((int) event.getX(), (int) event.getY())) { 109 | simulateClickForAccessibility(); 110 | } 111 | setClickable(true); 112 | break; 113 | } 114 | } 115 | 116 | return super.onHoverEvent(event); 117 | } 118 | 119 | /** 120 | * When accessibility is on, simulate press and release to preserve the 121 | * semantic meaning of performClick(). Required for Braille support. 122 | */ 123 | private void simulateClickForAccessibility() { 124 | // Checking the press state prevents double activation. 125 | if (isPressed()) { 126 | return; 127 | } 128 | 129 | setPressed(true); 130 | 131 | // Stay consistent with performClick() by sending the event after 132 | // setting the pressed state but before performing the action. 133 | sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED); 134 | 135 | setPressed(false); 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /src/net/nashlegend/quickid/view/DigitsEditText.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (C) 2011 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 net.nashlegend.quickid.view; 18 | 19 | import java.lang.reflect.Method; 20 | 21 | import android.annotation.SuppressLint; 22 | import android.content.Context; 23 | import android.graphics.Rect; 24 | import android.text.InputType; 25 | import android.util.AttributeSet; 26 | import android.view.MotionEvent; 27 | import android.view.inputmethod.InputMethodManager; 28 | import android.widget.EditText; 29 | 30 | /** 31 | * EditText which suppresses IME show up. 32 | */ 33 | public class DigitsEditText extends EditText { 34 | public DigitsEditText(Context context, AttributeSet attrs) { 35 | super(context, attrs); 36 | setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS); 37 | try { 38 | // 反射setShowSoftInputOnFocus(false); 39 | Method setShowSoftInputOnFocus; 40 | setShowSoftInputOnFocus = DigitsEditText.class.getMethod( 41 | "setShowSoftInputOnFocus", boolean.class); 42 | setShowSoftInputOnFocus.setAccessible(true); 43 | setShowSoftInputOnFocus.invoke(this, false); 44 | } catch (Exception e) { 45 | 46 | } 47 | } 48 | 49 | @Override 50 | protected void onFocusChanged(boolean focused, int direction, 51 | Rect previouslyFocusedRect) { 52 | super.onFocusChanged(focused, direction, previouslyFocusedRect); 53 | final InputMethodManager imm = ((InputMethodManager) getContext() 54 | .getSystemService(Context.INPUT_METHOD_SERVICE)); 55 | if (imm != null && imm.isActive(this)) { 56 | imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0); 57 | } 58 | } 59 | 60 | @SuppressLint("ClickableViewAccessibility") 61 | @Override 62 | public boolean onTouchEvent(MotionEvent event) { 63 | final boolean ret = super.onTouchEvent(event); 64 | // Must be done after super.onTouchEvent() 65 | final InputMethodManager imm = ((InputMethodManager) getContext() 66 | .getSystemService(Context.INPUT_METHOD_SERVICE)); 67 | if (imm != null && imm.isActive(this)) { 68 | imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0); 69 | } 70 | return ret; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /src/net/nashlegend/quickid/view/PhoneView.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.quickid.view; 3 | 4 | import net.nashlegend.quickid.model.Contact.PhoneStruct; 5 | import net.nashlegend.quickid.util.ContactHelper; 6 | 7 | import net.nashlegend.quickid.R; 8 | 9 | import android.annotation.SuppressLint; 10 | import android.content.Context; 11 | import android.graphics.Color; 12 | import android.os.Handler; 13 | import android.text.Spannable; 14 | import android.text.SpannableStringBuilder; 15 | import android.text.TextUtils; 16 | import android.text.style.ForegroundColorSpan; 17 | import android.view.LayoutInflater; 18 | import android.view.MotionEvent; 19 | import android.view.View; 20 | import android.widget.FrameLayout; 21 | import android.widget.ImageButton; 22 | import android.widget.LinearLayout; 23 | import android.widget.TextView; 24 | 25 | public class PhoneView extends FrameLayout { 26 | 27 | TextView numberText; 28 | LinearLayout layoutRoot; 29 | ImageButton smsButton; 30 | PhoneStruct phone; 31 | boolean matchNumber = false; 32 | String matchedNumber = ""; 33 | 34 | public PhoneView(Context context) { 35 | super(context); 36 | LayoutInflater inflater = (LayoutInflater) context 37 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 38 | inflater.inflate(R.layout.layout_phone_view, this); 39 | smsButton = (ImageButton) findViewById(R.id.button_send_sms); 40 | layoutRoot = (LinearLayout) findViewById(R.id.layout_phone); 41 | numberText = (TextView) findViewById(R.id.textview_phone_numbers); 42 | smsButton.setOnClickListener(onClickListener); 43 | layoutRoot.setClickable(true); 44 | layoutRoot.setOnTouchListener(new OnShortLongClickListener()); 45 | } 46 | 47 | public void setPhone(PhoneStruct p) { 48 | this.phone = p; 49 | numberText.setText(phone.phoneNumber); 50 | } 51 | 52 | public void setPhone(PhoneStruct p, String mStr) { 53 | this.phone = p; 54 | numberText.setText(phone.phoneNumber); 55 | String str = phone.phoneNumber; 56 | if (!TextUtils.isEmpty(mStr)) { 57 | int idx = str.indexOf(mStr); 58 | if (idx >= 0) { 59 | SpannableStringBuilder builder = new SpannableStringBuilder(str); 60 | ForegroundColorSpan redSpan = new ForegroundColorSpan(Color.RED); 61 | builder.setSpan(redSpan, idx, idx + mStr.length(), 62 | Spannable.SPAN_EXCLUSIVE_EXCLUSIVE); 63 | numberText.setText(builder); 64 | } 65 | } 66 | 67 | } 68 | 69 | private void onLongClick() { 70 | // do nothing 71 | } 72 | 73 | OnClickListener onClickListener = new OnClickListener() { 74 | 75 | @Override 76 | public void onClick(View v) { 77 | switch (v.getId()) { 78 | case R.id.button_send_sms: 79 | ContactHelper.sendSMS(numberText.getText().toString()); 80 | break; 81 | default: 82 | break; 83 | } 84 | } 85 | }; 86 | 87 | class OnShortLongClickListener implements OnTouchListener { 88 | long longDura = 1000L; 89 | long shortDura = 300L; 90 | long startTime = 0L; 91 | Handler handler = new Handler(); 92 | Runnable longPressRunnable = new Runnable() { 93 | public void run() { 94 | onLongClick(); 95 | } 96 | }; 97 | 98 | @SuppressLint("ClickableViewAccessibility") 99 | @Override 100 | public boolean onTouch(View v, MotionEvent event) { 101 | switch (event.getAction()) { 102 | case MotionEvent.ACTION_DOWN: 103 | startTime = System.currentTimeMillis(); 104 | handler.removeCallbacks(longPressRunnable); 105 | handler.postDelayed(longPressRunnable, longDura); 106 | break; 107 | case MotionEvent.ACTION_UP: 108 | handler.removeCallbacks(longPressRunnable); 109 | if (System.currentTimeMillis() - startTime < shortDura) { 110 | ContactHelper 111 | .makePhoneCall(numberText.getText().toString()); 112 | } 113 | break; 114 | case MotionEvent.ACTION_CANCEL: 115 | handler.removeCallbacks(longPressRunnable); 116 | break; 117 | default: 118 | break; 119 | } 120 | return false; 121 | } 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/net/nashlegend/quickid/view/SideBar.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.quickid.view; 3 | 4 | import net.nashlegend.legendutils.Tools.DisplayUtil; 5 | import android.annotation.SuppressLint; 6 | import android.content.Context; 7 | import android.graphics.Canvas; 8 | import android.graphics.Color; 9 | import android.graphics.Paint; 10 | import android.graphics.Typeface; 11 | import android.util.AttributeSet; 12 | import android.view.MotionEvent; 13 | import android.view.View; 14 | 15 | public class SideBar extends View { 16 | 17 | OnTouchingLetterChangedListener onTouchingLetterChangedListener; 18 | String[] chars = { 19 | "#", "A", "B", "C", "D", "E", "F", "G", "H", "I", "J", "K", "L", "M", "N", "O", "P", 20 | "Q", "R", "S", "T", "U", "V", "W", "X", "Y", "Z" 21 | }; 22 | int choose = -1; 23 | Paint paint = new Paint(); 24 | boolean showBkg = false; 25 | 26 | public SideBar(Context context, AttributeSet attrs, int defStyle) { 27 | super(context, attrs, defStyle); 28 | } 29 | 30 | public SideBar(Context context, AttributeSet attrs) { 31 | super(context, attrs); 32 | } 33 | 34 | public SideBar(Context context) { 35 | super(context); 36 | } 37 | 38 | public void setChars(String[] cs) { 39 | chars = cs; 40 | } 41 | 42 | @Override 43 | protected void onDraw(Canvas canvas) { 44 | super.onDraw(canvas); 45 | if (chars.length > 0) { 46 | if (showBkg) { 47 | canvas.drawColor(Color.parseColor("#00000000")); 48 | } 49 | int height = getHeight(); 50 | int width = getWidth(); 51 | int singleHeight = height / chars.length; 52 | for (int i = 0; i < chars.length; i++) { 53 | paint.setColor(Color.BLACK); 54 | paint.setTextSize(DisplayUtil.dip2px(10, getContext())); 55 | paint.setTypeface(Typeface.DEFAULT_BOLD); 56 | paint.setAntiAlias(true); 57 | if (i == choose) { 58 | paint.setColor(Color.parseColor("#3399ff")); 59 | paint.setFakeBoldText(true); 60 | } 61 | float xPos = width / 2 - paint.measureText(chars[i]) / 2; 62 | float yPos = singleHeight * (i + 0.5f); 63 | canvas.drawText(chars[i], xPos, yPos, paint); 64 | paint.reset(); 65 | } 66 | } 67 | } 68 | 69 | @Override 70 | public boolean dispatchTouchEvent(MotionEvent event) { 71 | if (chars.length > 0) { 72 | final int action = event.getAction(); 73 | final float y = event.getY(); 74 | final int oldChoose = choose; 75 | final OnTouchingLetterChangedListener listener = onTouchingLetterChangedListener; 76 | final int c = (int) (y / getHeight() * chars.length); 77 | 78 | switch (action) { 79 | case MotionEvent.ACTION_DOWN: 80 | showBkg = true; 81 | if (oldChoose != c && listener != null) { 82 | if (c >= 0 && c < chars.length) { 83 | listener.onTouchingLetterChanged(chars[c]); 84 | choose = c; 85 | invalidate(); 86 | } 87 | } 88 | 89 | break; 90 | case MotionEvent.ACTION_MOVE: 91 | if (oldChoose != c && listener != null) { 92 | if (c >= 0 && c < chars.length) { 93 | listener.onTouchingLetterChanged(chars[c]); 94 | choose = c; 95 | invalidate(); 96 | } 97 | } 98 | break; 99 | case MotionEvent.ACTION_UP: 100 | showBkg = false; 101 | choose = -1; 102 | invalidate(); 103 | break; 104 | } 105 | } 106 | return true; 107 | } 108 | 109 | @SuppressLint("ClickableViewAccessibility") 110 | @Override 111 | public boolean onTouchEvent(MotionEvent event) { 112 | return super.onTouchEvent(event); 113 | } 114 | 115 | public void setOnTouchingLetterChangedListener( 116 | OnTouchingLetterChangedListener onTouchingLetterChangedListener) { 117 | this.onTouchingLetterChangedListener = onTouchingLetterChangedListener; 118 | } 119 | 120 | public interface OnTouchingLetterChangedListener { 121 | public void onTouchingLetterChanged(String s); 122 | } 123 | 124 | } 125 | -------------------------------------------------------------------------------- /src/net/nashlegend/quickid/view/SplitterView.java: -------------------------------------------------------------------------------- 1 | 2 | package net.nashlegend.quickid.view; 3 | 4 | import net.nashlegend.quickid.R; 5 | 6 | import android.content.Context; 7 | import android.util.AttributeSet; 8 | import android.view.LayoutInflater; 9 | import android.widget.FrameLayout; 10 | import android.widget.TextView; 11 | 12 | public class SplitterView extends FrameLayout { 13 | 14 | TextView textView; 15 | public String splitter; 16 | 17 | public SplitterView(Context context) { 18 | super(context); 19 | LayoutInflater inflater = (LayoutInflater) context 20 | .getSystemService(Context.LAYOUT_INFLATER_SERVICE); 21 | inflater.inflate(R.layout.layout_splitter_view, this); 22 | textView = (TextView) findViewById(R.id.textview_splitter); 23 | } 24 | 25 | public SplitterView(Context context, AttributeSet attrs) { 26 | super(context, attrs); 27 | } 28 | 29 | public SplitterView(Context context, AttributeSet attrs, int defStyle) { 30 | super(context, attrs, defStyle); 31 | } 32 | 33 | public void build(String txt) { 34 | splitter = txt; 35 | textView.setText(splitter); 36 | } 37 | 38 | } 39 | --------------------------------------------------------------------------------