summaryrefslogtreecommitdiff
path: root/java/com/android/incallui/DialpadFragment.java
diff options
context:
space:
mode:
Diffstat (limited to 'java/com/android/incallui/DialpadFragment.java')
-rw-r--r--java/com/android/incallui/DialpadFragment.java461
1 files changed, 461 insertions, 0 deletions
diff --git a/java/com/android/incallui/DialpadFragment.java b/java/com/android/incallui/DialpadFragment.java
new file mode 100644
index 000000000..7f494aa61
--- /dev/null
+++ b/java/com/android/incallui/DialpadFragment.java
@@ -0,0 +1,461 @@
+/*
+ * Copyright (C) 2013 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.incallui;
+
+import android.content.Context;
+import android.os.Bundle;
+import android.text.Editable;
+import android.text.method.DialerKeyListener;
+import android.util.ArrayMap;
+import android.util.AttributeSet;
+import android.view.KeyEvent;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.View.OnClickListener;
+import android.view.View.OnKeyListener;
+import android.view.ViewGroup;
+import android.widget.EditText;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.dialpadview.DialpadKeyButton;
+import com.android.dialer.dialpadview.DialpadKeyButton.OnPressedListener;
+import com.android.dialer.dialpadview.DialpadView;
+import com.android.incallui.DialpadPresenter.DialpadUi;
+import com.android.incallui.baseui.BaseFragment;
+import java.util.Map;
+
+/** Fragment for call control buttons */
+public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
+ implements DialpadUi, OnKeyListener, OnClickListener, OnPressedListener {
+
+ /** Hash Map to map a view id to a character */
+ private static final Map<Integer, Character> mDisplayMap = new ArrayMap<>();
+
+ /** Set up the static maps */
+ static {
+ // Map the buttons to the display characters
+ mDisplayMap.put(R.id.one, '1');
+ mDisplayMap.put(R.id.two, '2');
+ mDisplayMap.put(R.id.three, '3');
+ mDisplayMap.put(R.id.four, '4');
+ mDisplayMap.put(R.id.five, '5');
+ mDisplayMap.put(R.id.six, '6');
+ mDisplayMap.put(R.id.seven, '7');
+ mDisplayMap.put(R.id.eight, '8');
+ mDisplayMap.put(R.id.nine, '9');
+ mDisplayMap.put(R.id.zero, '0');
+ mDisplayMap.put(R.id.pound, '#');
+ mDisplayMap.put(R.id.star, '*');
+ }
+
+ private final int[] mButtonIds =
+ 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
+ };
+ private EditText mDtmfDialerField;
+ // KeyListener used with the "dialpad digits" EditText widget.
+ private DTMFKeyListener mDialerKeyListener;
+ private DialpadView mDialpadView;
+ private int mCurrentTextColor;
+
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.dialpad_back) {
+ getActivity().onBackPressed();
+ }
+ }
+
+ @Override
+ public boolean onKey(View v, int keyCode, KeyEvent event) {
+ Log.d(this, "onKey: keyCode " + keyCode + ", view " + v);
+
+ if (keyCode == KeyEvent.KEYCODE_DPAD_CENTER || keyCode == KeyEvent.KEYCODE_ENTER) {
+ int viewId = v.getId();
+ if (mDisplayMap.containsKey(viewId)) {
+ switch (event.getAction()) {
+ case KeyEvent.ACTION_DOWN:
+ if (event.getRepeatCount() == 0) {
+ getPresenter().processDtmf(mDisplayMap.get(viewId));
+ }
+ break;
+ case KeyEvent.ACTION_UP:
+ getPresenter().stopDtmf();
+ break;
+ }
+ // do not return true [handled] here, since we want the
+ // press / click animation to be handled by the framework.
+ }
+ }
+ return false;
+ }
+
+ @Override
+ public DialpadPresenter createPresenter() {
+ return new DialpadPresenter();
+ }
+
+ @Override
+ public DialpadPresenter.DialpadUi getUi() {
+ return this;
+ }
+
+ // TODO Adds hardware keyboard listener
+
+ @Override
+ public View onCreateView(
+ LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
+ final View parent = inflater.inflate(R.layout.incall_dialpad_fragment, container, false);
+ mDialpadView = (DialpadView) parent.findViewById(R.id.dialpad_view);
+ mDialpadView.setCanDigitsBeEdited(false);
+ mDialpadView.setBackgroundResource(R.color.incall_dialpad_background);
+ mDtmfDialerField = (EditText) parent.findViewById(R.id.digits);
+ if (mDtmfDialerField != null) {
+ mDialerKeyListener = new DTMFKeyListener();
+ mDtmfDialerField.setKeyListener(mDialerKeyListener);
+ // remove the long-press context menus that support
+ // the edit (copy / paste / select) functions.
+ mDtmfDialerField.setLongClickable(false);
+ mDtmfDialerField.setElegantTextHeight(false);
+ configureKeypadListeners();
+ }
+ View backButton = mDialpadView.findViewById(R.id.dialpad_back);
+ backButton.setVisibility(View.VISIBLE);
+ backButton.setOnClickListener(this);
+
+ return parent;
+ }
+
+ @Override
+ public void onResume() {
+ super.onResume();
+ updateColors();
+ }
+
+ public void updateColors() {
+ int textColor = InCallPresenter.getInstance().getThemeColorManager().getPrimaryColor();
+
+ if (mCurrentTextColor == textColor) {
+ return;
+ }
+
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
+ ((TextView) dialpadKey.findViewById(R.id.dialpad_key_number)).setTextColor(textColor);
+ }
+
+ mCurrentTextColor = textColor;
+ }
+
+ @Override
+ public void onDestroyView() {
+ mDialerKeyListener = null;
+ super.onDestroyView();
+ }
+
+ /**
+ * Getter for Dialpad text.
+ *
+ * @return String containing current Dialpad EditText text.
+ */
+ public String getDtmfText() {
+ return mDtmfDialerField.getText().toString();
+ }
+
+ /**
+ * Sets the Dialpad text field with some text.
+ *
+ * @param text Text to set Dialpad EditText to.
+ */
+ public void setDtmfText(String text) {
+ mDtmfDialerField.setText(PhoneNumberUtilsCompat.createTtsSpannable(text));
+ }
+
+ @Override
+ public void setVisible(boolean on) {
+ if (on) {
+ getView().setVisibility(View.VISIBLE);
+ } else {
+ getView().setVisibility(View.INVISIBLE);
+ }
+ }
+
+ /** Starts the slide up animation for the Dialpad keys when the Dialpad is revealed. */
+ public void animateShowDialpad() {
+ final DialpadView dialpadView = (DialpadView) getView().findViewById(R.id.dialpad_view);
+ dialpadView.animateShow();
+ }
+
+ @Override
+ public void appendDigitsToField(char digit) {
+ if (mDtmfDialerField != null) {
+ // TODO: maybe *don't* manually append this digit if
+ // mDialpadDigits is focused and this key came from the HW
+ // keyboard, since in that case the EditText field will
+ // get the key event directly and automatically appends
+ // whetever the user types.
+ // (Or, a cleaner fix would be to just make mDialpadDigits
+ // *not* handle HW key presses. That seems to be more
+ // complicated than just setting focusable="false" on it,
+ // though.)
+ mDtmfDialerField.getText().append(digit);
+ }
+ }
+
+ /** Called externally (from InCallScreen) to play a DTMF Tone. */
+ /* package */ boolean onDialerKeyDown(KeyEvent event) {
+ Log.d(this, "Notifying dtmf key down.");
+ if (mDialerKeyListener != null) {
+ return mDialerKeyListener.onKeyDown(event);
+ } else {
+ return false;
+ }
+ }
+
+ /** Called externally (from InCallScreen) to cancel the last DTMF Tone played. */
+ public boolean onDialerKeyUp(KeyEvent event) {
+ Log.d(this, "Notifying dtmf key up.");
+ if (mDialerKeyListener != null) {
+ return mDialerKeyListener.onKeyUp(event);
+ } else {
+ return false;
+ }
+ }
+
+ private void configureKeypadListeners() {
+ DialpadKeyButton dialpadKey;
+ for (int i = 0; i < mButtonIds.length; i++) {
+ dialpadKey = (DialpadKeyButton) mDialpadView.findViewById(mButtonIds[i]);
+ dialpadKey.setOnKeyListener(this);
+ dialpadKey.setOnClickListener(this);
+ dialpadKey.setOnPressedListener(this);
+ }
+ }
+
+ @Override
+ public void onPressed(View view, boolean pressed) {
+ if (pressed && mDisplayMap.containsKey(view.getId())) {
+ Log.d(this, "onPressed: " + pressed + " " + mDisplayMap.get(view.getId()));
+ getPresenter().processDtmf(mDisplayMap.get(view.getId()));
+ }
+ if (!pressed) {
+ Log.d(this, "onPressed: " + pressed);
+ getPresenter().stopDtmf();
+ }
+ }
+
+ /**
+ * LinearLayout with getter and setter methods for the translationY property using floats, for
+ * animation purposes.
+ */
+ public static class DialpadSlidingLinearLayout extends LinearLayout {
+
+ public DialpadSlidingLinearLayout(Context context) {
+ super(context);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ }
+
+ public DialpadSlidingLinearLayout(Context context, AttributeSet attrs, int defStyle) {
+ super(context, attrs, defStyle);
+ }
+
+ public float getYFraction() {
+ final int height = getHeight();
+ if (height == 0) {
+ return 0;
+ }
+ return getTranslationY() / height;
+ }
+
+ public void setYFraction(float yFraction) {
+ setTranslationY(yFraction * getHeight());
+ }
+ }
+
+ /**
+ * Our own key listener, specialized for dealing with DTMF codes. 1. Ignore the backspace since it
+ * is irrelevant. 2. Allow ONLY valid DTMF characters to generate a tone and be sent as a DTMF
+ * code. 3. All other remaining characters are handled by the superclass.
+ *
+ * <p>This code is purely here to handle events from the hardware keyboard while the DTMF dialpad
+ * is up.
+ */
+ private class DTMFKeyListener extends DialerKeyListener {
+
+ /**
+ * Overrides the characters used in {@link DialerKeyListener#CHARACTERS} These are the valid
+ * dtmf characters.
+ */
+ public final char[] DTMF_CHARACTERS =
+ new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'};
+
+ private DTMFKeyListener() {
+ super();
+ }
+
+ /** Overriden to return correct DTMF-dialable characters. */
+ @Override
+ protected char[] getAcceptedChars() {
+ return DTMF_CHARACTERS;
+ }
+
+ /** special key listener ignores backspace. */
+ @Override
+ public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Overriden so that with each valid button press, we start sending a dtmf code and play a local
+ * dtmf tone.
+ */
+ @Override
+ public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
+ // if (DBG) log("DTMFKeyListener.onKeyDown, keyCode " + keyCode + ", view " + view);
+
+ // find the character
+ char c = (char) lookup(event, content);
+
+ // if not a long press, and parent onKeyDown accepts the input
+ if (event.getRepeatCount() == 0 && super.onKeyDown(view, content, keyCode, event)) {
+
+ boolean keyOK = ok(getAcceptedChars(), c);
+
+ // if the character is a valid dtmf code, start playing the tone and send the
+ // code.
+ if (keyOK) {
+ Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
+ getPresenter().processDtmf(c);
+ } else {
+ Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
+ }
+ return true;
+ }
+ return false;
+ }
+
+ /**
+ * Overriden so that with each valid button up, we stop sending a dtmf code and the dtmf tone.
+ */
+ @Override
+ public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) {
+ // if (DBG) log("DTMFKeyListener.onKeyUp, keyCode " + keyCode + ", view " + view);
+
+ super.onKeyUp(view, content, keyCode, event);
+
+ // find the character
+ char c = (char) lookup(event, content);
+
+ boolean keyOK = ok(getAcceptedChars(), c);
+
+ if (keyOK) {
+ Log.d(this, "Stopping the tone for '" + c + "'");
+ getPresenter().stopDtmf();
+ return true;
+ }
+
+ return false;
+ }
+
+ /** Handle individual keydown events when we DO NOT have an Editable handy. */
+ public boolean onKeyDown(KeyEvent event) {
+ char c = lookup(event);
+ Log.d(this, "DTMFKeyListener.onKeyDown: event '" + c + "'");
+
+ // if not a long press, and parent onKeyDown accepts the input
+ if (event.getRepeatCount() == 0 && c != 0) {
+ // if the character is a valid dtmf code, start playing the tone and send the
+ // code.
+ if (ok(getAcceptedChars(), c)) {
+ Log.d(this, "DTMFKeyListener reading '" + c + "' from input.");
+ getPresenter().processDtmf(c);
+ return true;
+ } else {
+ Log.d(this, "DTMFKeyListener rejecting '" + c + "' from input.");
+ }
+ }
+ return false;
+ }
+
+ /**
+ * Handle individual keyup events.
+ *
+ * @param event is the event we are trying to stop. If this is null, then we just force-stop the
+ * last tone without checking if the event is an acceptable dialer event.
+ */
+ public boolean onKeyUp(KeyEvent event) {
+ if (event == null) {
+ //the below piece of code sends stopDTMF event unnecessarily even when a null event
+ //is received, hence commenting it.
+ /*if (DBG) log("Stopping the last played tone.");
+ stopTone();*/
+ return true;
+ }
+
+ char c = lookup(event);
+ Log.d(this, "DTMFKeyListener.onKeyUp: event '" + c + "'");
+
+ // TODO: stopTone does not take in character input, we may want to
+ // consider checking for this ourselves.
+ if (ok(getAcceptedChars(), c)) {
+ Log.d(this, "Stopping the tone for '" + c + "'");
+ getPresenter().stopDtmf();
+ return true;
+ }
+
+ return false;
+ }
+
+ /**
+ * Find the Dialer Key mapped to this event.
+ *
+ * @return The char value of the input event, otherwise 0 if no matching character was found.
+ */
+ private char lookup(KeyEvent event) {
+ // This code is similar to {@link DialerKeyListener#lookup(KeyEvent, Spannable) lookup}
+ int meta = event.getMetaState();
+ int number = event.getNumber();
+
+ if (!((meta & (KeyEvent.META_ALT_ON | KeyEvent.META_SHIFT_ON)) == 0) || (number == 0)) {
+ int match = event.getMatch(getAcceptedChars(), meta);
+ number = (match != 0) ? match : number;
+ }
+
+ return (char) number;
+ }
+
+ /** Check to see if the keyEvent is dialable. */
+ boolean isKeyEventAcceptable(KeyEvent event) {
+ return (ok(getAcceptedChars(), lookup(event)));
+ }
+ }
+}