Add floating dialpad action button

* Replace fake action bar at the bottom of the screen with a floating
action button which casts a shadow.

* Remove the now unused mDialpadOverflowMenu button in DialtactsActivity

* Modify dialpad layout to add a bottom row of buttons which provide
access to the add contact button and overflow menu

* Fix dialpad layout so the heights applied in XML are actually
respected - previously they were being ignored and each dialpad key
was being assigned the height of MATCH_PARENT

Bug: 13932988

Change-Id: I6e48c00c5ceeeffed142c3dd259e630d6daf8111
diff --git a/src/com/android/dialer/DialtactsActivity.java b/src/com/android/dialer/DialtactsActivity.java
index 3f49006..5dabb93 100644
--- a/src/com/android/dialer/DialtactsActivity.java
+++ b/src/com/android/dialer/DialtactsActivity.java
@@ -45,6 +45,7 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
+import android.view.View.OnClickListener;
 import android.view.View.OnLayoutChangeListener;
 import android.view.ViewGroup.LayoutParams;
 import android.view.ViewTreeObserver.OnGlobalLayoutListener;
@@ -151,12 +152,10 @@
      */
     private ListsFragment mListsFragment;
 
-    private View mFakeActionBar;
+    private View mFloatingActionButton;
     private View mMenuButton;
-    private View mCallHistoryButton;
     private View mDialpadButton;
     private View mDialButton;
-    private PopupMenu mDialpadOverflowMenu;
 
     private View mFragmentsFrame;
 
@@ -329,16 +328,13 @@
 
         mFragmentsFrame = findViewById(R.id.dialtacts_frame);
 
-        mFakeActionBar = findViewById(R.id.fake_action_bar);
+        mFloatingActionButton = findViewById(R.id.floating_action_button);
+        setupFloatingActionButton(mFloatingActionButton);
 
-        mCallHistoryButton = findViewById(R.id.call_history_button);
-        mCallHistoryButton.setOnClickListener(this);
         mDialButton = findViewById(R.id.dial_button);
         mDialButton.setOnClickListener(this);
         mDialpadButton = findViewById(R.id.dialpad_button);
         mDialpadButton.setOnClickListener(this);
-        mMenuButton = findViewById(R.id.overflow_menu_button);
-        mMenuButton.setOnClickListener(this);
 
         mRemoveViewContainer = (RemoveView) findViewById(R.id.remove_view_container);
         mSearchAndRemoveViewContainer = findViewById(R.id.search_and_remove_view_container);
@@ -416,9 +412,6 @@
     @Override
     public void onClick(View view) {
         switch (view.getId()) {
-            case R.id.overflow_menu_button:
-                mDialpadOverflowMenu.show();
-                break;
             case R.id.dialpad_button:
                 // Reset the boolean flag that tracks whether the dialpad was up because
                 // we were in call. Regardless of whether it was true before, we want to
@@ -427,9 +420,6 @@
                 mInCallDialpadUp = false;
                 showDialpadFragment(true);
                 break;
-            case R.id.call_history_button:
-                showCallHistory();
-                break;
             case R.id.dial_button:
                 // Dial button was pressed; tell the Dialpad fragment
                 mDialpadFragment.dialButtonPressed();
@@ -582,11 +572,6 @@
     public void onDialpadShown() {
         mDialButton.setVisibility(View.VISIBLE);
         mDialpadButton.setVisibility(View.GONE);
-        mMenuButton.setVisibility(View.VISIBLE);
-        if (mDialpadOverflowMenu == null) {
-            mDialpadOverflowMenu = mDialpadFragment.buildOptionsMenu(mMenuButton);
-            mMenuButton.setOnTouchListener(mDialpadOverflowMenu.getDragToOpenListener());
-        }
 
         SearchFragment fragment = null;
         if (mInDialpadSearch) {
@@ -612,7 +597,6 @@
     public void onDialpadHidden() {
         mDialButton.setVisibility(View.GONE);
         mDialpadButton.setVisibility(View.VISIBLE);
-        mMenuButton.setVisibility(View.GONE);
 
         SearchFragment fragment = null;
         if (mInDialpadSearch) {
@@ -853,9 +837,6 @@
                 // fragment manager correctly figure out whatever fragment was last displayed.
                 return;
             }
-            if (mDialpadFragment != null) {
-                mDialpadOverflowMenu = mDialpadFragment.buildOptionsMenu(mMenuButton);
-            }
             mSearchView.setQuery(normalizedQuery, false);
         }
     }
@@ -884,7 +865,7 @@
 
     @Override
     public void setDialButtonContainerVisible(boolean visible) {
-        mFakeActionBar.setVisibility(visible ? View.VISIBLE : View.GONE);
+        mFloatingActionButton.setVisibility(visible ? View.VISIBLE : View.GONE);
     }
 
     private boolean phoneIsInUse() {
@@ -907,6 +888,34 @@
         return resolveInfo != null && resolveInfo.size() > 0;
     }
 
+    private void setupFloatingActionButton(View view) {
+        // Once layout is complete and the floating action button has been assigned a width
+        // and height, assign the outline.
+        view.addOnLayoutChangeListener(new OnLayoutChangeListener() {
+            @Override
+            public void onLayoutChange(View v,
+                    int left,
+                    int top,
+                    int right,
+                    int bottom,
+                    int oldLeft,
+                    int oldTop,
+                    int oldRight,
+                    int oldBottom) {
+                final Outline outline = new Outline();
+                final int minDimension = Math.min(right - left, bottom - top);
+                if (minDimension <= 0) {
+                    return;
+                }
+                outline.setRoundRect(0, 0, right - left, bottom - top, minDimension / 2);
+                v.setOutline(outline);
+                v.setClipToOutline(true);
+            }
+        });
+        view.setTranslationZ(getResources().getDimensionPixelSize(
+                R.dimen.floating_action_button_translation_z));
+    }
+
     @Override
     public void showCallHistory() {
         // Use explicit CallLogActivity intent instead of ACTION_VIEW +
diff --git a/src/com/android/dialer/dialpad/DialpadFragment.java b/src/com/android/dialer/dialpad/DialpadFragment.java
index 703c277..72abaa0 100644
--- a/src/com/android/dialer/dialpad/DialpadFragment.java
+++ b/src/com/android/dialer/dialpad/DialpadFragment.java
@@ -76,9 +76,11 @@
 import com.android.dialer.DialtactsActivity;
 import com.android.dialer.R;
 import com.android.dialer.SpecialCharSequenceMgr;
+import com.android.dialer.util.DialerUtils;
 import com.android.internal.telephony.ITelephony;
 import com.android.phone.common.CallLogAsync;
 import com.android.phone.common.HapticFeedback;
+
 import com.google.common.annotations.VisibleForTesting;
 
 import java.util.HashSet;
@@ -199,6 +201,8 @@
     /** Remembers if we need to clear digits field when the screen is completely gone. */
     private boolean mClearDigitsOnStop;
 
+    private View mAddContactButton;
+    private View mOverflowMenuButton;
     private View mDelete;
     private ToneGenerator mToneGenerator;
     private final Object mToneGeneratorLock = new Object();
@@ -333,6 +337,9 @@
         if (isDigitsEmpty()) {
             mDigitsFilledByIntent = false;
             mDigits.setCursorVisible(false);
+            mAddContactButton.setVisibility(View.INVISIBLE);
+        } else {
+            mAddContactButton.setVisibility(View.VISIBLE);
         }
 
         if (mDialpadQueryListener != null) {
@@ -612,8 +619,6 @@
 
         for (int i = 0; i < buttonIds.length; i++) {
             dialpadKey = (DialpadKeyButton) fragmentView.findViewById(buttonIds[i]);
-            dialpadKey.setLayoutParams(new TableRow.LayoutParams(
-                    TableRow.LayoutParams.MATCH_PARENT, TableRow.LayoutParams.MATCH_PARENT));
             dialpadKey.setOnPressedListener(this);
             numberView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_number);
             lettersView = (TextView) dialpadKey.findViewById(R.id.dialpad_key_letters);
@@ -637,6 +642,13 @@
         zero.setLongHoverContentDescription(
                 resources.getText(R.string.description_image_button_plus));
 
+        mAddContactButton = fragmentView.findViewById(R.id.dialpad_add_contact);
+        mAddContactButton.setOnClickListener(this);
+
+        mOverflowMenuButton = fragmentView.findViewById(R.id.dialpad_overflow);
+        mOverflowMenuButton.setOnClickListener(this);
+        final PopupMenu overflowMenu = buildOptionsMenu(mOverflowMenuButton);
+        mOverflowMenuButton.setOnTouchListener(overflowMenu.getDragToOpenListener());
     }
 
     @Override
@@ -768,22 +780,6 @@
         outState.putBoolean(PREF_DIGITS_FILLED_BY_INTENT, mDigitsFilledByIntent);
     }
 
-    private void setupMenuItems(Menu menu) {
-        final MenuItem addToContactMenuItem = menu.findItem(R.id.menu_add_contacts);
-
-        // We show "add to contacts" menu only when the user is
-        // seeing usual dialpad and has typed at least one digit.
-        // We never show a menu if the "choose dialpad" UI is up.
-        if (dialpadChooserVisible() || isDigitsEmpty()) {
-            addToContactMenuItem.setVisible(false);
-        } else {
-            final CharSequence digits = mDigits.getText();
-            // Put the current digits string into an intent
-            addToContactMenuItem.setIntent(DialtactsActivity.getAddNumberToContactIntent(digits));
-            addToContactMenuItem.setVisible(true);
-        }
-    }
-
     private void keyPressed(int keyCode) {
         if (getView().getTranslationY() != 0) {
             return;
@@ -937,7 +933,6 @@
         final PopupMenu popupMenu = new PopupMenu(getActivity(), invoker);
         popupMenu.inflate(R.menu.dialpad_options);
         popupMenu.setOnMenuItemClickListener(this);
-        setupMenuItems(popupMenu.getMenu());
         return popupMenu;
     }
 
@@ -963,6 +958,16 @@
                 }
                 return;
             }
+            case R.id.dialpad_add_contact: {
+                final CharSequence digits = mDigits.getText();
+                DialerUtils.startActivityWithErrorToast(getActivity(),
+                        DialtactsActivity.getAddNumberToContactIntent(digits));
+                return;
+            }
+            case R.id.dialpad_overflow: {
+                buildOptionsMenu(view).show();
+                break;
+            }
             default: {
                 Log.wtf(TAG, "Unexpected onClick() event from: " + view);
                 return;