diff options
author | Christine Chen <christinech@google.com> | 2013-09-20 17:46:37 -0700 |
---|---|---|
committer | Christine Chen <christinech@google.com> | 2013-09-23 12:22:36 -0700 |
commit | c60103d277b6656729b8f8c3f7168da31c7bffa7 (patch) | |
tree | ab7c7ba1b22016bd75c960140e6b2f9c052ef760 /InCallUI | |
parent | d9ba6ebfeb47fa94e19271179fbe295b381cc7bc (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.java | 226 | ||||
-rw-r--r-- | InCallUI/src/com/android/incallui/InCallActivity.java | 41 |
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); |