Move more Dialpad resources to ContactsCommon

Change-Id: I021f35bdeb6f77345b920a1dba4ce39057c02c93
diff --git a/src/com/android/contacts/common/dialpad/DialpadKeyButton.java b/src/com/android/contacts/common/dialpad/DialpadKeyButton.java
new file mode 100644
index 0000000..db62af8
--- /dev/null
+++ b/src/com/android/contacts/common/dialpad/DialpadKeyButton.java
@@ -0,0 +1,229 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.dialpad;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.os.Bundle;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.view.ViewConfiguration;
+import android.view.accessibility.AccessibilityEvent;
+import android.view.accessibility.AccessibilityManager;
+import android.view.accessibility.AccessibilityNodeInfo;
+import android.widget.FrameLayout;
+
+/**
+ * Custom class for dialpad buttons.
+ * <p>
+ * When touch exploration mode is enabled for accessibility, this class
+ * implements the lift-to-type interaction model:
+ * <ul>
+ * <li>Hovering over the button will cause it to gain accessibility focus
+ * <li>Removing the hover pointer while inside the bounds of the button will
+ * perform a click action
+ * <li>If long-click is supported, hovering over the button for a longer period
+ * of time will switch to the long-click action
+ * <li>Moving the hover pointer outside of the bounds of the button will restore
+ * to the normal click action
+ * <ul>
+ */
+public class DialpadKeyButton extends FrameLayout {
+    /** Timeout before switching to long-click accessibility mode. */
+    private static final int LONG_HOVER_TIMEOUT = ViewConfiguration.getLongPressTimeout() * 2;
+
+    /** Accessibility manager instance used to check touch exploration state. */
+    private AccessibilityManager mAccessibilityManager;
+
+    /** Bounds used to filter HOVER_EXIT events. */
+    private Rect mHoverBounds = new Rect();
+
+    /** Whether this view is currently in the long-hover state. */
+    private boolean mLongHovered;
+
+    /** Alternate content description for long-hover state. */
+    private CharSequence mLongHoverContentDesc;
+
+    /** Backup of standard content description. Used for accessibility. */
+    private CharSequence mBackupContentDesc;
+
+    /** Backup of clickable property. Used for accessibility. */
+    private boolean mWasClickable;
+
+    /** Backup of long-clickable property. Used for accessibility. */
+    private boolean mWasLongClickable;
+
+    /** Runnable used to trigger long-click mode for accessibility. */
+    private Runnable mLongHoverRunnable;
+
+    public interface OnPressedListener {
+        public void onPressed(View view, boolean pressed);
+    }
+
+    private OnPressedListener mOnPressedListener;
+
+    public void setOnPressedListener(OnPressedListener onPressedListener) {
+        mOnPressedListener = onPressedListener;
+    }
+
+    public DialpadKeyButton(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        initForAccessibility(context);
+    }
+
+    public DialpadKeyButton(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+        initForAccessibility(context);
+    }
+
+    private void initForAccessibility(Context context) {
+        mAccessibilityManager = (AccessibilityManager) context.getSystemService(
+                Context.ACCESSIBILITY_SERVICE);
+    }
+
+    public void setLongHoverContentDescription(CharSequence contentDescription) {
+        mLongHoverContentDesc = contentDescription;
+
+        if (mLongHovered) {
+            super.setContentDescription(mLongHoverContentDesc);
+        }
+    }
+
+    @Override
+    public void setContentDescription(CharSequence contentDescription) {
+        if (mLongHovered) {
+            mBackupContentDesc = contentDescription;
+        } else {
+            super.setContentDescription(contentDescription);
+        }
+    }
+
+    @Override
+    public void setPressed(boolean pressed) {
+        super.setPressed(pressed);
+        if (mOnPressedListener != null) {
+            mOnPressedListener.onPressed(this, pressed);
+        }
+    }
+
+    @Override
+    public void onSizeChanged(int w, int h, int oldw, int oldh) {
+        super.onSizeChanged(w, h, oldw, oldh);
+
+        mHoverBounds.left = getPaddingLeft();
+        mHoverBounds.right = w - getPaddingRight();
+        mHoverBounds.top = getPaddingTop();
+        mHoverBounds.bottom = h - getPaddingBottom();
+    }
+
+    @Override
+    public boolean performAccessibilityAction(int action, Bundle arguments) {
+        if (action == AccessibilityNodeInfo.ACTION_CLICK) {
+            simulateClickForAccessibility();
+            return true;
+        }
+
+        return super.performAccessibilityAction(action, arguments);
+    }
+
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        // When touch exploration is turned on, lifting a finger while inside
+        // the button's hover target bounds should perform a click action.
+        if (mAccessibilityManager.isEnabled()
+                && mAccessibilityManager.isTouchExplorationEnabled()) {
+            switch (event.getActionMasked()) {
+                case MotionEvent.ACTION_HOVER_ENTER:
+                    // Lift-to-type temporarily disables double-tap activation.
+                    mWasClickable = isClickable();
+                    mWasLongClickable = isLongClickable();
+                    if (mWasLongClickable && mLongHoverContentDesc != null) {
+                        if (mLongHoverRunnable == null) {
+                            mLongHoverRunnable = new Runnable() {
+                                @Override
+                                public void run() {
+                                    setLongHovered(true);
+                                    announceForAccessibility(mLongHoverContentDesc);
+                                }
+                            };
+                        }
+                        postDelayed(mLongHoverRunnable, LONG_HOVER_TIMEOUT);
+                    }
+
+                    setClickable(false);
+                    setLongClickable(false);
+                    break;
+                case MotionEvent.ACTION_HOVER_EXIT:
+                    if (mHoverBounds.contains((int) event.getX(), (int) event.getY())) {
+                        if (mLongHovered) {
+                            performLongClick();
+                        } else {
+                            simulateClickForAccessibility();
+                        }
+                    }
+
+                    cancelLongHover();
+                    setClickable(mWasClickable);
+                    setLongClickable(mWasLongClickable);
+                    break;
+            }
+        }
+
+        return super.onHoverEvent(event);
+    }
+
+    /**
+     * When accessibility is on, simulate press and release to preserve the
+     * semantic meaning of performClick(). Required for Braille support.
+     */
+    private void simulateClickForAccessibility() {
+        // Checking the press state prevents double activation.
+        if (isPressed()) {
+            return;
+        }
+
+        setPressed(true);
+
+        // Stay consistent with performClick() by sending the event after
+        // setting the pressed state but before performing the action.
+        sendAccessibilityEvent(AccessibilityEvent.TYPE_VIEW_CLICKED);
+
+        setPressed(false);
+    }
+
+    private void setLongHovered(boolean enabled) {
+        if (mLongHovered != enabled) {
+            mLongHovered = enabled;
+
+            // Switch between normal and alternate description, if available.
+            if (enabled) {
+                mBackupContentDesc = getContentDescription();
+                super.setContentDescription(mLongHoverContentDesc);
+            } else {
+                super.setContentDescription(mBackupContentDesc);
+            }
+        }
+    }
+
+    private void cancelLongHover() {
+        if (mLongHoverRunnable != null) {
+            removeCallbacks(mLongHoverRunnable);
+        }
+        setLongHovered(false);
+    }
+}
diff --git a/src/com/android/contacts/common/dialpad/DialpadView.java b/src/com/android/contacts/common/dialpad/DialpadView.java
new file mode 100644
index 0000000..7a31e16
--- /dev/null
+++ b/src/com/android/contacts/common/dialpad/DialpadView.java
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2014 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.dialpad;
+
+import android.content.Context;
+import android.content.res.Resources;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.EditText;
+import android.widget.ImageButton;
+import android.widget.LinearLayout;
+import android.widget.TableRow;
+import android.widget.TextView;
+
+import com.android.contacts.common.R;
+
+/**
+ * View that displays a twelve-key phone dialpad.
+ */
+public class DialpadView extends LinearLayout {
+    private static final String TAG = DialpadView.class.getSimpleName();
+
+    private EditText mDigits;
+    private ImageButton mDelete;
+
+    private boolean mCanDigitsBeEdited;
+
+    public DialpadView(Context context) {
+        this(context, null);
+    }
+
+    public DialpadView(Context context, AttributeSet attrs) {
+        this(context, attrs, 0);
+    }
+
+    public DialpadView(Context context, AttributeSet attrs, int defStyle) {
+        super(context, attrs, defStyle);
+    }
+
+    @Override
+    protected void onFinishInflate() {
+        setupKeypad();
+        mDigits = (EditText) findViewById(R.id.digits);
+        mDelete = (ImageButton) findViewById(R.id.deleteButton);
+    }
+
+    private void setupKeypad() {
+        final int[] buttonIds = new int[] {R.id.zero, R.id.one, R.id.two, R.id.three, R.id.four,
+                R.id.five, R.id.six, R.id.seven, R.id.eight, R.id.nine, R.id.star, R.id.pound};
+
+        final int[] numberIds = new int[] {R.string.dialpad_0_number, R.string.dialpad_1_number,
+                R.string.dialpad_2_number, R.string.dialpad_3_number, R.string.dialpad_4_number,
+                R.string.dialpad_5_number, R.string.dialpad_6_number, R.string.dialpad_7_number,
+                R.string.dialpad_8_number, R.string.dialpad_9_number, R.string.dialpad_star_number,
+                R.string.dialpad_pound_number};
+
+        final int[] letterIds = new int[] {R.string.dialpad_0_letters, R.string.dialpad_1_letters,
+                R.string.dialpad_2_letters, R.string.dialpad_3_letters, R.string.dialpad_4_letters,
+                R.string.dialpad_5_letters, R.string.dialpad_6_letters, R.string.dialpad_7_letters,
+                R.string.dialpad_8_letters, R.string.dialpad_9_letters,
+                R.string.dialpad_star_letters, R.string.dialpad_pound_letters};
+
+        final Resources resources = getContext().getResources();
+
+        DialpadKeyButton dialpadKey;
+        TextView numberView;
+        TextView lettersView;
+
+        for (int i = 0; i < buttonIds.length; i++) {
+            dialpadKey = (DialpadKeyButton) findViewById(buttonIds[i]);
+            numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
+            lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
+            final String numberString = resources.getString(numberIds[i]);
+            numberView.setText(numberString);
+            numberView.setElegantTextHeight(false);
+            dialpadKey.setContentDescription(numberString);
+            if (lettersView != null) {
+                lettersView.setText(resources.getString(letterIds[i]));
+            }
+        }
+
+        final DialpadKeyButton one = (DialpadKeyButton) findViewById(R.id.one);
+        one.setLongHoverContentDescription(
+                resources.getText(R.string.description_voicemail_button));
+
+        final DialpadKeyButton zero = (DialpadKeyButton) findViewById(R.id.zero);
+        zero.setLongHoverContentDescription(
+                resources.getText(R.string.description_image_button_plus));
+
+    }
+
+    public void setShowVoicemailButton(boolean show) {
+        View view = findViewById(R.id.dialpad_key_voicemail);
+        if (view != null) {
+            view.setVisibility(show ? View.VISIBLE : View.INVISIBLE);
+        }
+    }
+
+    /**
+     * Whether or not the digits above the dialer can be edited.
+     *
+     * @param canBeEdited If true, the backspace button will be shown and the digits EditText
+     *         will be configured to allow text manipulation.
+     */
+    public void setCanDigitsBeEdited(boolean canBeEdited) {
+        View deleteButton = findViewById(R.id.deleteButton);
+        deleteButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
+
+        EditText digits = (EditText) findViewById(R.id.digits);
+        digits.setClickable(canBeEdited);
+        digits.setLongClickable(canBeEdited);
+        digits.setFocusableInTouchMode(canBeEdited);
+        digits.setCursorVisible(false);
+
+        View overflowMenuButton = findViewById(R.id.dialpad_overflow);
+        overflowMenuButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
+
+        View addContactButton = findViewById(R.id.dialpad_add_contact);
+        addContactButton.setVisibility(canBeEdited ? View.VISIBLE : View.GONE);
+        mCanDigitsBeEdited = canBeEdited;
+    }
+
+    public boolean canDigitsBeEdited() {
+        return mCanDigitsBeEdited;
+    }
+
+    /**
+     * Always returns true for onHoverEvent callbacks, to fix problems with accessibility due to
+     * the dialpad overlaying other fragments.
+     */
+    @Override
+    public boolean onHoverEvent(MotionEvent event) {
+        return true;
+    }
+
+    public EditText getDigits() {
+        return mDigits;
+    }
+
+    public ImageButton getDeleteButton() {
+        return mDelete;
+    }
+}
diff --git a/src/com/android/contacts/common/dialpad/DigitsEditText.java b/src/com/android/contacts/common/dialpad/DigitsEditText.java
new file mode 100644
index 0000000..b7b9aff
--- /dev/null
+++ b/src/com/android/contacts/common/dialpad/DigitsEditText.java
@@ -0,0 +1,58 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.contacts.common.dialpad;
+
+import android.content.Context;
+import android.graphics.Rect;
+import android.text.InputType;
+import android.util.AttributeSet;
+import android.view.MotionEvent;
+import android.view.inputmethod.InputMethodManager;
+import android.widget.EditText;
+
+/**
+ * EditText which suppresses IME show up.
+ */
+public class DigitsEditText extends EditText {
+    public DigitsEditText(Context context, AttributeSet attrs) {
+        super(context, attrs);
+        setInputType(getInputType() | InputType.TYPE_TEXT_FLAG_NO_SUGGESTIONS);
+        setShowSoftInputOnFocus(false);
+    }
+
+    @Override
+    protected void onFocusChanged(boolean focused, int direction, Rect previouslyFocusedRect) {
+        super.onFocusChanged(focused, direction, previouslyFocusedRect);
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+    }
+
+    @Override
+    public boolean onTouchEvent(MotionEvent event) {
+        final boolean ret = super.onTouchEvent(event);
+        // Must be done after super.onTouchEvent()
+        final InputMethodManager imm = ((InputMethodManager) getContext()
+                .getSystemService(Context.INPUT_METHOD_SERVICE));
+        if (imm != null && imm.isActive(this)) {
+            imm.hideSoftInputFromWindow(getApplicationWindowToken(), 0);
+        }
+        return ret;
+    }
+}