Makes glowpad scalable.

Bug: 10634748
Change-Id: Iac3d424e7d6be94230da12f3058214b7ca3d8a84
diff --git a/InCallUI/res/layout/answer_fragment.xml b/InCallUI/res/layout/answer_fragment.xml
index d62a8d5..4f31e3f 100644
--- a/InCallUI/res/layout/answer_fragment.xml
+++ b/InCallUI/res/layout/answer_fragment.xml
@@ -39,4 +39,5 @@
         dc:vibrationDuration="20"
         dc:glowRadius="@dimen/glowpadview_glow_radius"
         dc:pointDrawable="@*android:drawable/ic_lockscreen_glowdot"
+        dc:allowScaling="true"
         />
diff --git a/InCallUI/res/values/attrs.xml b/InCallUI/res/values/attrs.xml
index 056615d..bc00c95 100644
--- a/InCallUI/res/values/attrs.xml
+++ b/InCallUI/res/values/attrs.xml
@@ -97,5 +97,10 @@
 
         <!-- Used when the handle shouldn't wait to be hit before following the finger -->
         <attr name="alwaysTrackFinger" format="boolean"/>
+
+        <!-- Determine whether the glow pad is allowed to scale to fit the bounds indicated
+            by its parent. If this is set to false, no scaling will occur. If this is set to true
+            scaling will occur to fit for any axis in which gravity is set to center. -->
+        <attr name="allowScaling" format="boolean" />
     </declare-styleable>
 </resources>
diff --git a/InCallUI/src/com/android/incallui/AnswerPresenter.java b/InCallUI/src/com/android/incallui/AnswerPresenter.java
index 607e966..e8bcc99 100644
--- a/InCallUI/src/com/android/incallui/AnswerPresenter.java
+++ b/InCallUI/src/com/android/incallui/AnswerPresenter.java
@@ -98,7 +98,7 @@
 
     @Override
     public void onCallStateChanged(Call call) {
-        Log.d(TAG, "onCallStateChange() " + call);
+        Log.d(this, "onCallStateChange() " + call);
         if (call.getState() != Call.State.INCOMING && call.getState() != Call.State.CALL_WAITING) {
             // Stop listening for updates.
             CallList.getInstance().removeCallUpdateListener(mCallId, this);
diff --git a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
index b577b7c..3fc978a 100644
--- a/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
+++ b/InCallUI/src/com/android/incallui/widget/multiwaveview/GlowPadView.java
@@ -48,8 +48,8 @@
 /**
  * This is a copy of com.android.internal.widget.multiwaveview.GlowPadView with minor changes
  * to remove dependencies on private api's.
- *
- * Contains changes up to If296b60af2421bfa1a9a082e608ba77b2392a218
+ * 
+ * Incoporated the scaling functionality.
  *
  * A re-usable widget containing a center, outer ring and wave animation.
  */
@@ -116,6 +116,8 @@
     private float mWaveCenterY;
     private int mMaxTargetHeight;
     private int mMaxTargetWidth;
+    private float mRingScaleFactor = 1f;
+    private boolean mAllowScaling;
 
     private float mOuterRadius = 0.0f;
     private float mSnapMargin = 0.0f;
@@ -219,6 +221,7 @@
                 mVibrationDuration);
         mFeedbackCount = a.getInt(R.styleable.GlowPadView_feedbackCount,
                 mFeedbackCount);
+        mAllowScaling = a.getBoolean(R.styleable.GlowPadView_allowScaling, false);
         TypedValue handle = a.peekValue(R.styleable.GlowPadView_handleDrawable);
         mHandleDrawable = new TargetDrawable(res, handle != null ? handle.resourceId : 0, 2);
         mHandleDrawable.setState(TargetDrawable.STATE_INACTIVE);
@@ -320,6 +323,22 @@
         return (int) (Math.max(mOuterRing.getHeight(), 2 * mOuterRadius) + mMaxTargetHeight);
     }
 
+    /**
+     * This gets the suggested width accounting for the ring's scale factor.
+     */
+    protected int getScaledSuggestedMinimumWidth() {
+        return (int) (mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius)
+                + mMaxTargetWidth);
+    }
+
+    /**
+     * This gets the suggested height accounting for the ring's scale factor.
+     */
+    protected int getScaledSuggestedMinimumHeight() {
+        return (int) (mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius)
+                + mMaxTargetHeight);
+    }
+
     private int resolveMeasured(int measureSpec, int desired)
     {
         int result = 0;
@@ -344,7 +363,14 @@
         final int minimumHeight = getSuggestedMinimumHeight();
         int computedWidth = resolveMeasured(widthMeasureSpec, minimumWidth);
         int computedHeight = resolveMeasured(heightMeasureSpec, minimumHeight);
-        computeInsets((computedWidth - minimumWidth), (computedHeight - minimumHeight));
+
+        mRingScaleFactor = computeScaleFactor(minimumWidth, minimumHeight,
+                computedWidth, computedHeight);
+
+        int scaledWidth = getScaledSuggestedMinimumWidth();
+        int scaledHeight = getScaledSuggestedMinimumHeight();
+
+        computeInsets(computedWidth - scaledWidth, computedHeight - scaledHeight);
         setMeasuredDimension(computedWidth, computedHeight);
     }
 
@@ -509,8 +535,9 @@
                     "onUpdate", mUpdateListener));
         }
 
-        final float ringScaleTarget = expanded ?
+        float ringScaleTarget = expanded ?
                 RING_SCALE_EXPANDED : RING_SCALE_COLLAPSED;
+        ringScaleTarget *= mRingScaleFactor;
         mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", interpolator,
                 "alpha", 0.0f,
@@ -540,11 +567,12 @@
                     "delay", delay,
                     "onUpdate", mUpdateListener));
         }
+        float ringScale = mRingScaleFactor * RING_SCALE_EXPANDED;
         mTargetAnimations.add(Tweener.to(mOuterRing, duration,
                 "ease", Ease.Cubic.easeOut,
                 "alpha", 1.0f,
-                "scaleX", 1.0f,
-                "scaleY", 1.0f,
+                "scaleX", ringScale,
+                "scaleY", ringScale,
                 "delay", delay,
                 "onUpdate", mUpdateListener,
                 "onComplete", mTargetUpdateListener));
@@ -784,8 +812,12 @@
     }
 
     private void updateGlowPosition(float x, float y) {
-        mPointCloud.glowManager.setX(x);
-        mPointCloud.glowManager.setY(y);
+        float dx = x - mOuterRing.getX();
+        float dy = y - mOuterRing.getY();
+        dx *= 1f / mRingScaleFactor;
+        dy *= 1f / mRingScaleFactor;
+        mPointCloud.glowManager.setX(mOuterRing.getX() + dx);
+        mPointCloud.glowManager.setY(mOuterRing.getY() + dy);
     }
 
     private void handleDown(MotionEvent event) {
@@ -857,7 +889,7 @@
 
             if (mDragging) {
                 // For multiple targets, snap to the one that matches
-                final float snapRadius = mOuterRadius - mSnapMargin;
+                final float snapRadius = mRingScaleFactor * mOuterRadius - mSnapMargin;
                 final float snapDistance2 = snapRadius * snapRadius;
                 // Find first target in range
                 for (int i = 0; i < ntargets; i++) {
@@ -1016,6 +1048,61 @@
         }
     }
 
+    /**
+     * Given the desired width and height of the ring and the allocated width and height, compute
+     * how much we need to scale the ring.
+     */
+    private float computeScaleFactor(int desiredWidth, int desiredHeight,
+            int actualWidth, int actualHeight) {
+
+        // Return unity if scaling is not allowed.
+        if (!mAllowScaling) return 1f;
+
+        final int layoutDirection = getLayoutDirection();
+        final int absoluteGravity = Gravity.getAbsoluteGravity(mGravity, layoutDirection);
+
+        float scaleX = 1f;
+        float scaleY = 1f;
+
+        // We use the gravity as a cue for whether we want to scale on a particular axis.
+        // We only scale to fit horizontally if we're not pinned to the left or right. Likewise,
+        // we only scale to fit vertically if we're not pinned to the top or bottom. In these
+        // cases, we want the ring to hang off the side or top/bottom, respectively.
+        switch (absoluteGravity & Gravity.HORIZONTAL_GRAVITY_MASK) {
+            case Gravity.LEFT:
+            case Gravity.RIGHT:
+                break;
+            case Gravity.CENTER_HORIZONTAL:
+            default:
+                if (desiredWidth > actualWidth) {
+                    scaleX = (1f * actualWidth - mMaxTargetWidth) /
+                            (desiredWidth - mMaxTargetWidth);
+                }
+                break;
+        }
+        switch (absoluteGravity & Gravity.VERTICAL_GRAVITY_MASK) {
+            case Gravity.TOP:
+            case Gravity.BOTTOM:
+                break;
+            case Gravity.CENTER_VERTICAL:
+            default:
+                if (desiredHeight > actualHeight) {
+                    scaleY = (1f * actualHeight - mMaxTargetHeight) /
+                            (desiredHeight - mMaxTargetHeight);
+                }
+                break;
+        }
+        return Math.min(scaleX, scaleY);
+    }
+
+    private float getRingWidth() {
+        return mRingScaleFactor * Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
+    }
+
+    private float getRingHeight() {
+        return mRingScaleFactor * Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
+    }
+
     @Override
     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
         super.onLayout(changed, left, top, right, bottom);
@@ -1024,8 +1111,8 @@
 
         // Target placement width/height. This puts the targets on the greater of the ring
         // width or the specified outer radius.
-        final float placementWidth = Math.max(mOuterRing.getWidth(), 2 * mOuterRadius);
-        final float placementHeight = Math.max(mOuterRing.getHeight(), 2 * mOuterRadius);
+        final float placementWidth = getRingWidth();
+        final float placementHeight = getRingHeight();
         float newWaveCenterX = mHorizontalInset
                 + Math.max(width, mMaxTargetWidth + placementWidth) / 2;
         float newWaveCenterY = mVerticalInset
@@ -1040,6 +1127,8 @@
         mOuterRing.setPositionX(newWaveCenterX);
         mOuterRing.setPositionY(newWaveCenterY);
 
+        mPointCloud.setScale(mRingScaleFactor);
+
         mHandleDrawable.setPositionX(newWaveCenterX);
         mHandleDrawable.setPositionY(newWaveCenterY);
 
@@ -1063,8 +1152,8 @@
             final float angle = alpha * i;
             targetIcon.setPositionX(centerX);
             targetIcon.setPositionY(centerY);
-            targetIcon.setX(mOuterRadius * (float) Math.cos(angle));
-            targetIcon.setY(mOuterRadius * (float) Math.sin(angle));
+            targetIcon.setX(getRingWidth() / 2 * (float) Math.cos(angle));
+            targetIcon.setY(getRingHeight() / 2 * (float) Math.sin(angle));
         }
     }