Increase max QC title size on 720dp+ tablets

Because of the way that View#setScaleY() works, the maximum size of the
title TextView has to be less than the minimum size of the of the TextView's
parent. As a result, increasing the title size caused unexpected behavior.

Fixing this odd behavior required me to remove the title TextView
out of the header ViewGroup and instead manually control its position
inside MultiShrinkScroller. This required me to make MultiShrinkScroller
a FrameLayout. This necessitated lots of little changes inside MultiShrinkScroller
(ie, what type of LayoutParams to cast into).

There is a reason nobody else has messed around with scaling title TextViews.

Bug: 16683381
Change-Id: Ib8fc065931a6a0c2a96b8bd5a11bb055457aa3dd
diff --git a/res/layout-land/quickcontact_activity.xml b/res/layout-land/quickcontact_activity.xml
index 9a07698..b22b5ef 100644
--- a/res/layout-land/quickcontact_activity.xml
+++ b/res/layout-land/quickcontact_activity.xml
@@ -23,19 +23,39 @@
     android:focusableInTouchMode="true"
     android:descendantFocusability="afterDescendants" >
 
-    <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
         android:layout_width="match_parent"
-        android:layout_height="match_parent">
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-        <include layout="@layout/quickcontact_header" />
+        <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_content" />
+        <LinearLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:orientation="horizontal">
+
+            <!-- Needs a non null background for elevation to work on this View. This will
+                 *not* cause an additional draw since the background is transparent. -->
+            <FrameLayout
+                android:layout_width="match_parent"
+                android:layout_height="match_parent"
+                android:background="#00000000"
+                android:id="@+id/toolbar_parent">
+
+                <include layout="@layout/quickcontact_header" />
+
+                <include layout="@layout/quickcontact_title" />
+
+            </FrameLayout>
+
+            <include layout="@layout/quickcontact_content" />
+
+        </LinearLayout>
 
     </LinearLayout>
 
diff --git a/res/layout/quickcontact_activity.xml b/res/layout/quickcontact_activity.xml
index 008062b..577a451 100644
--- a/res/layout/quickcontact_activity.xml
+++ b/res/layout/quickcontact_activity.xml
@@ -24,14 +24,34 @@
     android:focusableInTouchMode="true"
     android:descendantFocusability="afterDescendants" >
 
-    <View
+    <LinearLayout
         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" />
+        android:layout_height="match_parent"
+        android:orientation="vertical">
 
-    <include layout="@layout/quickcontact_header" />
+        <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_content" />
+        <!-- Needs a non null background for elevation to work on this View. This will *not*
+             cause an additional draw since the background is transparent. -->
+        <FrameLayout
+            android:layout_width="match_parent"
+            android:layout_height="match_parent"
+            android:background="#00000000"
+            android:id="@+id/toolbar_parent">
+            <include layout="@layout/quickcontact_header" />
+        </FrameLayout>
+
+        <include layout="@layout/quickcontact_content" />
+
+    </LinearLayout>
+
+    <!-- This title's maximum height must be less than the minimum size of its
+         parent ViewGroup because of an oddity in the way View#setScaleY() works. As a result,
+         this title can not be inside @style/quickcontact_header. -->
+    <include layout="@layout/quickcontact_title" />
 
 </com.android.contacts.widget.MultiShrinkScroller>
\ No newline at end of file
diff --git a/res/layout/quickcontact_header.xml b/res/layout/quickcontact_header.xml
index 1f73215..bb89dda 100644
--- a/res/layout/quickcontact_header.xml
+++ b/res/layout/quickcontact_header.xml
@@ -14,15 +14,7 @@
      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">
+<merge xmlns:android="http://schemas.android.com/apk/res/android">
 
     <com.android.contacts.widget.QuickContactImageView
         android:id="@+id/photo"
@@ -61,17 +53,4 @@
         android:background="#00000000"
         android:id="@+id/toolbar"/>
 
-    <TextView
-        android:layout_width="wrap_content"
-        android:layout_height="wrap_content"
-        android:textColor="@color/actionbar_text_color"
-        android:maxLines="@integer/quickcontact_title_lines"
-        android:ellipsize="end"
-        android:layout_gravity="bottom|start"
-        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>
+</merge>
diff --git a/res/layout/quickcontact_title.xml b/res/layout/quickcontact_title.xml
new file mode 100644
index 0000000..9b23a86
--- /dev/null
+++ b/res/layout/quickcontact_title.xml
@@ -0,0 +1,29 @@
+<?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.
+-->
+<TextView
+    xmlns:android="http://schemas.android.com/apk/res/android"
+    android:layout_width="wrap_content"
+    android:layout_height="wrap_content"
+    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:layout_gravity="top|start"
+    android:textColor="@color/actionbar_text_color"
+    android:maxLines="@integer/quickcontact_title_lines"
+    android:textSize="@dimen/quickcontact_maximum_title_size"
+    android:ellipsize="end"
+    android:id="@+id/large_title"/>
\ No newline at end of file
diff --git a/res/values-sw720dp/dimens.xml b/res/values-sw720dp/dimens.xml
index 495526d..827c71b 100644
--- a/res/values-sw720dp/dimens.xml
+++ b/res/values-sw720dp/dimens.xml
@@ -21,4 +21,9 @@
     <dimen name="quick_contact_photo_container_height">360dip</dimen>
     <dimen name="contact_picker_contact_list_min_height">650dip</dimen>
     <dimen name="list_visible_scrollbar_padding">48dip</dimen>
+
+    <!-- When QC is uncollapsed, the title has this much margin on its left, right and bottom -->
+    <dimen name="quickcontact_title_initial_margin">32dp</dimen>
+    <!-- Initial size of QuickContact's title size -->
+    <dimen name="quickcontact_maximum_title_size">64dp</dimen>
 </resources>
diff --git a/src/com/android/contacts/widget/MultiShrinkScroller.java b/src/com/android/contacts/widget/MultiShrinkScroller.java
index 8f1146a..7f17521 100644
--- a/src/com/android/contacts/widget/MultiShrinkScroller.java
+++ b/src/com/android/contacts/widget/MultiShrinkScroller.java
@@ -24,6 +24,7 @@
 import android.util.TypedValue;
 import android.view.Display;
 import android.view.DisplayInfo;
+import android.view.Gravity;
 import android.view.MotionEvent;
 import android.view.VelocityTracker;
 import android.view.View;
@@ -51,7 +52,7 @@
  * features are missing. For example: handling of KEYCODES, OverScroll bounce and saving
  * scroll state in savedInstanceState bundles.
  */
-public class MultiShrinkScroller extends LinearLayout {
+public class MultiShrinkScroller extends FrameLayout {
 
     /**
      * 1000 pixels per millisecond. Ie, 1 pixel per second.
@@ -283,18 +284,28 @@
                     mMaximumHeaderHeight = getHeight();
                     mMinimumHeaderHeight = mMaximumHeaderHeight;
                     mIntermediateHeaderHeight = mMaximumHeaderHeight;
+
+                    // Permanently set photo width and height.
                     final TypedValue photoRatio = new TypedValue();
                     getResources().getValue(R.vals.quickcontact_photo_ratio, photoRatio,
                             /* resolveRefs = */ true);
-                    final LayoutParams layoutParams
-                            = (LayoutParams) mPhotoViewContainer.getLayoutParams();
-                    layoutParams.height = mMaximumHeaderHeight;
-                    layoutParams.width = (int) (mMaximumHeaderHeight * photoRatio.getFloat());
-                    mPhotoViewContainer.setLayoutParams(layoutParams);
+                    final ViewGroup.LayoutParams photoLayoutParams
+                            = mPhotoViewContainer.getLayoutParams();
+                    photoLayoutParams.height = mMaximumHeaderHeight;
+                    photoLayoutParams.width = (int) (mMaximumHeaderHeight * photoRatio.getFloat());
+                    mPhotoViewContainer.setLayoutParams(photoLayoutParams);
+
+                    // Permanently set title width and margin.
+                    final FrameLayout.LayoutParams largeTextLayoutParams
+                            = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
+                    largeTextLayoutParams.width = photoLayoutParams.width -
+                            largeTextLayoutParams.leftMargin - largeTextLayoutParams.rightMargin;
+                    largeTextLayoutParams.gravity = Gravity.BOTTOM | Gravity.START;
+                    mLargeTextView.setLayoutParams(largeTextLayoutParams);
                 }
 
                 calculateCollapsedLargeTitlePadding();
-                updateHeaderTextSize();
+                updateHeaderTextSizeAndMargin();
                 configureGradientViewHeights();
             }
         });
@@ -558,7 +569,7 @@
             scrollDown(delta);
         }
         updatePhotoTintAndDropShadow();
-        updateHeaderTextSize();
+        updateHeaderTextSizeAndMargin();
         final boolean isFullscreen = getScrollNeededToBeFullScreen() <= 0;
         if (mListener != null) {
             if (wasFullscreen && !isFullscreen) {
@@ -575,13 +586,13 @@
      */
     @NeededForReflection
     public void setToolbarHeight(int delta) {
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
+        final ViewGroup.LayoutParams toolbarLayoutParams
+                = mToolbar.getLayoutParams();
         toolbarLayoutParams.height = delta;
         mToolbar.setLayoutParams(toolbarLayoutParams);
 
         updatePhotoTintAndDropShadow();
-        updateHeaderTextSize();
+        updateHeaderTextSizeAndMargin();
     }
 
     @NeededForReflection
@@ -594,12 +605,12 @@
      */
     @NeededForReflection
     public void setHeaderHeight(int height) {
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
+        final ViewGroup.LayoutParams toolbarLayoutParams
+                = mToolbar.getLayoutParams();
         toolbarLayoutParams.height = height;
         mToolbar.setLayoutParams(toolbarLayoutParams);
         updatePhotoTintAndDropShadow();
-        updateHeaderTextSize();
+        updateHeaderTextSizeAndMargin();
     }
 
     @NeededForReflection
@@ -618,10 +629,8 @@
      */
     @NeededForReflection
     public int getScroll() {
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
         return mTransparentStartHeight - getTransparentViewHeight()
-                + getMaximumScrollableHeaderHeight() - toolbarLayoutParams.height
+                + getMaximumScrollableHeaderHeight() - getToolbarHeight()
                 + mScrollView.getScrollY();
     }
 
@@ -641,10 +650,8 @@
      * This value should never be used in conjunction with {@link #getScroll} values.
      */
     private int getScroll_ignoreOversizedHeaderForSnapping() {
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
         return mTransparentStartHeight - getTransparentViewHeight()
-                + Math.max(getMaximumScrollableHeaderHeight() - toolbarLayoutParams.height, 0)
+                + Math.max(getMaximumScrollableHeaderHeight() - getToolbarHeight(), 0)
                 + mScrollView.getScrollY();
     }
 
@@ -766,8 +773,8 @@
             setTransparentViewHeight(Math.max(0, getTransparentViewHeight()));
             delta -= originalValue - getTransparentViewHeight();
         }
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
+        final ViewGroup.LayoutParams toolbarLayoutParams
+                = mToolbar.getLayoutParams();
         if (toolbarLayoutParams.height > getFullyCompressedHeaderHeight()) {
             final int originalValue = toolbarLayoutParams.height;
             toolbarLayoutParams.height -= delta;
@@ -802,8 +809,7 @@
             mScrollView.scrollBy(0, delta);
             delta -= mScrollView.getScrollY() - originalValue;
         }
-        final LinearLayout.LayoutParams toolbarLayoutParams
-                = (LayoutParams) mToolbar.getLayoutParams();
+        final ViewGroup.LayoutParams toolbarLayoutParams = mToolbar.getLayoutParams();
         if (toolbarLayoutParams.height < getMaximumScrollableHeaderHeight()) {
             final int originalValue = toolbarLayoutParams.height;
             toolbarLayoutParams.height -= delta;
@@ -831,9 +837,9 @@
     /**
      * Set the header size and padding, based on the current scroll position.
      */
-    private void updateHeaderTextSize() {
+    private void updateHeaderTextSizeAndMargin() {
         if (mIsTwoPanel) {
-            // The text size stays constant on two panel layouts.
+            // The text size stays at a constant size & location in two panel layouts.
             return;
         }
 
@@ -847,12 +853,12 @@
 
         final int START_TEXT_SCALING_THRESHOLD_COEFFICIENT = 2;
         final int threshold = START_TEXT_SCALING_THRESHOLD_COEFFICIENT * mMinimumHeaderHeight;
-        final int toolbarHeight = mToolbar.getLayoutParams().height;
+        final int toolbarHeight = getToolbarHeight();
         if (toolbarHeight >= threshold) {
             // Keep the text at maximum size since the header is smaller than threshold.
             mLargeTextView.setScaleX(1);
             mLargeTextView.setScaleY(1);
-            setInterpolatedTitleMargin(1);
+            setInterpolatedTitleMargins(1);
             return;
         }
         final float ratio = (toolbarHeight  - mMinimumHeaderHeight)
@@ -863,7 +869,7 @@
 
         mLargeTextView.setScaleX(scale);
         mLargeTextView.setScaleY(scale);
-        setInterpolatedTitleMargin(ratio);
+        setInterpolatedTitleMargins(ratio);
     }
 
     /**
@@ -896,14 +902,21 @@
      * 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
+    private void setInterpolatedTitleMargins(float x) {
+        final FrameLayout.LayoutParams titleLayoutParams
                 = (FrameLayout.LayoutParams) mLargeTextView.getLayoutParams();
-        layoutParams.bottomMargin = (int) (mCollapsedTitleBottomMargin * (1 - x)
-                + mMaximumTitleMargin * x) ;
-        layoutParams.setMarginStart((int) (mCollapsedTitleStartMargin * (1 - x)
+        final LinearLayout.LayoutParams toolbarLayoutParams
+                = (LinearLayout.LayoutParams) mToolbar.getLayoutParams();
+        titleLayoutParams.setMarginStart((int) (mCollapsedTitleStartMargin * (1 - x)
                 + mMaximumTitleMargin * x));
-        mLargeTextView.setLayoutParams(layoutParams);
+        // How offset the title should be from the bottom of the toolbar
+        final int pretendBottomMargin =  (int) (mCollapsedTitleBottomMargin * (1 - x)
+                + mMaximumTitleMargin * x) ;
+        // Calculate how offset the title should be from the top of the screen.
+        titleLayoutParams.topMargin = getTransparentViewHeight()
+                + toolbarLayoutParams.height - pretendBottomMargin
+                - mLargeTextView.getHeight();
+        mLargeTextView.setLayoutParams(titleLayoutParams);
     }
 
     private void updatePhotoTintAndDropShadow() {
@@ -922,7 +935,7 @@
 
         // 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;
+        final int toolbarHeight = getToolbarHeight();
 
         if (toolbarHeight <= mMinimumHeaderHeight && !mIsTwoPanel) {
             mPhotoViewContainer.setElevation(mToolbarElevation);
@@ -1118,7 +1131,7 @@
 
         final int newEmptyScrollViewSpace = -getOverflowingChildViewSize() + heightDelta;
         if (newEmptyScrollViewSpace > 0 && !mIsTwoPanel) {
-            final int newDesiredToolbarHeight = Math.min(mToolbar.getLayoutParams().height
+            final int newDesiredToolbarHeight = Math.min(getToolbarHeight()
                     + newEmptyScrollViewSpace, getMaximumScrollableHeaderHeight());
             ObjectAnimator.ofInt(this, "toolbarHeight", newDesiredToolbarHeight).setDuration(
                     ExpandingEntryCardView.DURATION_COLLAPSE_ANIMATION_CHANGE_BOUNDS).start();