summaryrefslogtreecommitdiff
path: root/java/com
diff options
context:
space:
mode:
authormaxwelb <maxwelb@google.com>2017-11-27 17:01:07 -0800
committerCopybara-Service <copybara-piper@google.com>2017-11-29 11:42:34 -0800
commit5e22e8009682c92b1b4bb44301da3c606b0090e8 (patch)
tree50c8e640eaadacfa9d750af7ebc2e29fc57ce60f /java/com
parentbadfdc429f77626fd7b28c7964de7cfd2981f219 (diff)
Refactor DialpadFragment's DtmfKeyListener
This change is the refactoring on DtmfKeyListener and adding tests. The class is an overridden version of DialerKeyListener, but it also supports direct onKeyDown and onKeyUp hooks from the DialpadFragment. There was a lot of duplicated code between these hooks and the overridden methods so the code was refactored to share more logic. The interesting part of this change is using EMPTY_SPANNABLE to call into super type methods instead of having to copy the implementation. This change may also help with OutOfMemoryExceptions because the DtmfKeyListener is promoted from a non-static inner class to a top level class. It no longer maintains an implicit reference to the DialpadFragment, holding onto the DialpadPresenter instead. To support testing this code, the ShadowCall had to be updated since it didn't support checking when a Dtmf tone was playing. Bug: 69133398 Test: DtmfKeyListenerTest, InCallActivityTest PiperOrigin-RevId: 177092841 Change-Id: I368083c71982e14db0ad936432a3a5bdf0dde5ee
Diffstat (limited to 'java/com')
-rw-r--r--java/com/android/incallui/DialpadFragment.java178
-rw-r--r--java/com/android/incallui/DtmfKeyListener.java156
2 files changed, 166 insertions, 168 deletions
diff --git a/java/com/android/incallui/DialpadFragment.java b/java/com/android/incallui/DialpadFragment.java
index fbcd4078e..2f3a68cda 100644
--- a/java/com/android/incallui/DialpadFragment.java
+++ b/java/com/android/incallui/DialpadFragment.java
@@ -18,8 +18,6 @@ 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;
@@ -32,6 +30,7 @@ import android.widget.EditText;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.android.contacts.common.compat.PhoneNumberUtilsCompat;
+import com.android.dialer.common.LogUtil;
import com.android.dialer.dialpadview.DialpadKeyButton;
import com.android.dialer.dialpadview.DialpadKeyButton.OnPressedListener;
import com.android.dialer.dialpadview.DialpadView;
@@ -82,7 +81,7 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
};
private EditText mDtmfDialerField;
// KeyListener used with the "dialpad digits" EditText widget.
- private DTMFKeyListener mDialerKeyListener;
+ private DtmfKeyListener mDtmfKeyListener;
private DialpadView mDialpadView;
private int mCurrentTextColor;
@@ -141,8 +140,9 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
mDialpadView.setBackgroundResource(R.color.incall_dialpad_background);
mDtmfDialerField = (EditText) parent.findViewById(R.id.digits);
if (mDtmfDialerField != null) {
- mDialerKeyListener = new DTMFKeyListener();
- mDtmfDialerField.setKeyListener(mDialerKeyListener);
+ LogUtil.i("DialpadFragment.onCreateView", "creating dtmfKeyListener");
+ mDtmfKeyListener = new DtmfKeyListener(getPresenter());
+ mDtmfDialerField.setKeyListener(mDtmfKeyListener);
// remove the long-press context menus that support
// the edit (copy / paste / select) functions.
mDtmfDialerField.setLongClickable(false);
@@ -180,7 +180,7 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
@Override
public void onDestroyView() {
- mDialerKeyListener = null;
+ mDtmfKeyListener = null;
super.onDestroyView();
}
@@ -236,8 +236,8 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
/** 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);
+ if (mDtmfKeyListener != null) {
+ return mDtmfKeyListener.onKeyDown(event);
} else {
return false;
}
@@ -246,8 +246,8 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
/** 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);
+ if (mDtmfKeyListener != null) {
+ return mDtmfKeyListener.onKeyUp(event);
} else {
return false;
}
@@ -307,162 +307,4 @@ public class DialpadFragment extends BaseFragment<DialpadPresenter, DialpadUi>
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[] dtmfCharacters =
- 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 dtmfCharacters;
- }
-
- /** 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)));
- }
- }
}
diff --git a/java/com/android/incallui/DtmfKeyListener.java b/java/com/android/incallui/DtmfKeyListener.java
new file mode 100644
index 000000000..a2a0de09e
--- /dev/null
+++ b/java/com/android/incallui/DtmfKeyListener.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2017 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.support.annotation.NonNull;
+import android.text.Editable;
+import android.text.Spannable;
+import android.text.SpannableString;
+import android.text.method.DialerKeyListener;
+import android.view.KeyEvent;
+import android.view.View;
+import com.android.dialer.common.LogUtil;
+
+/**
+ * Key listener specialized to deal with Dtmf codes.
+ *
+ * <p>This listener will listen for valid Dtmf characters, and in response will inform the
+ * associated presenter of the character. As an implementation of {@link DialerKeyListener}, this
+ * class will listen for <b>hardware keyboard</b> events.
+ *
+ * <p>From legacy documentation:
+ *
+ * <ul>
+ * <li>Ignores the backspace since it is irrelevant.
+ * <li>Allow ONLY valid DTMF characters to generate a tone and be sent as a DTMF code.
+ * <li>All other remaining characters are handled by the superclass.
+ * <li>This code is purely here to handle events from the hardware keyboard while the DTMF dialpad
+ * is up.
+ * </ul>
+ */
+final class DtmfKeyListener extends DialerKeyListener {
+ /**
+ * Overrides the characters used in {@link DialerKeyListener#CHARACTERS} These are the valid dtmf
+ * characters.
+ */
+ private static final char[] VALID_DTMF_CHARACTERS =
+ new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'};
+
+ /**
+ * Spannable used to call {@link DialerKeyListener#lookup(KeyEvent, Spannable)}, so it's not
+ * necessary to copy the implementation.
+ *
+ * <p>The Spannable is only used to determine which meta keys are pressed, e.g. shift, alt, see
+ * {@link android.text.method.MetaKeyKeyListener#getMetaState(CharSequence)}, so using a dummy
+ * value is fine here.
+ */
+ private static final Spannable EMPTY_SPANNABLE = new SpannableString("");
+
+ private final DialpadPresenter presenter;
+
+ DtmfKeyListener(@NonNull DialpadPresenter presenter) {
+ this.presenter = presenter;
+ }
+
+ @Override
+ protected char[] getAcceptedChars() {
+ return VALID_DTMF_CHARACTERS;
+ }
+
+ @Override
+ public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
+ return false;
+ }
+
+ /**
+ * Responds to keyDown events by firing a Dtmf tone, if the given event corresponds is a {@link
+ * #VALID_DTMF_CHARACTERS}.
+ *
+ * @return {@code true} if the event was handled.
+ */
+ @Override
+ public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
+ LogUtil.i("DtmfKeyListener.onKeyDown", "overload");
+ if (!super.onKeyDown(view, content, keyCode, event)) {
+ LogUtil.i("DtmfKeyListener.onKeyDown", "parent type didn't support event");
+ return false;
+ }
+
+ return onKeyDown(event);
+ }
+
+ /**
+ * Version of {@link #onKeyDown(View, Editable, int, KeyEvent)} used when a View/Editable isn't
+ * available.
+ */
+ boolean onKeyDown(KeyEvent event) {
+ LogUtil.enterBlock("DtmfKeyListener.onKeyDown");
+ if (event.getRepeatCount() != 0) {
+ LogUtil.i("DtmfKeyListener.onKeyDown", "long press, ignoring");
+ return false;
+ }
+
+ char c = (char) lookup(event, EMPTY_SPANNABLE);
+
+ if (!ok(getAcceptedChars(), c)) {
+ LogUtil.i("DtmfKeyListener.onKeyDown", "not an accepted character");
+ return false;
+ }
+
+ presenter.processDtmf(c);
+ return true;
+ }
+
+ /**
+ * Responds to keyUp events by stopping any playing Dtmf tone if the given event corresponds is a
+ * {@link #VALID_DTMF_CHARACTERS}.
+ *
+ * <p>Null events also stop the Dtmf tone.
+ *
+ * @return {@code true} if the event was handled
+ */
+ @Override
+ public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) {
+ LogUtil.i("DtmfKeyListener.onKeyUp", "overload");
+ super.onKeyUp(view, content, keyCode, event);
+
+ return onKeyUp(event);
+ }
+
+ /**
+ * 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.
+ */
+ boolean onKeyUp(KeyEvent event) {
+ LogUtil.enterBlock("DtmfKeyListener.onKeyUp");
+ if (event == null) {
+ return true;
+ }
+
+ char c = (char) lookup(event, EMPTY_SPANNABLE);
+
+ if (!ok(getAcceptedChars(), c)) {
+ LogUtil.i("DtmfKeyListener.onKeyUp", "not an accepted character");
+ return false;
+ }
+
+ presenter.stopDtmf();
+ return true;
+ }
+}