Home | History | Annotate | Download | only in incallui
      1 /*
      2  * Copyright (C) 2017 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License
     15  */
     16 
     17 package com.android.incallui;
     18 
     19 import android.support.annotation.NonNull;
     20 import android.text.Editable;
     21 import android.text.Spannable;
     22 import android.text.SpannableString;
     23 import android.text.method.DialerKeyListener;
     24 import android.view.KeyEvent;
     25 import android.view.View;
     26 import com.android.dialer.common.LogUtil;
     27 
     28 /**
     29  * Key listener specialized to deal with Dtmf codes.
     30  *
     31  * <p>This listener will listen for valid Dtmf characters, and in response will inform the
     32  * associated presenter of the character. As an implementation of {@link DialerKeyListener}, this
     33  * class will listen for <b>hardware keyboard</b> events.
     34  *
     35  * <p>From legacy documentation:
     36  *
     37  * <ul>
     38  *   <li>Ignores the backspace since it is irrelevant.
     39  *   <li>Allow ONLY valid DTMF characters to generate a tone and be sent as a DTMF code.
     40  *   <li>All other remaining characters are handled by the superclass.
     41  *   <li>This code is purely here to handle events from the hardware keyboard while the DTMF dialpad
     42  *       is up.
     43  * </ul>
     44  */
     45 final class DtmfKeyListener extends DialerKeyListener {
     46   /**
     47    * Overrides the characters used in {@link DialerKeyListener#CHARACTERS} These are the valid dtmf
     48    * characters.
     49    */
     50   private static final char[] VALID_DTMF_CHARACTERS =
     51       new char[] {'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', '#', '*'};
     52 
     53   /**
     54    * Spannable used to call {@link DialerKeyListener#lookup(KeyEvent, Spannable)}, so it's not
     55    * necessary to copy the implementation.
     56    *
     57    * <p>The Spannable is only used to determine which meta keys are pressed, e.g. shift, alt, see
     58    * {@link android.text.method.MetaKeyKeyListener#getMetaState(CharSequence)}, so using a dummy
     59    * value is fine here.
     60    */
     61   private static final Spannable EMPTY_SPANNABLE = new SpannableString("");
     62 
     63   private final DialpadPresenter presenter;
     64 
     65   DtmfKeyListener(@NonNull DialpadPresenter presenter) {
     66     this.presenter = presenter;
     67   }
     68 
     69   @Override
     70   protected char[] getAcceptedChars() {
     71     return VALID_DTMF_CHARACTERS;
     72   }
     73 
     74   @Override
     75   public boolean backspace(View view, Editable content, int keyCode, KeyEvent event) {
     76     return false;
     77   }
     78 
     79   /**
     80    * Responds to keyDown events by firing a Dtmf tone, if the given event corresponds is a {@link
     81    * #VALID_DTMF_CHARACTERS}.
     82    *
     83    * @return {@code true} if the event was handled.
     84    */
     85   @Override
     86   public boolean onKeyDown(View view, Editable content, int keyCode, KeyEvent event) {
     87     LogUtil.i("DtmfKeyListener.onKeyDown", "overload");
     88     if (!super.onKeyDown(view, content, keyCode, event)) {
     89       LogUtil.i("DtmfKeyListener.onKeyDown", "parent type didn't support event");
     90       return false;
     91     }
     92 
     93     return onKeyDown(event);
     94   }
     95 
     96   /**
     97    * Version of {@link #onKeyDown(View, Editable, int, KeyEvent)} used when a View/Editable isn't
     98    * available.
     99    */
    100   boolean onKeyDown(KeyEvent event) {
    101     LogUtil.enterBlock("DtmfKeyListener.onKeyDown");
    102     if (event.getRepeatCount() != 0) {
    103       LogUtil.i("DtmfKeyListener.onKeyDown", "long press, ignoring");
    104       return false;
    105     }
    106 
    107     char c = (char) lookup(event, EMPTY_SPANNABLE);
    108 
    109     if (!ok(getAcceptedChars(), c)) {
    110       LogUtil.i("DtmfKeyListener.onKeyDown", "not an accepted character");
    111       return false;
    112     }
    113 
    114     presenter.processDtmf(c);
    115     return true;
    116   }
    117 
    118   /**
    119    * Responds to keyUp events by stopping any playing Dtmf tone if the given event corresponds is a
    120    * {@link #VALID_DTMF_CHARACTERS}.
    121    *
    122    * <p>Null events also stop the Dtmf tone.
    123    *
    124    * @return {@code true} if the event was handled
    125    */
    126   @Override
    127   public boolean onKeyUp(View view, Editable content, int keyCode, KeyEvent event) {
    128     LogUtil.i("DtmfKeyListener.onKeyUp", "overload");
    129     super.onKeyUp(view, content, keyCode, event);
    130 
    131     return onKeyUp(event);
    132   }
    133 
    134   /**
    135    * Handle individual keyup events.
    136    *
    137    * @param event is the event we are trying to stop. If this is null, then we just force-stop the
    138    *     last tone without checking if the event is an acceptable dialer event.
    139    */
    140   boolean onKeyUp(KeyEvent event) {
    141     LogUtil.enterBlock("DtmfKeyListener.onKeyUp");
    142     if (event == null) {
    143       return true;
    144     }
    145 
    146     char c = (char) lookup(event, EMPTY_SPANNABLE);
    147 
    148     if (!ok(getAcceptedChars(), c)) {
    149       LogUtil.i("DtmfKeyListener.onKeyUp", "not an accepted character");
    150       return false;
    151     }
    152 
    153     presenter.stopDtmf();
    154     return true;
    155   }
    156 }
    157