Update dialer sources.

Test: Built package and system image.

This change clobbers the old source, and is an export
from an internal Google repository.

The internal repository was forked form Android in March,
and this change includes modifications since then, to
near the v8 release.

Since the fork, we've moved code from monolithic to independent modules. In addition,
we've switched to Blaze/Bazel as the build sysetm. This export, however, still uses make.

New dependencies have been added:
- Dagger
- Auto-Value
- Glide
- Libshortcutbadger

Going forward, development will still be in Google3, and the Gerrit release
will become an automated export, with the next drop happening in ~ two weeks.

Android.mk includes local modifications from ToT.

Abridged changelog:

Bug fixes
● Not able to mute, add a call when using Phone app in multiwindow mode
● Double tap on keypad triggering multiple key and tones
● Reported spam numbers not showing as spam in the call log
● Crash when user tries to block number while Phone app is not set as default
● Crash when user picks a number from search auto-complete list

Visual Voicemail (VVM) improvements
● Share Voicemail audio via standard exporting mechanisms that support file attachment
(email, MMS, etc.)
● Make phone number, email and web sites in VVM transcript clickable
● Set PIN before declining VVM Terms of Service {Carrier}
● Set client type for outbound visual voicemail SMS {Carrier}

New incoming call and incall UI on older devices
(Android M)
● Updated Phone app icon
● New incall UI (large buttons, button labels)
● New and animated Answer/Reject gestures

Accessibility
● Add custom answer/decline call buttons on answer screen for touch exploration
accessibility services
● Increase size of touch target
● Add verbal feedback when a Voicemail fails to load
● Fix pressing of Phone buttons while in a phone call using Switch Access
● Fix selecting and opening contacts in talkback mode
● Split focus for ‘Learn More’ link in caller id & spam to help distinguish similar text

Other
● Backup & Restore for App Preferences
● Prompt user to enable Wi-Fi calling if the call ends due to out of service and Wi-Fi is
connected
● Rename “Dialpad” to “Keypad”
● Show "Private number" for restricted calls
● Delete unused items (vcard, add contact, call history) from Phone menu

Change-Id: I2a7e53532a24c21bf308bf0a6d178d7ddbca4958
diff --git a/java/com/android/incallui/DialpadFragment.java b/java/com/android/incallui/DialpadFragment.java
new file mode 100644
index 0000000..7f494aa
--- /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)));
+    }
+  }
+}