MediaRouter: Implement the volume group animaion without window animation

Note: when animating the dialog size, we need to fix the window size since
the animation will be jerky without it because view animation and window animation
do not happen in sync. To address this, this change creates a transparent layout
which expands the maximum area of possible animation and animate the dialog
within the area.

Bug: 24271568
Change-Id: I1e691cbf3124205f55624a4a04d713206d6be528
diff --git a/v7/mediarouter/res/interpolator/mr_fast_out_slow_in.xml b/v7/mediarouter/res/interpolator/mr_fast_out_slow_in.xml
new file mode 100644
index 0000000..a51bfce
--- /dev/null
+++ b/v7/mediarouter/res/interpolator/mr_fast_out_slow_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0.4"
+    android:controlY1="0"
+    android:controlX2="0.2"
+    android:controlY2="1"/>
diff --git a/v7/mediarouter/res/interpolator/mr_linear_out_slow_in.xml b/v7/mediarouter/res/interpolator/mr_linear_out_slow_in.xml
new file mode 100644
index 0000000..d500c8b
--- /dev/null
+++ b/v7/mediarouter/res/interpolator/mr_linear_out_slow_in.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+  ~ Copyright (C) 2015 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
+  -->
+
+<pathInterpolator xmlns:android="http://schemas.android.com/apk/res/android"
+    android:controlX1="0"
+    android:controlY1="0"
+    android:controlX2="0.2"
+    android:controlY2="1"/>
diff --git a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
index 8d17c25..b7cc517 100644
--- a/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
+++ b/v7/mediarouter/res/layout/mr_controller_material_dialog_b.xml
@@ -14,41 +14,46 @@
      limitations under the License.
 -->
 
-<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
-              android:layout_width="fill_parent"
-              android:layout_height="wrap_content"
-              android:orientation="vertical">
-    <LinearLayout android:id="@+id/mr_title_bar"
+<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
+          android:id="@+id/mr_expandable_area"
+          android:background="@android:color/transparent"
+          android:layout_width="fill_parent"
+          android:layout_height="fill_parent">
+    <LinearLayout android:id="@+id/mr_dialog_area"
                   android:layout_width="fill_parent"
                   android:layout_height="wrap_content"
-                  android:paddingLeft="24dp"
-                  android:paddingRight="12dp"
-                  android:orientation="horizontal" >
-        <TextView android:id="@+id/mr_name"
-                  android:layout_width="0dp"
-                  android:layout_height="72dp"
-                  android:layout_weight="1"
-                  android:gravity="center_vertical"
-                  android:singleLine="true"
-                  android:ellipsize="end"
-                  android:textAppearance="?attr/mediaRouteControllerTitleTextStyle" />
-        <ImageButton android:id="@+id/mr_close"
-                     android:layout_width="48dp"
-                     android:layout_height="48dp"
-                     android:layout_gravity="center_vertical"
-                     android:contentDescription="@string/mr_controller_close_description"
-                     android:src="?attr/mediaRouteCloseDrawable"
-                     android:background="?attr/selectableItemBackgroundBorderless" />
-    </LinearLayout>
-    <FrameLayout android:id="@+id/mr_custom_control"
-                 android:layout_width="fill_parent"
-                 android:layout_height="wrap_content"
-                 android:visibility="gone" />
-    <FrameLayout android:id="@+id/mr_default_control"
-                 android:layout_width="fill_parent"
-                 android:layout_height="wrap_content">
-        <FrameLayout android:layout_width="fill_parent"
-                     android:layout_height="fill_parent">
+                  android:background="?android:attr/windowBackground"
+                  android:layout_gravity="center"
+                  android:orientation="vertical">
+        <LinearLayout android:id="@+id/mr_title_bar"
+                      android:layout_width="fill_parent"
+                      android:layout_height="wrap_content"
+                      android:paddingLeft="24dp"
+                      android:paddingRight="12dp"
+                      android:orientation="horizontal" >
+            <TextView android:id="@+id/mr_name"
+                      android:layout_width="0dp"
+                      android:layout_height="72dp"
+                      android:layout_weight="1"
+                      android:gravity="center_vertical"
+                      android:singleLine="true"
+                      android:ellipsize="end"
+                      android:textAppearance="?attr/mediaRouteControllerTitleTextStyle" />
+            <ImageButton android:id="@+id/mr_close"
+                         android:layout_width="48dp"
+                         android:layout_height="48dp"
+                         android:layout_gravity="center_vertical"
+                         android:contentDescription="@string/mr_controller_close_description"
+                         android:src="?attr/mediaRouteCloseDrawable"
+                         android:background="?attr/selectableItemBackgroundBorderless" />
+        </LinearLayout>
+        <FrameLayout android:id="@+id/mr_custom_control"
+                     android:layout_width="fill_parent"
+                     android:layout_height="wrap_content"
+                     android:visibility="gone" />
+        <FrameLayout android:id="@+id/mr_default_control"
+                     android:layout_width="fill_parent"
+                     android:layout_height="wrap_content">
             <ImageView android:id="@+id/mr_art"
                        android:layout_width="fill_parent"
                        android:layout_height="wrap_content"
@@ -57,35 +62,39 @@
                        android:background="?attr/colorPrimary"
                        android:layout_gravity="top"
                        android:visibility="gone" />
-            <ListView android:id="@+id/mr_volume_group_list"
-                      android:layout_width="fill_parent"
-                      android:layout_height="wrap_content"
-                      android:paddingTop="@dimen/mr_controller_volume_group_list_padding_top"
-                      android:scrollbarStyle="outsideInset"
-                      android:clipToPadding="false"
-                      android:background="?attr/colorPrimaryDark"
-                      android:layout_gravity="bottom"
-                      android:visibility="gone" />
+            <LinearLayout android:layout_width="fill_parent"
+                          android:layout_height="wrap_content"
+                          android:orientation="vertical"
+                          android:layout_gravity="bottom">
+                <LinearLayout android:id="@+id/mr_media_main_control"
+                              android:layout_width="fill_parent"
+                              android:layout_height="wrap_content"
+                              android:orientation="vertical"
+                              android:paddingTop="16dp"
+                              android:paddingBottom="16dp"
+                              android:background="?attr/colorPrimary"
+                              android:layout_gravity="bottom">
+                    <include android:id="@+id/mr_playback_control"
+                             layout="@layout/mr_playback_control" />
+                    <View android:id="@+id/mr_control_divider"
+                          android:layout_width="fill_parent"
+                          android:layout_height="8dp"
+                          android:background="?attr/colorPrimary"
+                          android:visibility="gone" />
+                    <include android:id="@+id/mr_volume_control"
+                             layout="@layout/mr_volume_control" />
+                </LinearLayout>
+                <ListView android:id="@+id/mr_volume_group_list"
+                          android:layout_width="fill_parent"
+                          android:layout_height="wrap_content"
+                          android:paddingTop="@dimen/mr_controller_volume_group_list_padding_top"
+                          android:scrollbarStyle="outsideInset"
+                          android:clipToPadding="false"
+                          android:background="?attr/colorPrimaryDark"
+                          android:transcriptMode="alwaysScroll"
+                          android:visibility="gone" />
+            </LinearLayout>
         </FrameLayout>
-        <LinearLayout android:id="@+id/mr_media_main_control"
-                      android:layout_width="fill_parent"
-                      android:layout_height="wrap_content"
-                      android:orientation="vertical"
-                      android:paddingTop="16dp"
-                      android:paddingBottom="16dp"
-                      android:background="?attr/colorPrimary"
-                      android:layout_gravity="bottom">
-            <include android:id="@+id/mr_playback_control"
-                     layout="@layout/mr_playback_control" />
-            <View android:id="@+id/mr_control_divider"
-                  android:layout_width="fill_parent"
-                  android:layout_height="8dp"
-                  android:background="?attr/colorPrimary"
-                  android:visibility="gone" />
-            <include android:id="@+id/mr_volume_control"
-                     layout="@layout/mr_volume_control" />
-        </LinearLayout>
-    </FrameLayout>
-
-    <include layout="@layout/abc_alert_dialog_button_bar_material" />
-</LinearLayout>
+        <include layout="@layout/abc_alert_dialog_button_bar_material" />
+    </LinearLayout>
+</FrameLayout>
\ No newline at end of file
diff --git a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
index f4797cf..d235026 100644
--- a/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
+++ b/v7/mediarouter/src/android/support/v7/app/MediaRouteControllerDialog.java
@@ -107,8 +107,10 @@
     private ImageButton mCloseButton;
     private MediaRouteExpandCollapseButton mGroupExpandCollapseButton;
 
-    private FrameLayout mCustomControlLayout;
+    private FrameLayout mExpandableAreaLayout;
+    private LinearLayout mDialogAreaLayout;
     private FrameLayout mDefaultControlLayout;
+    private FrameLayout mCustomControlLayout;
     private ImageView mArtView;
     private TextView mTitleView;
     private TextView mSubtitleView;
@@ -268,6 +270,7 @@
     protected void onCreate(Bundle savedInstanceState) {
         super.onCreate(savedInstanceState);
 
+        getWindow().setBackgroundDrawableResource(android.R.color.transparent);
         setContentView(R.layout.mr_controller_material_dialog_b);
 
         // Remove the neutral button.
@@ -275,6 +278,20 @@
 
         ClickListener listener = new ClickListener();
 
+        mExpandableAreaLayout = (FrameLayout) findViewById(R.id.mr_expandable_area);
+        mExpandableAreaLayout.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                dismiss();
+            }
+        });
+        mDialogAreaLayout = (LinearLayout) findViewById(R.id.mr_dialog_area);
+        mDialogAreaLayout.setOnClickListener(new View.OnClickListener() {
+            @Override
+            public void onClick(View v) {
+                // Eat unhandled touch events.
+            }
+        });
         int color = MediaRouterThemeHelper.getButtonTextColor(mContext);
         mDisconnectButton = (Button) findViewById(BUTTON_DISCONNECT_RES_ID);
         mDisconnectButton.setText(R.string.mr_controller_disconnect);
@@ -328,7 +345,7 @@
             }
         });
         mGroupListAnimationDurationMs = mContext.getResources().getInteger(
-                        R.integer.mr_controller_volume_group_list_animation_duration_ms);
+                R.integer.mr_controller_volume_group_list_animation_duration_ms);
 
         mCustomControlView = onCreateMediaControlView(savedInstanceState);
         if (mCustomControlView != null) {
@@ -481,14 +498,14 @@
             return;
         }
         // Measure the size of widgets and get the height of main components.
+        int oldHeight = getLayoutHeight(mMediaMainControlLayout);
+        setLayoutHeight(mMediaMainControlLayout, ViewGroup.LayoutParams.FILL_PARENT);
         updateMediaControlVisibility(isPlaybackControlAvailable());
-        int oldBottomMargin = getLayoutBottomMargin(mMediaMainControlLayout);
-        setLayoutBottomMargin(mMediaMainControlLayout, 0);
         View decorView = getWindow().getDecorView();
         decorView.measure(
                 MeasureSpec.makeMeasureSpec(getWindow().getAttributes().width, MeasureSpec.EXACTLY),
                 MeasureSpec.UNSPECIFIED);
-        setLayoutBottomMargin(mMediaMainControlLayout, oldBottomMargin);
+        setLayoutHeight(mMediaMainControlLayout, oldHeight);
         int artViewHeight = 0;
         if (mArtView.getDrawable() instanceof BitmapDrawable) {
             Bitmap art = ((BitmapDrawable) mArtView.getDrawable()).getBitmap();
@@ -527,7 +544,7 @@
         // Height of non-control views in decor view.
         // This includes title bar, button bar, and dialog's vertical padding which should be
         // always shown.
-        int nonControlViewHeight = decorView.getMeasuredHeight()
+        int nonControlViewHeight = mDialogAreaLayout.getMeasuredHeight()
                 - mDefaultControlLayout.getMeasuredHeight();
         // Maximum allowed height for controls to fit screen.
         int maximumControlViewHeight = visibleRect.height() - nonControlViewHeight;
@@ -537,10 +554,14 @@
             mArtView.setVisibility(View.VISIBLE);
             setLayoutHeight(mArtView, artViewHeight);
         } else {
+            if (getLayoutHeight(mVolumeGroupList) + mMediaMainControlLayout.getMeasuredHeight()
+                    >= mDefaultControlLayout.getMeasuredHeight()) {
+                mArtView.setVisibility(View.GONE);
+            }
             artViewHeight = 0;
             desiredControlLayoutHeight = visibleGroupListHeight + mainControllerHeight;
         }
-        // Show control if it fits the screen
+        // Show the playback control if it fits the screen.
         if (isPlaybackControlAvailable()
                 && desiredControlLayoutHeight <= maximumControlViewHeight) {
             mPlaybackControl.setVisibility(View.VISIBLE);
@@ -558,82 +579,40 @@
             visibleGroupListHeight -= (desiredControlLayoutHeight - maximumControlViewHeight);
             desiredControlLayoutHeight = maximumControlViewHeight;
         }
-        setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
-
-        // Animate the main control position if needed.
-        if (mVolumeGroupList.getVisibility() == View.VISIBLE
-                && mArtView.getVisibility() == View.VISIBLE && mIsGroupListAnimationNeeded) {
-            setLayoutHeight(mVolumeGroupList, mIsGroupExpanded ? expandedGroupListHeight
-                    : Math.min(mArtView.getHeight(), getLayoutHeight(mVolumeGroupList)));
-            updateMainControlBottomMargin(visibleGroupListHeight, mainControllerHeight,
-                    true /* animation */);
+        // Update the layouts with the computed heights.
+        mMediaMainControlLayout.clearAnimation();
+        mVolumeGroupList.clearAnimation();
+        mDefaultControlLayout.clearAnimation();
+        if (mIsGroupListAnimationNeeded) {
+            animateLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
+            animateLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
+            animateLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
         } else {
-            // Rely on AlertDialog's animation if there is no art work.
-            // TODO: Add group list animation even when there is no art work.
+            setLayoutHeight(mMediaMainControlLayout, mainControllerHeight);
             setLayoutHeight(mVolumeGroupList, visibleGroupListHeight);
-            updateMainControlBottomMargin(visibleGroupListHeight, mainControllerHeight,
-                    false /* animation */);
-            if (artViewHeight == 0) {
-                mArtView.setVisibility(View.GONE);
-            }
-            if (!mIsGroupExpanded) {
-                mVolumeGroupList.setVisibility(View.GONE);
-            }
+            setLayoutHeight(mDefaultControlLayout, desiredControlLayoutHeight);
         }
         mIsGroupListAnimationNeeded = false;
+        // Maximize the window size with a transparent layout in advance for smooth animation.
+        setLayoutHeight(mExpandableAreaLayout, visibleRect.height());
     }
 
-    private void updateMainControlBottomMargin(final int bottomMargin,
-            final int mainControllerHeight, boolean animation) {
-        final boolean isExpanding = bottomMargin != 0;
-        if (!animation) {
-            setLayoutBottomMargin(mMediaMainControlLayout, bottomMargin);
-            View frontView = isExpanding ? mVolumeGroupList : mArtView;
-            frontView.bringToFront();
-            ((View) frontView.getParent()).invalidate();
-        } else {
-            Animation existingAnim = mMediaMainControlLayout.getAnimation();
-            boolean animationInProgress = existingAnim != null && !existingAnim.hasEnded();
-            if (animationInProgress) {
-                mMediaMainControlLayout.clearAnimation();
+    private void animateLayoutHeight(final View view, int targetHeight) {
+        final int startValue = getLayoutHeight(view);
+        final int endValue = targetHeight;
+        Animation anim = new Animation() {
+            @Override
+            protected void applyTransformation(float interpolatedTime, Transformation t) {
+                int height = startValue - (int) ((startValue - endValue) * interpolatedTime);
+                setLayoutHeight(view, height);
             }
-            final int volumeGroupListHeight = getLayoutHeight(mVolumeGroupList);
-            int rightBelowArtWork = getLayoutHeight(mDefaultControlLayout)
-                    - mArtView.getHeight() - mainControllerHeight;
-            final int startValue = animationInProgress
-                    ? getLayoutBottomMargin(mMediaMainControlLayout)
-                    : isExpanding ? rightBelowArtWork : volumeGroupListHeight;
-            final int endValue = bottomMargin;
-            Animation anim = new Animation() {
-                private boolean mReordered;
-
-                @Override
-                protected void applyTransformation(float interpolatedTime, Transformation t) {
-                    int margin = startValue - (int) ((startValue - endValue) * interpolatedTime);
-                    setLayoutBottomMargin(mMediaMainControlLayout, margin);
-                    // Since there could be an overlapping area of the artwork and volume group list
-                    // , z-order of the art work and volume group list should be exchanged when the
-                    // main control covers the overlapping area.
-                    if (!mReordered) {
-                        if (isExpanding) {
-                            if (margin + mainControllerHeight >= volumeGroupListHeight) {
-                                mVolumeGroupList.bringToFront();
-                                ((View) mVolumeGroupList.getParent()).invalidate();
-                                mReordered = true;
-                            }
-                        } else {
-                            if (volumeGroupListHeight >= margin + mainControllerHeight) {
-                                mArtView.bringToFront();
-                                ((View) mArtView.getParent()).invalidate();
-                                mReordered = true;
-                            }
-                        }
-                    }
-                }
-            };
-            anim.setDuration(mGroupListAnimationDurationMs);
-            mMediaMainControlLayout.startAnimation(anim);
+        };
+        anim.setDuration(mGroupListAnimationDurationMs);
+        if (android.os.Build.VERSION.SDK_INT >= 21) {
+            anim.setInterpolator(mContext, mIsGroupExpanded ? R.interpolator.mr_linear_out_slow_in
+                    : R.interpolator.mr_fast_out_slow_in);
         }
+        view.startAnimation(anim);
     }
 
     private void updateVolumeControl() {
@@ -649,7 +628,7 @@
                     VolumeGroupAdapter adapter =
                             (VolumeGroupAdapter) mVolumeGroupList.getAdapter();
                     if (adapter != null) {
-                        adapter.notifyDataSetChanged();
+                       adapter.notifyDataSetChanged();
                     }
                 }
             } else {