Default QC avatar

UX still hasn't decided how they want to display the default avatar.
I'm confidant most of the work in this CL will be usable once they
make up their mind.

Change-Id: Id24160d4339b0bd1bdd428c007d32f42942c0a43
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/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index 12da3f6..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;
@@ -87,6 +88,8 @@
     private int mMaximumHeaderTextSize;
     private int mCollapsedTitleBottomMargin;
     private int mCollapsedTitleStartMargin;
+    private int mMinimumPortraitHeaderHeight;
+    private int mMaximumPortraitHeaderHeight;
 
     private final Scroller mScroller;
     private final EdgeEffect mEdgeGlowBottom;
@@ -196,6 +199,9 @@
         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();
     }
 
@@ -230,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() {
@@ -771,6 +778,11 @@
     }
 
     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;
@@ -784,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);
@@ -816,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;
@@ -831,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);
-    }
 }