Merge "Remove unused permissions, libs"
diff --git a/res/drawable-hdpi/default_avatar_white.png b/res/drawable-hdpi/default_avatar_white.png
new file mode 100644
index 0000000..711286c
--- /dev/null
+++ b/res/drawable-hdpi/default_avatar_white.png
Binary files differ
diff --git a/res/drawable-mdpi/default_avatar_white.png b/res/drawable-mdpi/default_avatar_white.png
new file mode 100644
index 0000000..0983eb0
--- /dev/null
+++ b/res/drawable-mdpi/default_avatar_white.png
Binary files differ
diff --git a/res/drawable-xhdpi/default_avatar_white.png b/res/drawable-xhdpi/default_avatar_white.png
new file mode 100644
index 0000000..f645ff2
--- /dev/null
+++ b/res/drawable-xhdpi/default_avatar_white.png
Binary files differ
diff --git a/res/drawable-xxhdpi/default_avatar_white.png b/res/drawable-xxhdpi/default_avatar_white.png
new file mode 100644
index 0000000..b92f026
--- /dev/null
+++ b/res/drawable-xxhdpi/default_avatar_white.png
Binary files differ
diff --git a/res/drawable/default_avatar_white.xml b/res/drawable/default_avatar_white.xml
new file mode 100644
index 0000000..9bc5cbf
--- /dev/null
+++ b/res/drawable/default_avatar_white.xml
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Copyright (C) 2014 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.
+-->
+
+<inset
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:drawable="@drawable/default_avatar_white"
+    android:insetRight="102dp"
+    android:insetLeft="102dp" />
\ No newline at end of file
diff --git a/res/layout-land/quickcontact_activity.xml b/res/layout-land/quickcontact_activity.xml
index 65fcd7d..9a07698 100644
--- a/res/layout-land/quickcontact_activity.xml
+++ b/res/layout-land/quickcontact_activity.xml
@@ -26,6 +26,7 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="@dimen/quickcontact_starting_empty_height"
+        android:contentDescription="@string/quickcontact_transparent_view_description"
         android:id="@+id/transparent_view" />
 
     <LinearLayout
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index 7b81ea2..008062b 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -27,6 +27,7 @@
     <View
         android:layout_width="match_parent"
         android:layout_height="@dimen/quickcontact_starting_empty_height"
+        android:contentDescription="@string/quickcontact_transparent_view_description"
         android:id="@+id/transparent_view" />
 
     <include layout="@layout/quickcontact_header" />
diff --git a/res/layout/quickcontact_header.xml b/res/layout/quickcontact_header.xml
index 55b23c9..0c51746 100644
--- a/res/layout/quickcontact_header.xml
+++ b/res/layout/quickcontact_header.xml
@@ -14,10 +14,14 @@
      See the License for the specific language governing permissions and
      limitations under the License.
 -->
+
+<!-- Needs a non null background in for elevation to work on this View. This will *not* cause an
+     additional draw since the background is transparent. -->
 <FrameLayout
     xmlns:android="http://schemas.android.com/apk/res/android"
     android:layout_width="match_parent"
     android:layout_height="match_parent"
+    android:background="#00000000"
     android:id="@+id/toolbar_parent">
 
     <com.android.contacts.widget.QuickContactImageView
@@ -27,8 +31,9 @@
         android:scaleType="centerCrop"
         android:contentDescription="@string/description_contact_photo" />
 
-    <!-- Need to set a non null background on Toolbar in order for MenuItem
-        ripples to be drawn on this view, instead of another-->
+    <!-- Need to set a non null background on Toolbar in order for MenuItem ripples to be drawn on
+         this view, instead of another. This will *not* cause an additional draw since the
+         background is transparent.-->
     <Toolbar
         android:layout_width="match_parent"
         android:layout_height="?android:attr/actionBarSize"
@@ -43,6 +48,9 @@
         android:ellipsize="end"
         android:layout_gravity="bottom"
         android:textSize="@dimen/quickcontact_maximum_title_size"
+        android:layout_marginStart="@dimen/quickcontact_title_initial_margin"
+        android:layout_marginEnd="@dimen/quickcontact_title_initial_margin"
+        android:layout_marginBottom="@dimen/quickcontact_title_initial_margin"
         android:id="@+id/large_title"/>
 
-</FrameLayout>
\ No newline at end of file
+</FrameLayout>
diff --git a/res/values/dimens.xml b/res/values/dimens.xml
index 9d8ba70..5a2f7ab 100644
--- a/res/values/dimens.xml
+++ b/res/values/dimens.xml
@@ -20,6 +20,8 @@
     <dimen name="quickcontact_starting_empty_height">150dp</dimen>
     <!-- Initial size of QuickContact's title size -->
     <dimen name="quickcontact_maximum_title_size">36dp</dimen>
+    <!-- When QC is uncollapsed, the title has this much margin on its left, right and bottom -->
+    <dimen name="quickcontact_title_initial_margin">16dp</dimen>
 
     <!-- Top padding of the entire contact editor  -->
     <dimen name="editor_padding_top">0dip</dimen>
@@ -142,19 +144,18 @@
 
 
     <!-- Margins for ExpandingEntryCardView -->
-    <dimen name="expanding_entry_card_marginStart">8dp</dimen>
-    <dimen name="expanding_entry_card_marginEnd">8dp</dimen>
-    <dimen name="expanding_entry_card_marginBottom">8dp</dimen>
+    <dimen name="expanding_entry_card_marginStart">12dp</dimen>
+    <dimen name="expanding_entry_card_marginEnd">12dp</dimen>
+    <dimen name="expanding_entry_card_marginBottom">12dp</dimen>
+
+    <!-- Top margin for the communication card, used to add space from header. -->
+    <dimen name="communication_card_marginTop">12dp</dimen>
 
     <!-- Elevation of an ExpandingEntryCard, for the sake of shadow casting -->
     <dimen name="expanding_entry_card_elevation">1dp</dimen>
     <!-- Elevation of the QuickContact's Toolbar, for the sake of shadow casting -->
     <dimen name="quick_contact_toolbar_elevation">4.5dp</dimen>
 
-    <!-- Top margin for the communication card, used to add space from header. -->
-    <dimen name="communication_card_marginTop">8dp</dimen>
-
-
     <!-- Size of the title text for a ExpandingEntryCardView -->
     <dimen name="expanding_entry_card_title_text_size">16sp</dimen>
     <!-- Padding for the title text for a ExpandingEntryCardView -->
diff --git a/res/values/strings.xml b/res/values/strings.xml
index afd0f40..434a62b 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -309,6 +309,9 @@
     <!-- Shown as a toast when the user taps on a QuickContact icon, and no application
          was found that could perform the selected action. [CHAR LIMIT=NONE] -->
     <string name="quickcontact_missing_app">No app was found to handle this action.</string>
+    <!-- Content description for the transparent view above the visible section of QuickContacts.
+         Clicking this view causes Quick Contacts to close. [CHAR LIMIT=NONE] -->
+    <string name="quickcontact_transparent_view_description">Click to return to previous screen</string>
 
     <!-- Shown as a toast when the user attempts an action (add contact, edit
          contact, etc) and no application was found that could perform that
diff --git a/src/com/android/contacts/quickcontact/QuickContactActivity.java b/src/com/android/contacts/quickcontact/QuickContactActivity.java
index 99d8094..f0c7241 100644
--- a/src/com/android/contacts/quickcontact/QuickContactActivity.java
+++ b/src/com/android/contacts/quickcontact/QuickContactActivity.java
@@ -689,6 +689,7 @@
          * type is also sorted, based off of {super primary, primary, times used} in that order.
          */
         final List<Action> topActions = new ArrayList<>();
+        final List<Action> allActions = new ArrayList<>();
         for (List<Action> mimeTypeActions : mActions.values()) {
             Collections.sort(mimeTypeActions, new Comparator<Action>() {
                 @Override
@@ -716,6 +717,9 @@
                 }
             });
             topActions.add(mimeTypeActions.get(0));
+            // Add all the other actions and remove the top one
+            allActions.addAll(mimeTypeActions);
+            allActions.remove(mimeTypeActions.get(0));
         }
 
         // topActions now contains the top action for each mimetype. This list now needs to be
@@ -762,6 +766,7 @@
         });
 
         entries.addAll(actionsToEntries(topActions));
+        entries.addAll(actionsToEntries(allActions));
         Trace.endSection();
     }
 
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index 3666420..18f032a 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -17,6 +17,7 @@
 import android.graphics.Point;
 import android.graphics.Rect;
 import android.hardware.display.DisplayManagerGlobal;
+import android.os.Trace;
 import android.util.AttributeSet;
 import android.util.TypedValue;
 import android.view.Display;
@@ -85,6 +86,10 @@
     private int mMinimumHeaderHeight;
     private int mIntermediateHeaderHeight;
     private int mMaximumHeaderTextSize;
+    private int mCollapsedTitleBottomMargin;
+    private int mCollapsedTitleStartMargin;
+    private int mMinimumPortraitHeaderHeight;
+    private int mMaximumPortraitHeaderHeight;
 
     private final Scroller mScroller;
     private final EdgeEffect mEdgeGlowBottom;
@@ -92,10 +97,9 @@
     private final int mMaximumVelocity;
     private final int mMinimumVelocity;
     private final int mTransparentStartHeight;
+    private final int mMaximumTitleMargin;
     private final float mToolbarElevation;
     private final boolean mIsTwoPanel;
-    final Rect mLargeTextViewRect = new Rect();
-    final Rect mInvisiblePlaceholderTextViewRect = new Rect();
 
     // Objects used to perform color filtering on the header. These are stored as fields for
     // the sole purpose of avoiding "new" operations inside animation loops.
@@ -186,13 +190,18 @@
         mMaximumVelocity = configuration.getScaledMaximumFlingVelocity();
         mTransparentStartHeight = (int) getResources().getDimension(
                 R.dimen.quickcontact_starting_empty_height);
-        mToolbarElevation = mContext.getResources().getDimension(
+        mToolbarElevation = getResources().getDimension(
                 R.dimen.quick_contact_toolbar_elevation);
-        mIsTwoPanel = mContext.getResources().getBoolean(R.bool.quickcontact_two_panel);
+        mIsTwoPanel = getResources().getBoolean(R.bool.quickcontact_two_panel);
+        mMaximumTitleMargin = (int) getResources().getDimension(
+                R.dimen.quickcontact_title_initial_margin);
 
         final TypedArray attributeArray = context.obtainStyledAttributes(
                 new int[]{android.R.attr.actionBarSize});
         mMinimumHeaderHeight = attributeArray.getDimensionPixelSize(0, 0);
+        // This value is approximately equal to the portrait ActionBar size. It isn't exactly the
+        // same, since the landscape and portrait ActionBar sizes can be different.
+        mMinimumPortraitHeaderHeight = mMinimumHeaderHeight;
         attributeArray.recycle();
     }
 
@@ -227,6 +236,7 @@
             mIntermediateHeaderHeight = (int) (mMaximumHeaderHeight
                     * INTERMEDIATE_HEADER_HEIGHT_RATIO);
         }
+        mMaximumPortraitHeaderHeight = Math.min(windowSize.x, windowSize.y);
         setHeaderHeight(mIntermediateHeaderHeight);
 
         SchedulingUtils.doOnPreDraw(this, /* drawNextFrame = */ false, new Runnable() {
@@ -251,6 +261,7 @@
                     mPhotoViewContainer.setLayoutParams(layoutParams);
                 }
 
+                calculateCollapsedLargeTitlePadding();
                 updateHeaderTextSize();
             }
         });
@@ -712,7 +723,7 @@
             // Keep the text at maximum size since the header is smaller than threshold.
             mLargeTextView.setScaleX(1);
             mLargeTextView.setScaleY(1);
-            configureLargeTitlePadding();
+            setInterpolatedTitleMargin(1);
             return;
         }
         final float ratio = (toolbarHeight  - mMinimumHeaderHeight)
@@ -723,40 +734,55 @@
 
         mLargeTextView.setScaleX(scale);
         mLargeTextView.setScaleY(scale);
-        configureLargeTitlePadding();
+        setInterpolatedTitleMargin(ratio);
     }
 
     /**
-     * Configure the padding around mLargeTextView so that it will look appropriate once it
+     * Calculate the padding around mLargeTextView so that it will look appropriate once it
      * finishes moving into its target location/size.
      */
-    private void configureLargeTitlePadding() {
-        mToolbar.getBoundsOnScreen(mLargeTextViewRect);
-        mInvisiblePlaceholderTextView.getBoundsOnScreen(mInvisiblePlaceholderTextViewRect);
-        final int neededPaddingStart;
+    private void calculateCollapsedLargeTitlePadding() {
+        final Rect largeTextViewRect = new Rect();
+        final Rect invisiblePlaceholderTextViewRect = new Rect();
+        mToolbar.getBoundsOnScreen(largeTextViewRect);
+        mInvisiblePlaceholderTextView.getBoundsOnScreen(invisiblePlaceholderTextViewRect);
         if (isLayoutRtl()) {
-            neededPaddingStart = mInvisiblePlaceholderTextViewRect.right - mLargeTextViewRect.right;
+            mCollapsedTitleStartMargin = invisiblePlaceholderTextViewRect.right
+                    - largeTextViewRect.right;
         } else {
-            neededPaddingStart = mInvisiblePlaceholderTextViewRect.left - mLargeTextViewRect.left;
+            mCollapsedTitleStartMargin = invisiblePlaceholderTextViewRect.left
+                    - largeTextViewRect.left;
         }
 
         // Distance between top of toolbar to the center of the target rectangle.
         final int desiredTopToCenter = (
-                mInvisiblePlaceholderTextViewRect.top + mInvisiblePlaceholderTextViewRect.bottom)
-                / 2 - mLargeTextViewRect.top;
-        // Additional padding needed on the mLargeTextView so that it has the same amount of
+                invisiblePlaceholderTextViewRect.top + invisiblePlaceholderTextViewRect.bottom)
+                / 2 - largeTextViewRect.top;
+        // Padding needed on the mLargeTextView so that it has the same amount of
         // padding as the target rectangle.
-        final int additionalBottomPaddingNeeded = desiredTopToCenter - mLargeTextView.getHeight()
-                / 2;
+        mCollapsedTitleBottomMargin = desiredTopToCenter - mLargeTextView.getHeight() / 2;
+    }
 
+    /**
+     * Interpolate the title's margin size. When {@param x}=1, use the maximum title margins.
+     * When {@param x}=0, use the margin values taken from {@link #mInvisiblePlaceholderTextView}.
+     */
+    private void setInterpolatedTitleMargin(float x) {
         final FrameLayout.LayoutParams layoutParams
                 = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
-        layoutParams.bottomMargin = additionalBottomPaddingNeeded;
-        layoutParams.setMarginStart(neededPaddingStart);
+        layoutParams.bottomMargin = (int) (mCollapsedTitleBottomMargin * (1 - x)
+                + mMaximumTitleMargin * x) ;
+        layoutParams.setMarginStart((int) (mCollapsedTitleStartMargin * (1 - x)
+                + mMaximumTitleMargin * x));
         mLargeTextView.setLayoutParams(layoutParams);
     }
 
     private void updatePhotoTintAndDropShadow() {
+        // Let's keep an eye on how long this method takes to complete. Right now, it takes ~0.2ms
+        // on a Nexus 5. If it starts to get much slower, there are a number of easy optimizations
+        // available.
+        Trace.beginSection("updatePhotoTintAndDropShadow");
+
         // We need to use toolbarLayoutParams to determine the height, since the layout
         // params can be updated before the height change is reflected inside the View#getHeight().
         final int toolbarHeight = mToolbar.getLayoutParams().height;
@@ -770,22 +796,17 @@
         // Reuse an existing mColorFilter (to avoid GC pauses) to change the photo's tint.
         mPhotoView.clearColorFilter();
 
+        // Ratio of current size to maximum size of the header.
         final float ratio;
-        final float intermediateRatio;
+        // The value that "ratio" will have when the header is at its starting/intermediate size.
+        final float intermediateRatio = calculateHeightRatio((int)
+                (mMaximumPortraitHeaderHeight * INTERMEDIATE_HEADER_HEIGHT_RATIO));
         if (!mIsTwoPanel) {
-            // Ratio of current size to maximum size of the header.
-            ratio =  (toolbarHeight  - mMinimumHeaderHeight)
-                    / (float) (mMaximumHeaderHeight - mMinimumHeaderHeight) ;
-            // The value that "ratio" will have when the header is at its
-            // starting/intermediate size.
-            intermediateRatio = (mIntermediateHeaderHeight - mMinimumHeaderHeight)
-                    / (float) (mMaximumHeaderHeight - mMinimumHeaderHeight);
+            ratio = calculateHeightRatio(toolbarHeight);
         } else {
-            // Set ratio and intermediateRatio to the same arbitrary value, so that
-            // the math below considers us to be in the intermediate position. The specific
-            // values are not very important.
-            ratio = 0.5f;
-            intermediateRatio = 0.5f;
+            // We want the ratio and intermediateRatio to have the *approximate* values
+            // they would have in portrait mode when at the intermediate position.
+            ratio = intermediateRatio;
         }
 
         final float linearBeforeMiddle = Math.max(1 - (1 - ratio) / intermediateRatio, 0);
@@ -802,11 +823,16 @@
         final float colorAlpha;
         if (mPhotoView.isBasedOffLetterTile()) {
             // Since the letter tile only has white and grey, tint it more slowly. Otherwise
-            // it will be completely invisible before we reach the intermediate point.
-            final float SLOWING_FACTOR = 1.6f;
+            // it will be completely invisible before we reach the intermediate point. The values
+            // for TILE_EXPONENT and slowingFactor are chosen to achieve DESIRED_INTERMEDIATE_ALPHA
+            // at the intermediate/starting position.
+            final float DESIRED_INTERMEDIATE_ALPHA = 0.9f;
+            final float TILE_EXPONENT = 1.5f;
+            final float slowingFactor = (float) ((1 - intermediateRatio) / intermediateRatio
+                    / (1 - Math.pow(1 - DESIRED_INTERMEDIATE_ALPHA, 1/TILE_EXPONENT)));
             float linearBeforeMiddleish = Math.max(1 - (1 - ratio) / intermediateRatio
-                    / SLOWING_FACTOR, 0);
-            colorAlpha = 1 - (float) Math.pow(linearBeforeMiddleish, EXPONENT_ALMOST_ONE);
+                    / slowingFactor, 0);
+            colorAlpha = 1 - (float) Math.pow(linearBeforeMiddleish, TILE_EXPONENT);
             mColorMatrix.postConcat(alphaMatrix(colorAlpha, mHeaderTintColor));
         } else {
             colorAlpha = 1 - semiLinearBeforeMiddle;
@@ -817,7 +843,14 @@
         mPhotoView.setColorFilter(mColorFilter);
         // Tell the photo view what tint we are trying to achieve. Depending on the type of
         // drawable used, the photo view may or may not use this tint.
-        mPhotoView.setTint(((int) (0xFF * colorAlpha)) << 24 | (mHeaderTintColor & 0xffffff));
+        mPhotoView.setTint(mHeaderTintColor);
+
+        Trace.endSection();
+    }
+
+    private float calculateHeightRatio(int height) {
+        return (height - mMinimumPortraitHeaderHeight)
+                / (float) (mMaximumPortraitHeaderHeight - mMinimumPortraitHeaderHeight);
     }
 
     /**
diff --git a/src/com/android/contacts/widget/QuickContactImageView.java b/src/com/android/contacts/widget/QuickContactImageView.java
index 9dbf85e..987b27d 100644
--- a/src/com/android/contacts/widget/QuickContactImageView.java
+++ b/src/com/android/contacts/widget/QuickContactImageView.java
@@ -1,28 +1,27 @@
 package com.android.contacts.widget;
 
-
 import com.android.contacts.common.lettertiles.LetterTileDrawable;
 
 import android.content.Context;
-import android.graphics.Canvas;
-import android.graphics.PorterDuff.Mode;
-import android.graphics.PorterDuffXfermode;
-import android.graphics.Xfermode;
 import android.graphics.drawable.BitmapDrawable;
 import android.graphics.drawable.Drawable;
 import android.util.AttributeSet;
 import android.widget.ImageView;
 
+import com.android.contacts.R;
+
 /**
- * An {@link ImageView} designed to display QuickContact's contact photo. In addition to
- * supporting {@link ImageView#setColorFilter} this also performs a second color blending with
- * the tint set in {@link #setTint}. This requires a second draw pass.
+ * An {@link ImageView} designed to display QuickContact's contact photo. When requested to draw
+ * {@link LetterTileDrawable}'s, this class instead draws a different default avatar drawable.
+ *
+ * In addition to supporting {@link ImageView#setColorFilter} this also supports a {@link #setTint}
+ * method.
+ *
+ * This entire class can be deleted once use of LetterTileDrawable is no longer used
+ * inside QuickContactsActivity at all.
  */
 public class QuickContactImageView extends ImageView {
 
-    private Xfermode mMode = new PorterDuffXfermode(Mode.MULTIPLY);
-    private int mTintColor;
-    private BitmapDrawable mBitmapDrawable;
     private Drawable mOriginalDrawable;
 
     public QuickContactImageView(Context context) {
@@ -43,7 +42,11 @@
     }
 
     public void setTint(int color) {
-        mTintColor = color;
+        if (isBasedOffLetterTile()) {
+            setBackgroundColor(color);
+        } else {
+            setBackground(null);
+        }
         postInvalidate();
     }
 
@@ -55,31 +58,24 @@
     public void setImageDrawable(Drawable drawable) {
         // There is no way to avoid all this casting. Blending modes aren't equally
         // supported for all drawable types.
+        final BitmapDrawable bitmapDrawable;
         if (drawable == null || drawable instanceof BitmapDrawable) {
-            mBitmapDrawable = (BitmapDrawable) drawable;
+            bitmapDrawable = (BitmapDrawable) drawable;
+            setScaleType(ScaleType.CENTER_CROP);
         } else if (drawable instanceof LetterTileDrawable) {
-            // TODO: set a desired hardcoded BitmapDrawable here
-            mBitmapDrawable = null;
+            bitmapDrawable = (BitmapDrawable) getResources().getDrawable(
+                    R.drawable.default_avatar_white);
+            setScaleType(ScaleType.CENTER);
         } else {
             throw new IllegalArgumentException("Does not support this type of drawable");
 
         }
         mOriginalDrawable = drawable;
-        super.setImageDrawable(mBitmapDrawable);
+        super.setImageDrawable(bitmapDrawable);
     }
 
     @Override
     public Drawable getDrawable() {
         return mOriginalDrawable;
     }
-
-    @Override
-    protected void onDraw(Canvas canvas) {
-        if (isBasedOffLetterTile()) {
-            // The LetterTileDrawable's bitmaps have a lot of pixels with alpha=0. These
-            // look stupid unless we fill in the background and use a different blending mode.
-            canvas.drawColor(((LetterTileDrawable) mOriginalDrawable).getColor());
-        }
-        super.onDraw(canvas);
-    }
 }