summaryrefslogtreecommitdiff
path: root/InCallUI
diff options
context:
space:
mode:
authorChristine Chen <christinech@google.com>2013-09-20 17:46:37 -0700
committerChristine Chen <christinech@google.com>2013-09-23 12:22:36 -0700
commitc60103d277b6656729b8f8c3f7168da31c7bffa7 (patch)
treeab7c7ba1b22016bd75c960140e6b2f9c052ef760 /InCallUI
parentd9ba6ebfeb47fa94e19271179fbe295b381cc7bc (diff)
Adds hardware keyboard support for DTMF
Bug: 10808399 Change-Id: Id12acee7baf11335916a38c50a266f83f578e0bd
Diffstat (limited to 'InCallUI')
-rw-r--r--InCallUI/src/com/android/incallui/DialpadFragment.java226
-rw-r--r--InCallUI/src/com/android/incallui/InCallActivity.java41
2 files changed, 266 insertions, 1 deletions
diff --git a/InCallUI/src/com/android/incallui/DialpadFragment.java b/InCallUI/src/com/android/incallui/DialpadFragment.java
index 428750dbf..5087561e6 100644
--- a/InCallUI/src/com/android/incallui/DialpadFragment.java
+++ b/InCallUI/src/com/android/incallui/DialpadFragment.java
@@ -18,6 +18,8 @@ package com.android.incallui;
import android.content.Context;
import android.os.Bundle;
+import android.text.Editable;
+import android.text.method.DialerKeyListener;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.MotionEvent;
@@ -58,6 +60,198 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
mDisplayMap.put(R.id.star, '*');
}
+ // KeyListener used with the "dialpad digits" EditText widget.
+ private DTMFKeyListener mDialerKeyListener;
+
+ /**
+ * 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.
+ *
+ * This code is purely here to handle events from the hardware keyboard
+ * while the DTMF dialpad is up.
+ */
+ private class DTMFKeyListener extends DialerKeyListener {
+
+ 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;
+ }
+
+ /**
+ * Return true if the keyCode is an accepted modifier key for the
+ * dialer (ALT or SHIFT).
+ */
+ private boolean isAcceptableModifierKey(int keyCode) {
+ switch (keyCode) {
+ case KeyEvent.KEYCODE_ALT_LEFT:
+ case KeyEvent.KEYCODE_ALT_RIGHT:
+ case KeyEvent.KEYCODE_SHIFT_LEFT:
+ case KeyEvent.KEYCODE_SHIFT_RIGHT:
+ return true;
+ default:
+ 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().stopTone();
+ 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().stopTone();
+ 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)));
+ }
+
+ /**
+ * 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', '#', '*'
+ };
+ }
+
@Override
public void onClick(View v) {
Log.d(this, "onClick");
@@ -180,6 +374,8 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
com.android.incallui.R.layout.dtmf_twelve_key_dialer_view, container, false);
mDtmfDialerField = (EditText) parent.findViewById(R.id.dtmfDialerField);
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);
@@ -190,6 +386,12 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
}
@Override
+ public void onDestroyView() {
+ mDialerKeyListener = null;
+ super.onDestroyView();
+ }
+
+ @Override
public void setVisible(boolean on) {
if (on) {
getView().setVisibility(View.VISIBLE);
@@ -215,6 +417,30 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadPrese
}
/**
+ * 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;
+ }
+ }
+
+ /**
* setup the keys on the dialer activity, using the keymaps.
*/
private void setupKeypad(View parent) {
diff --git a/InCallUI/src/com/android/incallui/InCallActivity.java b/InCallUI/src/com/android/incallui/InCallActivity.java
index d2a7f21ab..41e01fff7 100644
--- a/InCallUI/src/com/android/incallui/InCallActivity.java
+++ b/InCallUI/src/com/android/incallui/InCallActivity.java
@@ -82,7 +82,7 @@ public class InCallActivity extends Activity {
@Override
protected void onResume() {
- Log.d(this, "onResume()...");
+ Log.i(this, "onResume()...");
super.onResume();
mIsForegroundActivity = true;
@@ -102,6 +102,9 @@ public class InCallActivity extends Activity {
super.onPause();
mIsForegroundActivity = false;
+
+ mDialpadFragment.onDialerKeyUp(null);
+
InCallPresenter.getInstance().onUiShowing(false);
}
@@ -189,6 +192,18 @@ public class InCallActivity extends Activity {
}
@Override
+ public boolean onKeyUp(int keyCode, KeyEvent event) {
+ // push input to the dialer.
+ if ((mDialpadFragment.isVisible()) && (mDialpadFragment.onDialerKeyUp(event))){
+ return true;
+ } else if (keyCode == KeyEvent.KEYCODE_CALL) {
+ // Always consume CALL to be sure the PhoneWindow won't do anything with it
+ return true;
+ }
+ return super.onKeyUp(keyCode, event);
+ }
+
+ @Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
switch (keyCode) {
case KeyEvent.KEYCODE_CALL:
@@ -238,9 +253,33 @@ public class InCallActivity extends Activity {
break;
}
+ if (event.getRepeatCount() == 0 && handleDialerKeyDown(keyCode, event)) {
+ return true;
+ }
+
return super.onKeyDown(keyCode, event);
}
+ private boolean handleDialerKeyDown(int keyCode, KeyEvent event) {
+ Log.v(this, "handleDialerKeyDown: keyCode " + keyCode + ", event " + event + "...");
+
+ // As soon as the user starts typing valid dialable keys on the
+ // keyboard (presumably to type DTMF tones) we start passing the
+ // key events to the DTMFDialer's onDialerKeyDown.
+ if (mDialpadFragment.isVisible()) {
+ return mDialpadFragment.onDialerKeyDown(event);
+
+ // TODO: If the dialpad isn't currently visible, maybe
+ // consider automatically bringing it up right now?
+ // (Just to make sure the user sees the digits widget...)
+ // But this probably isn't too critical since it's awkward to
+ // use the hard keyboard while in-call in the first place,
+ // especially now that the in-call UI is portrait-only...
+ }
+
+ return false;
+ }
+
@Override
public void onConfigurationChanged(Configuration config) {
InCallPresenter.getInstance().getProximitySensor().onConfigurationChanged(config);